Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit93667f7

Browse files
committed
chore: add mutagen prompting gRPC
1 parentf25e09f commit93667f7

File tree

7 files changed

+883
-19
lines changed

7 files changed

+883
-19
lines changed

‎Coder-Desktop/VPNLib/FileSync/FileSyncDaemon.swift

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ public protocol FileSyncDaemon: ObservableObject {
1919

2020
@MainActor
2121
publicclassMutagenDaemon:FileSyncDaemon{
22-
privateletlogger=Logger(subsystem:Bundle.main.bundleIdentifier!, category:"mutagen")
22+
letlogger=Logger(subsystem:Bundle.main.bundleIdentifier!, category:"mutagen")
2323

2424
@Publishedpublicvarstate:DaemonState=.stopped{
2525
didSet{
@@ -42,9 +42,9 @@ public class MutagenDaemon: FileSyncDaemon {
4242
privateletmutagenDaemonSocket:URL
4343

4444
// Non-nil when the daemon is running
45+
varclient:DaemonClient?
4546
privatevargroup:MultiThreadedEventLoopGroup?
4647
privatevarchannel:GRPCChannel?
47-
privatevarclient:DaemonClient?
4848

4949
// Protect start & stop transitions against re-entrancy
5050
privatelettransition=AsyncSemaphore(value:1)
@@ -171,7 +171,8 @@ public class MutagenDaemon: FileSyncDaemon {
171171
)
172172
client=DaemonClient(
173173
mgmt:Daemon_DaemonAsyncClient(channel: channel!),
174-
sync:Synchronization_SynchronizationAsyncClient(channel: channel!)
174+
sync:Synchronization_SynchronizationAsyncClient(channel: channel!),
175+
prompt:Prompting_PromptingAsyncClient(channel: channel!)
175176
)
176177
logger.info(
177178
"Successfully connected to mutagen daemon, socket:\(self.mutagenDaemonSocket.path, privacy:.public)"
@@ -301,6 +302,7 @@ public class MutagenDaemon: FileSyncDaemon {
301302
structDaemonClient{
302303
letmgmt:Daemon_DaemonAsyncClient
303304
letsync:Synchronization_SynchronizationAsyncClient
305+
letprompt:Prompting_PromptingAsyncClient
304306
}
305307

306308
publicenumDaemonState{
@@ -342,6 +344,8 @@ public enum DaemonError: Error {
342344
case connectionFailure(Error)
343345
case terminatedUnexpectedly
344346
case grpcFailure(Error)
347+
case invalidGrpcResponse(String)
348+
case unexpectedStreamClosure
345349

346350
publicvardescription:String{
347351
switchself{
@@ -355,6 +359,10 @@ public enum DaemonError: Error {
355359
"The daemon must be started first"
356360
caselet.grpcFailure(error):
357361
"Failed to communicate with daemon:\(error)"
362+
caselet.invalidGrpcResponse(response):
363+
"Invalid gRPC response:\(response)"
364+
case.unexpectedStreamClosure:
365+
"Unexpected stream closure"
358366
}
359367
}
360368

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import GRPC
2+
3+
extensionMutagenDaemon{
4+
typealiasPromptStream=GRPCAsyncBidirectionalStreamingCall<Prompting_HostRequest,Prompting_HostResponse>
5+
6+
func host(allowPrompts:Bool=true)asyncthrows(DaemonError)->(PromptStream, identifier:String){
7+
letstream= client!.prompt.makeHostCall()
8+
9+
do{
10+
tryawait stream.requestStream.send(.with{ reqin req.allowPrompts= allowPrompts})
11+
}catch{
12+
throw.grpcFailure(error)
13+
}
14+
15+
// We can't make call `makeAsyncIterator` more than once
16+
// (as a for-loop would do implicitly)
17+
variter= stream.responseStream.makeAsyncIterator()
18+
19+
letinitResp:Prompting_HostResponse?
20+
do{
21+
initResp=tryawait iter.next()
22+
}catch{
23+
throw.grpcFailure(error)
24+
}
25+
guardlet initRespelse{
26+
throw.unexpectedStreamClosure
27+
}
28+
try initResp.ensureValid(first:true, allowPrompts: allowPrompts)
29+
30+
Task.detached(priority:.background){
31+
do{
32+
whilelet msg=tryawait iter.next(){
33+
try msg.ensureValid(first:false, allowPrompts: allowPrompts)
34+
varreply:Prompting_HostRequest=.init()
35+
if msg.isPrompt{
36+
// Handle SSH key prompts
37+
if msg.message.contains("yes/no/[fingerprint]"){
38+
reply.response="yes"
39+
}
40+
// Any other messages that require a non-empty response will
41+
// cause the create op to fail, showing an error. This is ok for now.
42+
}
43+
tryawait stream.requestStream.send(reply)
44+
}
45+
}catchlet errorasGRPCStatuswhere error.code==.cancelled{
46+
return
47+
} catch{
48+
self.logger.critical("Prompt stream failed:\(error)")
49+
}
50+
}
51+
return(stream, identifier: initResp.identifier)
52+
}
53+
}

‎Coder-Desktop/VPNLib/FileSync/MutagenConvert.swift

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,3 +57,26 @@ func accumulateErrors(from state: Synchronization_State) -> [FileSyncError] {
5757
func humanReadableBytes(_ bytes:UInt64)->String{
5858
ByteCountFormatter().string(fromByteCount:Int64(bytes))
5959
}
60+
61+
extensionPrompting_HostResponse{
62+
func ensureValid(first:Bool, allowPrompts:Bool)throws(DaemonError){
63+
if first{
64+
if identifier.isEmpty{
65+
throw.invalidGrpcResponse("empty prompter identifier")
66+
}
67+
if isPrompt{
68+
throw.invalidGrpcResponse("unexpected message type specification")
69+
}
70+
if !message.isEmpty{
71+
throw.invalidGrpcResponse("unexpected message")
72+
}
73+
}else{
74+
if !identifier.isEmpty{
75+
throw.invalidGrpcResponse("unexpected prompter identifier")
76+
}
77+
if isPrompt, !allowPrompts{
78+
throw.invalidGrpcResponse("disallowed prompt message type")
79+
}
80+
}
81+
}
82+
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp