diff --git a/Coder-Desktop/VPNLib/FileSync/FileSyncDaemon.swift b/Coder-Desktop/VPNLib/FileSync/FileSyncDaemon.swift index 00633744..eafd4dc7 100644 --- a/Coder-Desktop/VPNLib/FileSync/FileSyncDaemon.swift +++ b/Coder-Desktop/VPNLib/FileSync/FileSyncDaemon.swift @@ -19,7 +19,7 @@ public protocol FileSyncDaemon: ObservableObject { @MainActor public class MutagenDaemon: FileSyncDaemon { - private let logger = Logger(subsystem: Bundle.main.bundleIdentifier!, category: "mutagen") + let logger = Logger(subsystem: Bundle.main.bundleIdentifier!, category: "mutagen") @Published public var state: DaemonState = .stopped { didSet { @@ -42,9 +42,9 @@ public class MutagenDaemon: FileSyncDaemon { private let mutagenDaemonSocket: URL // Non-nil when the daemon is running + var client: DaemonClient? private var group: MultiThreadedEventLoopGroup? private var channel: GRPCChannel? - private var client: DaemonClient? // Protect start & stop transitions against re-entrancy private let transition = AsyncSemaphore(value: 1) @@ -171,7 +171,8 @@ public class MutagenDaemon: FileSyncDaemon { ) client = DaemonClient( mgmt: Daemon_DaemonAsyncClient(channel: channel!), - sync: Synchronization_SynchronizationAsyncClient(channel: channel!) + sync: Synchronization_SynchronizationAsyncClient(channel: channel!), + prompt: Prompting_PromptingAsyncClient(channel: channel!) ) logger.info( "Successfully connected to mutagen daemon, socket: \(self.mutagenDaemonSocket.path, privacy: .public)" @@ -301,6 +302,7 @@ public class MutagenDaemon: FileSyncDaemon { struct DaemonClient { let mgmt: Daemon_DaemonAsyncClient let sync: Synchronization_SynchronizationAsyncClient + let prompt: Prompting_PromptingAsyncClient } public enum DaemonState { @@ -342,6 +344,8 @@ public enum DaemonError: Error { case connectionFailure(Error) case terminatedUnexpectedly case grpcFailure(Error) + case invalidGrpcResponse(String) + case unexpectedStreamClosure public var description: String { switch self { @@ -355,6 +359,10 @@ public enum DaemonError: Error { "The daemon must be started first" case let .grpcFailure(error): "Failed to communicate with daemon: \(error)" + case let .invalidGrpcResponse(response): + "Invalid gRPC response: \(response)" + case .unexpectedStreamClosure: + "Unexpected stream closure" } } diff --git a/Coder-Desktop/VPNLib/FileSync/FileSyncPrompting.swift b/Coder-Desktop/VPNLib/FileSync/FileSyncPrompting.swift new file mode 100644 index 00000000..d5a49b42 --- /dev/null +++ b/Coder-Desktop/VPNLib/FileSync/FileSyncPrompting.swift @@ -0,0 +1,53 @@ +import GRPC + +extension MutagenDaemon { + typealias PromptStream = GRPCAsyncBidirectionalStreamingCall + + func host(allowPrompts: Bool = true) async throws(DaemonError) -> (PromptStream, identifier: String) { + let stream = client!.prompt.makeHostCall() + + do { + try await stream.requestStream.send(.with { req in req.allowPrompts = allowPrompts }) + } catch { + throw .grpcFailure(error) + } + + // We can't make call `makeAsyncIterator` more than once + // (as a for-loop would do implicitly) + var iter = stream.responseStream.makeAsyncIterator() + + let initResp: Prompting_HostResponse? + do { + initResp = try await iter.next() + } catch { + throw .grpcFailure(error) + } + guard let initResp else { + throw .unexpectedStreamClosure + } + try initResp.ensureValid(first: true, allowPrompts: allowPrompts) + + Task.detached(priority: .background) { + do { + while let msg = try await iter.next() { + try msg.ensureValid(first: false, allowPrompts: allowPrompts) + var reply: Prompting_HostRequest = .init() + if msg.isPrompt { + // Handle SSH key prompts + if msg.message.contains("yes/no/[fingerprint]") { + reply.response = "yes" + } + // Any other messages that require a non-empty response will + // cause the create op to fail, showing an error. This is ok for now. + } + try await stream.requestStream.send(reply) + } + } catch let error as GRPCStatus where error.code == .cancelled { + return + } catch { + self.logger.critical("Prompt stream failed: \(error)") + } + } + return (stream, identifier: initResp.identifier) + } +} diff --git a/Coder-Desktop/VPNLib/FileSync/MutagenConvert.swift b/Coder-Desktop/VPNLib/FileSync/MutagenConvert.swift index 7afefee1..8a59b238 100644 --- a/Coder-Desktop/VPNLib/FileSync/MutagenConvert.swift +++ b/Coder-Desktop/VPNLib/FileSync/MutagenConvert.swift @@ -57,3 +57,26 @@ func accumulateErrors(from state: Synchronization_State) -> [FileSyncError] { func humanReadableBytes(_ bytes: UInt64) -> String { ByteCountFormatter().string(fromByteCount: Int64(bytes)) } + +extension Prompting_HostResponse { + func ensureValid(first: Bool, allowPrompts: Bool) throws(DaemonError) { + if first { + if identifier.isEmpty { + throw .invalidGrpcResponse("empty prompter identifier") + } + if isPrompt { + throw .invalidGrpcResponse("unexpected message type specification") + } + if !message.isEmpty { + throw .invalidGrpcResponse("unexpected message") + } + } else { + if !identifier.isEmpty { + throw .invalidGrpcResponse("unexpected prompter identifier") + } + if isPrompt, !allowPrompts { + throw .invalidGrpcResponse("disallowed prompt message type") + } + } + } +} diff --git a/Coder-Desktop/VPNLib/FileSync/MutagenSDK/service_prompting_prompting.grpc.swift b/Coder-Desktop/VPNLib/FileSync/MutagenSDK/service_prompting_prompting.grpc.swift new file mode 100644 index 00000000..a79eb510 --- /dev/null +++ b/Coder-Desktop/VPNLib/FileSync/MutagenSDK/service_prompting_prompting.grpc.swift @@ -0,0 +1,421 @@ +// +// DO NOT EDIT. +// swift-format-ignore-file +// +// Generated by the protocol buffer compiler. +// Source: service_prompting_prompting.proto +// +import GRPC +import NIO +import NIOConcurrencyHelpers +import SwiftProtobuf + + +/// Prompting allows clients to host and request prompting. +/// +/// Usage: instantiate `Prompting_PromptingClient`, then call methods of this protocol to make API calls. +internal protocol Prompting_PromptingClientProtocol: GRPCClient { + var serviceName: String { get } + var interceptors: Prompting_PromptingClientInterceptorFactoryProtocol? { get } + + func host( + callOptions: CallOptions?, + handler: @escaping (Prompting_HostResponse) -> Void + ) -> BidirectionalStreamingCall + + func prompt( + _ request: Prompting_PromptRequest, + callOptions: CallOptions? + ) -> UnaryCall +} + +extension Prompting_PromptingClientProtocol { + internal var serviceName: String { + return "prompting.Prompting" + } + + /// Host allows clients to perform prompt hosting. + /// + /// Callers should use the `send` method on the returned object to send messages + /// to the server. The caller should send an `.end` after the final message has been sent. + /// + /// - Parameters: + /// - callOptions: Call options. + /// - handler: A closure called when each response is received from the server. + /// - Returns: A `ClientStreamingCall` with futures for the metadata and status. + internal func host( + callOptions: CallOptions? = nil, + handler: @escaping (Prompting_HostResponse) -> Void + ) -> BidirectionalStreamingCall { + return self.makeBidirectionalStreamingCall( + path: Prompting_PromptingClientMetadata.Methods.host.path, + callOptions: callOptions ?? self.defaultCallOptions, + interceptors: self.interceptors?.makeHostInterceptors() ?? [], + handler: handler + ) + } + + /// Prompt performs prompting using a specific prompter. + /// + /// - Parameters: + /// - request: Request to send to Prompt. + /// - callOptions: Call options. + /// - Returns: A `UnaryCall` with futures for the metadata, status and response. + internal func prompt( + _ request: Prompting_PromptRequest, + callOptions: CallOptions? = nil + ) -> UnaryCall { + return self.makeUnaryCall( + path: Prompting_PromptingClientMetadata.Methods.prompt.path, + request: request, + callOptions: callOptions ?? self.defaultCallOptions, + interceptors: self.interceptors?.makePromptInterceptors() ?? [] + ) + } +} + +@available(*, deprecated) +extension Prompting_PromptingClient: @unchecked Sendable {} + +@available(*, deprecated, renamed: "Prompting_PromptingNIOClient") +internal final class Prompting_PromptingClient: Prompting_PromptingClientProtocol { + private let lock = Lock() + private var _defaultCallOptions: CallOptions + private var _interceptors: Prompting_PromptingClientInterceptorFactoryProtocol? + internal let channel: GRPCChannel + internal var defaultCallOptions: CallOptions { + get { self.lock.withLock { return self._defaultCallOptions } } + set { self.lock.withLockVoid { self._defaultCallOptions = newValue } } + } + internal var interceptors: Prompting_PromptingClientInterceptorFactoryProtocol? { + get { self.lock.withLock { return self._interceptors } } + set { self.lock.withLockVoid { self._interceptors = newValue } } + } + + /// Creates a client for the prompting.Prompting service. + /// + /// - Parameters: + /// - channel: `GRPCChannel` to the service host. + /// - defaultCallOptions: Options to use for each service call if the user doesn't provide them. + /// - interceptors: A factory providing interceptors for each RPC. + internal init( + channel: GRPCChannel, + defaultCallOptions: CallOptions = CallOptions(), + interceptors: Prompting_PromptingClientInterceptorFactoryProtocol? = nil + ) { + self.channel = channel + self._defaultCallOptions = defaultCallOptions + self._interceptors = interceptors + } +} + +internal struct Prompting_PromptingNIOClient: Prompting_PromptingClientProtocol { + internal var channel: GRPCChannel + internal var defaultCallOptions: CallOptions + internal var interceptors: Prompting_PromptingClientInterceptorFactoryProtocol? + + /// Creates a client for the prompting.Prompting service. + /// + /// - Parameters: + /// - channel: `GRPCChannel` to the service host. + /// - defaultCallOptions: Options to use for each service call if the user doesn't provide them. + /// - interceptors: A factory providing interceptors for each RPC. + internal init( + channel: GRPCChannel, + defaultCallOptions: CallOptions = CallOptions(), + interceptors: Prompting_PromptingClientInterceptorFactoryProtocol? = nil + ) { + self.channel = channel + self.defaultCallOptions = defaultCallOptions + self.interceptors = interceptors + } +} + +/// Prompting allows clients to host and request prompting. +@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *) +internal protocol Prompting_PromptingAsyncClientProtocol: GRPCClient { + static var serviceDescriptor: GRPCServiceDescriptor { get } + var interceptors: Prompting_PromptingClientInterceptorFactoryProtocol? { get } + + func makeHostCall( + callOptions: CallOptions? + ) -> GRPCAsyncBidirectionalStreamingCall + + func makePromptCall( + _ request: Prompting_PromptRequest, + callOptions: CallOptions? + ) -> GRPCAsyncUnaryCall +} + +@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *) +extension Prompting_PromptingAsyncClientProtocol { + internal static var serviceDescriptor: GRPCServiceDescriptor { + return Prompting_PromptingClientMetadata.serviceDescriptor + } + + internal var interceptors: Prompting_PromptingClientInterceptorFactoryProtocol? { + return nil + } + + internal func makeHostCall( + callOptions: CallOptions? = nil + ) -> GRPCAsyncBidirectionalStreamingCall { + return self.makeAsyncBidirectionalStreamingCall( + path: Prompting_PromptingClientMetadata.Methods.host.path, + callOptions: callOptions ?? self.defaultCallOptions, + interceptors: self.interceptors?.makeHostInterceptors() ?? [] + ) + } + + internal func makePromptCall( + _ request: Prompting_PromptRequest, + callOptions: CallOptions? = nil + ) -> GRPCAsyncUnaryCall { + return self.makeAsyncUnaryCall( + path: Prompting_PromptingClientMetadata.Methods.prompt.path, + request: request, + callOptions: callOptions ?? self.defaultCallOptions, + interceptors: self.interceptors?.makePromptInterceptors() ?? [] + ) + } +} + +@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *) +extension Prompting_PromptingAsyncClientProtocol { + internal func host( + _ requests: RequestStream, + callOptions: CallOptions? = nil + ) -> GRPCAsyncResponseStream where RequestStream: Sequence, RequestStream.Element == Prompting_HostRequest { + return self.performAsyncBidirectionalStreamingCall( + path: Prompting_PromptingClientMetadata.Methods.host.path, + requests: requests, + callOptions: callOptions ?? self.defaultCallOptions, + interceptors: self.interceptors?.makeHostInterceptors() ?? [] + ) + } + + internal func host( + _ requests: RequestStream, + callOptions: CallOptions? = nil + ) -> GRPCAsyncResponseStream where RequestStream: AsyncSequence & Sendable, RequestStream.Element == Prompting_HostRequest { + return self.performAsyncBidirectionalStreamingCall( + path: Prompting_PromptingClientMetadata.Methods.host.path, + requests: requests, + callOptions: callOptions ?? self.defaultCallOptions, + interceptors: self.interceptors?.makeHostInterceptors() ?? [] + ) + } + + internal func prompt( + _ request: Prompting_PromptRequest, + callOptions: CallOptions? = nil + ) async throws -> Prompting_PromptResponse { + return try await self.performAsyncUnaryCall( + path: Prompting_PromptingClientMetadata.Methods.prompt.path, + request: request, + callOptions: callOptions ?? self.defaultCallOptions, + interceptors: self.interceptors?.makePromptInterceptors() ?? [] + ) + } +} + +@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *) +internal struct Prompting_PromptingAsyncClient: Prompting_PromptingAsyncClientProtocol { + internal var channel: GRPCChannel + internal var defaultCallOptions: CallOptions + internal var interceptors: Prompting_PromptingClientInterceptorFactoryProtocol? + + internal init( + channel: GRPCChannel, + defaultCallOptions: CallOptions = CallOptions(), + interceptors: Prompting_PromptingClientInterceptorFactoryProtocol? = nil + ) { + self.channel = channel + self.defaultCallOptions = defaultCallOptions + self.interceptors = interceptors + } +} + +internal protocol Prompting_PromptingClientInterceptorFactoryProtocol: Sendable { + + /// - Returns: Interceptors to use when invoking 'host'. + func makeHostInterceptors() -> [ClientInterceptor] + + /// - Returns: Interceptors to use when invoking 'prompt'. + func makePromptInterceptors() -> [ClientInterceptor] +} + +internal enum Prompting_PromptingClientMetadata { + internal static let serviceDescriptor = GRPCServiceDescriptor( + name: "Prompting", + fullName: "prompting.Prompting", + methods: [ + Prompting_PromptingClientMetadata.Methods.host, + Prompting_PromptingClientMetadata.Methods.prompt, + ] + ) + + internal enum Methods { + internal static let host = GRPCMethodDescriptor( + name: "Host", + path: "/prompting.Prompting/Host", + type: GRPCCallType.bidirectionalStreaming + ) + + internal static let prompt = GRPCMethodDescriptor( + name: "Prompt", + path: "/prompting.Prompting/Prompt", + type: GRPCCallType.unary + ) + } +} + +/// Prompting allows clients to host and request prompting. +/// +/// To build a server, implement a class that conforms to this protocol. +internal protocol Prompting_PromptingProvider: CallHandlerProvider { + var interceptors: Prompting_PromptingServerInterceptorFactoryProtocol? { get } + + /// Host allows clients to perform prompt hosting. + func host(context: StreamingResponseCallContext) -> EventLoopFuture<(StreamEvent) -> Void> + + /// Prompt performs prompting using a specific prompter. + func prompt(request: Prompting_PromptRequest, context: StatusOnlyCallContext) -> EventLoopFuture +} + +extension Prompting_PromptingProvider { + internal var serviceName: Substring { + return Prompting_PromptingServerMetadata.serviceDescriptor.fullName[...] + } + + /// Determines, calls and returns the appropriate request handler, depending on the request's method. + /// Returns nil for methods not handled by this service. + internal func handle( + method name: Substring, + context: CallHandlerContext + ) -> GRPCServerHandlerProtocol? { + switch name { + case "Host": + return BidirectionalStreamingServerHandler( + context: context, + requestDeserializer: ProtobufDeserializer(), + responseSerializer: ProtobufSerializer(), + interceptors: self.interceptors?.makeHostInterceptors() ?? [], + observerFactory: self.host(context:) + ) + + case "Prompt": + return UnaryServerHandler( + context: context, + requestDeserializer: ProtobufDeserializer(), + responseSerializer: ProtobufSerializer(), + interceptors: self.interceptors?.makePromptInterceptors() ?? [], + userFunction: self.prompt(request:context:) + ) + + default: + return nil + } + } +} + +/// Prompting allows clients to host and request prompting. +/// +/// To implement a server, implement an object which conforms to this protocol. +@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *) +internal protocol Prompting_PromptingAsyncProvider: CallHandlerProvider, Sendable { + static var serviceDescriptor: GRPCServiceDescriptor { get } + var interceptors: Prompting_PromptingServerInterceptorFactoryProtocol? { get } + + /// Host allows clients to perform prompt hosting. + func host( + requestStream: GRPCAsyncRequestStream, + responseStream: GRPCAsyncResponseStreamWriter, + context: GRPCAsyncServerCallContext + ) async throws + + /// Prompt performs prompting using a specific prompter. + func prompt( + request: Prompting_PromptRequest, + context: GRPCAsyncServerCallContext + ) async throws -> Prompting_PromptResponse +} + +@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *) +extension Prompting_PromptingAsyncProvider { + internal static var serviceDescriptor: GRPCServiceDescriptor { + return Prompting_PromptingServerMetadata.serviceDescriptor + } + + internal var serviceName: Substring { + return Prompting_PromptingServerMetadata.serviceDescriptor.fullName[...] + } + + internal var interceptors: Prompting_PromptingServerInterceptorFactoryProtocol? { + return nil + } + + internal func handle( + method name: Substring, + context: CallHandlerContext + ) -> GRPCServerHandlerProtocol? { + switch name { + case "Host": + return GRPCAsyncServerHandler( + context: context, + requestDeserializer: ProtobufDeserializer(), + responseSerializer: ProtobufSerializer(), + interceptors: self.interceptors?.makeHostInterceptors() ?? [], + wrapping: { try await self.host(requestStream: $0, responseStream: $1, context: $2) } + ) + + case "Prompt": + return GRPCAsyncServerHandler( + context: context, + requestDeserializer: ProtobufDeserializer(), + responseSerializer: ProtobufSerializer(), + interceptors: self.interceptors?.makePromptInterceptors() ?? [], + wrapping: { try await self.prompt(request: $0, context: $1) } + ) + + default: + return nil + } + } +} + +internal protocol Prompting_PromptingServerInterceptorFactoryProtocol: Sendable { + + /// - Returns: Interceptors to use when handling 'host'. + /// Defaults to calling `self.makeInterceptors()`. + func makeHostInterceptors() -> [ServerInterceptor] + + /// - Returns: Interceptors to use when handling 'prompt'. + /// Defaults to calling `self.makeInterceptors()`. + func makePromptInterceptors() -> [ServerInterceptor] +} + +internal enum Prompting_PromptingServerMetadata { + internal static let serviceDescriptor = GRPCServiceDescriptor( + name: "Prompting", + fullName: "prompting.Prompting", + methods: [ + Prompting_PromptingServerMetadata.Methods.host, + Prompting_PromptingServerMetadata.Methods.prompt, + ] + ) + + internal enum Methods { + internal static let host = GRPCMethodDescriptor( + name: "Host", + path: "/prompting.Prompting/Host", + type: GRPCCallType.bidirectionalStreaming + ) + + internal static let prompt = GRPCMethodDescriptor( + name: "Prompt", + path: "/prompting.Prompting/Prompt", + type: GRPCCallType.unary + ) + } +} diff --git a/Coder-Desktop/VPNLib/FileSync/MutagenSDK/service_prompting_prompting.pb.swift b/Coder-Desktop/VPNLib/FileSync/MutagenSDK/service_prompting_prompting.pb.swift new file mode 100644 index 00000000..74afe922 --- /dev/null +++ b/Coder-Desktop/VPNLib/FileSync/MutagenSDK/service_prompting_prompting.pb.swift @@ -0,0 +1,279 @@ +// DO NOT EDIT. +// swift-format-ignore-file +// swiftlint:disable all +// +// Generated by the Swift generator plugin for the protocol buffer compiler. +// Source: service_prompting_prompting.proto +// +// For information on using the generated types, please see the documentation: +// https://github.com/apple/swift-protobuf/ + +// +// This file was taken from +// https://github.com/mutagen-io/mutagen/tree/v0.18.1/pkg/service/prompting/prompting.proto +// +// MIT License +// +// Copyright (c) 2016-present Docker, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +import SwiftProtobuf + +// If the compiler emits an error on this type, it is because this file +// was generated by a version of the `protoc` Swift plug-in that is +// incompatible with the version of SwiftProtobuf to which you are linking. +// Please ensure that you are building against the same version of the API +// that was used to generate this file. +fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck { + struct _2: SwiftProtobuf.ProtobufAPIVersion_2 {} + typealias Version = _2 +} + +/// HostRequest encodes either an initial request to perform prompt hosting or a +/// follow-up response to a message or prompt. +struct Prompting_HostRequest: Sendable { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + /// AllowPrompts indicates whether or not the hoster will allow prompts. If + /// not, it will only receive message requests. This field may only be set on + /// the initial request. + var allowPrompts: Bool = false + + /// Response is the prompt response, if any. On the initial request, this + /// must be an empty string. When responding to a prompt, it may be any + /// value. When responding to a message, it must be an empty string. + var response: String = String() + + var unknownFields = SwiftProtobuf.UnknownStorage() + + init() {} +} + +/// HostResponse encodes either an initial response to perform prompt hosting or +/// a follow-up request for messaging or prompting. +struct Prompting_HostResponse: Sendable { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + /// Identifier is the prompter identifier. It is only set in the initial + /// response sent after the initial request. + var identifier: String = String() + + /// IsPrompt indicates if the response is requesting a prompt (as opposed to + /// simple message display). + var isPrompt: Bool = false + + /// Message is the message associated with the prompt or message. + var message: String = String() + + var unknownFields = SwiftProtobuf.UnknownStorage() + + init() {} +} + +/// PromptRequest encodes a request for prompting by a specific prompter. +struct Prompting_PromptRequest: Sendable { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + /// Prompter is the prompter identifier. + var prompter: String = String() + + /// Prompt is the prompt to present. + var prompt: String = String() + + var unknownFields = SwiftProtobuf.UnknownStorage() + + init() {} +} + +/// PromptResponse encodes the response from a prompter. +struct Prompting_PromptResponse: Sendable { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + /// Response is the response returned by the prompter. + var response: String = String() + + var unknownFields = SwiftProtobuf.UnknownStorage() + + init() {} +} + +// MARK: - Code below here is support for the SwiftProtobuf runtime. + +fileprivate let _protobuf_package = "prompting" + +extension Prompting_HostRequest: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + static let protoMessageName: String = _protobuf_package + ".HostRequest" + static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .same(proto: "allowPrompts"), + 2: .same(proto: "response"), + ] + + mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { try decoder.decodeSingularBoolField(value: &self.allowPrompts) }() + case 2: try { try decoder.decodeSingularStringField(value: &self.response) }() + default: break + } + } + } + + func traverse(visitor: inout V) throws { + if self.allowPrompts != false { + try visitor.visitSingularBoolField(value: self.allowPrompts, fieldNumber: 1) + } + if !self.response.isEmpty { + try visitor.visitSingularStringField(value: self.response, fieldNumber: 2) + } + try unknownFields.traverse(visitor: &visitor) + } + + static func ==(lhs: Prompting_HostRequest, rhs: Prompting_HostRequest) -> Bool { + if lhs.allowPrompts != rhs.allowPrompts {return false} + if lhs.response != rhs.response {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + +extension Prompting_HostResponse: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + static let protoMessageName: String = _protobuf_package + ".HostResponse" + static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .same(proto: "identifier"), + 2: .same(proto: "isPrompt"), + 3: .same(proto: "message"), + ] + + mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { try decoder.decodeSingularStringField(value: &self.identifier) }() + case 2: try { try decoder.decodeSingularBoolField(value: &self.isPrompt) }() + case 3: try { try decoder.decodeSingularStringField(value: &self.message) }() + default: break + } + } + } + + func traverse(visitor: inout V) throws { + if !self.identifier.isEmpty { + try visitor.visitSingularStringField(value: self.identifier, fieldNumber: 1) + } + if self.isPrompt != false { + try visitor.visitSingularBoolField(value: self.isPrompt, fieldNumber: 2) + } + if !self.message.isEmpty { + try visitor.visitSingularStringField(value: self.message, fieldNumber: 3) + } + try unknownFields.traverse(visitor: &visitor) + } + + static func ==(lhs: Prompting_HostResponse, rhs: Prompting_HostResponse) -> Bool { + if lhs.identifier != rhs.identifier {return false} + if lhs.isPrompt != rhs.isPrompt {return false} + if lhs.message != rhs.message {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + +extension Prompting_PromptRequest: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + static let protoMessageName: String = _protobuf_package + ".PromptRequest" + static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .same(proto: "prompter"), + 2: .same(proto: "prompt"), + ] + + mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { try decoder.decodeSingularStringField(value: &self.prompter) }() + case 2: try { try decoder.decodeSingularStringField(value: &self.prompt) }() + default: break + } + } + } + + func traverse(visitor: inout V) throws { + if !self.prompter.isEmpty { + try visitor.visitSingularStringField(value: self.prompter, fieldNumber: 1) + } + if !self.prompt.isEmpty { + try visitor.visitSingularStringField(value: self.prompt, fieldNumber: 2) + } + try unknownFields.traverse(visitor: &visitor) + } + + static func ==(lhs: Prompting_PromptRequest, rhs: Prompting_PromptRequest) -> Bool { + if lhs.prompter != rhs.prompter {return false} + if lhs.prompt != rhs.prompt {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + +extension Prompting_PromptResponse: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + static let protoMessageName: String = _protobuf_package + ".PromptResponse" + static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .same(proto: "response"), + ] + + mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { try decoder.decodeSingularStringField(value: &self.response) }() + default: break + } + } + } + + func traverse(visitor: inout V) throws { + if !self.response.isEmpty { + try visitor.visitSingularStringField(value: self.response, fieldNumber: 1) + } + try unknownFields.traverse(visitor: &visitor) + } + + static func ==(lhs: Prompting_PromptResponse, rhs: Prompting_PromptResponse) -> Bool { + if lhs.response != rhs.response {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} diff --git a/Coder-Desktop/VPNLib/FileSync/MutagenSDK/service_prompting_prompting.proto b/Coder-Desktop/VPNLib/FileSync/MutagenSDK/service_prompting_prompting.proto new file mode 100644 index 00000000..337a1544 --- /dev/null +++ b/Coder-Desktop/VPNLib/FileSync/MutagenSDK/service_prompting_prompting.proto @@ -0,0 +1,80 @@ +/* + * This file was taken from + * https://github.com/mutagen-io/mutagen/tree/v0.18.1/pkg/service/prompting/prompting.proto + * + * MIT License + * + * Copyright (c) 2016-present Docker, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +syntax = "proto3"; + +package prompting; + +option go_package = "github.com/mutagen-io/mutagen/pkg/service/prompting"; + +// HostRequest encodes either an initial request to perform prompt hosting or a +// follow-up response to a message or prompt. +message HostRequest { + // AllowPrompts indicates whether or not the hoster will allow prompts. If + // not, it will only receive message requests. This field may only be set on + // the initial request. + bool allowPrompts = 1; + // Response is the prompt response, if any. On the initial request, this + // must be an empty string. When responding to a prompt, it may be any + // value. When responding to a message, it must be an empty string. + string response = 2; +} + +// HostResponse encodes either an initial response to perform prompt hosting or +// a follow-up request for messaging or prompting. +message HostResponse { + // Identifier is the prompter identifier. It is only set in the initial + // response sent after the initial request. + string identifier = 1; + // IsPrompt indicates if the response is requesting a prompt (as opposed to + // simple message display). + bool isPrompt = 2; + // Message is the message associated with the prompt or message. + string message = 3; +} + +// PromptRequest encodes a request for prompting by a specific prompter. +message PromptRequest { + // Prompter is the prompter identifier. + string prompter = 1; + // Prompt is the prompt to present. + string prompt = 2; +} + +// PromptResponse encodes the response from a prompter. +message PromptResponse { + // Response is the response returned by the prompter. + string response = 1; +} + +// Prompting allows clients to host and request prompting. +service Prompting { + // Host allows clients to perform prompt hosting. + rpc Host(stream HostRequest) returns (stream HostResponse) {} + // Prompt performs prompting using a specific prompter. + rpc Prompt(PromptRequest) returns (PromptResponse) {} +} diff --git a/scripts/mutagen-proto.sh b/scripts/mutagen-proto.sh index 4fc6cf67..fb01413b 100755 --- a/scripts/mutagen-proto.sh +++ b/scripts/mutagen-proto.sh @@ -4,9 +4,9 @@ # It is very similar to `Update-Proto.ps1` on `coder/coder-desktop-windows`. # It's very unlikely that we'll use this script regularly. # -# Unlike the Go compiler, the Swift compiler does not support multiple files -# with the same name in different directories. -# To handle this, this script flattens the directory structure of the proto +# Unlike the Go compiler, the Swift compiler does not support multiple files +# with the same name in different directories. +# To handle this, this script flattens the directory structure of the proto # files into the filename, i.e. `service/synchronization/synchronization.proto` # becomes `service_synchronization_synchronization.proto`. # It also updates the proto imports to use these paths. @@ -24,7 +24,7 @@ mutagen_tag="$1" repo="mutagen-io/mutagen" proto_prefix="pkg" # Right now, we only care about the synchronization and daemon management gRPC -entry_files=("service/synchronization/synchronization.proto" "service/daemon/daemon.proto") +entry_files=("service/synchronization/synchronization.proto" "service/daemon/daemon.proto" "service/prompting/prompting.proto") out_folder="Coder-Desktop/VPNLib/FileSync/MutagenSDK" @@ -33,7 +33,7 @@ if [ -d "$clone_dir" ]; then echo "Found existing mutagen repo at $clone_dir, checking out $mutagen_tag..." pushd "$clone_dir" > /dev/null git clean -fdx - + current_tag=$(git name-rev --name-only HEAD) if [ "$current_tag" != "tags/$mutagen_tag" ]; then git fetch --all @@ -62,27 +62,27 @@ add_file() { local proto_path="${filepath#"$clone_dir"/"$proto_prefix"/}" local flat_name flat_name=$(echo "$proto_path" | sed 's/\//_/g') - + # Skip if already processed if [[ -n "${file_map[$proto_path]:-}" ]]; then return fi - + echo "Adding $proto_path -> $flat_name" file_map[$proto_path]=$flat_name file_paths+=("$filepath") - + # Process imports while IFS= read -r line; do if [[ $line =~ ^import\ \"(.+)\" ]]; then import_path="${BASH_REMATCH[1]}" - + # Ignore google imports, as they're not vendored if [[ $import_path =~ ^google/ ]]; then echo "Skipping $import_path" continue fi - + import_file_path="$clone_dir/$proto_prefix/$import_path" if [ -f "$import_file_path" ]; then add_file "$import_file_path" @@ -109,24 +109,24 @@ for file_path in "${file_paths[@]}"; do proto_path="${file_path#"$clone_dir"/"$proto_prefix"/}" flat_name="${file_map[$proto_path]}" dst_path="$out_folder/$flat_name" - + cp -f "$file_path" "$dst_path" - + file_header="/*\n * This file was taken from\n * https://github.com/$repo/tree/$mutagen_tag/$proto_prefix/$proto_path\n *\n$license_header\n */\n\n" content=$(cat "$dst_path") echo -e "$file_header$content" > "$dst_path" - + tmp_file=$(mktemp) while IFS= read -r line; do if [[ $line =~ ^import\ \"(.+)\" ]]; then import_path="${BASH_REMATCH[1]}" - + # Retain google imports if [[ $import_path =~ ^google/ ]]; then echo "$line" >> "$tmp_file" continue fi - + # Convert import path to flattened format flat_import=$(echo "$import_path" | sed 's/\//_/g') echo "import \"$flat_import\";" >> "$tmp_file" @@ -135,7 +135,7 @@ for file_path in "${file_paths[@]}"; do fi done < "$dst_path" mv "$tmp_file" "$dst_path" - + echo "Processed $proto_path -> $flat_name" done pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy