@graftland/plugin-remote 0.0.0-stage → 0.2.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (380) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +76 -2
  3. package/README.zh-CN.md +71 -0
  4. package/README.zh-TW.md +71 -0
  5. package/dist/Auth-BElTC9_X.js +6 -0
  6. package/dist/Auth.d.ts +49 -0
  7. package/dist/Auth.d.ts.map +1 -0
  8. package/dist/Auth.js +8 -0
  9. package/dist/Auth.js.map +1 -0
  10. package/dist/Bootstrap-MCwivGN1.js +9 -0
  11. package/dist/ConfigDirectory-BCC6q2R4.js +5 -0
  12. package/dist/Contract-CcpSIJm4.js +121 -0
  13. package/dist/Contract.d.ts +71 -0
  14. package/dist/Contract.d.ts.map +1 -0
  15. package/dist/Contract.js +1 -0
  16. package/dist/Contract.js.map +1 -0
  17. package/dist/Errors-oPsTLY7l.js +45 -0
  18. package/dist/Errors.d.ts +67 -0
  19. package/dist/Errors.d.ts.map +1 -0
  20. package/dist/Errors.js +47 -0
  21. package/dist/Errors.js.map +1 -0
  22. package/dist/FailureGroup-BcH0XOBi.js +53 -0
  23. package/dist/Framing-zaYdOJnT.js +61 -0
  24. package/dist/Graft-BsYq3F-J.js +985 -0
  25. package/dist/Instance.d.ts +7 -0
  26. package/dist/Instance.d.ts.map +1 -0
  27. package/dist/Instance.js +14 -0
  28. package/dist/Instance.js.map +1 -0
  29. package/dist/Packages-B1TPk8pa.js +111 -0
  30. package/dist/Peer-IJgFK6mD.js +342 -0
  31. package/dist/ProcessObservation-DEGrEMfP.js +38 -0
  32. package/dist/ServeLifecycle-CJM0gG5B.js +24 -0
  33. package/dist/Testing.d.ts +26 -0
  34. package/dist/Testing.d.ts.map +1 -0
  35. package/dist/Testing.js +69 -0
  36. package/dist/Testing.js.map +1 -0
  37. package/dist/Transport-DULzpsgR.js +6 -0
  38. package/dist/Transport.d.ts +39 -0
  39. package/dist/Transport.d.ts.map +1 -0
  40. package/dist/Transport.js +8 -0
  41. package/dist/Transport.js.map +1 -0
  42. package/dist/WatchdogProtocol-BZmVxrBP.js +17 -0
  43. package/dist/_virtual/_rolldown/runtime.js +12 -0
  44. package/dist/bin.d.ts +1 -0
  45. package/dist/bin.js +28275 -0
  46. package/dist/bin.js.map +1 -0
  47. package/dist/childRuntime-DSqtFN9o.js +188 -0
  48. package/dist/cleanup-BPWr-5U9.js +13 -0
  49. package/dist/command-9zrHdbwT.js +7 -0
  50. package/dist/connectionEnded-DUgcobEd.js +9 -0
  51. package/dist/directTransport-DrexuPSm.js +5 -0
  52. package/dist/entry-SYsx9VNb.js +708 -0
  53. package/dist/host/auth/bearer.d.ts +5 -0
  54. package/dist/host/auth/bearer.d.ts.map +1 -0
  55. package/dist/host/auth/bearer.js +4 -0
  56. package/dist/host/auth/bearer.js.map +1 -0
  57. package/dist/host/auth/jwt.d.ts +5 -0
  58. package/dist/host/auth/jwt.d.ts.map +1 -0
  59. package/dist/host/auth/jwt.js +4 -0
  60. package/dist/host/auth/jwt.js.map +1 -0
  61. package/dist/host/auth/mtls.d.ts +8 -0
  62. package/dist/host/auth/mtls.d.ts.map +1 -0
  63. package/dist/host/auth/mtls.js +34 -0
  64. package/dist/host/auth/mtls.js.map +1 -0
  65. package/dist/host/auth/none.d.ts +3 -0
  66. package/dist/host/auth/none.d.ts.map +1 -0
  67. package/dist/host/auth/none.js +13 -0
  68. package/dist/host/auth/none.js.map +1 -0
  69. package/dist/host/command.d.ts +22 -0
  70. package/dist/host/command.d.ts.map +1 -0
  71. package/dist/host/command.js +61 -0
  72. package/dist/host/command.js.map +1 -0
  73. package/dist/host/local.d.ts +4 -0
  74. package/dist/host/local.d.ts.map +1 -0
  75. package/dist/host/local.js +52 -0
  76. package/dist/host/local.js.map +1 -0
  77. package/dist/host/unix.d.ts +11 -0
  78. package/dist/host/unix.d.ts.map +1 -0
  79. package/dist/host/unix.js +39 -0
  80. package/dist/host/unix.js.map +1 -0
  81. package/dist/host/websocket.d.ts +13 -0
  82. package/dist/host/websocket.d.ts.map +1 -0
  83. package/dist/host/websocket.js +36 -0
  84. package/dist/host/websocket.js.map +1 -0
  85. package/dist/host/worker.d.ts +4 -0
  86. package/dist/host/worker.d.ts.map +1 -0
  87. package/dist/host/worker.js +52 -0
  88. package/dist/host/worker.js.map +1 -0
  89. package/dist/importer-D7zSgqxq.js +347 -0
  90. package/dist/index.d.ts +29 -0
  91. package/dist/index.d.ts.map +1 -0
  92. package/dist/index.js +86 -0
  93. package/dist/index.js.map +1 -0
  94. package/dist/internal/Certificates.js +182 -0
  95. package/dist/internal/Certificates.js.map +1 -0
  96. package/dist/internal/FailureGroup.js +55 -0
  97. package/dist/internal/FailureGroup.js.map +1 -0
  98. package/dist/internal/Limits.d.ts +1 -0
  99. package/dist/internal/Limits.js +17 -0
  100. package/dist/internal/Limits.js.map +1 -0
  101. package/dist/internal/Peer.js +6 -0
  102. package/dist/internal/Peer.js.map +1 -0
  103. package/dist/internal/ServeLifecycle.js +26 -0
  104. package/dist/internal/ServeLifecycle.js.map +1 -0
  105. package/dist/internal/Supervisor.js +7 -0
  106. package/dist/internal/Supervisor.js.map +1 -0
  107. package/dist/internal/assembly.d.ts +5 -0
  108. package/dist/internal/assembly.js +138 -0
  109. package/dist/internal/assembly.js.map +1 -0
  110. package/dist/internal/auth/mtls.js +23 -0
  111. package/dist/internal/auth/mtls.js.map +1 -0
  112. package/dist/internal/auth/token.js +19 -0
  113. package/dist/internal/auth/token.js.map +1 -0
  114. package/dist/internal/auth/tokenCredentials.d.ts +8 -0
  115. package/dist/internal/auth/tokenCredentials.d.ts.map +1 -0
  116. package/dist/internal/auth/tokenCredentials.js +33 -0
  117. package/dist/internal/auth/tokenCredentials.js.map +1 -0
  118. package/dist/internal/child.js +125 -0
  119. package/dist/internal/child.js.map +1 -0
  120. package/dist/internal/childErrors.d.ts +33 -0
  121. package/dist/internal/childErrors.d.ts.map +1 -0
  122. package/dist/internal/childErrors.js +29 -0
  123. package/dist/internal/childErrors.js.map +1 -0
  124. package/dist/internal/childRuntime.js +59 -0
  125. package/dist/internal/childRuntime.js.map +1 -0
  126. package/dist/internal/cleanup.js +15 -0
  127. package/dist/internal/cleanup.js.map +1 -0
  128. package/dist/internal/config.js +6 -0
  129. package/dist/internal/config.js.map +1 -0
  130. package/dist/internal/control.js +42 -0
  131. package/dist/internal/control.js.map +1 -0
  132. package/dist/internal/credentials.d.ts +44 -0
  133. package/dist/internal/credentials.d.ts.map +1 -0
  134. package/dist/internal/credentials.js +54 -0
  135. package/dist/internal/credentials.js.map +1 -0
  136. package/dist/internal/directTransport.d.ts +3 -0
  137. package/dist/internal/directTransport.js +7 -0
  138. package/dist/internal/directTransport.js.map +1 -0
  139. package/dist/internal/entry.d.ts +9 -0
  140. package/dist/internal/entry.d.ts.map +1 -0
  141. package/dist/internal/entry.js +144 -0
  142. package/dist/internal/entry.js.map +1 -0
  143. package/dist/internal/entryConfig.d.ts +202 -0
  144. package/dist/internal/entryConfig.d.ts.map +1 -0
  145. package/dist/internal/entryConfig.js +176 -0
  146. package/dist/internal/entryConfig.js.map +1 -0
  147. package/dist/internal/execution.d.ts +5 -0
  148. package/dist/internal/execution.js +43 -0
  149. package/dist/internal/execution.js.map +1 -0
  150. package/dist/internal/follow.js +11 -0
  151. package/dist/internal/follow.js.map +1 -0
  152. package/dist/internal/forkTree.js +7 -0
  153. package/dist/internal/forkTree.js.map +1 -0
  154. package/dist/internal/host/local.js +28 -0
  155. package/dist/internal/host/local.js.map +1 -0
  156. package/dist/internal/host/runner.js +100 -0
  157. package/dist/internal/host/runner.js.map +1 -0
  158. package/dist/internal/host.js +71 -0
  159. package/dist/internal/host.js.map +1 -0
  160. package/dist/internal/importer.js +18 -0
  161. package/dist/internal/importer.js.map +1 -0
  162. package/dist/internal/listenerStartup.js +17 -0
  163. package/dist/internal/listenerStartup.js.map +1 -0
  164. package/dist/internal/loadModule.js +20 -0
  165. package/dist/internal/loadModule.js.map +1 -0
  166. package/dist/internal/platform.js +9 -0
  167. package/dist/internal/platform.js.map +1 -0
  168. package/dist/internal/port.d.ts +13 -0
  169. package/dist/internal/port.d.ts.map +1 -0
  170. package/dist/internal/port.js +275 -0
  171. package/dist/internal/port.js.map +1 -0
  172. package/dist/internal/release.js +25 -0
  173. package/dist/internal/release.js.map +1 -0
  174. package/dist/internal/runner/Bootstrap.js +11 -0
  175. package/dist/internal/runner/Bootstrap.js.map +1 -0
  176. package/dist/internal/runner/Connection.d.ts +11 -0
  177. package/dist/internal/runner/Connection.js +56 -0
  178. package/dist/internal/runner/Connection.js.map +1 -0
  179. package/dist/internal/runner/Execution.js +115 -0
  180. package/dist/internal/runner/Execution.js.map +1 -0
  181. package/dist/internal/runner/Framing.d.ts +11 -0
  182. package/dist/internal/runner/Framing.d.ts.map +1 -0
  183. package/dist/internal/runner/Framing.js +63 -0
  184. package/dist/internal/runner/Framing.js.map +1 -0
  185. package/dist/internal/runner/Local.js +46 -0
  186. package/dist/internal/runner/Local.js.map +1 -0
  187. package/dist/internal/runner/Lock.js +25 -0
  188. package/dist/internal/runner/Lock.js.map +1 -0
  189. package/dist/internal/runner/Multiplex.d.ts +5 -0
  190. package/dist/internal/runner/Multiplex.js +49 -0
  191. package/dist/internal/runner/Multiplex.js.map +1 -0
  192. package/dist/internal/runner/Packages.d.ts +3 -0
  193. package/dist/internal/runner/Packages.js +108 -0
  194. package/dist/internal/runner/Packages.js.map +1 -0
  195. package/dist/internal/runner/ProcessObservation.js +40 -0
  196. package/dist/internal/runner/ProcessObservation.js.map +1 -0
  197. package/dist/internal/runner/Processes.js +83 -0
  198. package/dist/internal/runner/Processes.js.map +1 -0
  199. package/dist/internal/runner/Retention.js +13 -0
  200. package/dist/internal/runner/Retention.js.map +1 -0
  201. package/dist/internal/runner/Store.js +78 -0
  202. package/dist/internal/runner/Store.js.map +1 -0
  203. package/dist/internal/runner/Supervisor.js +375 -0
  204. package/dist/internal/runner/Supervisor.js.map +1 -0
  205. package/dist/internal/runner/Watchdog.js +47 -0
  206. package/dist/internal/runner/Watchdog.js.map +1 -0
  207. package/dist/internal/runner/WatchdogProtocol.js +19 -0
  208. package/dist/internal/runner/WatchdogProtocol.js.map +1 -0
  209. package/dist/internal/runner/Wire.d.ts +5 -0
  210. package/dist/internal/runner/Wire.js +64 -0
  211. package/dist/internal/runner/Wire.js.map +1 -0
  212. package/dist/internal/runner/accept.js +57 -0
  213. package/dist/internal/runner/accept.js.map +1 -0
  214. package/dist/internal/runner/command.js +9 -0
  215. package/dist/internal/runner/command.js.map +1 -0
  216. package/dist/internal/runner/connectionEnded.js +11 -0
  217. package/dist/internal/runner/connectionEnded.js.map +1 -0
  218. package/dist/internal/runner/model.d.ts +17 -0
  219. package/dist/internal/runner/model.d.ts.map +1 -0
  220. package/dist/internal/runner/model.js +231 -0
  221. package/dist/internal/runner/model.js.map +1 -0
  222. package/dist/internal/serve.js +60 -0
  223. package/dist/internal/serve.js.map +1 -0
  224. package/dist/internal/services.d.ts +18 -0
  225. package/dist/internal/services.d.ts.map +1 -0
  226. package/dist/internal/services.js +22 -0
  227. package/dist/internal/services.js.map +1 -0
  228. package/dist/internal/socketChannel.js +24 -0
  229. package/dist/internal/socketChannel.js.map +1 -0
  230. package/dist/internal/stdioChannel.js +23 -0
  231. package/dist/internal/stdioChannel.js.map +1 -0
  232. package/dist/internal/supervisorPlugin.js +1 -0
  233. package/dist/internal/supervisorPlugin.js.map +1 -0
  234. package/dist/internal/transportOptions.d.ts +18 -0
  235. package/dist/internal/transportOptions.d.ts.map +1 -0
  236. package/dist/internal/watchdogMain.d.ts +1 -0
  237. package/dist/internal/watchdogMain.js +55 -0
  238. package/dist/internal/watchdogMain.js.map +1 -0
  239. package/dist/internal/websocket.js +46 -0
  240. package/dist/internal/websocket.js.map +1 -0
  241. package/dist/internal/websocketServer.js +61 -0
  242. package/dist/internal/websocketServer.js.map +1 -0
  243. package/dist/internal/workerBootstrap.js +9 -0
  244. package/dist/internal/workerBootstrap.js.map +1 -0
  245. package/dist/internal/workerChannel.js +24 -0
  246. package/dist/internal/workerChannel.js.map +1 -0
  247. package/dist/internal/workerMain.d.ts +1 -0
  248. package/dist/internal/workerMain.js +18 -0
  249. package/dist/internal/workerMain.js.map +1 -0
  250. package/dist/port-Blbh8tms.js +285 -0
  251. package/dist/release-BY4Mlluy.js +23 -0
  252. package/dist/rolldown-runtime-DYf_mQCB.js +34 -0
  253. package/dist/runner/auth/bearer.d.ts +7 -0
  254. package/dist/runner/auth/bearer.d.ts.map +1 -0
  255. package/dist/runner/auth/bearer.js +42 -0
  256. package/dist/runner/auth/bearer.js.map +1 -0
  257. package/dist/runner/auth/jwt.d.ts +19 -0
  258. package/dist/runner/auth/jwt.d.ts.map +1 -0
  259. package/dist/runner/auth/jwt.js +65 -0
  260. package/dist/runner/auth/jwt.js.map +1 -0
  261. package/dist/runner/auth/mtls.d.ts +11 -0
  262. package/dist/runner/auth/mtls.d.ts.map +1 -0
  263. package/dist/runner/auth/mtls.js +48 -0
  264. package/dist/runner/auth/mtls.js.map +1 -0
  265. package/dist/runner/auth/none.d.ts +3 -0
  266. package/dist/runner/auth/none.d.ts.map +1 -0
  267. package/dist/runner/auth/none.js +13 -0
  268. package/dist/runner/auth/none.js.map +1 -0
  269. package/dist/runner/index.d.ts +3 -0
  270. package/dist/runner/index.d.ts.map +1 -0
  271. package/dist/runner/index.js +71 -0
  272. package/dist/runner/index.js.map +1 -0
  273. package/dist/runner/stdio.d.ts +7 -0
  274. package/dist/runner/stdio.d.ts.map +1 -0
  275. package/dist/runner/stdio.js +39 -0
  276. package/dist/runner/stdio.js.map +1 -0
  277. package/dist/runner/unix.d.ts +7 -0
  278. package/dist/runner/unix.d.ts.map +1 -0
  279. package/dist/runner/unix.js +42 -0
  280. package/dist/runner/unix.js.map +1 -0
  281. package/dist/runner/websocket.d.ts +28 -0
  282. package/dist/runner/websocket.d.ts.map +1 -0
  283. package/dist/runner/websocket.js +77 -0
  284. package/dist/runner/websocket.js.map +1 -0
  285. package/dist/socketChannel-KmtzF-9G.js +3658 -0
  286. package/dist/supervisorPlugin-Inb1S5IV.js +737 -0
  287. package/dist/token-v5ne2I0-.js +17 -0
  288. package/dist/tokenCredentials-WUJm2AKm.js +31 -0
  289. package/dist/websocketServer-MXcCJx0v.js +58 -0
  290. package/dist/workerChannel-P82YdNjh.js +27 -0
  291. package/package.json +69 -3
  292. package/src/Auth.ts +62 -0
  293. package/src/Contract.ts +100 -0
  294. package/src/Errors.ts +82 -0
  295. package/src/Instance.ts +12 -0
  296. package/src/Testing.ts +135 -0
  297. package/src/Transport.ts +52 -0
  298. package/src/bin.ts +145 -0
  299. package/src/host/auth/bearer.ts +10 -0
  300. package/src/host/auth/jwt.ts +10 -0
  301. package/src/host/auth/mtls.ts +48 -0
  302. package/src/host/auth/none.ts +13 -0
  303. package/src/host/command.ts +131 -0
  304. package/src/host/local.ts +41 -0
  305. package/src/host/unix.ts +65 -0
  306. package/src/host/websocket.ts +66 -0
  307. package/src/host/worker.ts +90 -0
  308. package/src/index.ts +115 -0
  309. package/src/internal/Certificates.ts +330 -0
  310. package/src/internal/FailureGroup.ts +176 -0
  311. package/src/internal/Limits.ts +12 -0
  312. package/src/internal/Peer.ts +4 -0
  313. package/src/internal/ServeLifecycle.ts +53 -0
  314. package/src/internal/Supervisor.ts +14 -0
  315. package/src/internal/assembly.ts +337 -0
  316. package/src/internal/auth/mtls.ts +40 -0
  317. package/src/internal/auth/token.ts +26 -0
  318. package/src/internal/auth/tokenCredentials.ts +65 -0
  319. package/src/internal/child.ts +323 -0
  320. package/src/internal/childErrors.ts +44 -0
  321. package/src/internal/childRuntime.ts +111 -0
  322. package/src/internal/cleanup.ts +34 -0
  323. package/src/internal/config.ts +8 -0
  324. package/src/internal/control.ts +49 -0
  325. package/src/internal/credentials.ts +112 -0
  326. package/src/internal/directTransport.ts +38 -0
  327. package/src/internal/entry.ts +288 -0
  328. package/src/internal/entryConfig.ts +190 -0
  329. package/src/internal/execution.ts +121 -0
  330. package/src/internal/follow.ts +17 -0
  331. package/src/internal/forkTree.ts +13 -0
  332. package/src/internal/host/local.ts +53 -0
  333. package/src/internal/host/runner.ts +187 -0
  334. package/src/internal/host.ts +209 -0
  335. package/src/internal/importer.ts +27 -0
  336. package/src/internal/listenerStartup.ts +55 -0
  337. package/src/internal/loadModule.ts +23 -0
  338. package/src/internal/platform.ts +12 -0
  339. package/src/internal/port.ts +623 -0
  340. package/src/internal/release.ts +71 -0
  341. package/src/internal/runner/Bootstrap.ts +9 -0
  342. package/src/internal/runner/Connection.ts +184 -0
  343. package/src/internal/runner/Execution.ts +244 -0
  344. package/src/internal/runner/Framing.ts +122 -0
  345. package/src/internal/runner/Local.ts +73 -0
  346. package/src/internal/runner/Lock.ts +29 -0
  347. package/src/internal/runner/Multiplex.ts +120 -0
  348. package/src/internal/runner/Packages.ts +178 -0
  349. package/src/internal/runner/ProcessObservation.ts +63 -0
  350. package/src/internal/runner/Processes.ts +109 -0
  351. package/src/internal/runner/Retention.ts +49 -0
  352. package/src/internal/runner/Store.ts +160 -0
  353. package/src/internal/runner/Supervisor.ts +901 -0
  354. package/src/internal/runner/Watchdog.ts +83 -0
  355. package/src/internal/runner/WatchdogProtocol.ts +12 -0
  356. package/src/internal/runner/Wire.ts +99 -0
  357. package/src/internal/runner/accept.ts +103 -0
  358. package/src/internal/runner/command.ts +15 -0
  359. package/src/internal/runner/connectionEnded.ts +28 -0
  360. package/src/internal/runner/model.ts +364 -0
  361. package/src/internal/serve.ts +117 -0
  362. package/src/internal/services.ts +74 -0
  363. package/src/internal/socketChannel.ts +64 -0
  364. package/src/internal/stdioChannel.ts +25 -0
  365. package/src/internal/supervisorPlugin.ts +67 -0
  366. package/src/internal/transportOptions.ts +17 -0
  367. package/src/internal/watchdogMain.ts +72 -0
  368. package/src/internal/websocket.ts +81 -0
  369. package/src/internal/websocketServer.ts +152 -0
  370. package/src/internal/workerBootstrap.ts +6 -0
  371. package/src/internal/workerChannel.ts +54 -0
  372. package/src/internal/workerMain.ts +23 -0
  373. package/src/runner/auth/bearer.ts +66 -0
  374. package/src/runner/auth/jwt.ts +114 -0
  375. package/src/runner/auth/mtls.ts +37 -0
  376. package/src/runner/auth/none.ts +13 -0
  377. package/src/runner/index.ts +97 -0
  378. package/src/runner/stdio.ts +76 -0
  379. package/src/runner/unix.ts +53 -0
  380. package/src/runner/websocket.ts +143 -0
