diff --git a/Sources/CodexBarCLI/CLIConfigCommand.swift b/Sources/CodexBarCLI/CLIConfigCommand.swift index 81fdd96f8..380dc8bde 100644 --- a/Sources/CodexBarCLI/CLIConfigCommand.swift +++ b/Sources/CodexBarCLI/CLIConfigCommand.swift @@ -221,8 +221,7 @@ extension CodexBarCLI { if (value.hasPrefix("\"") && value.hasSuffix("\"")) || (value.hasPrefix("'") && value.hasSuffix("'")) { - value.removeFirst() - value.removeLast() + value = String(value.dropFirst().dropLast()) } value = value.trimmingCharacters(in: .whitespacesAndNewlines) return value.isEmpty ? nil : value diff --git a/Sources/CodexBarCore/Config/CodexBarConfig.swift b/Sources/CodexBarCore/Config/CodexBarConfig.swift index ab0526d66..57ef8e221 100644 --- a/Sources/CodexBarCore/Config/CodexBarConfig.swift +++ b/Sources/CodexBarCore/Config/CodexBarConfig.swift @@ -157,8 +157,7 @@ public struct ProviderConfig: Codable, Sendable, Identifiable { if (value.hasPrefix("\"") && value.hasSuffix("\"")) || (value.hasPrefix("'") && value.hasSuffix("'")) { - value.removeFirst() - value.removeLast() + value = String(value.dropFirst().dropLast()) } value = value.trimmingCharacters(in: .whitespacesAndNewlines) return value.isEmpty ? nil : value diff --git a/Sources/CodexBarCore/Providers/Alibaba/AlibabaCodingPlanSettingsReader.swift b/Sources/CodexBarCore/Providers/Alibaba/AlibabaCodingPlanSettingsReader.swift index aa88d36ed..54d2493aa 100644 --- a/Sources/CodexBarCore/Providers/Alibaba/AlibabaCodingPlanSettingsReader.swift +++ b/Sources/CodexBarCore/Providers/Alibaba/AlibabaCodingPlanSettingsReader.swift @@ -52,8 +52,7 @@ public struct AlibabaCodingPlanSettingsReader: Sendable { if (value.hasPrefix("\"") && value.hasSuffix("\"")) || (value.hasPrefix("'") && value.hasSuffix("'")) { - value.removeFirst() - value.removeLast() + value = String(value.dropFirst().dropLast()) } value = value.trimmingCharacters(in: .whitespacesAndNewlines) diff --git a/Sources/CodexBarCore/Providers/Alibaba/AlibabaTokenPlanSettingsReader.swift b/Sources/CodexBarCore/Providers/Alibaba/AlibabaTokenPlanSettingsReader.swift index ba7c5c3a3..6f04e6d09 100644 --- a/Sources/CodexBarCore/Providers/Alibaba/AlibabaTokenPlanSettingsReader.swift +++ b/Sources/CodexBarCore/Providers/Alibaba/AlibabaTokenPlanSettingsReader.swift @@ -34,8 +34,7 @@ public struct AlibabaTokenPlanSettingsReader: Sendable { if (value.hasPrefix("\"") && value.hasSuffix("\"")) || (value.hasPrefix("'") && value.hasSuffix("'")) { - value.removeFirst() - value.removeLast() + value = String(value.dropFirst().dropLast()) } value = value.trimmingCharacters(in: .whitespacesAndNewlines) return value.isEmpty ? nil : value diff --git a/Sources/CodexBarCore/Providers/AzureOpenAI/AzureOpenAISettingsReader.swift b/Sources/CodexBarCore/Providers/AzureOpenAI/AzureOpenAISettingsReader.swift index 3cdfb6a41..97949a367 100644 --- a/Sources/CodexBarCore/Providers/AzureOpenAI/AzureOpenAISettingsReader.swift +++ b/Sources/CodexBarCore/Providers/AzureOpenAI/AzureOpenAISettingsReader.swift @@ -46,8 +46,7 @@ public enum AzureOpenAISettingsReader { if (value.hasPrefix("\"") && value.hasSuffix("\"")) || (value.hasPrefix("'") && value.hasSuffix("'")) { - value.removeFirst() - value.removeLast() + value = String(value.dropFirst().dropLast()) } value = value.trimmingCharacters(in: .whitespacesAndNewlines) diff --git a/Sources/CodexBarCore/Providers/Bedrock/BedrockSettingsReader.swift b/Sources/CodexBarCore/Providers/Bedrock/BedrockSettingsReader.swift index c16b4b99c..4959d8b16 100644 --- a/Sources/CodexBarCore/Providers/Bedrock/BedrockSettingsReader.swift +++ b/Sources/CodexBarCore/Providers/Bedrock/BedrockSettingsReader.swift @@ -59,8 +59,7 @@ public enum BedrockSettingsReader { if (value.hasPrefix("\"") && value.hasSuffix("\"")) || (value.hasPrefix("'") && value.hasSuffix("'")) { - value.removeFirst() - value.removeLast() + value = String(value.dropFirst().dropLast()) } value = value.trimmingCharacters(in: .whitespacesAndNewlines) diff --git a/Sources/CodexBarCore/Providers/Claude/ClaudeAdminAPISettingsReader.swift b/Sources/CodexBarCore/Providers/Claude/ClaudeAdminAPISettingsReader.swift index 90553b5c8..cbb179114 100644 --- a/Sources/CodexBarCore/Providers/Claude/ClaudeAdminAPISettingsReader.swift +++ b/Sources/CodexBarCore/Providers/Claude/ClaudeAdminAPISettingsReader.swift @@ -23,8 +23,7 @@ public enum ClaudeAdminAPISettingsReader { if (value.hasPrefix("\"") && value.hasSuffix("\"")) || (value.hasPrefix("'") && value.hasSuffix("'")) { - value.removeFirst() - value.removeLast() + value = String(value.dropFirst().dropLast()) } value = value.trimmingCharacters(in: .whitespacesAndNewlines) diff --git a/Sources/CodexBarCore/Providers/Codebuff/CodebuffSettingsReader.swift b/Sources/CodexBarCore/Providers/Codebuff/CodebuffSettingsReader.swift index dbfe85eeb..a046b43ca 100644 --- a/Sources/CodexBarCore/Providers/Codebuff/CodebuffSettingsReader.swift +++ b/Sources/CodexBarCore/Providers/Codebuff/CodebuffSettingsReader.swift @@ -54,8 +54,7 @@ public enum CodebuffSettingsReader { if (value.hasPrefix("\"") && value.hasSuffix("\"")) || (value.hasPrefix("'") && value.hasSuffix("'")) { - value.removeFirst() - value.removeLast() + value = String(value.dropFirst().dropLast()) } value = value.trimmingCharacters(in: .whitespacesAndNewlines) diff --git a/Sources/CodexBarCore/Providers/Crof/CrofSettingsReader.swift b/Sources/CodexBarCore/Providers/Crof/CrofSettingsReader.swift index f21949129..14680a9ce 100644 --- a/Sources/CodexBarCore/Providers/Crof/CrofSettingsReader.swift +++ b/Sources/CodexBarCore/Providers/Crof/CrofSettingsReader.swift @@ -19,8 +19,7 @@ public enum CrofSettingsReader { if (value.hasPrefix("\"") && value.hasSuffix("\"")) || (value.hasPrefix("'") && value.hasSuffix("'")) { - value.removeFirst() - value.removeLast() + value = String(value.dropFirst().dropLast()) } value = value.trimmingCharacters(in: .whitespacesAndNewlines) return value.isEmpty ? nil : value diff --git a/Sources/CodexBarCore/Providers/DeepSeek/DeepSeekSettingsReader.swift b/Sources/CodexBarCore/Providers/DeepSeek/DeepSeekSettingsReader.swift index 6f622d332..3c787cabc 100644 --- a/Sources/CodexBarCore/Providers/DeepSeek/DeepSeekSettingsReader.swift +++ b/Sources/CodexBarCore/Providers/DeepSeek/DeepSeekSettingsReader.swift @@ -26,8 +26,7 @@ public struct DeepSeekSettingsReader: Sendable { if (value.hasPrefix("\"") && value.hasSuffix("\"")) || (value.hasPrefix("'") && value.hasSuffix("'")) { - value.removeFirst() - value.removeLast() + value = String(value.dropFirst().dropLast()) } return value.trimmingCharacters(in: .whitespacesAndNewlines) } diff --git a/Sources/CodexBarCore/Providers/Deepgram/DeepgramSettingsReader.swift b/Sources/CodexBarCore/Providers/Deepgram/DeepgramSettingsReader.swift index 4ebe40242..beb86103f 100644 --- a/Sources/CodexBarCore/Providers/Deepgram/DeepgramSettingsReader.swift +++ b/Sources/CodexBarCore/Providers/Deepgram/DeepgramSettingsReader.swift @@ -24,8 +24,7 @@ public struct DeepgramSettingsReader: Sendable { if (value.hasPrefix("\"") && value.hasSuffix("\"")) || (value.hasPrefix("'") && value.hasSuffix("'")) { - value.removeFirst() - value.removeLast() + value = String(value.dropFirst().dropLast()) } value = value.trimmingCharacters(in: .whitespacesAndNewlines) diff --git a/Sources/CodexBarCore/Providers/Doubao/DoubaoSettingsReader.swift b/Sources/CodexBarCore/Providers/Doubao/DoubaoSettingsReader.swift index 43568d4a5..fc5942e54 100644 --- a/Sources/CodexBarCore/Providers/Doubao/DoubaoSettingsReader.swift +++ b/Sources/CodexBarCore/Providers/Doubao/DoubaoSettingsReader.swift @@ -29,8 +29,7 @@ public struct DoubaoSettingsReader: Sendable { if (value.hasPrefix("\"") && value.hasSuffix("\"")) || (value.hasPrefix("'") && value.hasSuffix("'")) { - value.removeFirst() - value.removeLast() + value = String(value.dropFirst().dropLast()) } return value.trimmingCharacters(in: .whitespacesAndNewlines) } diff --git a/Sources/CodexBarCore/Providers/ElevenLabs/ElevenLabsSettingsReader.swift b/Sources/CodexBarCore/Providers/ElevenLabs/ElevenLabsSettingsReader.swift index 6746e983b..bd3cdf4ca 100644 --- a/Sources/CodexBarCore/Providers/ElevenLabs/ElevenLabsSettingsReader.swift +++ b/Sources/CodexBarCore/Providers/ElevenLabs/ElevenLabsSettingsReader.swift @@ -32,8 +32,7 @@ public enum ElevenLabsSettingsReader { if (value.hasPrefix("\"") && value.hasSuffix("\"")) || (value.hasPrefix("'") && value.hasSuffix("'")) { - value.removeFirst() - value.removeLast() + value = String(value.dropFirst().dropLast()) } value = value.trimmingCharacters(in: .whitespacesAndNewlines) return value.isEmpty ? nil : value diff --git a/Sources/CodexBarCore/Providers/Groq/GroqSettingsReader.swift b/Sources/CodexBarCore/Providers/Groq/GroqSettingsReader.swift index 130a8d294..4b4f20804 100644 --- a/Sources/CodexBarCore/Providers/Groq/GroqSettingsReader.swift +++ b/Sources/CodexBarCore/Providers/Groq/GroqSettingsReader.swift @@ -28,8 +28,7 @@ public enum GroqSettingsReader { if (value.hasPrefix("\"") && value.hasSuffix("\"")) || (value.hasPrefix("'") && value.hasSuffix("'")) { - value.removeFirst() - value.removeLast() + value = String(value.dropFirst().dropLast()) } value = value.trimmingCharacters(in: .whitespacesAndNewlines) return value.isEmpty ? nil : value diff --git a/Sources/CodexBarCore/Providers/Kilo/KiloSettingsReader.swift b/Sources/CodexBarCore/Providers/Kilo/KiloSettingsReader.swift index b32f88b98..e315fc2e6 100644 --- a/Sources/CodexBarCore/Providers/Kilo/KiloSettingsReader.swift +++ b/Sources/CodexBarCore/Providers/Kilo/KiloSettingsReader.swift @@ -44,8 +44,7 @@ public enum KiloSettingsReader { if (value.hasPrefix("\"") && value.hasSuffix("\"")) || (value.hasPrefix("'") && value.hasSuffix("'")) { - value.removeFirst() - value.removeLast() + value = String(value.dropFirst().dropLast()) } value = value.trimmingCharacters(in: .whitespacesAndNewlines) diff --git a/Sources/CodexBarCore/Providers/Kimi/KimiSettingsReader.swift b/Sources/CodexBarCore/Providers/Kimi/KimiSettingsReader.swift index bcb29d9c0..4874ca4d5 100644 --- a/Sources/CodexBarCore/Providers/Kimi/KimiSettingsReader.swift +++ b/Sources/CodexBarCore/Providers/Kimi/KimiSettingsReader.swift @@ -14,8 +14,7 @@ public enum KimiSettingsReader { if (value.hasPrefix("\"") && value.hasSuffix("\"")) || (value.hasPrefix("'") && value.hasSuffix("'")) { - value.removeFirst() - value.removeLast() + value = String(value.dropFirst().dropLast()) } value = value.trimmingCharacters(in: .whitespacesAndNewlines) diff --git a/Sources/CodexBarCore/Providers/KimiK2/KimiK2SettingsReader.swift b/Sources/CodexBarCore/Providers/KimiK2/KimiK2SettingsReader.swift index 3e59199d4..507e597dc 100644 --- a/Sources/CodexBarCore/Providers/KimiK2/KimiK2SettingsReader.swift +++ b/Sources/CodexBarCore/Providers/KimiK2/KimiK2SettingsReader.swift @@ -29,8 +29,7 @@ public struct KimiK2SettingsReader: Sendable { if (value.hasPrefix("\"") && value.hasSuffix("\"")) || (value.hasPrefix("'") && value.hasSuffix("'")) { - value.removeFirst() - value.removeLast() + value = String(value.dropFirst().dropLast()) } return value.trimmingCharacters(in: .whitespacesAndNewlines) } diff --git a/Sources/CodexBarCore/Providers/LLMProxy/LLMProxySettingsReader.swift b/Sources/CodexBarCore/Providers/LLMProxy/LLMProxySettingsReader.swift index 47409b346..e753752f9 100644 --- a/Sources/CodexBarCore/Providers/LLMProxy/LLMProxySettingsReader.swift +++ b/Sources/CodexBarCore/Providers/LLMProxy/LLMProxySettingsReader.swift @@ -24,8 +24,7 @@ public enum LLMProxySettingsReader { if (value.hasPrefix("\"") && value.hasSuffix("\"")) || (value.hasPrefix("'") && value.hasSuffix("'")) { - value.removeFirst() - value.removeLast() + value = String(value.dropFirst().dropLast()) } value = value.trimmingCharacters(in: .whitespacesAndNewlines) return value.isEmpty ? nil : value diff --git a/Sources/CodexBarCore/Providers/Manus/ManusSettingsReader.swift b/Sources/CodexBarCore/Providers/Manus/ManusSettingsReader.swift index fa5874c12..3d9fbfa84 100644 --- a/Sources/CodexBarCore/Providers/Manus/ManusSettingsReader.swift +++ b/Sources/CodexBarCore/Providers/Manus/ManusSettingsReader.swift @@ -22,8 +22,7 @@ public enum ManusSettingsReader { if (value.hasPrefix("\"") && value.hasSuffix("\"")) || (value.hasPrefix("'") && value.hasSuffix("'")) { - value.removeFirst() - value.removeLast() + value = String(value.dropFirst().dropLast()) } value = value.trimmingCharacters(in: .whitespacesAndNewlines) diff --git a/Sources/CodexBarCore/Providers/MiniMax/MiniMaxAPISettingsReader.swift b/Sources/CodexBarCore/Providers/MiniMax/MiniMaxAPISettingsReader.swift index 08d495669..ab1a2359b 100644 --- a/Sources/CodexBarCore/Providers/MiniMax/MiniMaxAPISettingsReader.swift +++ b/Sources/CodexBarCore/Providers/MiniMax/MiniMaxAPISettingsReader.swift @@ -44,8 +44,7 @@ public struct MiniMaxAPISettingsReader: Sendable { if (value.hasPrefix("\"") && value.hasSuffix("\"")) || (value.hasPrefix("'") && value.hasSuffix("'")) { - value.removeFirst() - value.removeLast() + value = String(value.dropFirst().dropLast()) } value = value.trimmingCharacters(in: .whitespacesAndNewlines) diff --git a/Sources/CodexBarCore/Providers/MiniMax/MiniMaxSettingsReader.swift b/Sources/CodexBarCore/Providers/MiniMax/MiniMaxSettingsReader.swift index b78f709cd..0e0684fdc 100644 --- a/Sources/CodexBarCore/Providers/MiniMax/MiniMaxSettingsReader.swift +++ b/Sources/CodexBarCore/Providers/MiniMax/MiniMaxSettingsReader.swift @@ -56,8 +56,7 @@ public struct MiniMaxSettingsReader: Sendable { if (value.hasPrefix("\"") && value.hasSuffix("\"")) || (value.hasPrefix("'") && value.hasSuffix("'")) { - value.removeFirst() - value.removeLast() + value = String(value.dropFirst().dropLast()) } value = value.trimmingCharacters(in: .whitespacesAndNewlines) diff --git a/Sources/CodexBarCore/Providers/Moonshot/MoonshotSettingsReader.swift b/Sources/CodexBarCore/Providers/Moonshot/MoonshotSettingsReader.swift index 06bfb49de..b01db6eca 100644 --- a/Sources/CodexBarCore/Providers/Moonshot/MoonshotSettingsReader.swift +++ b/Sources/CodexBarCore/Providers/Moonshot/MoonshotSettingsReader.swift @@ -40,8 +40,7 @@ public struct MoonshotSettingsReader: Sendable { if (value.hasPrefix("\"") && value.hasSuffix("\"")) || (value.hasPrefix("'") && value.hasSuffix("'")) { - value.removeFirst() - value.removeLast() + value = String(value.dropFirst().dropLast()) } return value.trimmingCharacters(in: .whitespacesAndNewlines) } diff --git a/Sources/CodexBarCore/Providers/Ollama/OllamaUsageFetcher.swift b/Sources/CodexBarCore/Providers/Ollama/OllamaUsageFetcher.swift index 5b70674f0..6b58dd804 100644 --- a/Sources/CodexBarCore/Providers/Ollama/OllamaUsageFetcher.swift +++ b/Sources/CodexBarCore/Providers/Ollama/OllamaUsageFetcher.swift @@ -651,8 +651,7 @@ public struct OllamaAPISettingsReader: Sendable { if (value.hasPrefix("\"") && value.hasSuffix("\"")) || (value.hasPrefix("'") && value.hasSuffix("'")) { - value.removeFirst() - value.removeLast() + value = String(value.dropFirst().dropLast()) } return value.trimmingCharacters(in: .whitespacesAndNewlines) } diff --git a/Sources/CodexBarCore/Providers/OpenAI/OpenAIAPISettingsReader.swift b/Sources/CodexBarCore/Providers/OpenAI/OpenAIAPISettingsReader.swift index db03e8feb..61ab0a222 100644 --- a/Sources/CodexBarCore/Providers/OpenAI/OpenAIAPISettingsReader.swift +++ b/Sources/CodexBarCore/Providers/OpenAI/OpenAIAPISettingsReader.swift @@ -23,8 +23,7 @@ public enum OpenAIAPISettingsReader { if (value.hasPrefix("\"") && value.hasSuffix("\"")) || (value.hasPrefix("'") && value.hasSuffix("'")) { - value.removeFirst() - value.removeLast() + value = String(value.dropFirst().dropLast()) } value = value.trimmingCharacters(in: .whitespacesAndNewlines) diff --git a/Sources/CodexBarCore/Providers/OpenRouter/OpenRouterSettingsReader.swift b/Sources/CodexBarCore/Providers/OpenRouter/OpenRouterSettingsReader.swift index e5e3f4d78..1072ebe08 100644 --- a/Sources/CodexBarCore/Providers/OpenRouter/OpenRouterSettingsReader.swift +++ b/Sources/CodexBarCore/Providers/OpenRouter/OpenRouterSettingsReader.swift @@ -28,8 +28,7 @@ public enum OpenRouterSettingsReader { if (value.hasPrefix("\"") && value.hasSuffix("\"")) || (value.hasPrefix("'") && value.hasSuffix("'")) { - value.removeFirst() - value.removeLast() + value = String(value.dropFirst().dropLast()) } value = value.trimmingCharacters(in: .whitespacesAndNewlines) diff --git a/Sources/CodexBarCore/Providers/Perplexity/PerplexitySettingsReader.swift b/Sources/CodexBarCore/Providers/Perplexity/PerplexitySettingsReader.swift index e36eb0e60..ef1952b5a 100644 --- a/Sources/CodexBarCore/Providers/Perplexity/PerplexitySettingsReader.swift +++ b/Sources/CodexBarCore/Providers/Perplexity/PerplexitySettingsReader.swift @@ -29,8 +29,7 @@ public enum PerplexitySettingsReader { if (value.hasPrefix("\"") && value.hasSuffix("\"")) || (value.hasPrefix("'") && value.hasSuffix("'")) { - value.removeFirst() - value.removeLast() + value = String(value.dropFirst().dropLast()) } value = value.trimmingCharacters(in: .whitespacesAndNewlines) diff --git a/Sources/CodexBarCore/Providers/ProviderTokenResolver.swift b/Sources/CodexBarCore/Providers/ProviderTokenResolver.swift index 7e5b14ff9..cd555a6bd 100644 --- a/Sources/CodexBarCore/Providers/ProviderTokenResolver.swift +++ b/Sources/CodexBarCore/Providers/ProviderTokenResolver.swift @@ -380,8 +380,7 @@ public enum ProviderTokenResolver { if (value.hasPrefix("\"") && value.hasSuffix("\"")) || (value.hasPrefix("'") && value.hasSuffix("'")) { - value.removeFirst() - value.removeLast() + value = String(value.dropFirst().dropLast()) } value = value.trimmingCharacters(in: .whitespacesAndNewlines) diff --git a/Sources/CodexBarCore/Providers/StepFun/StepFunSettingsReader.swift b/Sources/CodexBarCore/Providers/StepFun/StepFunSettingsReader.swift index 9c3227e39..b8a497a6f 100644 --- a/Sources/CodexBarCore/Providers/StepFun/StepFunSettingsReader.swift +++ b/Sources/CodexBarCore/Providers/StepFun/StepFunSettingsReader.swift @@ -30,8 +30,7 @@ public struct StepFunSettingsReader: Sendable { if (value.hasPrefix("\"") && value.hasSuffix("\"")) || (value.hasPrefix("'") && value.hasSuffix("'")) { - value.removeFirst() - value.removeLast() + value = String(value.dropFirst().dropLast()) } value = value.trimmingCharacters(in: .whitespacesAndNewlines) return value.isEmpty ? nil : value diff --git a/Sources/CodexBarCore/Providers/Synthetic/SyntheticSettingsReader.swift b/Sources/CodexBarCore/Providers/Synthetic/SyntheticSettingsReader.swift index a6134f883..14e07f430 100644 --- a/Sources/CodexBarCore/Providers/Synthetic/SyntheticSettingsReader.swift +++ b/Sources/CodexBarCore/Providers/Synthetic/SyntheticSettingsReader.swift @@ -18,8 +18,7 @@ public struct SyntheticSettingsReader: Sendable { if (value.hasPrefix("\"") && value.hasSuffix("\"")) || (value.hasPrefix("'") && value.hasSuffix("'")) { - value.removeFirst() - value.removeLast() + value = String(value.dropFirst().dropLast()) } value = value.trimmingCharacters(in: .whitespacesAndNewlines) diff --git a/Sources/CodexBarCore/Providers/Venice/VeniceSettingsReader.swift b/Sources/CodexBarCore/Providers/Venice/VeniceSettingsReader.swift index 57386ac96..fcfffa0c3 100644 --- a/Sources/CodexBarCore/Providers/Venice/VeniceSettingsReader.swift +++ b/Sources/CodexBarCore/Providers/Venice/VeniceSettingsReader.swift @@ -26,8 +26,7 @@ public struct VeniceSettingsReader: Sendable { if (value.hasPrefix("\"") && value.hasSuffix("\"")) || (value.hasPrefix("'") && value.hasSuffix("'")) { - value.removeFirst() - value.removeLast() + value = String(value.dropFirst().dropLast()) } return value.trimmingCharacters(in: .whitespacesAndNewlines) } diff --git a/Sources/CodexBarCore/Providers/Warp/WarpSettingsReader.swift b/Sources/CodexBarCore/Providers/Warp/WarpSettingsReader.swift index cd3c639c1..afe5df0c3 100644 --- a/Sources/CodexBarCore/Providers/Warp/WarpSettingsReader.swift +++ b/Sources/CodexBarCore/Providers/Warp/WarpSettingsReader.swift @@ -28,8 +28,7 @@ public struct WarpSettingsReader: Sendable { if (value.hasPrefix("\"") && value.hasSuffix("\"")) || (value.hasPrefix("'") && value.hasSuffix("'")) { - value.removeFirst() - value.removeLast() + value = String(value.dropFirst().dropLast()) } return value.trimmingCharacters(in: .whitespacesAndNewlines) } diff --git a/Sources/CodexBarCore/Providers/Zai/ZaiSettingsReader.swift b/Sources/CodexBarCore/Providers/Zai/ZaiSettingsReader.swift index 4df2223e3..637913797 100644 --- a/Sources/CodexBarCore/Providers/Zai/ZaiSettingsReader.swift +++ b/Sources/CodexBarCore/Providers/Zai/ZaiSettingsReader.swift @@ -38,8 +38,7 @@ public struct ZaiSettingsReader: Sendable { if (value.hasPrefix("\"") && value.hasSuffix("\"")) || (value.hasPrefix("'") && value.hasSuffix("'")) { - value.removeFirst() - value.removeLast() + value = String(value.dropFirst().dropLast()) } value = value.trimmingCharacters(in: .whitespacesAndNewlines) diff --git a/TestsLinux/SettingsReaderQuoteUnwrapTrapTests.swift b/TestsLinux/SettingsReaderQuoteUnwrapTrapTests.swift new file mode 100644 index 000000000..91c0e1e7a --- /dev/null +++ b/TestsLinux/SettingsReaderQuoteUnwrapTrapTests.swift @@ -0,0 +1,75 @@ +import CodexBarCore +import Foundation +import Testing + +/// Regression tests for a copy-pasted quote-unwrap helper that traps on length-1 input. +/// +/// 32 provider settings readers (plus `CodexBarConfig` and `CLIConfigCommand`) share a +/// `cleaned(_:)` helper of the form: +/// +/// if (value.hasPrefix("\"") && value.hasSuffix("\"")) || +/// (value.hasPrefix("'") && value.hasSuffix("'")) +/// { +/// value.removeFirst() +/// value.removeLast() +/// } +/// +/// For a value of length 1 (the single character `"` or `'`), both `hasPrefix` and +/// `hasSuffix` return true, `removeFirst()` empties the string, and `removeLast()` then +/// traps with "Can't remove last element from empty collection." This is reachable from +/// a misconfigured env var (e.g. `ALIBABA_TOKEN_PLAN_COOKIE='"'`) and from quoted JSON +/// values in `~/.codexbar/config.json`, both of which are user-controllable. +/// +/// These tests exercise two representative public readers — Alibaba Token Plan (the +/// newest addition in #1098) and the Ollama API key reader (added in #1087) — by +/// passing the trap-inducing single-quote inputs and asserting the readers return nil +/// instead of crashing. The patch swaps `removeFirst()/removeLast()` for +/// `String(value.dropFirst().dropLast())`, which is empty-safe. +@Suite +struct SettingsReaderQuoteUnwrapTrapTests { + @Test + func alibabaTokenPlanCookieHeader_returnsNilForLoneDoubleQuoteValue() { + let env = [AlibabaTokenPlanSettingsReader.cookieHeaderKey: "\""] + #expect(AlibabaTokenPlanSettingsReader.cookieHeader(environment: env) == nil) + } + + @Test + func alibabaTokenPlanCookieHeader_returnsNilForLoneApostropheValue() { + let env = [AlibabaTokenPlanSettingsReader.cookieHeaderKey: "'"] + #expect(AlibabaTokenPlanSettingsReader.cookieHeader(environment: env) == nil) + } + + @Test + func alibabaTokenPlanCookieHeader_unwrapsProperlyDoubleQuotedValue() { + let env = [AlibabaTokenPlanSettingsReader.cookieHeaderKey: "\"abc=def\""] + #expect(AlibabaTokenPlanSettingsReader.cookieHeader(environment: env) == "abc=def") + } + + @Test + func alibabaTokenPlanCookieHeader_unwrapsProperlySingleQuotedValue() { + let env = [AlibabaTokenPlanSettingsReader.cookieHeaderKey: "'abc=def'"] + #expect(AlibabaTokenPlanSettingsReader.cookieHeader(environment: env) == "abc=def") + } + + @Test + func ollamaAPIKey_returnsNilForLoneDoubleQuoteValue() { + for key in OllamaAPISettingsReader.apiKeyEnvironmentKeys { + let env = [key: "\""] + #expect(OllamaAPISettingsReader.apiKey(environment: env) == nil) + } + } + + @Test + func ollamaAPIKey_returnsNilForLoneApostropheValue() { + for key in OllamaAPISettingsReader.apiKeyEnvironmentKeys { + let env = [key: "'"] + #expect(OllamaAPISettingsReader.apiKey(environment: env) == nil) + } + } + + @Test + func ollamaAPIKey_unwrapsProperlyQuotedValue() { + let env = [OllamaAPISettingsReader.apiKeyEnvironmentKeys[0]: "\"sk-token\""] + #expect(OllamaAPISettingsReader.apiKey(environment: env) == "sk-token") + } +}