/** * API client for the Foxy Dev Team backend. */ const BASE_URL = ''; // ─── Types ────────────────────────────────────────────────────────────────── export interface Project { id: number; name: string; slug: string; description: string; status: string; workflow_type: string; project_type: string; test_mode: boolean; install_path?: string; gitea_repo?: string; deployment_target?: string; deploy_server_id?: number; git_server_id?: number; deploy_server_name?: string; git_server_name?: string; created_at: string; updated_at: string; tasks: Task[]; audit_logs: AuditLog[]; agent_executions: AgentExecution[]; } export interface ProjectSummary { id: number; name: string; slug: string; status: string; workflow_type: string; project_type: string; test_mode: boolean; install_path?: string; task_count: number; tasks_done: number; deploy_server_name?: string; git_server_name?: string; created_at: string; updated_at: string; } export interface Task { id: number; project_id: number; task_id: string; type: string; title: string; priority: string; assigned_to?: string; status: string; dependencies: string[]; acceptance_criteria?: string; agent_payloads: Record; created_at: string; updated_at: string; } export interface AuditLog { id: number; project_id: number; timestamp: string; agent: string; action: string; target?: string; message?: string; source: string; } export interface AgentExecution { id: number; project_id: number; agent_name: string; status: string; pid?: number; started_at: string; finished_at?: string; exit_code?: number; error_output?: string; } export interface AgentStatus { name: string; display_name: string; model: string; current_status: string; current_project?: string; total_executions: number; success_count: number; failure_count: number; } export interface WorkflowDef { type: string; label: string; path: string; steps: { agent: string; label: string; model: string; awaiting_status: string; running_status: string; }[]; } export interface ProjectTypeInfo { value: string; emoji: string; label: string; description: string; } export interface DeployServer { id: number; name: string; host: string; user: string; ssh_port: number; description: string; created_at: string; } export interface GitServer { id: number; name: string; url: string; org: string; description: string; created_at: string; } export interface AppConfig { FOXY_HOME: string; FOXY_WORKSPACE: string; GITEA_SERVER: string; DEPLOYMENT_SERVER: string; DEPLOYMENT_USER: string; TELEGRAM_CHAT_ID: string; LOG_LEVEL: string; GITEA_OPENCLAW_TOKEN: string; DEPLOYMENT_PWD: string; TELEGRAM_BOT_TOKEN: string; } // ─── OpenClaw Types ───────────────────────────────────────────────────────── export interface OpenClawStatus { gateway_online: boolean; openclaw_type: string; foxy_home: string; gateway_bind: string; gateway_mode: string; gateway_port: number; agent_count: number; skill_count: number; log_file_size: number; config_exists: boolean; timestamp: string; } export interface OpenClawAgent { name: string; type: string; config_files?: string[]; config?: Record; model?: string; system_prompt?: string; system_prompt_preview?: string; file_count?: number; has_workspace?: boolean; identity_files?: { name: string; size: number }[]; } export interface OpenClawSkill { name: string; type: string; source: string; description?: string; description_short?: string; file_count?: number; subdirs?: string[]; size?: number; } export interface OpenClawLogs { lines: string[]; total_lines: number; showing: number; log_path: string; error?: string; } export interface OpenClawModels { models: Record[]; providers: Record[]; raw_keys?: string[]; error?: string; } export interface FileTreeNode { name: string; path: string; type: 'file' | 'directory'; size?: number; children?: FileTreeNode[]; } export interface ReadFileResult { content: string | null; pretty?: string | null; binary: boolean; path: string; size: number; language: string; modified?: string; } export interface WriteFileResult { message: string; size: number; } export interface IdentityFile { name: string; size: number; modified?: string | null; } export interface AgentFilesResult { files: IdentityFile[]; other_files: IdentityFile[]; workspace: string; exists: boolean; } export interface AgentFileResult { content: string; filename: string; size: number; language: string; } export interface AgentMemoryResult { has_db: boolean; db_path?: string; size?: number; tables?: string[]; timeline: Record[]; stats: { total_thoughts: number }; message?: string; error?: string; } // ─── HTTP Helpers ─────────────────────────────────────────────────────────── async function request(path: string, options?: RequestInit): Promise { const res = await fetch(`${BASE_URL}${path}`, { ...options, headers: { 'Content-Type': 'application/json', ...options?.headers, }, }); if (!res.ok) { const err = await res.json().catch(() => ({ detail: res.statusText })); throw new Error(err.detail || `HTTP ${res.status}`); } return res.json(); } // ─── API ──────────────────────────────────────────────────────────────────── export const api = { // Projects listProjects: (params?: Record) => { const qs = params ? '?' + new URLSearchParams(params).toString() : ''; return request(`/api/projects${qs}`); }, getProject: (id: number) => request(`/api/projects/${id}`), createProject: (data: { name: string; description: string; workflow_type: string; project_type: string; test_mode?: boolean; install_path?: string; deploy_server_id?: number | null; git_server_id?: number | null; }) => request('/api/projects', { method: 'POST', body: JSON.stringify(data) }), startProject: (id: number) => request<{ status: string }>(`/api/projects/${id}/start`, { method: 'POST' }), pauseProject: (id: number) => request<{ status: string }>(`/api/projects/${id}/pause`, { method: 'POST' }), stopProject: (id: number) => request<{ status: string }>(`/api/projects/${id}/stop`, { method: 'POST' }), resetProject: (id: number) => request<{ status: string }>(`/api/projects/${id}/reset`, { method: 'POST' }), deleteProject: (id: number) => request<{ message: string }>(`/api/projects/${id}`, { method: 'DELETE' }), getProgress: (id: number) => request<{ current_step: number; total_steps: number; percentage: number }>(`/api/projects/${id}/progress`), // Project Types listProjectTypes: () => request('/api/projects/types'), // Agents listAgents: () => request('/api/agents'), getAgentHistory: (name: string) => request(`/api/agents/${name}/history`), // Logs listLogs: (params?: Record) => { const qs = params ? '?' + new URLSearchParams(params).toString() : ''; return request(`/api/logs${qs}`); }, // Workflows listWorkflows: () => request('/api/workflows'), // Config getConfig: () => request('/api/config'), updateConfig: (data: Record) => request<{ message: string }>('/api/config', { method: 'PUT', body: JSON.stringify(data) }), // Deploy Servers listDeployServers: () => request('/api/config/deploy-servers'), createDeployServer: (data: { name: string; host: string; user?: string; password?: string; ssh_port?: number; description?: string }) => request('/api/config/deploy-servers', { method: 'POST', body: JSON.stringify(data) }), deleteDeployServer: (id: number) => request<{ message: string }>(`/api/config/deploy-servers/${id}`, { method: 'DELETE' }), // Git Servers listGitServers: () => request('/api/config/git-servers'), createGitServer: (data: { name: string; url: string; token?: string; org?: string; description?: string }) => request('/api/config/git-servers', { method: 'POST', body: JSON.stringify(data) }), deleteGitServer: (id: number) => request<{ message: string }>(`/api/config/git-servers/${id}`, { method: 'DELETE' }), // Health health: () => request<{ status: string; version: string }>('/api/health'), // OpenClaw openclawStatus: () => request('/api/openclaw/status'), openclawConfig: () => request<{ config: Record }>('/api/openclaw/config'), openclawAgents: () => request<{ agents: OpenClawAgent[]; count: number }>('/api/openclaw/agents'), openclawDeleteAgent: (name: string) => request<{ message: string }>(`/api/openclaw/agents/${encodeURIComponent(name)}`, { method: 'DELETE' }), openclawAgentFiles: (name: string) => request(`/api/openclaw/agents/${encodeURIComponent(name)}/files`), openclawReadAgentFile: (name: string, filename: string) => request(`/api/openclaw/agents/${encodeURIComponent(name)}/file?filename=${encodeURIComponent(filename)}`), openclawWriteAgentFile: (name: string, filename: string, content: string) => request(`/api/openclaw/agents/${encodeURIComponent(name)}/file?filename=${encodeURIComponent(filename)}`, { method: 'PUT', body: JSON.stringify({ content }), }), openclawAgentMemory: (name: string, limit: number = 100) => request(`/api/openclaw/agents/${encodeURIComponent(name)}/memory?limit=${limit}`), openclawSkills: () => request<{ skills: OpenClawSkill[]; count: number }>('/api/openclaw/skills'), openclawLogs: (params: { lines?: number; level?: string }) => { const qs = new URLSearchParams(); if (params.lines) qs.set('lines', params.lines.toString()); if (params.level) qs.set('level', params.level); return request(`/api/openclaw/logs?${qs.toString()}`); }, openclawModels: () => request('/api/openclaw/models'), openclawFilesystem: () => request<{ root: string; tree: FileTreeNode[] }>('/api/openclaw/filesystem'), openclawReadFile: (path: string) => request(`/api/openclaw/file?path=${encodeURIComponent(path)}`), openclawWriteFile: (path: string, content: string) => request(`/api/openclaw/file?path=${encodeURIComponent(path)}`, { method: 'PUT', body: JSON.stringify({ content }), }), };