@@ -0,0 +1,83 @@
1
+ import { Cause, Deferred, Effect, Schema } from "effect"
2
+ import * as FailureGroup from "../FailureGroup"
3
+ import { Worker } from "node:worker_threads"
4
+ import { ExecutionError } from "./model"
5
+ import { Notice } from "./WatchdogProtocol"
6
+
7
+ export const start = Effect.gen(function* () {
8
+ const ready = yield* Deferred.make<void>()
9
+ const begin = yield* Deferred.make<void>()
10
+ const stop = yield* Deferred.make<void>()
11
+ const worker = yield* Effect.acquireRelease(
12
+ Effect.try({
13
+ try: () =>
14
+ new Worker(
15
+ new URL(
16
+ import.meta.resolve("@graftland/plugin-remote/internal/watchdog"),
17
+ ),
18
+ ),
19
+ catch: (cause) =>
20
+ ExecutionError.make({
21
+ detail: "Starting the child watchdog failed",
22
+ cause: Cause.die(cause),
23
+ }),
24
+ }),
25
+ (worker) => Effect.promise(() => worker.terminate()).pipe(Effect.asVoid),
26
+ )
27
+ const listeners = yield* FailureGroup.make<ExecutionError>()
28
+ const run = yield* listeners.runtime<never>()
29
+ const message = (value: unknown) =>
30
+ run(
31
+ Schema.decodeUnknownEffect(Notice)(value).pipe(
32
+ Effect.mapError((error) =>
33
+ ExecutionError.make({
34
+ detail: "Invalid watchdog notice",
35
+ cause: Cause.fail(error),
36
+ }),
37
+ ),
38
+ Effect.flatMap((notice) =>
39
+ Deferred.succeed(
40
+ notice === "ready" ? ready : notice === "start" ? begin : stop,
41
+ undefined,
42
+ ),
43
+ ),
44
+ Effect.asVoid,
45
+ ),
46
+ )
47
+ const error = (error: Error) =>
48
+ run(
49
+ Effect.fail(
50
+ ExecutionError.make({
51
+ detail: "Child watchdog failed",
52
+ cause: Cause.die(error),
53
+ }),
54
+ ),
55
+ )
56
+ const exit = (code: number) =>
57
+ run(
58
+ Effect.fail(
59
+ ExecutionError.make({
60
+ detail: "Child watchdog exited: " + code,
61
+ }),
62
+ ),
63
+ )
64
+ yield* Effect.acquireRelease(
65
+ Effect.sync(() => {
66
+ worker.on("message", message)
67
+ worker.on("error", error)
68
+ worker.on("exit", exit)
69
+ }),
70
+ () =>
71
+ Effect.sync(() => {
72
+ worker.off("message", message)
73
+ worker.off("error", error)
74
+ worker.off("exit", exit)
75
+ }),
76
+ )
77
+ const failure = listeners.await
78
+ yield* Effect.raceFirst(Deferred.await(ready), failure)
79
+ return {
80
+ begin: Effect.raceFirst(Deferred.await(begin), failure),
81
+ stop: Effect.raceFirst(Deferred.await(stop), failure),
82
+ }
83
+ })
@@ -0,0 +1,12 @@
1
+ import { Schema } from "effect"
2
+
3
+ export const Command = Schema.Union([
4
+ Schema.Struct({
5
+ kind: Schema.Literal("renew"),
6
+ millis: Schema.Finite.check(Schema.isGreaterThan(0)),
7
+ }),
8
+ Schema.Struct({ kind: Schema.Literal("start") }),
9
+ Schema.Struct({ kind: Schema.Literal("stop") }),
10
+ ])
11
+ export const CommandJson = Schema.fromJsonString(Command)
12
+ export const Notice = Schema.Literals(["ready", "start", "stop"])
@@ -0,0 +1,99 @@
1
+ import { Cause, Effect, Sink, Stream, Schema } from "effect"
2
+ import { Rpc, RpcGroup } from "effect/rpc"
3
+ import { TransportError } from "../../Errors"
4
+ import * as Port from "../port"
5
+ import type * as Multiplex from "./Multiplex"
6
+ import {
7
+ CodeIdentity,
8
+ Handle,
9
+ Limits,
10
+ PositiveInt,
11
+ PositiveFinite,
12
+ SessionRecord,
13
+ SessionAccessDenied,
14
+ StaleSession,
15
+ LeaseExpired,
16
+ SessionStopped,
17
+ SessionConflict,
18
+ StateStorageError,
19
+ ExecutionError,
20
+ PackageError,
21
+ TerminationUnconfirmed,
22
+ RunnerClosing,
23
+ StateDirectoryLocked,
24
+ Termination,
25
+ } from "./model"
26
+
27
+ /** The RPC boundary is the only place that assembles the runner's error schema. */
28
+ export const SessionError = Schema.Union([
29
+ SessionAccessDenied,
30
+ StaleSession,
31
+ LeaseExpired,
32
+ SessionStopped,
33
+ SessionConflict,
34
+ StateStorageError,
35
+ ExecutionError,
36
+ PackageError,
37
+ TerminationUnconfirmed,
38
+ RunnerClosing,
39
+ StateDirectoryLocked,
40
+ ])
41
+ export type SessionError = typeof SessionError.Type
42
+
43
+ export const Session = RpcGroup.make(
44
+ Rpc.make("HighWater", {
45
+ payload: { instance: Schema.NonEmptyString },
46
+ success: Schema.NullOr(PositiveInt),
47
+ error: SessionError,
48
+ }),
49
+ Rpc.make("Open", {
50
+ payload: {
51
+ instance: Schema.NonEmptyString,
52
+ requestId: Schema.NonEmptyString,
53
+ expectedGeneration: Schema.NullOr(PositiveInt),
54
+ leaseMillis: Schema.optionalKey(PositiveFinite),
55
+ limits: Limits,
56
+ },
57
+ success: SessionRecord,
58
+ error: SessionError,
59
+ }),
60
+ Rpc.make("Load", {
61
+ payload: {
62
+ handle: Handle,
63
+ specifier: Schema.NonEmptyString,
64
+ digest: Schema.optionalKey(Schema.Boolean),
65
+ },
66
+ success: CodeIdentity,
67
+ error: SessionError,
68
+ }),
69
+ Rpc.make("Renew", {
70
+ payload: Handle,
71
+ success: Schema.Finite,
72
+ error: SessionError,
73
+ }),
74
+ Rpc.make("Dispose", {
75
+ payload: Handle,
76
+ success: Termination,
77
+ error: SessionError,
78
+ }),
79
+ Rpc.make("Status", {
80
+ payload: Handle,
81
+ success: SessionRecord,
82
+ error: SessionError,
83
+ }),
84
+ ).annotateRpcs(Port.Priority, "control")
85
+
86
+ export type Peer = Multiplex.Peer
87
+ /** Preserve the port writer order within each byte channel. */
88
+ export const channel = (peer: Peer, id: 0 | 1): Port.Channel => ({
89
+ input: Stream.fromEffectRepeat(peer.take(id)),
90
+ output: Sink.forEach((bytes: Uint8Array) =>
91
+ peer
92
+ .send(id, bytes)
93
+ .pipe(
94
+ Effect.mapError((error) =>
95
+ TransportError.make({ cause: Cause.fail(error) }),
96
+ ),
97
+ ),
98
+ ),
99
+ })
@@ -0,0 +1,103 @@
1
+ import { Cause, Effect, Ref, Option, Scope, Semaphore } from "effect"
2
+ import { RpcServer } from "effect/rpc"
3
+ import { Peer } from "../Peer"
4
+ import { TransportError } from "../../Errors"
5
+ import type { Channel } from "../../Transport"
6
+ import type { Supervisor } from "./Supervisor"
7
+ import type { Channels } from "./Execution"
8
+ import * as Port from "../port"
9
+ import * as Multiplex from "./Multiplex"
10
+ import * as Wire from "./Wire"
11
+ import { SessionAccessDenied, SessionConflict, type Handle } from "./model"
12
+
13
+ export const accept = (
14
+ supervisor: typeof Supervisor.Service,
15
+ execution: typeof Channels.Service,
16
+ channel: Channel,
17
+ ) =>
18
+ Effect.gen(function* () {
19
+ const identity = yield* Peer
20
+ const scope = yield* Effect.scope
21
+ const mux = yield* Multiplex.make(channel)
22
+ const peer = yield* Port.make(Wire.channel(mux, 0), {
23
+ session: "session-v1",
24
+ serve: { session: Wire.Session },
25
+ call: {},
26
+ })
27
+ const attached = yield* Ref.make(Option.none<string>())
28
+ const loading = yield* Semaphore.make(1)
29
+ const authorize = (handle: Handle) =>
30
+ handle.key.identity === identity
31
+ ? Effect.void
32
+ : Effect.fail(
33
+ SessionAccessDenied.make({
34
+ detail: "The session belongs to another authenticated identity",
35
+ }),
36
+ )
37
+ const handlers = yield* Wire.Session.toHandlers({
38
+ HighWater: ({ instance }) => supervisor.highWater({ identity, instance }),
39
+ Open: ({
40
+ instance,
41
+ requestId,
42
+ expectedGeneration,
43
+ leaseMillis,
44
+ limits,
45
+ }) =>
46
+ supervisor.open({
47
+ key: { identity, instance },
48
+ requestId,
49
+ expectedGeneration,
50
+ ...(leaseMillis === undefined ? {} : { lease: leaseMillis }),
51
+ limits,
52
+ }),
53
+ Load: ({ handle, ...request }) =>
54
+ Effect.gen(function* () {
55
+ yield* authorize(handle)
56
+ const attachment = yield* Ref.get(attached)
57
+ if (Option.isSome(attachment) && attachment.value !== handle.token)
58
+ return yield* SessionConflict.make({
59
+ detail: "A connection can attach only one execution",
60
+ })
61
+ const code = yield* supervisor.load(handle, request)
62
+ if (Option.isNone(attachment)) {
63
+ const child = yield* execution
64
+ .peer(handle.token)
65
+ .pipe(Scope.provide(scope))
66
+ yield* Ref.set(attached, Option.some(handle.token))
67
+ // A child EOF must not destroy the session channel before Dispose can acknowledge exit.
68
+ yield* Effect.all(
69
+ [
70
+ Effect.forever(
71
+ Effect.flatMap(mux.take(1), (bytes) => child.send(1, bytes)),
72
+ ),
73
+ Effect.forever(
74
+ Effect.flatMap(child.take(1), (bytes) => mux.send(1, bytes)),
75
+ ),
76
+ ],
77
+ { concurrency: "unbounded" },
78
+ ).pipe(
79
+ Effect.catchCause((cause) =>
80
+ Cause.hasInterruptsOnly(cause)
81
+ ? Effect.void
82
+ : child.fail(TransportError.make({ cause })),
83
+ ),
84
+ Effect.forkIn(scope),
85
+ )
86
+ }
87
+ return code
88
+ }).pipe(loading.withPermit),
89
+ Renew: (handle) =>
90
+ authorize(handle).pipe(Effect.andThen(supervisor.renew(handle))),
91
+ Dispose: (handle) =>
92
+ authorize(handle).pipe(Effect.andThen(supervisor.dispose(handle))),
93
+ Status: (handle) =>
94
+ authorize(handle).pipe(Effect.andThen(supervisor.status(handle))),
95
+ })
96
+ return yield* RpcServer.make(Wire.Session, {
97
+ disableFatalDefects: true,
98
+ }).pipe(
99
+ Effect.provideContext(handlers),
100
+ Effect.provideService(RpcServer.Protocol, peer.serverProtocol("session")),
101
+ Effect.raceFirst(mux.await),
102
+ )
103
+ })
@@ -0,0 +1,15 @@
1
+ import { Effect } from "effect"
2
+ import process from "node:process"
3
+ import { fileURLToPath } from "node:url"
4
+
5
+ /** Resolve the executable through the installed package export, never a relative dist path. */
6
+ export const command = (override?: readonly [string, ...string[]]) =>
7
+ Effect.sync(
8
+ (): readonly [string, ...string[]] =>
9
+ override ?? [
10
+ process.execPath,
11
+ fileURLToPath(
12
+ import.meta.resolve("@graftland/plugin-remote/internal/bin"),
13
+ ),
14
+ ],
15
+ )
@@ -0,0 +1,28 @@
1
+ import { Array, Cause, Effect, Schema } from "effect"
2
+ import { Socket } from "effect/socket"
3
+ import { TransportError } from "../../Errors"
4
+ import { EndOfStream } from "./Framing"
5
+ import { Peer } from "../Peer"
6
+
7
+ // Multiplex preserves the socket reason inside TransportError; only an entirely normal close is silent.
8
+ export const closedNormally = (cause: Cause.Cause<unknown>): boolean =>
9
+ cause.reasons.length > 0 &&
10
+ Array.every(
11
+ cause.reasons,
12
+ (reason) =>
13
+ reason._tag === "Fail" &&
14
+ (Schema.is(TransportError)(reason.error)
15
+ ? closedNormally(reason.error.cause)
16
+ : Schema.is(EndOfStream)(reason.error) ||
17
+ (Socket.isSocketError(reason.error) &&
18
+ reason.error.reason._tag === "SocketCloseError" &&
19
+ reason.error.reason.code === 1000)),
20
+ )
21
+ export const connectionEnded = (cause: Cause.Cause<unknown>) =>
22
+ Cause.hasInterruptsOnly(cause) || closedNormally(cause)
23
+ ? Effect.void
24
+ : Effect.flatMap(Peer, (identity) =>
25
+ Effect.logWarning("Remote host connection failed", cause).pipe(
26
+ Effect.annotateLogs({ "remote.peer": identity }),
27
+ ),
28
+ )
@@ -0,0 +1,364 @@
1
+ import { Limits, PositiveInt } from "../Limits"
2
+ export { Limits, PositiveInt, PositiveFinite, defaults } from "../Limits"
3
+ import {
4
+ Array,
5
+ Cause,
6
+ Context,
7
+ Data,
8
+ Duration,
9
+ Effect,
10
+ Option,
11
+ Schema,
12
+ } from "effect"
13
+ import type { Scope } from "effect"
14
+
15
+ const diagnosticCause = Schema.Cause(
16
+ Schema.Defect({ includeStack: true }),
17
+ Schema.Defect({ includeStack: true }),
18
+ )
19
+ export class PackageError extends Schema.TaggedError<
20
+ PackageError,
21
+ { readonly brand: unique symbol }
22
+ >()("PackageError", {
23
+ detail: Schema.String,
24
+ cause: Schema.optionalKey(diagnosticCause),
25
+ }) {
26
+ override get message() {
27
+ return this.detail
28
+ }
29
+ }
30
+ export class SessionAccessDenied extends Schema.TaggedError<
31
+ SessionAccessDenied,
32
+ { readonly brand: unique symbol }
33
+ >()("SessionAccessDenied", {
34
+ detail: Schema.String,
35
+ cause: Schema.optionalKey(diagnosticCause),
36
+ }) {
37
+ override get message() {
38
+ return this.detail
39
+ }
40
+ }
41
+ export class StaleSession extends Schema.TaggedError<
42
+ StaleSession,
43
+ { readonly brand: unique symbol }
44
+ >()("StaleSession", {
45
+ detail: Schema.String,
46
+ cause: Schema.optionalKey(diagnosticCause),
47
+ }) {
48
+ override get message() {
49
+ return this.detail
50
+ }
51
+ }
52
+ export class LeaseExpired extends Schema.TaggedError<
53
+ LeaseExpired,
54
+ { readonly brand: unique symbol }
55
+ >()("LeaseExpired", {
56
+ detail: Schema.String,
57
+ cause: Schema.optionalKey(diagnosticCause),
58
+ }) {
59
+ override get message() {
60
+ return this.detail
61
+ }
62
+ }
63
+ export class SessionStopped extends Schema.TaggedError<
64
+ SessionStopped,
65
+ { readonly brand: unique symbol }
66
+ >()("SessionStopped", {
67
+ detail: Schema.String,
68
+ cause: Schema.optionalKey(diagnosticCause),
69
+ }) {
70
+ override get message() {
71
+ return this.detail
72
+ }
73
+ }
74
+ export class SessionConflict extends Schema.TaggedError<
75
+ SessionConflict,
76
+ { readonly brand: unique symbol }
77
+ >()("SessionConflict", {
78
+ detail: Schema.String,
79
+ cause: Schema.optionalKey(diagnosticCause),
80
+ }) {
81
+ override get message() {
82
+ return this.detail
83
+ }
84
+ }
85
+ export class StateStorageError extends Schema.TaggedError<
86
+ StateStorageError,
87
+ { readonly brand: unique symbol }
88
+ >()("StateStorageError", {
89
+ detail: Schema.String,
90
+ cause: Schema.optionalKey(diagnosticCause),
91
+ }) {
92
+ override get message() {
93
+ return this.detail
94
+ }
95
+ }
96
+ export class ExecutionError extends Schema.TaggedError<
97
+ ExecutionError,
98
+ { readonly brand: unique symbol }
99
+ >()("ExecutionError", {
100
+ detail: Schema.String,
101
+ cause: Schema.optionalKey(diagnosticCause),
102
+ }) {
103
+ override get message() {
104
+ return this.detail
105
+ }
106
+ }
107
+ export class TerminationUnconfirmed extends Schema.TaggedError<
108
+ TerminationUnconfirmed,
109
+ { readonly brand: unique symbol }
110
+ >()("TerminationUnconfirmed", {
111
+ detail: Schema.String,
112
+ cause: Schema.optionalKey(diagnosticCause),
113
+ }) {
114
+ override get message() {
115
+ return this.detail
116
+ }
117
+ }
118
+ export class RunnerClosing extends Schema.TaggedError<
119
+ RunnerClosing,
120
+ { readonly brand: unique symbol }
121
+ >()("RunnerClosing", {
122
+ detail: Schema.String,
123
+ cause: Schema.optionalKey(diagnosticCause),
124
+ }) {
125
+ override get message() {
126
+ return this.detail
127
+ }
128
+ }
129
+ export class StateDirectoryLocked extends Schema.TaggedError<
130
+ StateDirectoryLocked,
131
+ { readonly brand: unique symbol }
132
+ >()("StateDirectoryLocked", {
133
+ detail: Schema.String,
134
+ cause: Schema.optionalKey(diagnosticCause),
135
+ }) {
136
+ override get message() {
137
+ return this.detail
138
+ }
139
+ }
140
+ export const Key = Schema.Struct({
141
+ identity: Schema.NonEmptyString,
142
+ instance: Schema.NonEmptyString,
143
+ })
144
+ export type Key = typeof Key.Type
145
+ /** Structural keys keep identity namespaces separate without delimiter conventions. */
146
+ export class SessionKey extends Data.Class<Key> {}
147
+ export const keyOf = (key: Key) => new SessionKey(key)
148
+ export const negotiate = (local: Limits, peer: Limits): Limits => ({
149
+ control: Math.min(local.control, peer.control),
150
+ data: Math.min(local.data, peer.data),
151
+ log: Math.min(local.log, peer.log),
152
+ })
153
+ export const Handle = Schema.Struct({
154
+ key: Key,
155
+ token: Schema.NonEmptyString,
156
+ generation: PositiveInt,
157
+ })
158
+ export type Handle = typeof Handle.Type
159
+ export const ProcessIdentity = Schema.Struct({
160
+ pid: Schema.Int,
161
+ start: Schema.Struct({ born: Schema.String }),
162
+ })
163
+ export type ProcessIdentity = typeof ProcessIdentity.Type
164
+ export const ExitStatus = Schema.Struct({
165
+ stderr: Schema.optionalKey(Schema.String),
166
+ cause: Schema.optionalKey(diagnosticCause),
167
+ code: Schema.NullOr(Schema.Int),
168
+ signal: Schema.NullOr(Schema.String),
169
+ })
170
+ export type ExitStatus = typeof ExitStatus.Type
171
+ export const CodeIdentity = Schema.Struct({
172
+ name: Schema.String,
173
+ version: Schema.String,
174
+ digest: Schema.optionalKey(Schema.String),
175
+ })
176
+ export type CodeIdentity = typeof CodeIdentity.Type
177
+ export const Load = Schema.Struct({
178
+ specifier: Schema.String,
179
+ digest: Schema.optionalKey(Schema.Boolean),
180
+ })
181
+ export type Load = typeof Load.Type
182
+ export const Termination = Schema.Struct({
183
+ kind: Schema.Literals(["Exited", "Killed"]),
184
+ exit: ExitStatus,
185
+ cause: diagnosticCause,
186
+ })
187
+ export type Termination = typeof Termination.Type
188
+ const Fields = {
189
+ version: Schema.Literal(1),
190
+ handle: Handle,
191
+ requestId: Schema.String,
192
+ leaseMillis: Schema.Finite,
193
+ limits: Limits,
194
+ }
195
+ export const OpenRecord = Schema.Struct({
196
+ ...Fields,
197
+ phase: Schema.Literal("Open"),
198
+ })
199
+ export const Launch = Schema.Union([
200
+ Schema.TaggedStruct("Starting", {}),
201
+ Schema.TaggedStruct("Registered", { child: ProcessIdentity }),
202
+ ])
203
+ export const LoadingRecord = Schema.Struct({
204
+ ...Fields,
205
+ phase: Schema.Literal("Loading"),
206
+ load: Load,
207
+ launch: Launch,
208
+ })
209
+ export const LoadedRecord = Schema.Struct({
210
+ ...Fields,
211
+ phase: Schema.Literal("Loaded"),
212
+ load: Load,
213
+ child: ProcessIdentity,
214
+ code: CodeIdentity,
215
+ })
216
+ export const ActiveRecord = Schema.Union([
217
+ OpenRecord,
218
+ LoadingRecord,
219
+ LoadedRecord,
220
+ ])
221
+ export type ActiveRecord = typeof ActiveRecord.Type
222
+ export const StopReason = Schema.Union([
223
+ ...Array.map(
224
+ [
225
+ "Disposed",
226
+ "Expired",
227
+ "Superseded",
228
+ "Recovery",
229
+ "Shutdown",
230
+ "Exited",
231
+ ] as const,
232
+ (tag) => Schema.TaggedStruct(tag, {}),
233
+ ),
234
+ Schema.TaggedStruct("LoadFailed", { cause: diagnosticCause }),
235
+ ])
236
+ export type StopReason = typeof StopReason.Type
237
+ export const StoppingRecord = Schema.Struct({
238
+ ...Fields,
239
+ phase: Schema.Literal("Stopping"),
240
+ previous: ActiveRecord,
241
+ reason: StopReason,
242
+ })
243
+ export const StoppedRecord = Schema.Struct({
244
+ ...Fields,
245
+ phase: Schema.Literal("Stopped"),
246
+ previous: ActiveRecord,
247
+ reason: StopReason,
248
+ termination: Termination,
249
+ })
250
+ export const SessionRecord = Schema.Union([
251
+ OpenRecord,
252
+ LoadingRecord,
253
+ LoadedRecord,
254
+ StoppingRecord,
255
+ StoppedRecord,
256
+ ])
257
+ export type SessionRecord = typeof SessionRecord.Type
258
+ export const childOf = (
259
+ record: SessionRecord,
260
+ ): Option.Option<ProcessIdentity> => {
261
+ switch (record.phase) {
262
+ case "Open":
263
+ return Option.none()
264
+ case "Loading":
265
+ return record.launch._tag === "Registered"
266
+ ? Option.some(record.launch.child)
267
+ : Option.none()
268
+ case "Loaded":
269
+ return Option.some(record.child)
270
+ case "Stopping":
271
+ case "Stopped":
272
+ return childOf(record.previous)
273
+ }
274
+ }
275
+ export const stoppingRecord = (
276
+ previous: ActiveRecord,
277
+ reason: StopReason,
278
+ ): typeof StoppingRecord.Type => ({
279
+ version: previous.version,
280
+ handle: previous.handle,
281
+ requestId: previous.requestId,
282
+ leaseMillis: previous.leaseMillis,
283
+ limits: previous.limits,
284
+ phase: "Stopping",
285
+ previous,
286
+ reason,
287
+ })
288
+ export const stoppedRecord = (
289
+ previous: ActiveRecord,
290
+ reason: StopReason,
291
+ termination: Termination,
292
+ ): typeof StoppedRecord.Type => ({
293
+ ...stoppingRecord(previous, reason),
294
+ phase: "Stopped",
295
+ termination,
296
+ })
297
+
298
+ export class Store extends Context.Service<
299
+ Store,
300
+ {
301
+ readonly list: Effect.Effect<
302
+ ReadonlyArray<SessionRecord>,
303
+ StateStorageError
304
+ >
305
+ readonly save: (
306
+ record: SessionRecord,
307
+ ) => Effect.Effect<void, StateStorageError>
308
+ }
309
+ >()("@graftland/plugin-remote/internal/runner/model/Store") {}
310
+ export type ExecutionFailure =
311
+ ExecutionError | PackageError | TerminationUnconfirmed
312
+ export interface Unit {
313
+ readonly code: CodeIdentity
314
+ /** The supervisor persists identity and renews the lease before releasing the child startup gate. */
315
+ readonly start: Effect.Effect<void, ExecutionFailure>
316
+ readonly identity: ProcessIdentity
317
+ readonly exited: Effect.Effect<ExitStatus, ExecutionFailure>
318
+ readonly interrupt: Effect.Effect<void, ExecutionFailure>
319
+ readonly kill: Effect.Effect<void, ExecutionFailure>
320
+ readonly renew: (millis: number) => Effect.Effect<void, ExecutionFailure>
321
+ }
322
+ export class Execution extends Context.Service<
323
+ Execution,
324
+ {
325
+ readonly spawn: (
326
+ handle: Handle,
327
+ request: Load,
328
+ limits: Limits,
329
+ ) => Effect.Effect<Unit, ExecutionFailure, Scope.Scope>
330
+ readonly recover: (
331
+ child: ProcessIdentity,
332
+ ) => Effect.Effect<Termination, ExecutionFailure>
333
+ }
334
+ >()("@graftland/plugin-remote/internal/runner/model/Execution") {}
335
+ export class RemoteTerminationUnconfirmed extends Schema.TaggedError<
336
+ RemoteTerminationUnconfirmed,
337
+ { readonly brand: unique symbol }
338
+ >()("RemoteTerminationUnconfirmed", { handle: Handle }) {}
339
+ /** Losing contact never becomes a successful stop merely because a lease duration passed. */
340
+ export const awaitTermination = <E, R>(
341
+ handle: Handle,
342
+ confirmation: Effect.Effect<Termination, E, R>,
343
+ leaseMillis: number,
344
+ marginMillis: number,
345
+ ) =>
346
+ Effect.gen(function* () {
347
+ const [elapsed, result] = yield* Effect.exit(
348
+ Effect.timeoutOption(confirmation, leaseMillis + marginMillis),
349
+ ).pipe(Effect.timed)
350
+ if (result._tag === "Success") {
351
+ if (Option.isSome(result.value)) return result.value.value
352
+ return yield* RemoteTerminationUnconfirmed.make({ handle })
353
+ }
354
+ // Confirmation and the disconnected wait share one deadline.
355
+ yield* Effect.sleep(
356
+ Math.max(0, leaseMillis + marginMillis - Duration.toMillis(elapsed)),
357
+ )
358
+ return yield* Effect.failCause(
359
+ Cause.combine(
360
+ result.cause,
361
+ Cause.fail(RemoteTerminationUnconfirmed.make({ handle })),
362
+ ),
363
+ )
364
+ })