mirror of
https://github.com/coleam00/Archon.git
synced 2025-12-24 02:39:17 -05:00
Updates to get Docker working and adding Claude OAUTH token variable, and finish of the style guide mockup.
This commit is contained in:
@@ -31,6 +31,9 @@ LOG_LEVEL=INFO
|
|||||||
# Get your API key from: https://console.anthropic.com/
|
# Get your API key from: https://console.anthropic.com/
|
||||||
# Required for the agent work orders service to execute Claude CLI commands
|
# Required for the agent work orders service to execute Claude CLI commands
|
||||||
ANTHROPIC_API_KEY=
|
ANTHROPIC_API_KEY=
|
||||||
|
# Generate an OAUTH token in terminal and it will use your Claude OAUTH token from your subscription.
|
||||||
|
CLAUDE_CODE_OAUTH_TOKEN=
|
||||||
|
|
||||||
|
|
||||||
# GitHub Personal Access Token (Required for Agent Work Orders PR creation)
|
# GitHub Personal Access Token (Required for Agent Work Orders PR creation)
|
||||||
# Get your token from: https://github.com/settings/tokens
|
# Get your token from: https://github.com/settings/tokens
|
||||||
@@ -55,7 +58,7 @@ ARCHON_DOCS_PORT=3838
|
|||||||
# Default: false (feature disabled)
|
# Default: false (feature disabled)
|
||||||
# Set to "true" to enable: ENABLE_AGENT_WORK_ORDERS=true
|
# Set to "true" to enable: ENABLE_AGENT_WORK_ORDERS=true
|
||||||
# When enabled, requires Claude API key and GitHub PAT (see above)
|
# When enabled, requires Claude API key and GitHub PAT (see above)
|
||||||
ENABLE_AGENT_WORK_ORDERS=false
|
ENABLE_AGENT_WORK_ORDERS=true
|
||||||
|
|
||||||
# Agent Work Orders Service Configuration (Optional)
|
# Agent Work Orders Service Configuration (Optional)
|
||||||
# Only needed if ENABLE_AGENT_WORK_ORDERS=true
|
# Only needed if ENABLE_AGENT_WORK_ORDERS=true
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { Button } from "@/features/ui/primitives/button";
|
|||||||
import { Card } from "@/features/ui/primitives/card";
|
import { Card } from "@/features/ui/primitives/card";
|
||||||
import { cn } from "@/features/ui/primitives/styles";
|
import { cn } from "@/features/ui/primitives/styles";
|
||||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/features/ui/primitives/tooltip";
|
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/features/ui/primitives/tooltip";
|
||||||
|
import { RealTimeStatsExample } from "./components/RealTimeStatsExample";
|
||||||
import { StepHistoryCard } from "./components/StepHistoryCard";
|
import { StepHistoryCard } from "./components/StepHistoryCard";
|
||||||
import { WorkflowStepButton } from "./components/WorkflowStepButton";
|
import { WorkflowStepButton } from "./components/WorkflowStepButton";
|
||||||
|
|
||||||
@@ -116,6 +117,9 @@ export const AgentWorkOrderExample = () => {
|
|||||||
collapsible history, and integrated document editing for human-in-the-loop approval.
|
collapsible history, and integrated document editing for human-in-the-loop approval.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
{/* Real-Time Execution Stats */}
|
||||||
|
<RealTimeStatsExample status="plan" stepNumber={2} />
|
||||||
|
|
||||||
{/* Workflow Progress Bar */}
|
{/* Workflow Progress Bar */}
|
||||||
<Card blur="md" transparency="light" edgePosition="top" edgeColor="cyan" size="lg" className="overflow-visible">
|
<Card blur="md" transparency="light" edgePosition="top" edgeColor="cyan" size="lg" className="overflow-visible">
|
||||||
<div className="flex items-center justify-between mb-6">
|
<div className="flex items-center justify-between mb-6">
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import {
|
import {
|
||||||
Activity,
|
Activity,
|
||||||
CheckCircle2,
|
CheckCircle2,
|
||||||
|
ChevronDown,
|
||||||
|
ChevronUp,
|
||||||
Clock,
|
Clock,
|
||||||
Copy,
|
Copy,
|
||||||
Eye,
|
Eye,
|
||||||
@@ -10,6 +12,7 @@ import {
|
|||||||
Pin,
|
Pin,
|
||||||
Play,
|
Play,
|
||||||
Plus,
|
Plus,
|
||||||
|
Search,
|
||||||
Trash2,
|
Trash2,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
@@ -24,6 +27,7 @@ import { SelectableCard } from "@/features/ui/primitives/selectable-card";
|
|||||||
import { cn } from "@/features/ui/primitives/styles";
|
import { cn } from "@/features/ui/primitives/styles";
|
||||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/features/ui/primitives/tooltip";
|
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/features/ui/primitives/tooltip";
|
||||||
import { AgentWorkOrderExample } from "./AgentWorkOrderExample";
|
import { AgentWorkOrderExample } from "./AgentWorkOrderExample";
|
||||||
|
import { RealTimeStatsExample } from "./components/RealTimeStatsExample";
|
||||||
|
|
||||||
const MOCK_REPOSITORIES = [
|
const MOCK_REPOSITORIES = [
|
||||||
{
|
{
|
||||||
@@ -69,7 +73,7 @@ interface WorkOrder {
|
|||||||
|
|
||||||
const MOCK_WORK_ORDERS: WorkOrder[] = [
|
const MOCK_WORK_ORDERS: WorkOrder[] = [
|
||||||
{
|
{
|
||||||
id: "wo-1",
|
id: "wo-1dc27d9e",
|
||||||
repositoryId: "1",
|
repositoryId: "1",
|
||||||
repositoryName: "archon-frontend",
|
repositoryName: "archon-frontend",
|
||||||
request: "Add dark mode toggle to settings page",
|
request: "Add dark mode toggle to settings page",
|
||||||
@@ -78,7 +82,7 @@ const MOCK_WORK_ORDERS: WorkOrder[] = [
|
|||||||
createdAt: "2024-01-15T10:30:00Z",
|
createdAt: "2024-01-15T10:30:00Z",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "wo-2",
|
id: "wo-2af8b3c1",
|
||||||
repositoryId: "1",
|
repositoryId: "1",
|
||||||
repositoryName: "archon-frontend",
|
repositoryName: "archon-frontend",
|
||||||
request: "Refactor navigation component to use new design system",
|
request: "Refactor navigation component to use new design system",
|
||||||
@@ -87,7 +91,7 @@ const MOCK_WORK_ORDERS: WorkOrder[] = [
|
|||||||
createdAt: "2024-01-15T09:15:00Z",
|
createdAt: "2024-01-15T09:15:00Z",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "wo-3",
|
id: "wo-4e372af3",
|
||||||
repositoryId: "2",
|
repositoryId: "2",
|
||||||
repositoryName: "archon-backend",
|
repositoryName: "archon-backend",
|
||||||
request: "Implement caching layer for API responses",
|
request: "Implement caching layer for API responses",
|
||||||
@@ -96,7 +100,7 @@ const MOCK_WORK_ORDERS: WorkOrder[] = [
|
|||||||
createdAt: "2024-01-14T16:45:00Z",
|
createdAt: "2024-01-14T16:45:00Z",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "wo-4",
|
id: "wo-8b91f2d6",
|
||||||
repositoryId: "2",
|
repositoryId: "2",
|
||||||
repositoryName: "archon-backend",
|
repositoryName: "archon-backend",
|
||||||
request: "Add rate limiting to authentication endpoints",
|
request: "Add rate limiting to authentication endpoints",
|
||||||
@@ -105,7 +109,7 @@ const MOCK_WORK_ORDERS: WorkOrder[] = [
|
|||||||
createdAt: "2024-01-14T14:20:00Z",
|
createdAt: "2024-01-14T14:20:00Z",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "wo-5",
|
id: "wo-5c7d4a89",
|
||||||
repositoryId: "1",
|
repositoryId: "1",
|
||||||
repositoryName: "archon-frontend",
|
repositoryName: "archon-frontend",
|
||||||
request: "Fix responsive layout issues on mobile devices",
|
request: "Fix responsive layout issues on mobile devices",
|
||||||
@@ -114,7 +118,7 @@ const MOCK_WORK_ORDERS: WorkOrder[] = [
|
|||||||
createdAt: "2024-01-13T11:00:00Z",
|
createdAt: "2024-01-13T11:00:00Z",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "wo-6",
|
id: "wo-9f3e1b5a",
|
||||||
repositoryId: "3",
|
repositoryId: "3",
|
||||||
repositoryName: "archon-docs",
|
repositoryName: "archon-docs",
|
||||||
request: "Update API documentation with new endpoints",
|
request: "Update API documentation with new endpoints",
|
||||||
@@ -126,7 +130,7 @@ const MOCK_WORK_ORDERS: WorkOrder[] = [
|
|||||||
|
|
||||||
export const AgentWorkOrderLayoutExample = () => {
|
export const AgentWorkOrderLayoutExample = () => {
|
||||||
const [selectedRepositoryId, setSelectedRepositoryId] = useState("1");
|
const [selectedRepositoryId, setSelectedRepositoryId] = useState("1");
|
||||||
const [layoutMode, setLayoutMode] = useState<"horizontal" | "sidebar">("horizontal");
|
const [layoutMode, setLayoutMode] = useState<"horizontal" | "sidebar">("sidebar");
|
||||||
const [sidebarExpanded, setSidebarExpanded] = useState(true);
|
const [sidebarExpanded, setSidebarExpanded] = useState(true);
|
||||||
const [showAddRepoModal, setShowAddRepoModal] = useState(false);
|
const [showAddRepoModal, setShowAddRepoModal] = useState(false);
|
||||||
const [showNewWorkOrderModal, setShowNewWorkOrderModal] = useState(false);
|
const [showNewWorkOrderModal, setShowNewWorkOrderModal] = useState(false);
|
||||||
@@ -134,6 +138,7 @@ export const AgentWorkOrderLayoutExample = () => {
|
|||||||
const [activeTab, setActiveTab] = useState<string>("all");
|
const [activeTab, setActiveTab] = useState<string>("all");
|
||||||
const [showDetailView, setShowDetailView] = useState(false);
|
const [showDetailView, setShowDetailView] = useState(false);
|
||||||
const [selectedWorkOrderId, setSelectedWorkOrderId] = useState<string | null>(null);
|
const [selectedWorkOrderId, setSelectedWorkOrderId] = useState<string | null>(null);
|
||||||
|
const [searchQuery, setSearchQuery] = useState("");
|
||||||
|
|
||||||
const selectedRepository = MOCK_REPOSITORIES.find((r) => r.id === selectedRepositoryId);
|
const selectedRepository = MOCK_REPOSITORIES.find((r) => r.id === selectedRepositoryId);
|
||||||
const selectedWorkOrder = workOrders.find((wo) => wo.id === selectedWorkOrderId);
|
const selectedWorkOrder = workOrders.find((wo) => wo.id === selectedWorkOrderId);
|
||||||
@@ -183,9 +188,42 @@ export const AgentWorkOrderLayoutExample = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
{/* Layout Mode Toggle */}
|
{/* Header Section */}
|
||||||
<div className="flex justify-end">
|
<div className="flex items-center justify-between gap-4">
|
||||||
<div className="flex gap-1 p-1 bg-black/30 rounded-lg border border-white/10">
|
{/* Title */}
|
||||||
|
<h1 className="text-2xl font-bold text-gray-900 dark:text-white">Agent Work Orders</h1>
|
||||||
|
|
||||||
|
{/* Search Bar */}
|
||||||
|
<div className="relative flex-1 max-w-md">
|
||||||
|
<Search
|
||||||
|
className="absolute left-3 top-1/2 transform -translate-y-1/2 w-4 h-4 text-gray-400 dark:text-gray-500"
|
||||||
|
aria-hidden="true"
|
||||||
|
/>
|
||||||
|
<Input
|
||||||
|
type="text"
|
||||||
|
placeholder="Search repositories..."
|
||||||
|
value={searchQuery}
|
||||||
|
onChange={(e) => setSearchQuery(e.target.value)}
|
||||||
|
className="pl-10"
|
||||||
|
aria-label="Search repositories"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* View Toggle - Sidebar is default/primary */}
|
||||||
|
<div className="flex gap-1 p-1 bg-black/30 dark:bg-white/10 rounded-lg border border-white/10 dark:border-gray-700">
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="sm"
|
||||||
|
onClick={() => setLayoutMode("sidebar")}
|
||||||
|
className={cn(
|
||||||
|
"px-3",
|
||||||
|
layoutMode === "sidebar" && "bg-purple-500/20 dark:bg-purple-500/30 text-purple-400 dark:text-purple-300",
|
||||||
|
)}
|
||||||
|
aria-label="Switch to sidebar layout"
|
||||||
|
aria-pressed={layoutMode === "sidebar"}
|
||||||
|
>
|
||||||
|
<List className="w-4 h-4" aria-hidden="true" />
|
||||||
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="sm"
|
size="sm"
|
||||||
@@ -200,22 +238,18 @@ export const AgentWorkOrderLayoutExample = () => {
|
|||||||
>
|
>
|
||||||
<LayoutGrid className="w-4 h-4" aria-hidden="true" />
|
<LayoutGrid className="w-4 h-4" aria-hidden="true" />
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
|
||||||
variant="ghost"
|
|
||||||
size="sm"
|
|
||||||
onClick={() => setLayoutMode("sidebar")}
|
|
||||||
className={cn(
|
|
||||||
"px-3",
|
|
||||||
layoutMode === "sidebar" && "bg-purple-500/20 dark:bg-purple-500/30 text-purple-400 dark:text-purple-300",
|
|
||||||
)}
|
|
||||||
aria-label="Switch to sidebar layout"
|
|
||||||
aria-pressed={layoutMode === "sidebar"}
|
|
||||||
>
|
|
||||||
<List className="w-4 h-4" aria-hidden="true" />
|
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* New Repo Button */}
|
||||||
|
<Button variant="cyan" onClick={() => setShowAddRepoModal(true)} aria-label="Add new repository">
|
||||||
|
<Plus className="w-4 h-4 mr-2" aria-hidden="true" />
|
||||||
|
New Repo
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Add Repository Modal */}
|
||||||
|
<AddRepositoryModal open={showAddRepoModal} onOpenChange={setShowAddRepoModal} />
|
||||||
|
|
||||||
{layoutMode === "horizontal" ? (
|
{layoutMode === "horizontal" ? (
|
||||||
<>
|
<>
|
||||||
{/* Horizontal Repository Cards - ONLY cards scroll, not whole page */}
|
{/* Horizontal Repository Cards - ONLY cards scroll, not whole page */}
|
||||||
@@ -233,8 +267,6 @@ export const AgentWorkOrderLayoutExample = () => {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
{/* Add Repository Button */}
|
|
||||||
<AddRepositoryModal open={showAddRepoModal} onOpenChange={setShowAddRepoModal} />
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -756,8 +788,9 @@ const WorkOrdersTableView = ({
|
|||||||
<thead>
|
<thead>
|
||||||
<tr className="bg-gradient-to-r from-gray-50 to-gray-100 dark:from-gray-900 dark:to-gray-800 border-b-2 border-gray-200 dark:border-gray-700">
|
<tr className="bg-gradient-to-r from-gray-50 to-gray-100 dark:from-gray-900 dark:to-gray-800 border-b-2 border-gray-200 dark:border-gray-700">
|
||||||
<th className="w-12" aria-label="Status indicator" />
|
<th className="w-12" aria-label="Status indicator" />
|
||||||
<th className="px-4 py-3 text-left text-sm font-medium text-gray-700 dark:text-gray-300">
|
<th className="px-4 py-3 text-left text-sm font-medium text-gray-700 dark:text-gray-300">WO ID</th>
|
||||||
Work Order ID
|
<th className="px-4 py-3 text-left text-sm font-medium text-gray-700 dark:text-gray-300 w-40">
|
||||||
|
Repository
|
||||||
</th>
|
</th>
|
||||||
<th className="px-4 py-3 text-left text-sm font-medium text-gray-700 dark:text-gray-300">
|
<th className="px-4 py-3 text-left text-sm font-medium text-gray-700 dark:text-gray-300">
|
||||||
Request Summary
|
Request Summary
|
||||||
@@ -783,7 +816,7 @@ const WorkOrdersTableView = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Work Order Row with status-based styling
|
// Work Order Row with status-based styling and expandable real-time stats
|
||||||
const WorkOrderRow = ({
|
const WorkOrderRow = ({
|
||||||
workOrder,
|
workOrder,
|
||||||
index,
|
index,
|
||||||
@@ -795,103 +828,165 @@ const WorkOrderRow = ({
|
|||||||
onStart: () => void;
|
onStart: () => void;
|
||||||
onViewDetails: () => void;
|
onViewDetails: () => void;
|
||||||
}) => {
|
}) => {
|
||||||
|
const [isExpanded, setIsExpanded] = useState(false);
|
||||||
|
|
||||||
// Status colors - STATIC lookup with all properties
|
// Status colors - STATIC lookup with all properties
|
||||||
const statusColors: Record<
|
const statusColors: Record<
|
||||||
WorkOrderStatus,
|
WorkOrderStatus,
|
||||||
{ color: "pink" | "cyan" | "blue" | "orange" | "purple" | "green"; edge: string; glow: string; label: string }
|
{
|
||||||
|
color: "pink" | "cyan" | "blue" | "orange" | "purple" | "green";
|
||||||
|
edge: string;
|
||||||
|
glow: string;
|
||||||
|
label: string;
|
||||||
|
stepNumber: number;
|
||||||
|
}
|
||||||
> = {
|
> = {
|
||||||
pending: {
|
pending: {
|
||||||
color: "pink",
|
color: "pink",
|
||||||
edge: "bg-pink-500",
|
edge: "bg-pink-500",
|
||||||
glow: "rgba(236,72,153,0.5)",
|
glow: "rgba(236,72,153,0.5)",
|
||||||
label: "Pending",
|
label: "Pending",
|
||||||
|
stepNumber: 0,
|
||||||
},
|
},
|
||||||
create_branch: {
|
create_branch: {
|
||||||
color: "cyan",
|
color: "cyan",
|
||||||
edge: "bg-cyan-500",
|
edge: "bg-cyan-500",
|
||||||
glow: "rgba(34,211,238,0.5)",
|
glow: "rgba(34,211,238,0.5)",
|
||||||
label: "+ Branch",
|
label: "+ Branch",
|
||||||
|
stepNumber: 1,
|
||||||
},
|
},
|
||||||
plan: {
|
plan: {
|
||||||
color: "blue",
|
color: "blue",
|
||||||
edge: "bg-blue-500",
|
edge: "bg-blue-500",
|
||||||
glow: "rgba(59,130,246,0.5)",
|
glow: "rgba(59,130,246,0.5)",
|
||||||
label: "Planning",
|
label: "Planning",
|
||||||
|
stepNumber: 2,
|
||||||
},
|
},
|
||||||
execute: {
|
execute: {
|
||||||
color: "orange",
|
color: "orange",
|
||||||
edge: "bg-orange-500",
|
edge: "bg-orange-500",
|
||||||
glow: "rgba(249,115,22,0.5)",
|
glow: "rgba(249,115,22,0.5)",
|
||||||
label: "Executing",
|
label: "Executing",
|
||||||
|
stepNumber: 3,
|
||||||
},
|
},
|
||||||
commit: {
|
commit: {
|
||||||
color: "purple",
|
color: "purple",
|
||||||
edge: "bg-purple-500",
|
edge: "bg-purple-500",
|
||||||
glow: "rgba(168,85,247,0.5)",
|
glow: "rgba(168,85,247,0.5)",
|
||||||
label: "Commit",
|
label: "Commit",
|
||||||
|
stepNumber: 4,
|
||||||
},
|
},
|
||||||
create_pr: {
|
create_pr: {
|
||||||
color: "green",
|
color: "green",
|
||||||
edge: "bg-green-500",
|
edge: "bg-green-500",
|
||||||
glow: "rgba(34,197,94,0.5)",
|
glow: "rgba(34,197,94,0.5)",
|
||||||
label: "Create PR",
|
label: "Create PR",
|
||||||
|
stepNumber: 5,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const colors = statusColors[workOrder.status];
|
const colors = statusColors[workOrder.status];
|
||||||
|
const canExpand = workOrder.status !== "pending";
|
||||||
|
|
||||||
|
const handleStart = () => {
|
||||||
|
setIsExpanded(true); // Auto-expand when started
|
||||||
|
onStart();
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<tr
|
<>
|
||||||
className={cn(
|
<tr
|
||||||
"group transition-all duration-200",
|
className={cn(
|
||||||
index % 2 === 0 ? "bg-white/50 dark:bg-black/50" : "bg-gray-50/80 dark:bg-gray-900/30",
|
"group transition-all duration-200",
|
||||||
"hover:bg-gradient-to-r hover:from-cyan-50/70 hover:to-purple-50/70 dark:hover:from-cyan-900/20 dark:hover:to-purple-900/20",
|
index % 2 === 0 ? "bg-white/50 dark:bg-black/50" : "bg-gray-50/80 dark:bg-gray-900/30",
|
||||||
"border-b border-gray-200 dark:border-gray-800",
|
"hover:bg-gradient-to-r hover:from-cyan-50/70 hover:to-purple-50/70 dark:hover:from-cyan-900/20 dark:hover:to-purple-900/20",
|
||||||
)}
|
"border-b border-gray-200 dark:border-gray-800",
|
||||||
>
|
|
||||||
{/* Status indicator - glowing circle */}
|
|
||||||
<td className="px-3 py-2 w-12">
|
|
||||||
<div className="flex items-center justify-center">
|
|
||||||
<div className={cn("w-3 h-3 rounded-full", colors.edge)} style={{ boxShadow: `0 0 8px ${colors.glow}` }} />
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
|
|
||||||
{/* Work Order ID */}
|
|
||||||
<td className="px-4 py-2">
|
|
||||||
<span className="font-mono text-sm text-gray-700 dark:text-gray-300">{workOrder.id}</span>
|
|
||||||
</td>
|
|
||||||
|
|
||||||
{/* Request Summary */}
|
|
||||||
<td className="px-4 py-2">
|
|
||||||
<p className="text-sm text-gray-900 dark:text-white line-clamp-2">{workOrder.request}</p>
|
|
||||||
</td>
|
|
||||||
|
|
||||||
{/* Status Badge - using StatPill */}
|
|
||||||
<td className="px-4 py-2 w-32">
|
|
||||||
<StatPill color={colors.color} value={colors.label} size="sm" />
|
|
||||||
</td>
|
|
||||||
|
|
||||||
{/* Actions */}
|
|
||||||
<td className="px-4 py-2 w-32">
|
|
||||||
{workOrder.status === "pending" ? (
|
|
||||||
<Button onClick={onStart} size="xs" variant="green" className="w-full text-xs" aria-label="Start work order">
|
|
||||||
<Play className="w-3 h-3 mr-1" aria-hidden="true" />
|
|
||||||
Start
|
|
||||||
</Button>
|
|
||||||
) : (
|
|
||||||
<Button
|
|
||||||
onClick={onViewDetails}
|
|
||||||
size="xs"
|
|
||||||
variant="blue"
|
|
||||||
className="w-full text-xs"
|
|
||||||
aria-label="Observe work order details"
|
|
||||||
>
|
|
||||||
<Eye className="w-3 h-3 mr-1" aria-hidden="true" />
|
|
||||||
Observe
|
|
||||||
</Button>
|
|
||||||
)}
|
)}
|
||||||
</td>
|
>
|
||||||
</tr>
|
{/* Status indicator - glowing circle with optional collapse button */}
|
||||||
|
<td className="px-3 py-2 w-12">
|
||||||
|
<div className="flex items-center justify-center gap-1">
|
||||||
|
{canExpand && (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => setIsExpanded(!isExpanded)}
|
||||||
|
className="p-0.5 hover:bg-gray-200 dark:hover:bg-gray-700 rounded transition-colors"
|
||||||
|
aria-label={isExpanded ? "Collapse details" : "Expand details"}
|
||||||
|
aria-expanded={isExpanded}
|
||||||
|
>
|
||||||
|
{isExpanded ? (
|
||||||
|
<ChevronUp className="w-3 h-3 text-gray-600 dark:text-gray-400" aria-hidden="true" />
|
||||||
|
) : (
|
||||||
|
<ChevronDown className="w-3 h-3 text-gray-600 dark:text-gray-400" aria-hidden="true" />
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
<div className={cn("w-3 h-3 rounded-full", colors.edge)} style={{ boxShadow: `0 0 8px ${colors.glow}` }} />
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
{/* Work Order ID */}
|
||||||
|
<td className="px-4 py-2">
|
||||||
|
<span className="font-mono text-sm text-gray-700 dark:text-gray-300">{workOrder.id}</span>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
{/* Repository */}
|
||||||
|
<td className="px-4 py-2 w-40">
|
||||||
|
<span className="text-sm text-gray-900 dark:text-white">{workOrder.repositoryName}</span>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
{/* Request Summary */}
|
||||||
|
<td className="px-4 py-2">
|
||||||
|
<p className="text-sm text-gray-900 dark:text-white line-clamp-2">{workOrder.request}</p>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
{/* Status Badge - using StatPill */}
|
||||||
|
<td className="px-4 py-2 w-32">
|
||||||
|
<StatPill color={colors.color} value={colors.label} size="sm" />
|
||||||
|
</td>
|
||||||
|
|
||||||
|
{/* Actions */}
|
||||||
|
<td className="px-4 py-2 w-32">
|
||||||
|
{workOrder.status === "pending" ? (
|
||||||
|
<Button
|
||||||
|
onClick={handleStart}
|
||||||
|
size="xs"
|
||||||
|
variant="green"
|
||||||
|
className="w-full text-xs"
|
||||||
|
aria-label="Start work order"
|
||||||
|
>
|
||||||
|
<Play className="w-3 h-3 mr-1" aria-hidden="true" />
|
||||||
|
Start
|
||||||
|
</Button>
|
||||||
|
) : (
|
||||||
|
<Button
|
||||||
|
onClick={onViewDetails}
|
||||||
|
size="xs"
|
||||||
|
variant="blue"
|
||||||
|
className="w-full text-xs"
|
||||||
|
aria-label="View work order details"
|
||||||
|
>
|
||||||
|
<Eye className="w-3 h-3 mr-1" aria-hidden="true" />
|
||||||
|
Details
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
{/* Expanded row with real-time stats */}
|
||||||
|
{isExpanded && canExpand && (
|
||||||
|
<tr
|
||||||
|
className={cn(
|
||||||
|
index % 2 === 0 ? "bg-white/50 dark:bg-black/50" : "bg-gray-50/80 dark:bg-gray-900/30",
|
||||||
|
"border-b border-gray-200 dark:border-gray-800",
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<td colSpan={6} className="px-4 py-4">
|
||||||
|
<RealTimeStatsExample status={workOrder.status} stepNumber={colors.stepNumber} />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -926,23 +1021,6 @@ const AddRepositoryModal = ({ open, onOpenChange }: { open: boolean; onOpenChang
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||||
<DialogTrigger asChild>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
className={cn(
|
|
||||||
"w-72 min-h-[180px] flex flex-col items-center justify-center shrink-0",
|
|
||||||
"rounded-lg border-2 border-dashed border-gray-300 dark:border-gray-700",
|
|
||||||
"hover:border-cyan-400 dark:hover:border-cyan-500",
|
|
||||||
"transition-colors duration-200",
|
|
||||||
"bg-white/30 dark:bg-black/20",
|
|
||||||
"backdrop-blur-sm",
|
|
||||||
)}
|
|
||||||
aria-label="Add repository"
|
|
||||||
>
|
|
||||||
<Plus className="w-8 h-8 text-gray-400 dark:text-gray-500 mb-2" aria-hidden="true" />
|
|
||||||
<span className="text-sm font-medium text-gray-600 dark:text-gray-400">Add Repository</span>
|
|
||||||
</button>
|
|
||||||
</DialogTrigger>
|
|
||||||
<DialogContent>
|
<DialogContent>
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle>Add Repository</DialogTitle>
|
<DialogTitle>Add Repository</DialogTitle>
|
||||||
|
|||||||
@@ -0,0 +1,212 @@
|
|||||||
|
import { Trash2 } from "lucide-react";
|
||||||
|
import { useState } from "react";
|
||||||
|
import { Button } from "@/features/ui/primitives/button";
|
||||||
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/features/ui/primitives/select";
|
||||||
|
import { cn } from "@/features/ui/primitives/styles";
|
||||||
|
import { Switch } from "@/features/ui/primitives/switch";
|
||||||
|
|
||||||
|
interface ExecutionLogsExampleProps {
|
||||||
|
/** Work order status to generate appropriate mock logs */
|
||||||
|
status: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface MockLog {
|
||||||
|
timestamp: string;
|
||||||
|
level: "info" | "warning" | "error" | "debug";
|
||||||
|
event: string;
|
||||||
|
step?: string;
|
||||||
|
progress?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get color class for log level badge - STATIC lookup
|
||||||
|
*/
|
||||||
|
const logLevelColors: Record<string, string> = {
|
||||||
|
info: "bg-blue-500/20 text-blue-600 dark:text-blue-400 border-blue-400/30",
|
||||||
|
warning: "bg-yellow-500/20 text-yellow-600 dark:text-yellow-400 border-yellow-400/30",
|
||||||
|
error: "bg-red-500/20 text-red-600 dark:text-red-400 border-red-400/30",
|
||||||
|
debug: "bg-gray-500/20 text-gray-600 dark:text-gray-400 border-gray-400/30",
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format timestamp to relative time
|
||||||
|
*/
|
||||||
|
function formatRelativeTime(timestamp: string): string {
|
||||||
|
const now = Date.now();
|
||||||
|
const logTime = new Date(timestamp).getTime();
|
||||||
|
const diffSeconds = Math.floor((now - logTime) / 1000);
|
||||||
|
|
||||||
|
if (diffSeconds < 60) return `${diffSeconds}s ago`;
|
||||||
|
if (diffSeconds < 3600) return `${Math.floor(diffSeconds / 60)}m ago`;
|
||||||
|
return `${Math.floor(diffSeconds / 3600)}h ago`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Individual log entry component
|
||||||
|
*/
|
||||||
|
function LogEntryRow({ log }: { log: MockLog }) {
|
||||||
|
const colorClass = logLevelColors[log.level] || logLevelColors.debug;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex items-start gap-2 py-1 px-2 hover:bg-white/5 dark:hover:bg-black/20 rounded font-mono text-sm">
|
||||||
|
<span className="text-gray-500 dark:text-gray-400 text-xs whitespace-nowrap">
|
||||||
|
{formatRelativeTime(log.timestamp)}
|
||||||
|
</span>
|
||||||
|
<span className={cn("px-1.5 py-0.5 rounded text-xs border uppercase whitespace-nowrap", colorClass)}>
|
||||||
|
{log.level}
|
||||||
|
</span>
|
||||||
|
{log.step && <span className="text-cyan-600 dark:text-cyan-400 text-xs whitespace-nowrap">[{log.step}]</span>}
|
||||||
|
<span className="text-gray-900 dark:text-gray-300 flex-1">{log.event}</span>
|
||||||
|
{log.progress && (
|
||||||
|
<span className="text-gray-500 dark:text-gray-400 text-xs whitespace-nowrap">{log.progress}</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ExecutionLogsExample({ status }: ExecutionLogsExampleProps) {
|
||||||
|
const [autoScroll, setAutoScroll] = useState(true);
|
||||||
|
const [levelFilter, setLevelFilter] = useState<string>("all");
|
||||||
|
|
||||||
|
// Generate mock logs based on status
|
||||||
|
const generateMockLogs = (): MockLog[] => {
|
||||||
|
const now = Date.now();
|
||||||
|
const baseTime = now - 300000; // 5 minutes ago
|
||||||
|
|
||||||
|
const logs: MockLog[] = [
|
||||||
|
{ timestamp: new Date(baseTime).toISOString(), level: "info", event: "workflow_started" },
|
||||||
|
{ timestamp: new Date(baseTime + 1000).toISOString(), level: "info", event: "sandbox_setup_started" },
|
||||||
|
{
|
||||||
|
timestamp: new Date(baseTime + 3000).toISOString(),
|
||||||
|
level: "info",
|
||||||
|
event: "repository_cloned",
|
||||||
|
step: "setup",
|
||||||
|
},
|
||||||
|
{ timestamp: new Date(baseTime + 5000).toISOString(), level: "info", event: "sandbox_setup_completed" },
|
||||||
|
];
|
||||||
|
|
||||||
|
if (status !== "pending") {
|
||||||
|
logs.push(
|
||||||
|
{
|
||||||
|
timestamp: new Date(baseTime + 10000).toISOString(),
|
||||||
|
level: "info",
|
||||||
|
event: "step_started",
|
||||||
|
step: "create-branch",
|
||||||
|
progress: "1/5",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
timestamp: new Date(baseTime + 12000).toISOString(),
|
||||||
|
level: "info",
|
||||||
|
event: "agent_command_started",
|
||||||
|
step: "create-branch",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
timestamp: new Date(baseTime + 45000).toISOString(),
|
||||||
|
level: "info",
|
||||||
|
event: "branch_created",
|
||||||
|
step: "create-branch",
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status === "plan" || status === "execute" || status === "commit" || status === "create_pr") {
|
||||||
|
logs.push(
|
||||||
|
{
|
||||||
|
timestamp: new Date(baseTime + 60000).toISOString(),
|
||||||
|
level: "info",
|
||||||
|
event: "step_started",
|
||||||
|
step: "planning",
|
||||||
|
progress: "2/5",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
timestamp: new Date(baseTime + 120000).toISOString(),
|
||||||
|
level: "debug",
|
||||||
|
event: "analyzing_codebase",
|
||||||
|
step: "planning",
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return logs;
|
||||||
|
};
|
||||||
|
|
||||||
|
const mockLogs = generateMockLogs();
|
||||||
|
const filteredLogs = levelFilter === "all" ? mockLogs : mockLogs.filter((log) => log.level === levelFilter);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="border border-white/10 dark:border-gray-700/30 rounded-lg overflow-hidden bg-black/20 dark:bg-white/5 backdrop-blur">
|
||||||
|
{/* Header with controls */}
|
||||||
|
<div className="flex items-center justify-between px-4 py-3 border-b border-white/10 dark:border-gray-700/30 bg-gray-900/50 dark:bg-gray-800/30">
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<span className="font-semibold text-gray-900 dark:text-gray-300">Execution Logs</span>
|
||||||
|
|
||||||
|
{/* Live indicator */}
|
||||||
|
<div className="flex items-center gap-1">
|
||||||
|
<div className="w-2 h-2 bg-green-500 rounded-full animate-pulse" />
|
||||||
|
<span className="text-xs text-green-600 dark:text-green-400">Live</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<span className="text-xs text-gray-500 dark:text-gray-400">({filteredLogs.length} entries)</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Controls */}
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
{/* Level filter using proper Select primitive */}
|
||||||
|
<Select value={levelFilter} onValueChange={setLevelFilter}>
|
||||||
|
<SelectTrigger className="w-32 h-8 text-xs" aria-label="Filter log level">
|
||||||
|
<SelectValue />
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
<SelectItem value="all">All Levels</SelectItem>
|
||||||
|
<SelectItem value="info">Info</SelectItem>
|
||||||
|
<SelectItem value="warning">Warning</SelectItem>
|
||||||
|
<SelectItem value="error">Error</SelectItem>
|
||||||
|
<SelectItem value="debug">Debug</SelectItem>
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
|
||||||
|
{/* Auto-scroll toggle using Switch primitive */}
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<label htmlFor="auto-scroll-toggle" className="text-xs text-gray-700 dark:text-gray-300">
|
||||||
|
Auto-scroll:
|
||||||
|
</label>
|
||||||
|
<Switch
|
||||||
|
id="auto-scroll-toggle"
|
||||||
|
checked={autoScroll}
|
||||||
|
onCheckedChange={setAutoScroll}
|
||||||
|
aria-label="Toggle auto-scroll"
|
||||||
|
/>
|
||||||
|
<span
|
||||||
|
className={cn(
|
||||||
|
"text-xs font-medium",
|
||||||
|
autoScroll ? "text-cyan-600 dark:text-cyan-400" : "text-gray-500 dark:text-gray-400",
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{autoScroll ? "ON" : "OFF"}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Clear logs button */}
|
||||||
|
<Button variant="ghost" size="xs" aria-label="Clear logs">
|
||||||
|
<Trash2 className="w-3 h-3" aria-hidden="true" />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Log content - scrollable area */}
|
||||||
|
<div className="max-h-96 overflow-y-auto bg-black/40 dark:bg-black/20">
|
||||||
|
{filteredLogs.length === 0 ? (
|
||||||
|
<div className="flex flex-col items-center justify-center py-12 text-gray-500 dark:text-gray-400">
|
||||||
|
<p>No logs match the current filter</p>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="p-2">
|
||||||
|
{filteredLogs.map((log, index) => (
|
||||||
|
<LogEntryRow key={`${log.timestamp}-${index}`} log={log} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,151 @@
|
|||||||
|
import { Activity, ChevronDown, ChevronUp, Clock, TrendingUp } from "lucide-react";
|
||||||
|
import { useState } from "react";
|
||||||
|
import { Button } from "@/features/ui/primitives/button";
|
||||||
|
import { ExecutionLogsExample } from "./ExecutionLogsExample";
|
||||||
|
|
||||||
|
interface RealTimeStatsExampleProps {
|
||||||
|
/** Work order status for determining progress */
|
||||||
|
status: string;
|
||||||
|
/** Step number (1-5) */
|
||||||
|
stepNumber: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format elapsed seconds to human-readable duration
|
||||||
|
*/
|
||||||
|
function formatDuration(seconds: number): string {
|
||||||
|
const hours = Math.floor(seconds / 3600);
|
||||||
|
const minutes = Math.floor((seconds % 3600) / 60);
|
||||||
|
const secs = seconds % 60;
|
||||||
|
|
||||||
|
if (hours > 0) {
|
||||||
|
return `${hours}h ${minutes}m ${secs}s`;
|
||||||
|
}
|
||||||
|
if (minutes > 0) {
|
||||||
|
return `${minutes}m ${secs}s`;
|
||||||
|
}
|
||||||
|
return `${secs}s`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function RealTimeStatsExample({ status, stepNumber }: RealTimeStatsExampleProps) {
|
||||||
|
const [showLogs, setShowLogs] = useState(false);
|
||||||
|
|
||||||
|
// Mock data based on status
|
||||||
|
const stepNames: Record<string, string> = {
|
||||||
|
create_branch: "create-branch",
|
||||||
|
plan: "planning",
|
||||||
|
execute: "execute",
|
||||||
|
commit: "commit",
|
||||||
|
create_pr: "create-pr",
|
||||||
|
};
|
||||||
|
|
||||||
|
const currentStep = stepNames[status] || "initializing";
|
||||||
|
const progressPct = (stepNumber / 5) * 100;
|
||||||
|
const mockElapsedSeconds = stepNumber * 120; // 2 minutes per step
|
||||||
|
|
||||||
|
const activities: Record<string, string> = {
|
||||||
|
create_branch: "Creating new branch for work order...",
|
||||||
|
plan: "Analyzing codebase and generating implementation plan...",
|
||||||
|
execute: "Writing code and applying changes...",
|
||||||
|
commit: "Committing changes to branch...",
|
||||||
|
create_pr: "Creating pull request on GitHub...",
|
||||||
|
};
|
||||||
|
|
||||||
|
const currentActivity = activities[status] || "Initializing workflow...";
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="space-y-3">
|
||||||
|
<div className="border border-white/10 dark:border-gray-700/30 rounded-lg p-4 bg-black/20 dark:bg-white/5 backdrop-blur">
|
||||||
|
<h3 className="text-sm font-semibold text-gray-900 dark:text-gray-300 mb-3 flex items-center gap-2">
|
||||||
|
<Activity className="w-4 h-4" aria-hidden="true" />
|
||||||
|
Real-Time Execution
|
||||||
|
</h3>
|
||||||
|
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||||
|
{/* Current Step */}
|
||||||
|
<div className="space-y-1">
|
||||||
|
<div className="text-xs text-gray-500 dark:text-gray-400 uppercase tracking-wide">Current Step</div>
|
||||||
|
<div className="text-sm font-medium text-gray-900 dark:text-gray-200">
|
||||||
|
{currentStep}
|
||||||
|
<span className="text-gray-500 dark:text-gray-400 ml-2">({stepNumber}/5)</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Progress */}
|
||||||
|
<div className="space-y-1">
|
||||||
|
<div className="text-xs text-gray-500 dark:text-gray-400 uppercase tracking-wide flex items-center gap-1">
|
||||||
|
<TrendingUp className="w-3 h-3" aria-hidden="true" />
|
||||||
|
Progress
|
||||||
|
</div>
|
||||||
|
<div className="space-y-1">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<div className="flex-1 h-2 bg-gray-700 dark:bg-gray-200/20 rounded-full overflow-hidden">
|
||||||
|
<div
|
||||||
|
className="h-full bg-gradient-to-r from-cyan-500 to-blue-500 transition-all duration-500 ease-out"
|
||||||
|
style={{ width: `${progressPct}%` }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<span className="text-sm font-medium text-cyan-600 dark:text-cyan-400">{progressPct}%</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Elapsed Time */}
|
||||||
|
<div className="space-y-1">
|
||||||
|
<div className="text-xs text-gray-500 dark:text-gray-400 uppercase tracking-wide flex items-center gap-1">
|
||||||
|
<Clock className="w-3 h-3" aria-hidden="true" />
|
||||||
|
Elapsed Time
|
||||||
|
</div>
|
||||||
|
<div className="text-sm font-medium text-gray-900 dark:text-gray-200">
|
||||||
|
{formatDuration(mockElapsedSeconds)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Latest Activity with Status Indicator - at top */}
|
||||||
|
<div className="mt-4 pt-3 border-t border-white/10 dark:border-gray-700/30">
|
||||||
|
<div className="flex items-center justify-between gap-4">
|
||||||
|
<div className="flex items-start gap-2 flex-1 min-w-0">
|
||||||
|
<div className="text-xs text-gray-500 dark:text-gray-400 uppercase tracking-wide whitespace-nowrap">
|
||||||
|
Latest Activity:
|
||||||
|
</div>
|
||||||
|
<div className="text-sm text-gray-900 dark:text-gray-300 flex-1 truncate">{currentActivity}</div>
|
||||||
|
</div>
|
||||||
|
{/* Status Indicator - right side of Latest Activity */}
|
||||||
|
<div className="flex items-center gap-1 text-xs text-blue-600 dark:text-blue-400 flex-shrink-0">
|
||||||
|
<div className="w-2 h-2 bg-blue-500 rounded-full animate-pulse" />
|
||||||
|
<span>Running</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Show Execution Logs button - at bottom */}
|
||||||
|
<div className="mt-3 pt-3 border-t border-white/10 dark:border-gray-700/30">
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="sm"
|
||||||
|
onClick={() => setShowLogs(!showLogs)}
|
||||||
|
className="w-full justify-center text-cyan-600 dark:text-cyan-400 hover:bg-cyan-500/10"
|
||||||
|
aria-label={showLogs ? "Hide execution logs" : "Show execution logs"}
|
||||||
|
aria-expanded={showLogs}
|
||||||
|
>
|
||||||
|
{showLogs ? (
|
||||||
|
<>
|
||||||
|
<ChevronUp className="w-4 h-4 mr-1" aria-hidden="true" />
|
||||||
|
Hide Execution Logs
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<ChevronDown className="w-4 h-4 mr-1" aria-hidden="true" />
|
||||||
|
Show Execution Logs
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Collapsible Execution Logs */}
|
||||||
|
{showLogs && <ExecutionLogsExample status={status} />}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
import { Briefcase, Database, FileText, FolderKanban, Navigation, Settings } from "lucide-react";
|
import { Briefcase, Database, FileText, FolderKanban, Navigation, Settings } from "lucide-react";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { AgentWorkOrderExample } from "../layouts/AgentWorkOrderExample";
|
|
||||||
import { AgentWorkOrderLayoutExample } from "../layouts/AgentWorkOrderLayoutExample";
|
import { AgentWorkOrderLayoutExample } from "../layouts/AgentWorkOrderLayoutExample";
|
||||||
import { DocumentBrowserExample } from "../layouts/DocumentBrowserExample";
|
import { DocumentBrowserExample } from "../layouts/DocumentBrowserExample";
|
||||||
import { KnowledgeLayoutExample } from "../layouts/KnowledgeLayoutExample";
|
import { KnowledgeLayoutExample } from "../layouts/KnowledgeLayoutExample";
|
||||||
|
|||||||
@@ -172,6 +172,7 @@ services:
|
|||||||
- SUPABASE_SERVICE_KEY=${SUPABASE_SERVICE_KEY}
|
- SUPABASE_SERVICE_KEY=${SUPABASE_SERVICE_KEY}
|
||||||
- OPENAI_API_KEY=${OPENAI_API_KEY:-}
|
- OPENAI_API_KEY=${OPENAI_API_KEY:-}
|
||||||
- ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY:-}
|
- ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY:-}
|
||||||
|
- CLAUDE_CODE_OAUTH_TOKEN=${CLAUDE_CODE_OAUTH_TOKEN:-}
|
||||||
- LOGFIRE_TOKEN=${LOGFIRE_TOKEN:-}
|
- LOGFIRE_TOKEN=${LOGFIRE_TOKEN:-}
|
||||||
- LOG_LEVEL=${LOG_LEVEL:-INFO}
|
- LOG_LEVEL=${LOG_LEVEL:-INFO}
|
||||||
- AGENT_WORK_ORDERS_PORT=${AGENT_WORK_ORDERS_PORT:-8053}
|
- AGENT_WORK_ORDERS_PORT=${AGENT_WORK_ORDERS_PORT:-8053}
|
||||||
|
|||||||
@@ -59,6 +59,10 @@ RUN mkdir -p /repos /tmp/agent-work-orders && \
|
|||||||
USER agentuser
|
USER agentuser
|
||||||
RUN curl -fsSL https://claude.ai/install.sh | bash
|
RUN curl -fsSL https://claude.ai/install.sh | bash
|
||||||
|
|
||||||
|
# Configure git to use gh CLI for GitHub authentication
|
||||||
|
# This allows git clone to authenticate using GH_TOKEN environment variable
|
||||||
|
RUN git config --global credential.helper '!gh auth git-credential'
|
||||||
|
|
||||||
# Set environment variables
|
# Set environment variables
|
||||||
ENV PYTHONPATH="/app:$PYTHONPATH"
|
ENV PYTHONPATH="/app:$PYTHONPATH"
|
||||||
ENV PYTHONUNBUFFERED=1
|
ENV PYTHONUNBUFFERED=1
|
||||||
|
|||||||
Reference in New Issue
Block a user