QGMGDD3MPCHENK66ZX3M3KMIGP7C6ZVXTAEABMP25UARSEJQL5LQC // Copyright © 2024 Ryan Booker. All rights reserved.import ArgumentParserimport Foundationextension URL: ExpressibleByArgument {public init?(argument: String) {self.init(string: argument)}public var defaultValueDescription: String {absoluteString}}
// Copyright © 2024 Ryan Booker. All rights reserved.import ArgumentParserimport Foundationextension Kagi {struct Summarize: AsyncParsableCommand {static let configuration = CommandConfiguration(commandName: "summarize",abstract: "Summarize something.",usage: "kg summarize \"Some text or a url to summarize.\"")@Argument(help: "Some text or a url to summarize") var query: Either<URL, String>@Option(help: Engine.help) var engine: Engine?@Option(help: SummaryType.help) var summaryType: SummaryType?mutating func run() async throws {do {let response = try await process(query, engine: engine, summaryType: summaryType)print(response)} catch {throw ValidationError(error.localizedDescription)}}}}// MARK: - Processingextension Kagi.Summarize {func process(_ query: Either<URL, String>, engine: Engine?, summaryType: SummaryType?) async throws -> Kagi.Response<Summarization> {var request = try Kagi.Endpoint.summarize.authorizedRequestvar body: [String: Any] = query.reduce(success: { ["url": $0.absoluteString] }, failure: { ["text": $0] })if let engine {body["engine"] = engine.rawValue}if let summaryType {body["summary_type"] = summaryType.rawValue}request.httpBody = try JSONSerialization.data(withJSONObject: body)let (data, response) = try await URLSession.shared.data(for: request)do {return try JSONDecoder().decode(Kagi.Response.self, from: data)} catch {#if DEBUGprint(response)print(error)print("-----")#endifthrow error}}}// MARK: - Responseextension Kagi.Summarize {enum Engine: String, CaseIterable, CustomStringConvertible, ExpressibleByArgument {case cecilcase agnescase daphnecase murielvar description: String {switch self {case .cecil:"Friendly, descriptive, fast summary (default)"case .agnes:"Formal, technical, analytical summary"case .daphne:"Informal, creative, friendly summary"case .muriel:"Best-in-class summary using our enterprise-grade model"}}static var help: ArgumentHelp? {.init(allCases.map { "- \($0.rawValue): \($0.description)" }.joined(separator: "\n") + "\n")}var defaultValueDescription: String {Self.cecil.rawValue}}enum SummaryType: String, CaseIterable, CustomStringConvertible, ExpressibleByArgument {case summarycase takeawayvar description: String {switch self {case .summary:"Paragraph(s) of summary prose (default)"case .takeaway:"Bulleted list of key points"}}static var help: ArgumentHelp? {.init(allCases.map { "- \($0.rawValue): \($0.description)" }.joined(separator: "\n") + "\n")}var defaultValueDescription: String {Self.summary.rawValue}}struct Summarization: CustomStringConvertible, Decodable {var output: Stringvar tokens: Intvar description: String {"""Summary:\(output)"""}}}
// Copyright © 2024 Ryan Booker. All rights reserved.import Foundationextension Result {func reduce<T>(success successResult: (Success) -> T,failure failureResult: (Failure) -> T) -> T {switch self {case let .success(value):successResult(value)case let .failure(error):failureResult(error)}}static func <|> <T, E>(lhs: Result<T, E>, rhs: Result<T, E>) -> Result<T, E> {lhs.flatMapError { _ inrhs}}}infix operator <|> : NilCoalescingPrecedence
// Copyright © 2024 Ryan Booker. All rights reserved.import Foundationextension Kagi {struct Response<T>: CustomStringConvertible, Decodable where T: CustomStringConvertible & Decodable {let meta: Metadatalet data: Result<T, Failure>var description: String {data.reduce(success: \.description, failure: \.description)}enum CodingKeys: CodingKey {case meta, data, error}init(from decoder: Decoder) throws {let container = try decoder.container(keyedBy: CodingKeys.self)self.meta = try container.decode(Metadata.self, forKey: .meta)do {let data = try container.decode(T.self, forKey: .data)self.data = .success(data)} catch {let reasons = try container.decode([Failure.Reason].self, forKey: .error)self.data = .failure(.init(reasons: reasons))}}}}// MARK: - Repsonse typesextension Kagi.Response {struct Failure: CustomStringConvertible, Decodable, Error {struct Reason: CustomStringConvertible, Decodable {let code: Intlet msg: Stringvar description: String {"\(code): \(msg)"}}let reasons: [Reason]var description: String {"""Whoops!\(reasons.map(\.description).joined(separator: "\n\n"))"""}}struct Metadata: Decodable {let id: Stringlet node: Stringlet ms: Int}}
// The Swift Programming Language// https://docs.swift.org/swift-book//// Swift Argument Parser// https://swiftpackageindex.com/apple/swift-argument-parser/documentationimport ArgumentParserimport Foundation@mainstruct Kagi: AsyncParsableCommand {static let configuration = CommandConfiguration(commandName: "kg",abstract: "Interact with Kagi services.",subcommands: [FastGPT.self, Summarize.self])}
// Copyright © 2024 Ryan Booker. All rights reserved.import ArgumentParserimport Foundationextension Kagi {struct FastGPT: AsyncParsableCommand {static let configuration = CommandConfiguration(commandName: "fastgpt",abstract: "Ask FastGPT something.",usage: "kg fastgpt \"What is the answer to life, the universe, and everything?\"")@Argument(help: "A query for FastGPT") var query: Stringmutating func run() async throws {do {let response = try await process(query)print(response)} catch {throw ValidationError(error.localizedDescription)}}}}// MARK: - Processingextension Kagi.FastGPT {func process(_ query: String) async throws -> Kagi.Response<Answer> {var request = try Kagi.Endpoint.fastGPT.authorizedRequestrequest.httpBody = try JSONSerialization.data(withJSONObject: ["query": query])let (data, response) = try await URLSession.shared.data(for: request)do {return try JSONDecoder().decode(Kagi.Response.self, from: data)} catch {#if DEBUGprint(response)print(error)print("-----")#endifthrow error}}}// MARK: - Responseextension Kagi.FastGPT {struct Answer: CustomStringConvertible, Decodable {struct Reference: CustomStringConvertible, Decodable {var title: Stringvar snippet: Stringvar url: URLvar description: String {"""- \(title)\(url)\(snippet.trimmingCharacters(in: .whitespaces))"""}}var output: Stringvar tokens: Intvar references: [Reference]var description: String {"""Answer:\(output)References:\(references.map(\.description).joined(separator: "\n\n"))"""}}}
// Copyright © 2024 Ryan Booker. All rights reserved.import ArgumentParserimport Foundationextension Kagi {enum Endpoint: String {static let baseUrl = URL(string: "https://kagi.com/api/v0/")!case fastGPTcase summarizevar url: URL {URL(string: rawValue.lowercased(), relativeTo: Self.baseUrl)!}var authorizedRequest: URLRequest {get throws {guard let apiToken = ProcessInfo.processInfo.environment["FASTGPT_API_TOKEN"] else {throw ValidationError("API key is missing. Set the environment variable FASTGPT_API_TOKEN.")}var request = URLRequest(url: url)request.setValue("application/json", forHTTPHeaderField: "Content-Type")request.setValue("Bot \(apiToken)", forHTTPHeaderField: "Authorization")request.httpMethod = "POST"return request}}}}
// Copyright © 2024 Ryan Booker. All rights reserved.import ArgumentParserimport Foundationenum Either<First, Second> {case first(First)case second(Second)func reduce<T>(success firstResult: (First) -> T,failure secondResult: (Second) -> T) -> T {switch self {case let .first(value):firstResult(value)case let .second(error):secondResult(error)}}}extension Either: ExpressibleByArgumentwhere First: ExpressibleByArgument, Second: ExpressibleByArgument {init?(argument: String) {if let value = First(argument: argument) {self = .first(value)} else if let value = Second(argument: argument) {self = .second(value)} else {return nil}}}
// swift-tools-version: 5.9// The swift-tools-version declares the minimum version of Swift required to build this package.import PackageDescriptionlet package = Package(name: "Kagi",platforms: [.macOS(.v12),],dependencies: [.package(url: "https://github.com/apple/swift-argument-parser.git", from: "1.2.0"),],targets: [// Targets are the basic building blocks of a package, defining a module or a test suite.// Targets can depend on other targets in this package and products from dependencies..executableTarget(name: "kg",dependencies: [.product(name: "ArgumentParser", package: "swift-argument-parser"),]),])
{"pins" : [{"identity" : "swift-argument-parser","kind" : "remoteSourceControl","location" : "https://github.com/apple/swift-argument-parser.git","state" : {"revision" : "c8ed701b513cf5177118a175d85fbbbcd707ab41","version" : "1.3.0"}}],"version" : 2}
<?xml version="1.0" encoding="UTF-8"?><SchemeLastUpgradeVersion = "1520"version = "1.7"><BuildActionparallelizeBuildables = "YES"buildImplicitDependencies = "YES"><BuildActionEntries><BuildActionEntrybuildForTesting = "YES"buildForRunning = "YES"buildForProfiling = "YES"buildForArchiving = "YES"buildForAnalyzing = "YES"><BuildableReferenceBuildableIdentifier = "primary"BlueprintIdentifier = "kg"BuildableName = "kg"BlueprintName = "kg"ReferencedContainer = "container:"></BuildableReference></BuildActionEntry></BuildActionEntries></BuildAction><TestActionbuildConfiguration = "Debug"selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"shouldUseLaunchSchemeArgsEnv = "YES"shouldAutocreateTestPlan = "YES"></TestAction><LaunchActionbuildConfiguration = "Debug"selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"launchStyle = "0"useCustomWorkingDirectory = "NO"ignoresPersistentStateOnLaunch = "NO"debugDocumentVersioning = "YES"debugServiceExtension = "internal"allowLocationSimulation = "YES"><BuildableProductRunnablerunnableDebuggingMode = "0"><BuildableReferenceBuildableIdentifier = "primary"BlueprintIdentifier = "kg"BuildableName = "kg"BlueprintName = "kg"ReferencedContainer = "container:"></BuildableReference></BuildableProductRunnable><CommandLineArguments><CommandLineArgumentargument = "summarize https://hypercritical.co/2024/01/11/i-made-this --summary-type takeaway"isEnabled = "YES"></CommandLineArgument><CommandLineArgumentargument = "help summarize"isEnabled = "NO"></CommandLineArgument><CommandLineArgumentargument = "fastgpt "What is the answer to life, the universe, and everything?""isEnabled = "NO"></CommandLineArgument><CommandLineArgumentargument = "help fastgpt"isEnabled = "NO"></CommandLineArgument></CommandLineArguments></LaunchAction><ProfileActionbuildConfiguration = "Release"shouldUseLaunchSchemeArgsEnv = "YES"savedToolIdentifier = ""useCustomWorkingDirectory = "NO"debugDocumentVersioning = "YES"><BuildableProductRunnablerunnableDebuggingMode = "0"><BuildableReferenceBuildableIdentifier = "primary"BlueprintIdentifier = "kg"BuildableName = "kg"BlueprintName = "kg"ReferencedContainer = "container:"></BuildableReference></BuildableProductRunnable></ProfileAction><AnalyzeActionbuildConfiguration = "Debug"></AnalyzeAction><ArchiveActionbuildConfiguration = "Release"revealArchiveInOrganizer = "YES"></ArchiveAction></Scheme>
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"><plist version="1.0"><dict><key>IDEDidComputeMac32BitWarning</key><true/></dict></plist>