diff --git a/Swabble/Package.swift b/Swabble/Package.swift index 588bd01d8..1c80254bd 100644 --- a/Swabble/Package.swift +++ b/Swabble/Package.swift @@ -33,5 +33,4 @@ let package = Package( .product(name: "Testing", package: "swift-testing"), ]), ], - swiftLanguageModes: [.v6] -) + swiftLanguageModes: [.v6]) diff --git a/Swabble/Sources/SwabbleCore/Hooks/HookRunner.swift b/Swabble/Sources/SwabbleCore/Hooks/HookRunner.swift index e94b0b002..e50740116 100644 --- a/Swabble/Sources/SwabbleCore/Hooks/HookRunner.swift +++ b/Swabble/Sources/SwabbleCore/Hooks/HookRunner.swift @@ -17,11 +17,11 @@ public actor HookRunner { public init(config: SwabbleConfig) { self.config = config - self.hostname = Host.current().localizedName ?? "host" + hostname = Host.current().localizedName ?? "host" } public func shouldRun() -> Bool { - guard self.config.hook.cooldownSeconds > 0 else { return true } + guard config.hook.cooldownSeconds > 0 else { return true } if let lastRun, Date().timeIntervalSince(lastRun) < config.hook.cooldownSeconds { return false } @@ -29,23 +29,23 @@ public actor HookRunner { } public func run(job: HookJob) async throws { - guard self.shouldRun() else { return } - guard !self.config.hook.command.isEmpty else { throw NSError( + guard shouldRun() else { return } + guard !config.hook.command.isEmpty else { throw NSError( domain: "Hook", code: 1, userInfo: [NSLocalizedDescriptionKey: "hook command not set"]) } - let prefix = self.config.hook.prefix.replacingOccurrences(of: "${hostname}", with: self.hostname) + let prefix = config.hook.prefix.replacingOccurrences(of: "${hostname}", with: hostname) let payload = prefix + job.text let process = Process() - process.executableURL = URL(fileURLWithPath: self.config.hook.command) - process.arguments = self.config.hook.args + [payload] + process.executableURL = URL(fileURLWithPath: config.hook.command) + process.arguments = config.hook.args + [payload] var env = ProcessInfo.processInfo.environment env["SWABBLE_TEXT"] = job.text env["SWABBLE_PREFIX"] = prefix - for (k, v) in self.config.hook.env { + for (k, v) in config.hook.env { env[k] = v } process.environment = env @@ -70,6 +70,6 @@ public actor HookRunner { try await group.next() group.cancelAll() } - self.lastRun = Date() + lastRun = Date() } } diff --git a/Swabble/Sources/SwabbleCore/Speech/SpeechPipeline.swift b/Swabble/Sources/SwabbleCore/Speech/SpeechPipeline.swift index faecc64d8..27a9c8dad 100644 --- a/Swabble/Sources/SwabbleCore/Speech/SpeechPipeline.swift +++ b/Swabble/Sources/SwabbleCore/Speech/SpeechPipeline.swift @@ -35,18 +35,18 @@ public actor SpeechPipeline { transcriptionOptions: etiquette ? [.etiquetteReplacements] : [], reportingOptions: [.volatileResults], attributeOptions: []) - self.transcriber = transcriberModule + transcriber = transcriberModule guard let analyzerFormat = await SpeechAnalyzer.bestAvailableAudioFormat(compatibleWith: [transcriberModule]) else { throw SpeechPipelineError.analyzerFormatUnavailable } - self.analyzer = SpeechAnalyzer(modules: [transcriberModule]) + analyzer = SpeechAnalyzer(modules: [transcriberModule]) let (stream, continuation) = AsyncStream.makeStream() - self.inputContinuation = continuation + inputContinuation = continuation - let inputNode = self.engine.inputNode + let inputNode = engine.inputNode let inputFormat = inputNode.outputFormat(forBus: 0) inputNode.removeTap(onBus: 0) inputNode.installTap(onBus: 0, bufferSize: 2048, format: inputFormat) { [weak self] buffer, _ in @@ -55,11 +55,11 @@ public actor SpeechPipeline { Task { await self.handleBuffer(boxed.buffer, targetFormat: analyzerFormat) } } - self.engine.prepare() - try self.engine.start() - try await self.analyzer?.start(inputSequence: stream) + engine.prepare() + try engine.start() + try await analyzer?.start(inputSequence: stream) - guard let transcriberForStream = self.transcriber else { + guard let transcriberForStream = transcriber else { throw SpeechPipelineError.transcriberUnavailable } @@ -82,18 +82,18 @@ public actor SpeechPipeline { } public func stop() async { - self.resultTask?.cancel() - self.inputContinuation?.finish() - self.engine.inputNode.removeTap(onBus: 0) - self.engine.stop() - try? await self.analyzer?.finalizeAndFinishThroughEndOfInput() + resultTask?.cancel() + inputContinuation?.finish() + engine.inputNode.removeTap(onBus: 0) + engine.stop() + try? await analyzer?.finalizeAndFinishThroughEndOfInput() } private func handleBuffer(_ buffer: AVAudioPCMBuffer, targetFormat: AVAudioFormat) async { do { let converted = try converter.convert(buffer, to: targetFormat) let input = AnalyzerInput(buffer: converted) - self.inputContinuation?.yield(input) + inputContinuation?.yield(input) } catch { // drop on conversion failure } diff --git a/Swabble/Sources/SwabbleCore/Support/Logging.swift b/Swabble/Sources/SwabbleCore/Support/Logging.swift index 695567ab8..7d917417f 100644 --- a/Swabble/Sources/SwabbleCore/Support/Logging.swift +++ b/Swabble/Sources/SwabbleCore/Support/Logging.swift @@ -27,11 +27,11 @@ public struct Logger: Sendable { print("[\(level.rawValue.uppercased())] \(ts) | \(message)") } - public func trace(_ msg: String) { self.log(.trace, msg) } - public func debug(_ msg: String) { self.log(.debug, msg) } - public func info(_ msg: String) { self.log(.info, msg) } - public func warn(_ msg: String) { self.log(.warn, msg) } - public func error(_ msg: String) { self.log(.error, msg) } + public func trace(_ msg: String) { log(.trace, msg) } + public func debug(_ msg: String) { log(.debug, msg) } + public func info(_ msg: String) { log(.info, msg) } + public func warn(_ msg: String) { log(.warn, msg) } + public func error(_ msg: String) { log(.error, msg) } } extension LogLevel { diff --git a/Swabble/Sources/SwabbleCore/Support/TranscriptsStore.swift b/Swabble/Sources/SwabbleCore/Support/TranscriptsStore.swift index a62eb5b30..312ee07cd 100644 --- a/Swabble/Sources/SwabbleCore/Support/TranscriptsStore.swift +++ b/Swabble/Sources/SwabbleCore/Support/TranscriptsStore.swift @@ -11,24 +11,24 @@ public actor TranscriptsStore { let dir = FileManager.default.homeDirectoryForCurrentUser .appendingPathComponent("Library/Application Support/swabble", isDirectory: true) try? FileManager.default.createDirectory(at: dir, withIntermediateDirectories: true) - self.fileURL = dir.appendingPathComponent("transcripts.log") + fileURL = dir.appendingPathComponent("transcripts.log") if let data = try? Data(contentsOf: fileURL), let text = String(data: data, encoding: .utf8) { - self.entries = text.split(separator: "\n").map(String.init).suffix(self.limit) + entries = text.split(separator: "\n").map(String.init).suffix(limit) } } public func append(text: String) { - self.entries.append(text) - if self.entries.count > self.limit { - self.entries.removeFirst(self.entries.count - self.limit) + entries.append(text) + if entries.count > limit { + entries.removeFirst(entries.count - limit) } - let body = self.entries.joined(separator: "\n") - try? body.write(to: self.fileURL, atomically: false, encoding: .utf8) + let body = entries.joined(separator: "\n") + try? body.write(to: fileURL, atomically: false, encoding: .utf8) } - public func latest() -> [String] { self.entries } + public func latest() -> [String] { entries } } extension String { diff --git a/Swabble/Sources/swabble/Commands/DoctorCommand.swift b/Swabble/Sources/swabble/Commands/DoctorCommand.swift index fc37e36e0..ec6c84ad4 100644 --- a/Swabble/Sources/swabble/Commands/DoctorCommand.swift +++ b/Swabble/Sources/swabble/Commands/DoctorCommand.swift @@ -14,14 +14,14 @@ struct DoctorCommand: ParsableCommand { init() {} init(parsed: ParsedValues) { self.init() - if let cfg = parsed.options["config"]?.last { self.configPath = cfg } + if let cfg = parsed.options["config"]?.last { configPath = cfg } } mutating func run() async throws { let auth = await SFSpeechRecognizer.authorizationStatus() print("Speech auth: \(auth)") do { - _ = try ConfigLoader.load(at: self.configURL) + _ = try ConfigLoader.load(at: configURL) print("Config: OK") } catch { print("Config missing or invalid; run setup") @@ -33,5 +33,5 @@ struct DoctorCommand: ParsableCommand { print("Mics found: \(session.devices.count)") } - private var configURL: URL? { self.configPath.map { URL(fileURLWithPath: $0) } } + private var configURL: URL? { configPath.map { URL(fileURLWithPath: $0) } } } diff --git a/Swabble/Sources/swabble/Commands/MicCommands.swift b/Swabble/Sources/swabble/Commands/MicCommands.swift index 3c31f74fe..6430c86d5 100644 --- a/Swabble/Sources/swabble/Commands/MicCommands.swift +++ b/Swabble/Sources/swabble/Commands/MicCommands.swift @@ -47,16 +47,16 @@ struct MicSet: ParsableCommand { init() {} init(parsed: ParsedValues) { self.init() - if let value = parsed.positional.first, let intVal = Int(value) { self.index = intVal } - if let cfg = parsed.options["config"]?.last { self.configPath = cfg } + if let value = parsed.positional.first, let intVal = Int(value) { index = intVal } + if let cfg = parsed.options["config"]?.last { configPath = cfg } } mutating func run() async throws { - var cfg = try ConfigLoader.load(at: self.configURL) - cfg.audio.deviceIndex = self.index - try ConfigLoader.save(cfg, at: self.configURL) - print("saved device index \(self.index)") + var cfg = try ConfigLoader.load(at: configURL) + cfg.audio.deviceIndex = index + try ConfigLoader.save(cfg, at: configURL) + print("saved device index \(index)") } - private var configURL: URL? { self.configPath.map { URL(fileURLWithPath: $0) } } + private var configURL: URL? { configPath.map { URL(fileURLWithPath: $0) } } } diff --git a/Swabble/Sources/swabble/Commands/ServeCommand.swift b/Swabble/Sources/swabble/Commands/ServeCommand.swift index 1493402d5..b9af6d650 100644 --- a/Swabble/Sources/swabble/Commands/ServeCommand.swift +++ b/Swabble/Sources/swabble/Commands/ServeCommand.swift @@ -17,19 +17,19 @@ struct ServeCommand: ParsableCommand { init(parsed: ParsedValues) { self.init() - if parsed.flags.contains("noWake") { self.noWake = true } - if let cfg = parsed.options["config"]?.last { self.configPath = cfg } + if parsed.flags.contains("noWake") { noWake = true } + if let cfg = parsed.options["config"]?.last { configPath = cfg } } mutating func run() async throws { var cfg: SwabbleConfig do { - cfg = try ConfigLoader.load(at: self.configURL) + cfg = try ConfigLoader.load(at: configURL) } catch { cfg = SwabbleConfig() - try ConfigLoader.save(cfg, at: self.configURL) + try ConfigLoader.save(cfg, at: configURL) } - if self.noWake { + if noWake { cfg.wake.enabled = false } @@ -64,7 +64,7 @@ struct ServeCommand: ParsableCommand { } private var configURL: URL? { - self.configPath.map { URL(fileURLWithPath: $0) } + configPath.map { URL(fileURLWithPath: $0) } } private static func matchesWake(text: String, cfg: SwabbleConfig) -> Bool { diff --git a/Swabble/Sources/swabble/Commands/ServiceCommands.swift b/Swabble/Sources/swabble/Commands/ServiceCommands.swift index 70b721378..3a8f31cdb 100644 --- a/Swabble/Sources/swabble/Commands/ServiceCommands.swift +++ b/Swabble/Sources/swabble/Commands/ServiceCommands.swift @@ -28,11 +28,11 @@ private enum LaunchdHelper { "KeepAlive": true, ] let data = try PropertyListSerialization.data(fromPropertyList: plist, format: .xml, options: 0) - try data.write(to: self.plistURL) + try data.write(to: plistURL) } static func removePlist() throws { - try? FileManager.default.removeItem(at: self.plistURL) + try? FileManager.default.removeItem(at: plistURL) } } diff --git a/Swabble/Sources/swabble/Commands/SetupCommand.swift b/Swabble/Sources/swabble/Commands/SetupCommand.swift index 240f4b5ce..469de233d 100644 --- a/Swabble/Sources/swabble/Commands/SetupCommand.swift +++ b/Swabble/Sources/swabble/Commands/SetupCommand.swift @@ -13,14 +13,14 @@ struct SetupCommand: ParsableCommand { init() {} init(parsed: ParsedValues) { self.init() - if let cfg = parsed.options["config"]?.last { self.configPath = cfg } + if let cfg = parsed.options["config"]?.last { configPath = cfg } } mutating func run() async throws { let cfg = SwabbleConfig() - try ConfigLoader.save(cfg, at: self.configURL) - print("wrote config to \(self.configURL?.path ?? SwabbleConfig.defaultPath.path)") + try ConfigLoader.save(cfg, at: configURL) + print("wrote config to \(configURL?.path ?? SwabbleConfig.defaultPath.path)") } - private var configURL: URL? { self.configPath.map { URL(fileURLWithPath: $0) } } + private var configURL: URL? { configPath.map { URL(fileURLWithPath: $0) } } } diff --git a/Swabble/Sources/swabble/Commands/StatusCommand.swift b/Swabble/Sources/swabble/Commands/StatusCommand.swift index ed68fbe52..19db16117 100644 --- a/Swabble/Sources/swabble/Commands/StatusCommand.swift +++ b/Swabble/Sources/swabble/Commands/StatusCommand.swift @@ -13,11 +13,11 @@ struct StatusCommand: ParsableCommand { init() {} init(parsed: ParsedValues) { self.init() - if let cfg = parsed.options["config"]?.last { self.configPath = cfg } + if let cfg = parsed.options["config"]?.last { configPath = cfg } } mutating func run() async throws { - let cfg = try? ConfigLoader.load(at: self.configURL) + let cfg = try? ConfigLoader.load(at: configURL) let wake = cfg?.wake.word ?? "clawd" let wakeEnabled = cfg?.wake.enabled ?? false let latest = await TranscriptsStore.shared.latest().suffix(3) @@ -30,5 +30,5 @@ struct StatusCommand: ParsableCommand { } } - private var configURL: URL? { self.configPath.map { URL(fileURLWithPath: $0) } } + private var configURL: URL? { configPath.map { URL(fileURLWithPath: $0) } } } diff --git a/Swabble/Sources/swabble/Commands/TestHookCommand.swift b/Swabble/Sources/swabble/Commands/TestHookCommand.swift index 7ca64bbf4..6cb4ae3c6 100644 --- a/Swabble/Sources/swabble/Commands/TestHookCommand.swift +++ b/Swabble/Sources/swabble/Commands/TestHookCommand.swift @@ -15,16 +15,16 @@ struct TestHookCommand: ParsableCommand { init(parsed: ParsedValues) { self.init() - if let positional = parsed.positional.first { self.text = positional } - if let cfg = parsed.options["config"]?.last { self.configPath = cfg } + if let positional = parsed.positional.first { text = positional } + if let cfg = parsed.options["config"]?.last { configPath = cfg } } mutating func run() async throws { - let cfg = try ConfigLoader.load(at: self.configURL) + let cfg = try ConfigLoader.load(at: configURL) let runner = HookRunner(config: cfg) - try await runner.run(job: HookJob(text: self.text, timestamp: Date())) + try await runner.run(job: HookJob(text: text, timestamp: Date())) print("hook invoked") } - private var configURL: URL? { self.configPath.map { URL(fileURLWithPath: $0) } } + private var configURL: URL? { configPath.map { URL(fileURLWithPath: $0) } } } diff --git a/Swabble/Sources/swabble/Commands/TranscribeCommand.swift b/Swabble/Sources/swabble/Commands/TranscribeCommand.swift index 81d157891..1bedca3fc 100644 --- a/Swabble/Sources/swabble/Commands/TranscribeCommand.swift +++ b/Swabble/Sources/swabble/Commands/TranscribeCommand.swift @@ -24,12 +24,12 @@ struct TranscribeCommand: ParsableCommand { init(parsed: ParsedValues) { self.init() - if let positional = parsed.positional.first { self.inputFile = positional } - if let loc = parsed.options["locale"]?.last { self.locale = loc } - if parsed.flags.contains("censor") { self.censor = true } - if let out = parsed.options["output"]?.last { self.outputFile = out } - if let fmt = parsed.options["format"]?.last { self.format = fmt } - if let len = parsed.options["maxLength"]?.last, let intVal = Int(len) { self.maxLength = intVal } + if let positional = parsed.positional.first { inputFile = positional } + if let loc = parsed.options["locale"]?.last { locale = loc } + if parsed.flags.contains("censor") { censor = true } + if let out = parsed.options["output"]?.last { outputFile = out } + if let fmt = parsed.options["format"]?.last { format = fmt } + if let len = parsed.options["maxLength"]?.last, let intVal = Int(len) { maxLength = intVal } } mutating func run() async throws { @@ -51,7 +51,7 @@ struct TranscribeCommand: ParsableCommand { transcript += result.text } - let output = outputFormat.text(for: transcript, maxLength: self.maxLength) + let output = outputFormat.text(for: transcript, maxLength: maxLength) if let path = outputFile { try output.write(to: URL(fileURLWithPath: path), atomically: false, encoding: .utf8) } else {