From 7e74e77a66f6a592927bf6acc39204ccf76ee372 Mon Sep 17 00:00:00 2001 From: Thomas Marchand Date: Thu, 15 Jan 2026 08:21:35 +0000 Subject: [PATCH] Fix iOS build by adding MarkdownView.swift to Xcode project (#34) * Fix iOS build by adding MarkdownView.swift to Xcode project The file existed on disk but was missing from the project file, causing 'cannot find MarkdownView in scope' compilation error. * Sync iOS Workspace model with backend and stub deprecated agents API - Add new fields to Workspace model: skills, tools, plugins, template, distro, envVars, initScript to match backend WorkspaceResponse - Add custom decoder to handle optional fields gracefully - Stub listAgents/createAgent methods since /api/agents endpoint no longer exists (agents are now library-managed) * Remove dead agents code from iOS app The agents management was moved to library configuration in the backend. This removes the orphaned iOS code: - Delete AgentConfig.swift model - Delete AgentsView.swift (was not in main navigation) - Remove stub API methods for /api/agents endpoint - Remove AgentConfig unit tests - Update Xcode project references Build: SUCCEEDED Tests: 21 passed (was 23, removed 2 AgentConfig tests) --- .../project.pbxproj | 20 +- .../Models/AgentConfig.swift | 35 -- .../OpenAgentDashboard/Models/Workspace.swift | 55 ++- .../Services/APIService.swift | 30 -- .../Views/Agents/AgentsView.swift | 318 ------------------ .../OpenAgentDashboardTests/ModelTests.swift | 48 --- 6 files changed, 55 insertions(+), 451 deletions(-) delete mode 100644 ios_dashboard/OpenAgentDashboard/Models/AgentConfig.swift delete mode 100644 ios_dashboard/OpenAgentDashboard/Views/Agents/AgentsView.swift diff --git a/ios_dashboard/OpenAgentDashboard.xcodeproj/project.pbxproj b/ios_dashboard/OpenAgentDashboard.xcodeproj/project.pbxproj index d8b8af4..8b830d8 100644 --- a/ios_dashboard/OpenAgentDashboard.xcodeproj/project.pbxproj +++ b/ios_dashboard/OpenAgentDashboard.xcodeproj/project.pbxproj @@ -10,14 +10,12 @@ 02DB7F25245D03FF72DD8E2E /* ControlView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A84519FDE8FC75084938B292 /* ControlView.swift */; }; 03176DF3878C25A0B557462C /* ToolUIOptionListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A4D419C8490A0C5FC4DCDF20 /* ToolUIOptionListView.swift */; }; 0620B298DEF91DFCAE050DAC /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 66A48A20D2178760301256C9 /* Assets.xcassets */; }; - 0672F3AC46E02746D9857565 /* AgentsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0FA1A2E7115E4E4B93D41A66 /* AgentsView.swift */; }; 0B5E1A6153270BFF21A54C23 /* TerminalState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52DDF35DB8CD7D70F3CFC4A6 /* TerminalState.swift */; }; 1BBE749F3758FD704D1BFA0B /* ToolUIDataTableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45213C3E550D451EDC566CDE /* ToolUIDataTableView.swift */; }; 29372E691F6A5C5D2CCD9331 /* HistoryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A09A33A3A1A99446C8A88DC /* HistoryView.swift */; }; 2E26F9659B38872F562C3B2B /* WorkspacesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 91BF18B4AEAEB407887401AC /* WorkspacesView.swift */; }; 3361B14E949CB2A6E75B6962 /* ToolUIView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02CBD2029F8CF6751AD7C4E2 /* ToolUIView.swift */; }; 3DD4D1D2E080C2F89C4881B7 /* ToolUIModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A6128ECBCA632D9E2D415F2 /* ToolUIModels.swift */; }; - 487AF98018941AF388DB5A80 /* AgentConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = E606D3ED9560CFEC056ADFD1 /* AgentConfig.swift */; }; 4B50B97618C0CC469FF64592 /* Theme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 504A1222CE8971417834D229 /* Theme.swift */; }; 4D0CF2666262F45370D000DF /* TerminalView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AC6317C4EAD4DB9A8190209 /* TerminalView.swift */; }; 51436A7671B1E3C8478F81A2 /* RunningMissionsBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = E7FC053808661C9A0E21E83C /* RunningMissionsBar.swift */; }; @@ -37,6 +35,7 @@ CA70EC5A864C3D007D42E781 /* ChatMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CB591B632D3EF26AB217976 /* ChatMessage.swift */; }; D64972881E36894950658708 /* APIService.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBC90C32FEF604E025FFBF78 /* APIService.swift */; }; DA4634D7424AF3FC985987E7 /* GlassButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5267DE67017A858357F68424 /* GlassButton.swift */; }; + E1A2B3C4D5E6F78901234567 /* MarkdownView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1B2C3D4E5F6789012345678 /* MarkdownView.swift */; }; EFABDC95B65F6ED3420186FC /* NewMissionSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1A8191F935AB50463216395 /* NewMissionSheet.swift */; }; FA7E68F22D16E1AC0B5F5E22 /* StatusBadge.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD6FB2E54DC07BE7A1EB08F8 /* StatusBadge.swift */; }; FF9C447978711CBA9185B8B0 /* OpenAgentDashboardApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 139C740B7D55C13F3B167EF3 /* OpenAgentDashboardApp.swift */; }; @@ -55,9 +54,9 @@ /* Begin PBXFileReference section */ 02CBD2029F8CF6751AD7C4E2 /* ToolUIView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToolUIView.swift; sourceTree = ""; }; 0AC6317C4EAD4DB9A8190209 /* TerminalView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TerminalView.swift; sourceTree = ""; }; - 0FA1A2E7115E4E4B93D41A66 /* AgentsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AgentsView.swift; sourceTree = ""; }; 139C740B7D55C13F3B167EF3 /* OpenAgentDashboardApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenAgentDashboardApp.swift; sourceTree = ""; }; 2B9834D4EE32058824F9DF00 /* LoadingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoadingView.swift; sourceTree = ""; }; + A1B2C3D4E5F6789012345678 /* MarkdownView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MarkdownView.swift; sourceTree = ""; }; 3729F39FBF53046124D05BC1 /* NavigationState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationState.swift; sourceTree = ""; }; 3CB591B632D3EF26AB217976 /* ChatMessage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatMessage.swift; sourceTree = ""; }; 43A2EBAE84C0FFDCA5E1D66E /* OpenAgentDashboard.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = OpenAgentDashboard.entitlements; sourceTree = ""; }; @@ -84,7 +83,6 @@ CD8D224B6758B664864F3987 /* ANSIParser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ANSIParser.swift; sourceTree = ""; }; D1A8191F935AB50463216395 /* NewMissionSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewMissionSheet.swift; sourceTree = ""; }; D4AB47CF121ABA1946A4D879 /* Mission.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Mission.swift; sourceTree = ""; }; - E606D3ED9560CFEC056ADFD1 /* AgentConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AgentConfig.swift; sourceTree = ""; }; E7C1198DDF17571DE85F5ABA /* Workspace.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Workspace.swift; sourceTree = ""; }; E7FC053808661C9A0E21E83C /* RunningMissionsBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunningMissionsBar.swift; sourceTree = ""; }; EB5A4720378F06807FDE73E1 /* GlassCard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GlassCard.swift; sourceTree = ""; }; @@ -140,6 +138,7 @@ 5267DE67017A858357F68424 /* GlassButton.swift */, EB5A4720378F06807FDE73E1 /* GlassCard.swift */, 2B9834D4EE32058824F9DF00 /* LoadingView.swift */, + A1B2C3D4E5F6789012345678 /* MarkdownView.swift */, E7FC053808661C9A0E21E83C /* RunningMissionsBar.swift */, CD6FB2E54DC07BE7A1EB08F8 /* StatusBadge.swift */, D09E84E812213CF7E52E4FEF /* ToolUI */, @@ -167,7 +166,6 @@ 73D80C56FA670F92E007E712 /* Views */ = { isa = PBXGroup; children = ( - F7F882A9E5E42DA9F9776160 /* Agents */, 2EF415E84544334B25BD8E26 /* Components */, DABAA3652C0B0A54CFC3221B /* Control */, A688A831235D3E218A0A6783 /* Desktop */, @@ -199,7 +197,6 @@ C786EDDB39D9D19A1A112CE9 /* Models */ = { isa = PBXGroup; children = ( - E606D3ED9560CFEC056ADFD1 /* AgentConfig.swift */, 3CB591B632D3EF26AB217976 /* ChatMessage.swift */, BA70A2A73D3A386EAFD69FC4 /* FileEntry.swift */, D4AB47CF121ABA1946A4D879 /* Mission.swift */, @@ -256,14 +253,6 @@ path = Services; sourceTree = ""; }; - F7F882A9E5E42DA9F9776160 /* Agents */ = { - isa = PBXGroup; - children = ( - 0FA1A2E7115E4E4B93D41A66 /* AgentsView.swift */, - ); - path = Agents; - sourceTree = ""; - }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -367,8 +356,6 @@ files = ( 652A0AE498D69C9DB728B2DF /* ANSIParser.swift in Sources */, D64972881E36894950658708 /* APIService.swift in Sources */, - 487AF98018941AF388DB5A80 /* AgentConfig.swift in Sources */, - 0672F3AC46E02746D9857565 /* AgentsView.swift in Sources */, CA70EC5A864C3D007D42E781 /* ChatMessage.swift in Sources */, AA02567226057045DDD61CB1 /* ContentView.swift in Sources */, 02DB7F25245D03FF72DD8E2E /* ControlView.swift in Sources */, @@ -380,6 +367,7 @@ 999ACAA94B0BD81A05288092 /* GlassCard.swift in Sources */, 29372E691F6A5C5D2CCD9331 /* HistoryView.swift in Sources */, 6865FE997D3E1D91D411F6BC /* LoadingView.swift in Sources */, + E1A2B3C4D5E6F78901234567 /* MarkdownView.swift in Sources */, 9BC40E40E1B5622B24328AEB /* Mission.swift in Sources */, 83BB0F0AAFE4F2735FF76B87 /* NavigationState.swift in Sources */, EFABDC95B65F6ED3420186FC /* NewMissionSheet.swift in Sources */, diff --git a/ios_dashboard/OpenAgentDashboard/Models/AgentConfig.swift b/ios_dashboard/OpenAgentDashboard/Models/AgentConfig.swift deleted file mode 100644 index 76bb36c..0000000 --- a/ios_dashboard/OpenAgentDashboard/Models/AgentConfig.swift +++ /dev/null @@ -1,35 +0,0 @@ -// -// AgentConfig.swift -// OpenAgentDashboard -// -// Agent configuration model -// - -import Foundation - -/// An agent configuration defining model, tools, and capabilities. -struct AgentConfig: Identifiable, Codable { - let id: String - var name: String - var model_id: String - var mcp_servers: [String] - var skills: [String] - var commands: [String] - let created_at: String - let updated_at: String -} - -// MARK: - Preview Data - -extension AgentConfig { - static let preview = AgentConfig( - id: "00000000-0000-0000-0000-000000000001", - name: "Default Agent", - model_id: "claude-sonnet-4-20250514", - mcp_servers: ["playwright", "supabase"], - skills: ["coding", "research"], - commands: ["review-pr"], - created_at: ISO8601DateFormatter().string(from: Date()), - updated_at: ISO8601DateFormatter().string(from: Date()) - ) -} diff --git a/ios_dashboard/OpenAgentDashboard/Models/Workspace.swift b/ios_dashboard/OpenAgentDashboard/Models/Workspace.swift index d9cb1f0..dc20a82 100644 --- a/ios_dashboard/OpenAgentDashboard/Models/Workspace.swift +++ b/ios_dashboard/OpenAgentDashboard/Models/Workspace.swift @@ -57,15 +57,31 @@ struct Workspace: Codable, Identifiable { let status: WorkspaceStatus let errorMessage: String? let createdAt: String + /// Skill names from library synced to this workspace + let skills: [String] + /// Tool names from library synced to this workspace + let tools: [String] + /// Plugin identifiers for hooks + let plugins: [String] + /// Workspace template name (if created from a template) + let template: String? + /// Preferred Linux distribution for container workspaces + let distro: String? + /// Environment variables always loaded in this workspace + let envVars: [String: String] + /// Init script to run when the workspace is built/rebuilt + let initScript: String? enum CodingKeys: String, CodingKey { - case id, name, path, status + case id, name, path, status, skills, tools, plugins, template, distro case workspaceType = "workspace_type" case errorMessage = "error_message" case createdAt = "created_at" + case envVars = "env_vars" + case initScript = "init_script" } - init(id: String, name: String, workspaceType: WorkspaceType, path: String, status: WorkspaceStatus, errorMessage: String?, createdAt: String) { + init(id: String, name: String, workspaceType: WorkspaceType, path: String, status: WorkspaceStatus, errorMessage: String?, createdAt: String, skills: [String] = [], tools: [String] = [], plugins: [String] = [], template: String? = nil, distro: String? = nil, envVars: [String: String] = [:], initScript: String? = nil) { self.id = id self.name = name self.workspaceType = workspaceType @@ -73,6 +89,31 @@ struct Workspace: Codable, Identifiable { self.status = status self.errorMessage = errorMessage self.createdAt = createdAt + self.skills = skills + self.tools = tools + self.plugins = plugins + self.template = template + self.distro = distro + self.envVars = envVars + self.initScript = initScript + } + + init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + id = try container.decode(String.self, forKey: .id) + name = try container.decode(String.self, forKey: .name) + workspaceType = try container.decode(WorkspaceType.self, forKey: .workspaceType) + path = try container.decode(String.self, forKey: .path) + status = try container.decode(WorkspaceStatus.self, forKey: .status) + errorMessage = try container.decodeIfPresent(String.self, forKey: .errorMessage) + createdAt = try container.decode(String.self, forKey: .createdAt) + skills = try container.decodeIfPresent([String].self, forKey: .skills) ?? [] + tools = try container.decodeIfPresent([String].self, forKey: .tools) ?? [] + plugins = try container.decodeIfPresent([String].self, forKey: .plugins) ?? [] + template = try container.decodeIfPresent(String.self, forKey: .template) + distro = try container.decodeIfPresent(String.self, forKey: .distro) + envVars = try container.decodeIfPresent([String: String].self, forKey: .envVars) ?? [:] + initScript = try container.decodeIfPresent(String.self, forKey: .initScript) } /// Check if this is the default host workspace. @@ -105,7 +146,10 @@ extension Workspace { path: "/root", status: .ready, errorMessage: nil, - createdAt: ISO8601DateFormatter().string(from: Date()) + createdAt: ISO8601DateFormatter().string(from: Date()), + skills: [], + tools: [], + plugins: [] ) static let previewChroot = Workspace( @@ -115,6 +159,9 @@ extension Workspace { path: "/var/lib/openagent/containers/project-sandbox", status: .ready, errorMessage: nil, - createdAt: ISO8601DateFormatter().string(from: Date()) + createdAt: ISO8601DateFormatter().string(from: Date()), + skills: ["code-review", "testing"], + tools: ["pytest", "eslint"], + plugins: [] ) } diff --git a/ios_dashboard/OpenAgentDashboard/Services/APIService.swift b/ios_dashboard/OpenAgentDashboard/Services/APIService.swift index 2ac674c..bb72a64 100644 --- a/ios_dashboard/OpenAgentDashboard/Services/APIService.swift +++ b/ios_dashboard/OpenAgentDashboard/Services/APIService.swift @@ -284,36 +284,6 @@ final class APIService { // MARK: - Workspaces - // MARK: - Agents - - func listAgents(completion: @escaping (Result<[AgentConfig], Error>) -> Void) { - Task { - do { - let agents: [AgentConfig] = try await get("/api/agents") - completion(.success(agents)) - } catch { - completion(.failure(error)) - } - } - } - - func createAgent(name: String, modelId: String, completion: @escaping (Result) -> Void) { - Task { - do { - struct CreateAgentRequest: Encodable { - let name: String - let model_id: String - } - let agent: AgentConfig = try await post("/api/agents", body: CreateAgentRequest(name: name, model_id: modelId)) - completion(.success(agent)) - } catch { - completion(.failure(error)) - } - } - } - - // MARK: - Workspaces - func listWorkspaces() async throws -> [Workspace] { try await get("/api/workspaces") } diff --git a/ios_dashboard/OpenAgentDashboard/Views/Agents/AgentsView.swift b/ios_dashboard/OpenAgentDashboard/Views/Agents/AgentsView.swift deleted file mode 100644 index 2a66d41..0000000 --- a/ios_dashboard/OpenAgentDashboard/Views/Agents/AgentsView.swift +++ /dev/null @@ -1,318 +0,0 @@ -// -// AgentsView.swift -// OpenAgentDashboard -// -// Agent configuration management view -// - -import SwiftUI - -// AgentConfig model is now in Models/AgentConfig.swift - -struct AgentsView: View { - @State private var agents: [AgentConfig] = [] - @State private var isLoading = true - @State private var errorMessage: String? - @State private var selectedAgent: AgentConfig? - @State private var showNewAgentSheet = false - - var body: some View { - ZStack { - Color.black.ignoresSafeArea() - - if isLoading { - ProgressView() - .progressViewStyle(CircularProgressViewStyle(tint: .white)) - } else if let error = errorMessage { - VStack(spacing: 16) { - Image(systemName: "exclamationmark.triangle") - .font(.system(size: 48)) - .foregroundColor(.red.opacity(0.6)) - Text(error) - .foregroundColor(.white.opacity(0.6)) - .multilineTextAlignment(.center) - Button("Retry") { - loadAgents() - } - .foregroundColor(.blue) - } - .padding() - } else { - VStack(spacing: 0) { - // Header - HStack { - Text("Agents") - .font(.largeTitle.bold()) - .foregroundColor(.white) - Spacer() - Button(action: { showNewAgentSheet = true }) { - Image(systemName: "plus") - .font(.title3) - .foregroundColor(.white) - .frame(width: 40, height: 40) - .background(Color.indigo.opacity(0.2)) - .cornerRadius(10) - } - } - .padding() - - if agents.isEmpty { - Spacer() - VStack(spacing: 16) { - Image(systemName: "cpu") - .font(.system(size: 60)) - .foregroundColor(.white.opacity(0.2)) - Text("No agents yet") - .foregroundColor(.white.opacity(0.4)) - Text("Create an agent to get started") - .font(.caption) - .foregroundColor(.white.opacity(0.3)) - } - Spacer() - } else { - ScrollView { - LazyVStack(spacing: 12) { - ForEach(agents) { agent in - AgentCard(agent: agent, onTap: { - selectedAgent = agent - }) - } - } - .padding() - } - } - } - } - } - .sheet(item: $selectedAgent) { agent in - AgentDetailView(agent: agent, onDismiss: { - selectedAgent = nil - loadAgents() - }) - } - .sheet(isPresented: $showNewAgentSheet) { - NewAgentSheet(onDismiss: { - showNewAgentSheet = false - loadAgents() - }) - } - .onAppear { - loadAgents() - } - } - - private func loadAgents() { - isLoading = true - errorMessage = nil - - APIService.shared.listAgents { result in - DispatchQueue.main.async { - isLoading = false - switch result { - case .success(let agentList): - agents = agentList - case .failure(let error): - errorMessage = error.localizedDescription - } - } - } - } -} - -struct AgentCard: View { - let agent: AgentConfig - let onTap: () -> Void - - var body: some View { - Button(action: onTap) { - VStack(alignment: .leading, spacing: 8) { - HStack { - Image(systemName: "cpu") - .foregroundColor(.indigo) - Text(agent.name) - .font(.headline) - .foregroundColor(.white) - Spacer() - Image(systemName: "chevron.right") - .font(.caption) - .foregroundColor(.white.opacity(0.4)) - } - - Text(agent.model_id) - .font(.caption) - .foregroundColor(.white.opacity(0.6)) - - if !agent.mcp_servers.isEmpty || !agent.skills.isEmpty || !agent.commands.isEmpty { - HStack(spacing: 12) { - if !agent.mcp_servers.isEmpty { - Label("\(agent.mcp_servers.count)", systemImage: "server.rack") - .font(.caption2) - .foregroundColor(.white.opacity(0.5)) - } - if !agent.skills.isEmpty { - Label("\(agent.skills.count)", systemImage: "doc.text") - .font(.caption2) - .foregroundColor(.white.opacity(0.5)) - } - if !agent.commands.isEmpty { - Label("\(agent.commands.count)", systemImage: "terminal") - .font(.caption2) - .foregroundColor(.white.opacity(0.5)) - } - } - } - } - .padding() - .background(Color.white.opacity(0.02)) - .cornerRadius(12) - .overlay( - RoundedRectangle(cornerRadius: 12) - .stroke(Color.white.opacity(0.06), lineWidth: 1) - ) - } - } -} - -struct AgentDetailView: View { - let agent: AgentConfig - let onDismiss: () -> Void - - var body: some View { - NavigationView { - ZStack { - Color.black.ignoresSafeArea() - - ScrollView { - VStack(alignment: .leading, spacing: 20) { - VStack(alignment: .leading, spacing: 8) { - Text("Model") - .font(.caption) - .foregroundColor(.white.opacity(0.4)) - Text(agent.model_id) - .foregroundColor(.white) - } - - if !agent.mcp_servers.isEmpty { - VStack(alignment: .leading, spacing: 8) { - Text("MCP Servers") - .font(.caption) - .foregroundColor(.white.opacity(0.4)) - ForEach(agent.mcp_servers, id: \.self) { server in - HStack { - Image(systemName: "server.rack") - .font(.caption) - .foregroundColor(.indigo.opacity(0.6)) - Text(server) - .foregroundColor(.white.opacity(0.8)) - } - } - } - } - - if !agent.skills.isEmpty { - VStack(alignment: .leading, spacing: 8) { - Text("Skills") - .font(.caption) - .foregroundColor(.white.opacity(0.4)) - ForEach(agent.skills, id: \.self) { skill in - HStack { - Image(systemName: "doc.text") - .font(.caption) - .foregroundColor(.indigo.opacity(0.6)) - Text(skill) - .foregroundColor(.white.opacity(0.8)) - } - } - } - } - - if !agent.commands.isEmpty { - VStack(alignment: .leading, spacing: 8) { - Text("Commands") - .font(.caption) - .foregroundColor(.white.opacity(0.4)) - ForEach(agent.commands, id: \.self) { command in - HStack { - Image(systemName: "terminal") - .font(.caption) - .foregroundColor(.indigo.opacity(0.6)) - Text("/\(command)") - .foregroundColor(.white.opacity(0.8)) - } - } - } - } - } - .padding() - } - } - .navigationTitle(agent.name) - .navigationBarTitleDisplayMode(.inline) - .toolbar { - ToolbarItem(placement: .navigationBarTrailing) { - Button("Done") { - onDismiss() - } - .foregroundColor(.indigo) - } - } - } - } -} - -struct NewAgentSheet: View { - let onDismiss: () -> Void - @State private var name = "" - @State private var isCreating = false - - var body: some View { - NavigationView { - ZStack { - Color.black.ignoresSafeArea() - - VStack(spacing: 20) { - TextField("Agent Name", text: $name) - .textFieldStyle(.roundedBorder) - - Text("Note: Create basic agent here, configure details in web dashboard") - .font(.caption) - .foregroundColor(.white.opacity(0.5)) - - Spacer() - } - .padding() - } - .navigationTitle("New Agent") - .navigationBarTitleDisplayMode(.inline) - .toolbar { - ToolbarItem(placement: .navigationBarLeading) { - Button("Cancel") { - onDismiss() - } - } - ToolbarItem(placement: .navigationBarTrailing) { - Button(action: createAgent) { - if isCreating { - ProgressView() - .progressViewStyle(CircularProgressViewStyle()) - } else { - Text("Create") - } - } - .disabled(name.isEmpty || isCreating) - } - } - } - } - - private func createAgent() { - isCreating = true - // Create with default model - APIService.shared.createAgent(name: name, modelId: "claude-sonnet-4-20250514") { result in - DispatchQueue.main.async { - isCreating = false - onDismiss() - } - } - } -} diff --git a/ios_dashboard/OpenAgentDashboardTests/ModelTests.swift b/ios_dashboard/OpenAgentDashboardTests/ModelTests.swift index 9058833..eff19cc 100644 --- a/ios_dashboard/OpenAgentDashboardTests/ModelTests.swift +++ b/ios_dashboard/OpenAgentDashboardTests/ModelTests.swift @@ -10,54 +10,6 @@ import XCTest final class ModelTests: XCTestCase { - // MARK: - AgentConfig Tests - - func testAgentConfigDecoding() throws { - let json = """ - { - "id": "test-id", - "name": "Test Agent", - "model_id": "claude-sonnet-4", - "mcp_servers": ["playwright"], - "skills": ["coding"], - "commands": ["review"], - "created_at": "2024-01-01T00:00:00Z", - "updated_at": "2024-01-01T00:00:00Z" - } - """.data(using: .utf8)! - - let decoder = JSONDecoder() - let config = try decoder.decode(AgentConfig.self, from: json) - - XCTAssertEqual(config.id, "test-id") - XCTAssertEqual(config.name, "Test Agent") - XCTAssertEqual(config.model_id, "claude-sonnet-4") - XCTAssertEqual(config.mcp_servers, ["playwright"]) - XCTAssertEqual(config.skills, ["coding"]) - XCTAssertEqual(config.commands, ["review"]) - } - - func testAgentConfigEncoding() throws { - let config = AgentConfig( - id: "encode-test", - name: "Encoded Agent", - model_id: "claude-opus-4", - mcp_servers: ["supabase", "playwright"], - skills: ["research", "coding"], - commands: ["commit"], - created_at: "2024-01-01T00:00:00Z", - updated_at: "2024-01-01T00:00:00Z" - ) - - let encoder = JSONEncoder() - let data = try encoder.encode(config) - let decoded = try JSONDecoder().decode(AgentConfig.self, from: data) - - XCTAssertEqual(decoded.id, config.id) - XCTAssertEqual(decoded.name, config.name) - XCTAssertEqual(decoded.mcp_servers.count, 2) - } - // MARK: - Workspace Tests func testWorkspaceDecoding() throws {