import Foundation /// The single networking seam the whole app codes against. The app builds entirely on /// `FixtureDSClient` (zero backend); `LiveDSClient` (FU-A) drops in later unchanged. /// /// `ask(backend:)` keeps `backend` as `String?` (raw AI-ROUTING §4 values) — S2 maps AIProviderID /// to that string. This shape is locked by AI-ROUTING.md §4 (a documented contract), verified at /// integration by the FU-B call-shape regression. public protocol DSClient: Sendable { // Auth func login(username: String, password: String, totpCode: String?) async throws -> AccessTokenResponse func me() async throws -> UserResponse func refresh() async throws -> AccessTokenResponse func logout() async throws // Documents func documents(_ query: DocumentListQuery) async throws -> DocumentListResponse func document(id: Int) async throws -> DocumentDetailResponse func documentContent(id: Int) async throws -> DocumentContentResponse func documentTree() async throws -> [DomainTreeNode] func categoryCounts() async throws -> CategoryCounts func duplicates() async throws -> DuplicatesResponse func patchDocument(id: Int, _ update: DocumentUpdate) async throws -> DocumentResponse func putContent(id: Int, content: String) async throws func deleteDocument(id: Int) async throws /// 멀티파트 업로드 (POST /documents/) → Inbox 저장 + 처리 큐 등록. 201 DocumentResponse. func uploadDocument(_ upload: DocumentUpload) async throws -> DocumentResponse // Search / Ask func search(q: String, mode: SearchMode?, page: Int?, debug: Bool?) async throws -> SearchResponse func ask(q: String, limit: Int?, backend: String?, debug: Bool?) async throws -> AskResponse // Memos func memos(_ query: MemoListQuery) async throws -> MemoListResponse func memo(id: Int) async throws -> MemoResponse func createMemo(_ create: MemoCreate) async throws -> MemoResponse func patchMemo(id: Int, _ update: MemoUpdate) async throws -> MemoResponse func pinMemo(id: Int, pinned: Bool) async throws -> MemoResponse func archiveMemo(id: Int, archived: Bool) async throws -> MemoResponse func toggleMemoTask(id: Int, taskIndex: Int, checked: Bool) async throws -> MemoResponse func deleteMemo(id: Int) async throws // Digest func digest(date: String?, country: String?) async throws -> DigestResponse }