fix(ai-copilot): 🐛 LiveCockpitAPI query-string URL building

appendingPathComponent percent-encoded '?' in 'content-posts?state=proposed'
(→ %3F), 404ing fetchPending so the approval queue silently kept mock data.
Split path/query and set query via URLComponents.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
autocommit 2026-06-07 00:19:01 -07:00
parent 3034ae8d5e
commit 03c1db30e0

View file

@ -24,8 +24,21 @@ public struct LiveCockpitAPI: CockpitAPI {
return d
}
/// Resolve a path (optionally carrying a `?query`) against `baseURL`.
/// `URL.appendingPathComponent` percent-encodes `?`/`=`, mangling queries
/// into the path so split the query off and set it via URLComponents.
private func requestURL(_ pathAndQuery: String) -> URL {
let parts = pathAndQuery.split(separator: "?", maxSplits: 1, omittingEmptySubsequences: false)
let url = baseURL.appendingPathComponent(String(parts[0]))
guard parts.count > 1, var comps = URLComponents(url: url, resolvingAgainstBaseURL: false) else {
return url
}
comps.percentEncodedQuery = String(parts[1])
return comps.url ?? url
}
private func get<T: Decodable>(_ path: String, _ type: T.Type) async throws -> T {
var req = URLRequest(url: baseURL.appendingPathComponent(path))
var req = URLRequest(url: requestURL(path))
if let token = try await auth.authToken() {
req.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
}
@ -38,7 +51,7 @@ public struct LiveCockpitAPI: CockpitAPI {
@discardableResult
private func post(_ path: String, body: [String: String] = [:]) async throws -> Data {
var req = URLRequest(url: baseURL.appendingPathComponent(path))
var req = URLRequest(url: requestURL(path))
req.httpMethod = "POST"
req.setValue("application/json", forHTTPHeaderField: "Content-Type")
if let token = try await auth.authToken() {