@graftland/plugin-remote 0.0.0-stage → 0.3.0
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.
- package/LICENSE +202 -0
- package/README.md +76 -2
- package/README.zh-CN.md +71 -0
- package/README.zh-TW.md +71 -0
- package/dist/Auth-BElTC9_X.js +6 -0
- package/dist/Auth.d.ts +49 -0
- package/dist/Auth.d.ts.map +1 -0
- package/dist/Auth.js +8 -0
- package/dist/Auth.js.map +1 -0
- package/dist/Bootstrap-MCwivGN1.js +9 -0
- package/dist/ConfigDirectory-BCC6q2R4.js +5 -0
- package/dist/Contract-CcpSIJm4.js +121 -0
- package/dist/Contract.d.ts +71 -0
- package/dist/Contract.d.ts.map +1 -0
- package/dist/Contract.js +1 -0
- package/dist/Contract.js.map +1 -0
- package/dist/Errors-oPsTLY7l.js +45 -0
- package/dist/Errors.d.ts +67 -0
- package/dist/Errors.d.ts.map +1 -0
- package/dist/Errors.js +47 -0
- package/dist/Errors.js.map +1 -0
- package/dist/FailureGroup-BcH0XOBi.js +53 -0
- package/dist/Framing-zaYdOJnT.js +61 -0
- package/dist/Graft-BsYq3F-J.js +985 -0
- package/dist/Instance.d.ts +7 -0
- package/dist/Instance.d.ts.map +1 -0
- package/dist/Instance.js +14 -0
- package/dist/Instance.js.map +1 -0
- package/dist/Packages-B1TPk8pa.js +111 -0
- package/dist/Peer-IJgFK6mD.js +342 -0
- package/dist/ProcessObservation-DEGrEMfP.js +38 -0
- package/dist/ServeLifecycle-CJM0gG5B.js +24 -0
- package/dist/Testing.d.ts +26 -0
- package/dist/Testing.d.ts.map +1 -0
- package/dist/Testing.js +69 -0
- package/dist/Testing.js.map +1 -0
- package/dist/Transport-DULzpsgR.js +6 -0
- package/dist/Transport.d.ts +39 -0
- package/dist/Transport.d.ts.map +1 -0
- package/dist/Transport.js +8 -0
- package/dist/Transport.js.map +1 -0
- package/dist/WatchdogProtocol-BZmVxrBP.js +17 -0
- package/dist/_virtual/_rolldown/runtime.js +12 -0
- package/dist/bin.d.ts +1 -0
- package/dist/bin.js +28275 -0
- package/dist/bin.js.map +1 -0
- package/dist/childRuntime-DSqtFN9o.js +188 -0
- package/dist/cleanup-BPWr-5U9.js +13 -0
- package/dist/command-9zrHdbwT.js +7 -0
- package/dist/connectionEnded-DUgcobEd.js +9 -0
- package/dist/directTransport-DrexuPSm.js +5 -0
- package/dist/entry-SYsx9VNb.js +708 -0
- package/dist/host/auth/bearer.d.ts +5 -0
- package/dist/host/auth/bearer.d.ts.map +1 -0
- package/dist/host/auth/bearer.js +4 -0
- package/dist/host/auth/bearer.js.map +1 -0
- package/dist/host/auth/jwt.d.ts +5 -0
- package/dist/host/auth/jwt.d.ts.map +1 -0
- package/dist/host/auth/jwt.js +4 -0
- package/dist/host/auth/jwt.js.map +1 -0
- package/dist/host/auth/mtls.d.ts +8 -0
- package/dist/host/auth/mtls.d.ts.map +1 -0
- package/dist/host/auth/mtls.js +34 -0
- package/dist/host/auth/mtls.js.map +1 -0
- package/dist/host/auth/none.d.ts +3 -0
- package/dist/host/auth/none.d.ts.map +1 -0
- package/dist/host/auth/none.js +13 -0
- package/dist/host/auth/none.js.map +1 -0
- package/dist/host/command.d.ts +22 -0
- package/dist/host/command.d.ts.map +1 -0
- package/dist/host/command.js +61 -0
- package/dist/host/command.js.map +1 -0
- package/dist/host/local.d.ts +4 -0
- package/dist/host/local.d.ts.map +1 -0
- package/dist/host/local.js +52 -0
- package/dist/host/local.js.map +1 -0
- package/dist/host/unix.d.ts +11 -0
- package/dist/host/unix.d.ts.map +1 -0
- package/dist/host/unix.js +39 -0
- package/dist/host/unix.js.map +1 -0
- package/dist/host/websocket.d.ts +13 -0
- package/dist/host/websocket.d.ts.map +1 -0
- package/dist/host/websocket.js +36 -0
- package/dist/host/websocket.js.map +1 -0
- package/dist/host/worker.d.ts +4 -0
- package/dist/host/worker.d.ts.map +1 -0
- package/dist/host/worker.js +52 -0
- package/dist/host/worker.js.map +1 -0
- package/dist/importer-D7zSgqxq.js +347 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +86 -0
- package/dist/index.js.map +1 -0
- package/dist/internal/Certificates.js +182 -0
- package/dist/internal/Certificates.js.map +1 -0
- package/dist/internal/FailureGroup.js +55 -0
- package/dist/internal/FailureGroup.js.map +1 -0
- package/dist/internal/Limits.d.ts +1 -0
- package/dist/internal/Limits.js +17 -0
- package/dist/internal/Limits.js.map +1 -0
- package/dist/internal/Peer.js +6 -0
- package/dist/internal/Peer.js.map +1 -0
- package/dist/internal/ServeLifecycle.js +26 -0
- package/dist/internal/ServeLifecycle.js.map +1 -0
- package/dist/internal/Supervisor.js +7 -0
- package/dist/internal/Supervisor.js.map +1 -0
- package/dist/internal/assembly.d.ts +5 -0
- package/dist/internal/assembly.js +138 -0
- package/dist/internal/assembly.js.map +1 -0
- package/dist/internal/auth/mtls.js +23 -0
- package/dist/internal/auth/mtls.js.map +1 -0
- package/dist/internal/auth/token.js +19 -0
- package/dist/internal/auth/token.js.map +1 -0
- package/dist/internal/auth/tokenCredentials.d.ts +8 -0
- package/dist/internal/auth/tokenCredentials.d.ts.map +1 -0
- package/dist/internal/auth/tokenCredentials.js +33 -0
- package/dist/internal/auth/tokenCredentials.js.map +1 -0
- package/dist/internal/child.js +125 -0
- package/dist/internal/child.js.map +1 -0
- package/dist/internal/childErrors.d.ts +33 -0
- package/dist/internal/childErrors.d.ts.map +1 -0
- package/dist/internal/childErrors.js +29 -0
- package/dist/internal/childErrors.js.map +1 -0
- package/dist/internal/childRuntime.js +59 -0
- package/dist/internal/childRuntime.js.map +1 -0
- package/dist/internal/cleanup.js +15 -0
- package/dist/internal/cleanup.js.map +1 -0
- package/dist/internal/config.js +6 -0
- package/dist/internal/config.js.map +1 -0
- package/dist/internal/control.js +42 -0
- package/dist/internal/control.js.map +1 -0
- package/dist/internal/credentials.d.ts +44 -0
- package/dist/internal/credentials.d.ts.map +1 -0
- package/dist/internal/credentials.js +54 -0
- package/dist/internal/credentials.js.map +1 -0
- package/dist/internal/directTransport.d.ts +3 -0
- package/dist/internal/directTransport.js +7 -0
- package/dist/internal/directTransport.js.map +1 -0
- package/dist/internal/entry.d.ts +9 -0
- package/dist/internal/entry.d.ts.map +1 -0
- package/dist/internal/entry.js +144 -0
- package/dist/internal/entry.js.map +1 -0
- package/dist/internal/entryConfig.d.ts +202 -0
- package/dist/internal/entryConfig.d.ts.map +1 -0
- package/dist/internal/entryConfig.js +176 -0
- package/dist/internal/entryConfig.js.map +1 -0
- package/dist/internal/execution.d.ts +5 -0
- package/dist/internal/execution.js +43 -0
- package/dist/internal/execution.js.map +1 -0
- package/dist/internal/follow.js +11 -0
- package/dist/internal/follow.js.map +1 -0
- package/dist/internal/forkTree.js +7 -0
- package/dist/internal/forkTree.js.map +1 -0
- package/dist/internal/host/local.js +28 -0
- package/dist/internal/host/local.js.map +1 -0
- package/dist/internal/host/runner.js +100 -0
- package/dist/internal/host/runner.js.map +1 -0
- package/dist/internal/host.js +71 -0
- package/dist/internal/host.js.map +1 -0
- package/dist/internal/importer.js +18 -0
- package/dist/internal/importer.js.map +1 -0
- package/dist/internal/listenerStartup.js +17 -0
- package/dist/internal/listenerStartup.js.map +1 -0
- package/dist/internal/loadModule.js +20 -0
- package/dist/internal/loadModule.js.map +1 -0
- package/dist/internal/platform.js +9 -0
- package/dist/internal/platform.js.map +1 -0
- package/dist/internal/port.d.ts +13 -0
- package/dist/internal/port.d.ts.map +1 -0
- package/dist/internal/port.js +275 -0
- package/dist/internal/port.js.map +1 -0
- package/dist/internal/release.js +25 -0
- package/dist/internal/release.js.map +1 -0
- package/dist/internal/runner/Bootstrap.js +11 -0
- package/dist/internal/runner/Bootstrap.js.map +1 -0
- package/dist/internal/runner/Connection.d.ts +11 -0
- package/dist/internal/runner/Connection.js +56 -0
- package/dist/internal/runner/Connection.js.map +1 -0
- package/dist/internal/runner/Execution.js +115 -0
- package/dist/internal/runner/Execution.js.map +1 -0
- package/dist/internal/runner/Framing.d.ts +11 -0
- package/dist/internal/runner/Framing.d.ts.map +1 -0
- package/dist/internal/runner/Framing.js +63 -0
- package/dist/internal/runner/Framing.js.map +1 -0
- package/dist/internal/runner/Local.js +46 -0
- package/dist/internal/runner/Local.js.map +1 -0
- package/dist/internal/runner/Lock.js +25 -0
- package/dist/internal/runner/Lock.js.map +1 -0
- package/dist/internal/runner/Multiplex.d.ts +5 -0
- package/dist/internal/runner/Multiplex.js +49 -0
- package/dist/internal/runner/Multiplex.js.map +1 -0
- package/dist/internal/runner/Packages.d.ts +3 -0
- package/dist/internal/runner/Packages.js +108 -0
- package/dist/internal/runner/Packages.js.map +1 -0
- package/dist/internal/runner/ProcessObservation.js +40 -0
- package/dist/internal/runner/ProcessObservation.js.map +1 -0
- package/dist/internal/runner/Processes.js +83 -0
- package/dist/internal/runner/Processes.js.map +1 -0
- package/dist/internal/runner/Retention.js +13 -0
- package/dist/internal/runner/Retention.js.map +1 -0
- package/dist/internal/runner/Store.js +78 -0
- package/dist/internal/runner/Store.js.map +1 -0
- package/dist/internal/runner/Supervisor.js +375 -0
- package/dist/internal/runner/Supervisor.js.map +1 -0
- package/dist/internal/runner/Watchdog.js +47 -0
- package/dist/internal/runner/Watchdog.js.map +1 -0
- package/dist/internal/runner/WatchdogProtocol.js +19 -0
- package/dist/internal/runner/WatchdogProtocol.js.map +1 -0
- package/dist/internal/runner/Wire.d.ts +5 -0
- package/dist/internal/runner/Wire.js +64 -0
- package/dist/internal/runner/Wire.js.map +1 -0
- package/dist/internal/runner/accept.js +57 -0
- package/dist/internal/runner/accept.js.map +1 -0
- package/dist/internal/runner/command.js +9 -0
- package/dist/internal/runner/command.js.map +1 -0
- package/dist/internal/runner/connectionEnded.js +11 -0
- package/dist/internal/runner/connectionEnded.js.map +1 -0
- package/dist/internal/runner/model.d.ts +17 -0
- package/dist/internal/runner/model.d.ts.map +1 -0
- package/dist/internal/runner/model.js +231 -0
- package/dist/internal/runner/model.js.map +1 -0
- package/dist/internal/serve.js +60 -0
- package/dist/internal/serve.js.map +1 -0
- package/dist/internal/services.d.ts +18 -0
- package/dist/internal/services.d.ts.map +1 -0
- package/dist/internal/services.js +22 -0
- package/dist/internal/services.js.map +1 -0
- package/dist/internal/socketChannel.js +24 -0
- package/dist/internal/socketChannel.js.map +1 -0
- package/dist/internal/stdioChannel.js +23 -0
- package/dist/internal/stdioChannel.js.map +1 -0
- package/dist/internal/supervisorPlugin.js +1 -0
- package/dist/internal/supervisorPlugin.js.map +1 -0
- package/dist/internal/transportOptions.d.ts +18 -0
- package/dist/internal/transportOptions.d.ts.map +1 -0
- package/dist/internal/watchdogMain.d.ts +1 -0
- package/dist/internal/watchdogMain.js +55 -0
- package/dist/internal/watchdogMain.js.map +1 -0
- package/dist/internal/websocket.js +46 -0
- package/dist/internal/websocket.js.map +1 -0
- package/dist/internal/websocketServer.js +61 -0
- package/dist/internal/websocketServer.js.map +1 -0
- package/dist/internal/workerBootstrap.js +9 -0
- package/dist/internal/workerBootstrap.js.map +1 -0
- package/dist/internal/workerChannel.js +24 -0
- package/dist/internal/workerChannel.js.map +1 -0
- package/dist/internal/workerMain.d.ts +1 -0
- package/dist/internal/workerMain.js +18 -0
- package/dist/internal/workerMain.js.map +1 -0
- package/dist/port-Blbh8tms.js +285 -0
- package/dist/release-BY4Mlluy.js +23 -0
- package/dist/rolldown-runtime-DYf_mQCB.js +34 -0
- package/dist/runner/auth/bearer.d.ts +7 -0
- package/dist/runner/auth/bearer.d.ts.map +1 -0
- package/dist/runner/auth/bearer.js +42 -0
- package/dist/runner/auth/bearer.js.map +1 -0
- package/dist/runner/auth/jwt.d.ts +19 -0
- package/dist/runner/auth/jwt.d.ts.map +1 -0
- package/dist/runner/auth/jwt.js +65 -0
- package/dist/runner/auth/jwt.js.map +1 -0
- package/dist/runner/auth/mtls.d.ts +11 -0
- package/dist/runner/auth/mtls.d.ts.map +1 -0
- package/dist/runner/auth/mtls.js +48 -0
- package/dist/runner/auth/mtls.js.map +1 -0
- package/dist/runner/auth/none.d.ts +3 -0
- package/dist/runner/auth/none.d.ts.map +1 -0
- package/dist/runner/auth/none.js +13 -0
- package/dist/runner/auth/none.js.map +1 -0
- package/dist/runner/index.d.ts +3 -0
- package/dist/runner/index.d.ts.map +1 -0
- package/dist/runner/index.js +71 -0
- package/dist/runner/index.js.map +1 -0
- package/dist/runner/stdio.d.ts +7 -0
- package/dist/runner/stdio.d.ts.map +1 -0
- package/dist/runner/stdio.js +39 -0
- package/dist/runner/stdio.js.map +1 -0
- package/dist/runner/unix.d.ts +7 -0
- package/dist/runner/unix.d.ts.map +1 -0
- package/dist/runner/unix.js +42 -0
- package/dist/runner/unix.js.map +1 -0
- package/dist/runner/websocket.d.ts +28 -0
- package/dist/runner/websocket.d.ts.map +1 -0
- package/dist/runner/websocket.js +77 -0
- package/dist/runner/websocket.js.map +1 -0
- package/dist/socketChannel-KmtzF-9G.js +3658 -0
- package/dist/supervisorPlugin-Inb1S5IV.js +737 -0
- package/dist/token-v5ne2I0-.js +17 -0
- package/dist/tokenCredentials-WUJm2AKm.js +31 -0
- package/dist/websocketServer-MXcCJx0v.js +58 -0
- package/dist/workerChannel-P82YdNjh.js +27 -0
- package/package.json +69 -3
- package/src/Auth.ts +62 -0
- package/src/Contract.ts +100 -0
- package/src/Errors.ts +82 -0
- package/src/Instance.ts +12 -0
- package/src/Testing.ts +135 -0
- package/src/Transport.ts +52 -0
- package/src/bin.ts +145 -0
- package/src/host/auth/bearer.ts +10 -0
- package/src/host/auth/jwt.ts +10 -0
- package/src/host/auth/mtls.ts +48 -0
- package/src/host/auth/none.ts +13 -0
- package/src/host/command.ts +131 -0
- package/src/host/local.ts +41 -0
- package/src/host/unix.ts +65 -0
- package/src/host/websocket.ts +66 -0
- package/src/host/worker.ts +90 -0
- package/src/index.ts +115 -0
- package/src/internal/Certificates.ts +330 -0
- package/src/internal/FailureGroup.ts +176 -0
- package/src/internal/Limits.ts +12 -0
- package/src/internal/Peer.ts +4 -0
- package/src/internal/ServeLifecycle.ts +53 -0
- package/src/internal/Supervisor.ts +14 -0
- package/src/internal/assembly.ts +337 -0
- package/src/internal/auth/mtls.ts +40 -0
- package/src/internal/auth/token.ts +26 -0
- package/src/internal/auth/tokenCredentials.ts +65 -0
- package/src/internal/child.ts +323 -0
- package/src/internal/childErrors.ts +44 -0
- package/src/internal/childRuntime.ts +111 -0
- package/src/internal/cleanup.ts +34 -0
- package/src/internal/config.ts +8 -0
- package/src/internal/control.ts +49 -0
- package/src/internal/credentials.ts +112 -0
- package/src/internal/directTransport.ts +38 -0
- package/src/internal/entry.ts +288 -0
- package/src/internal/entryConfig.ts +190 -0
- package/src/internal/execution.ts +121 -0
- package/src/internal/follow.ts +17 -0
- package/src/internal/forkTree.ts +13 -0
- package/src/internal/host/local.ts +53 -0
- package/src/internal/host/runner.ts +187 -0
- package/src/internal/host.ts +209 -0
- package/src/internal/importer.ts +27 -0
- package/src/internal/listenerStartup.ts +55 -0
- package/src/internal/loadModule.ts +23 -0
- package/src/internal/platform.ts +12 -0
- package/src/internal/port.ts +623 -0
- package/src/internal/release.ts +71 -0
- package/src/internal/runner/Bootstrap.ts +9 -0
- package/src/internal/runner/Connection.ts +184 -0
- package/src/internal/runner/Execution.ts +244 -0
- package/src/internal/runner/Framing.ts +122 -0
- package/src/internal/runner/Local.ts +73 -0
- package/src/internal/runner/Lock.ts +29 -0
- package/src/internal/runner/Multiplex.ts +120 -0
- package/src/internal/runner/Packages.ts +178 -0
- package/src/internal/runner/ProcessObservation.ts +63 -0
- package/src/internal/runner/Processes.ts +109 -0
- package/src/internal/runner/Retention.ts +49 -0
- package/src/internal/runner/Store.ts +160 -0
- package/src/internal/runner/Supervisor.ts +901 -0
- package/src/internal/runner/Watchdog.ts +83 -0
- package/src/internal/runner/WatchdogProtocol.ts +12 -0
- package/src/internal/runner/Wire.ts +99 -0
- package/src/internal/runner/accept.ts +103 -0
- package/src/internal/runner/command.ts +15 -0
- package/src/internal/runner/connectionEnded.ts +28 -0
- package/src/internal/runner/model.ts +364 -0
- package/src/internal/serve.ts +117 -0
- package/src/internal/services.ts +74 -0
- package/src/internal/socketChannel.ts +64 -0
- package/src/internal/stdioChannel.ts +25 -0
- package/src/internal/supervisorPlugin.ts +67 -0
- package/src/internal/transportOptions.ts +17 -0
- package/src/internal/watchdogMain.ts +72 -0
- package/src/internal/websocket.ts +81 -0
- package/src/internal/websocketServer.ts +152 -0
- package/src/internal/workerBootstrap.ts +6 -0
- package/src/internal/workerChannel.ts +54 -0
- package/src/internal/workerMain.ts +23 -0
- package/src/runner/auth/bearer.ts +66 -0
- package/src/runner/auth/jwt.ts +114 -0
- package/src/runner/auth/mtls.ts +37 -0
- package/src/runner/auth/none.ts +13 -0
- package/src/runner/index.ts +97 -0
- package/src/runner/stdio.ts +76 -0
- package/src/runner/unix.ts +53 -0
- package/src/runner/websocket.ts +143 -0
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import {
|
|
2
|
+
LocalTransport,
|
|
3
|
+
WorkerTransport,
|
|
4
|
+
type Execution,
|
|
5
|
+
} from "../directTransport"
|
|
6
|
+
import * as RunnerConnection from "../host/runner"
|
|
7
|
+
import {
|
|
8
|
+
Array,
|
|
9
|
+
Cause,
|
|
10
|
+
Context,
|
|
11
|
+
Effect,
|
|
12
|
+
Duration,
|
|
13
|
+
Exit,
|
|
14
|
+
Option,
|
|
15
|
+
Scope,
|
|
16
|
+
} from "effect"
|
|
17
|
+
import * as Release from "../release"
|
|
18
|
+
import type { RpcClientError } from "effect/rpc"
|
|
19
|
+
import type { Schema, PlatformError } from "effect"
|
|
20
|
+
import type { RemoteTerminationUnconfirmed } from "./model"
|
|
21
|
+
import type { Expected } from "./Packages"
|
|
22
|
+
|
|
23
|
+
import {
|
|
24
|
+
LocalTerminationTimeout,
|
|
25
|
+
ProcessExited,
|
|
26
|
+
TransportError,
|
|
27
|
+
} from "../../Errors"
|
|
28
|
+
import { cleanupCause } from "../cleanup"
|
|
29
|
+
import * as Port from "../port"
|
|
30
|
+
import * as LocalProcess from "./Local"
|
|
31
|
+
import * as Wire from "./Wire"
|
|
32
|
+
|
|
33
|
+
export interface LocalTarget {
|
|
34
|
+
readonly _tag: "Local"
|
|
35
|
+
readonly command?: readonly [string, ...string[]]
|
|
36
|
+
}
|
|
37
|
+
export interface WorkerTarget {
|
|
38
|
+
readonly _tag: "Worker"
|
|
39
|
+
readonly execArgv?: ReadonlyArray<string>
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface ConfiguredRunner {
|
|
43
|
+
readonly instance: string
|
|
44
|
+
readonly lease?: Duration.Input
|
|
45
|
+
readonly margin?: Duration.Input
|
|
46
|
+
readonly expect?: Expected
|
|
47
|
+
readonly _tag: "Connector"
|
|
48
|
+
readonly explicitAuth?: boolean
|
|
49
|
+
}
|
|
50
|
+
export type Target = LocalTarget | WorkerTarget | ConfiguredRunner
|
|
51
|
+
|
|
52
|
+
type Peer = Port.Port
|
|
53
|
+
export interface Connected {
|
|
54
|
+
readonly resources: Scope.Scope
|
|
55
|
+
readonly pid: Option.Option<number>
|
|
56
|
+
readonly threadId?: number
|
|
57
|
+
readonly diagnostics: Effect.Effect<Cause.Cause<unknown>>
|
|
58
|
+
readonly terminate: (
|
|
59
|
+
graceful: boolean,
|
|
60
|
+
) => Effect.Effect<
|
|
61
|
+
void,
|
|
62
|
+
| ProcessExited
|
|
63
|
+
| LocalTerminationTimeout
|
|
64
|
+
| TransportError
|
|
65
|
+
| Schema.SchemaError
|
|
66
|
+
| Wire.SessionError
|
|
67
|
+
| RemoteTerminationUnconfirmed
|
|
68
|
+
| RpcClientError.RpcClientError
|
|
69
|
+
>
|
|
70
|
+
readonly transport: {
|
|
71
|
+
readonly clientProtocol: Peer["clientProtocol"]
|
|
72
|
+
readonly serverProtocol: Peer["serverProtocol"]
|
|
73
|
+
readonly failure: Effect.Effect<never, ProcessExited | TransportError>
|
|
74
|
+
readonly report: (cause: Cause.Cause<unknown>) => Effect.Effect<void>
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
export class Connection extends Context.Service<Connection, Connected>()(
|
|
78
|
+
"@graftland/plugin-remote/internal/runner/Connection",
|
|
79
|
+
) {}
|
|
80
|
+
const connected = Connection.of
|
|
81
|
+
|
|
82
|
+
const Local = Effect.fnUntraced(function* (
|
|
83
|
+
target: LocalTarget | WorkerTarget,
|
|
84
|
+
contractModule: string,
|
|
85
|
+
options: Port.Options,
|
|
86
|
+
resources: Scope.Scope,
|
|
87
|
+
processScope: Scope.Scope,
|
|
88
|
+
) {
|
|
89
|
+
const transport = yield* target._tag === "Local"
|
|
90
|
+
? LocalTransport
|
|
91
|
+
: WorkerTransport
|
|
92
|
+
const started: Effect.Effect<
|
|
93
|
+
Execution,
|
|
94
|
+
PlatformError.PlatformError | TransportError,
|
|
95
|
+
Scope.Scope
|
|
96
|
+
> = transport.start({ ...target, contractModule, session: options.session })
|
|
97
|
+
const execution = yield* started.pipe(Scope.provide(processScope))
|
|
98
|
+
return yield* LocalProcess.connect(execution, options).pipe(
|
|
99
|
+
Scope.provide(resources),
|
|
100
|
+
)
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
const reason = <A, E>(exit: Exit.Exit<A, E>) =>
|
|
104
|
+
Exit.isFailure(exit) ? exit.cause : Cause.empty
|
|
105
|
+
const finish = (causes: ReadonlyArray<Cause.Cause<unknown>>) => {
|
|
106
|
+
const cause = Array.reduce(causes, Cause.empty, Cause.combine)
|
|
107
|
+
return cause.reasons.length === 0
|
|
108
|
+
? Effect.void
|
|
109
|
+
: Effect.failCause(cleanupCause(cause))
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/** Close partially acquired resources too; the normal release observes exit before stopping readers. */
|
|
113
|
+
const scopes = Effect.acquireRelease(
|
|
114
|
+
Effect.all({ resources: Scope.make(), process: Scope.make() }),
|
|
115
|
+
(scopes, exit) =>
|
|
116
|
+
Effect.gen(function* () {
|
|
117
|
+
const process = yield* Effect.exit(Release.close(scopes.process, exit))
|
|
118
|
+
const resources = yield* Effect.exit(
|
|
119
|
+
Release.close(scopes.resources, exit),
|
|
120
|
+
)
|
|
121
|
+
yield* finish([reason(process), reason(resources)])
|
|
122
|
+
}),
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
/** Setup supplies a concrete control lifecycle; release never reads a later-installed effect. */
|
|
126
|
+
export const acquire = <A, E, R, D, DR>(
|
|
127
|
+
target: Target,
|
|
128
|
+
contractModule: string,
|
|
129
|
+
options: Port.Options,
|
|
130
|
+
setup: Effect.Effect<A, E, R | Connection>,
|
|
131
|
+
dispose: (value: A) => Effect.Effect<Cause.Cause<unknown>, D, DR>,
|
|
132
|
+
timeout: Duration.Input,
|
|
133
|
+
) =>
|
|
134
|
+
Effect.gen(function* () {
|
|
135
|
+
const owned = yield* scopes
|
|
136
|
+
const connection = yield* Effect.acquireRelease(
|
|
137
|
+
Effect.gen(function* () {
|
|
138
|
+
return target._tag === "Local" || target._tag === "Worker"
|
|
139
|
+
? yield* Local(
|
|
140
|
+
target,
|
|
141
|
+
contractModule,
|
|
142
|
+
options,
|
|
143
|
+
owned.resources,
|
|
144
|
+
owned.process,
|
|
145
|
+
).pipe(Effect.map(connected))
|
|
146
|
+
: yield* RunnerConnection.connect(
|
|
147
|
+
target,
|
|
148
|
+
contractModule,
|
|
149
|
+
options,
|
|
150
|
+
owned.resources,
|
|
151
|
+
owned.process,
|
|
152
|
+
)
|
|
153
|
+
}).pipe(Effect.interruptible),
|
|
154
|
+
(connection, exit) =>
|
|
155
|
+
Effect.gen(function* () {
|
|
156
|
+
const process = yield* Effect.exit(Release.close(owned.process, exit))
|
|
157
|
+
// Wait for exit while readers are alive; collect their final cause after resources close.
|
|
158
|
+
yield* connection.diagnostics
|
|
159
|
+
const resources = yield* Effect.exit(
|
|
160
|
+
Release.close(owned.resources, exit),
|
|
161
|
+
)
|
|
162
|
+
const diagnostics = yield* connection.diagnostics
|
|
163
|
+
yield* finish([reason(process), reason(resources), diagnostics])
|
|
164
|
+
}),
|
|
165
|
+
)
|
|
166
|
+
return yield* Effect.acquireRelease(
|
|
167
|
+
setup.pipe(Effect.provideService(Connection, connection)),
|
|
168
|
+
(value) =>
|
|
169
|
+
Effect.gen(function* () {
|
|
170
|
+
const graceful = yield* dispose(value).pipe(
|
|
171
|
+
Effect.interruptible,
|
|
172
|
+
Effect.timeout(timeout),
|
|
173
|
+
Effect.exit,
|
|
174
|
+
)
|
|
175
|
+
const terminated = yield* Effect.exit(
|
|
176
|
+
connection.terminate(Exit.isSuccess(graceful)),
|
|
177
|
+
)
|
|
178
|
+
yield* finish([
|
|
179
|
+
Exit.isSuccess(graceful) ? graceful.value : graceful.cause,
|
|
180
|
+
reason(terminated),
|
|
181
|
+
])
|
|
182
|
+
}),
|
|
183
|
+
)
|
|
184
|
+
})
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
import * as ProcessObservation from "./ProcessObservation"
|
|
2
|
+
import * as FailureGroup from "../FailureGroup"
|
|
3
|
+
import { Command, CommandJson } from "./WatchdogProtocol"
|
|
4
|
+
import {
|
|
5
|
+
Array,
|
|
6
|
+
Cause,
|
|
7
|
+
FileSystem,
|
|
8
|
+
Path,
|
|
9
|
+
Schema,
|
|
10
|
+
Context,
|
|
11
|
+
Effect,
|
|
12
|
+
HashMap,
|
|
13
|
+
HashSet,
|
|
14
|
+
Queue,
|
|
15
|
+
Ref,
|
|
16
|
+
Sink,
|
|
17
|
+
Stream,
|
|
18
|
+
Layer,
|
|
19
|
+
Scope,
|
|
20
|
+
} from "effect"
|
|
21
|
+
import { ChildProcess, ChildProcessSpawner } from "effect/process"
|
|
22
|
+
import { TransportError } from "../../Errors"
|
|
23
|
+
import * as Multiplex from "./Multiplex"
|
|
24
|
+
import * as Packages from "./Packages"
|
|
25
|
+
import * as Processes from "./Processes"
|
|
26
|
+
import {
|
|
27
|
+
Execution,
|
|
28
|
+
ExecutionError,
|
|
29
|
+
SessionConflict,
|
|
30
|
+
SessionStopped,
|
|
31
|
+
} from "./model"
|
|
32
|
+
import { Bootstrap } from "./Bootstrap"
|
|
33
|
+
import { command } from "./command"
|
|
34
|
+
|
|
35
|
+
export class Channels extends Context.Service<
|
|
36
|
+
Channels,
|
|
37
|
+
{
|
|
38
|
+
readonly peer: (
|
|
39
|
+
token: string,
|
|
40
|
+
) => Effect.Effect<
|
|
41
|
+
Multiplex.Peer,
|
|
42
|
+
SessionStopped | SessionConflict,
|
|
43
|
+
Scope.Scope
|
|
44
|
+
>
|
|
45
|
+
}
|
|
46
|
+
>()("@graftland/plugin-remote/internal/runner/Execution/Channels") {}
|
|
47
|
+
|
|
48
|
+
export const make = (root: string) =>
|
|
49
|
+
Effect.gen(function* () {
|
|
50
|
+
const spawner = yield* ChildProcessSpawner.ChildProcessSpawner
|
|
51
|
+
const fs = yield* FileSystem.FileSystem
|
|
52
|
+
const path = yield* Path.Path
|
|
53
|
+
const platform = Context.make(
|
|
54
|
+
ChildProcessSpawner.ChildProcessSpawner,
|
|
55
|
+
spawner,
|
|
56
|
+
).pipe(Context.add(FileSystem.FileSystem, fs), Context.add(Path.Path, path))
|
|
57
|
+
|
|
58
|
+
const peers = yield* Ref.make(HashMap.empty<string, Multiplex.Peer>())
|
|
59
|
+
const claimed = yield* Ref.make(HashSet.empty<string>())
|
|
60
|
+
const driver = Execution.of({
|
|
61
|
+
recover: (child) =>
|
|
62
|
+
Processes.recover(child).pipe(Effect.provideContext(platform)),
|
|
63
|
+
spawn: (handle, request, limits) =>
|
|
64
|
+
Effect.gen(function* () {
|
|
65
|
+
const resolved = yield* Packages.resolve(
|
|
66
|
+
root,
|
|
67
|
+
request.specifier,
|
|
68
|
+
request.digest,
|
|
69
|
+
)
|
|
70
|
+
const argv = yield* command()
|
|
71
|
+
const child = yield* spawner.spawn(
|
|
72
|
+
ChildProcess.make(
|
|
73
|
+
argv[0],
|
|
74
|
+
[
|
|
75
|
+
...Array.drop(argv, 1),
|
|
76
|
+
"child",
|
|
77
|
+
resolved.entry,
|
|
78
|
+
handle.token,
|
|
79
|
+
yield* Schema.encodeEffect(Schema.fromJsonString(Bootstrap))({
|
|
80
|
+
identity: {
|
|
81
|
+
id: handle.key.instance,
|
|
82
|
+
generation: handle.generation,
|
|
83
|
+
},
|
|
84
|
+
limits,
|
|
85
|
+
}),
|
|
86
|
+
],
|
|
87
|
+
{
|
|
88
|
+
stdin: "pipe",
|
|
89
|
+
stdout: "pipe",
|
|
90
|
+
stderr: "pipe",
|
|
91
|
+
additionalFds: {
|
|
92
|
+
fd3: { type: "output" },
|
|
93
|
+
fd4: { type: "input" },
|
|
94
|
+
},
|
|
95
|
+
forceKillAfter: "1 second",
|
|
96
|
+
},
|
|
97
|
+
),
|
|
98
|
+
)
|
|
99
|
+
const observation = yield* ProcessObservation.observe(child)
|
|
100
|
+
const exited = observation.exited.pipe(
|
|
101
|
+
Effect.mapError((error) =>
|
|
102
|
+
ExecutionError.make({
|
|
103
|
+
detail: "Observing child exit failed",
|
|
104
|
+
cause: Cause.fail(error),
|
|
105
|
+
}),
|
|
106
|
+
),
|
|
107
|
+
)
|
|
108
|
+
const failures = yield* FailureGroup.make<ExecutionError>()
|
|
109
|
+
yield* failures.run(
|
|
110
|
+
observation.failure.pipe(
|
|
111
|
+
Effect.mapError((error) =>
|
|
112
|
+
ExecutionError.make({
|
|
113
|
+
detail: "Reading child output failed",
|
|
114
|
+
cause: Cause.fail(error),
|
|
115
|
+
}),
|
|
116
|
+
),
|
|
117
|
+
),
|
|
118
|
+
)
|
|
119
|
+
const controls = yield* Queue.bounded<string>(4)
|
|
120
|
+
|
|
121
|
+
yield* failures.run(
|
|
122
|
+
Stream.fromQueue(controls).pipe(
|
|
123
|
+
Stream.map((line) => new TextEncoder().encode(line + "\n")),
|
|
124
|
+
Stream.run(child.getInputFd(4)),
|
|
125
|
+
Effect.mapError((error) =>
|
|
126
|
+
ExecutionError.make({
|
|
127
|
+
detail: "Child supervision channel failed",
|
|
128
|
+
cause: Cause.fail(error),
|
|
129
|
+
}),
|
|
130
|
+
),
|
|
131
|
+
),
|
|
132
|
+
)
|
|
133
|
+
const send = (value: typeof Command.Type) =>
|
|
134
|
+
Effect.raceFirst(
|
|
135
|
+
Schema.encodeEffect(CommandJson)(value).pipe(
|
|
136
|
+
Effect.flatMap((line) => Queue.offer(controls, line)),
|
|
137
|
+
Effect.asVoid,
|
|
138
|
+
Effect.mapError((error) =>
|
|
139
|
+
ExecutionError.make({
|
|
140
|
+
detail: "Encoding watchdog command failed",
|
|
141
|
+
cause: Cause.fail(error),
|
|
142
|
+
}),
|
|
143
|
+
),
|
|
144
|
+
),
|
|
145
|
+
failures.await,
|
|
146
|
+
)
|
|
147
|
+
const peer = yield* Multiplex.make({
|
|
148
|
+
input: child
|
|
149
|
+
.getOutputFd(3)
|
|
150
|
+
.pipe(
|
|
151
|
+
Stream.mapError((error) =>
|
|
152
|
+
TransportError.make({ cause: Cause.fail(error) }),
|
|
153
|
+
),
|
|
154
|
+
),
|
|
155
|
+
output: child.stdin.pipe(
|
|
156
|
+
Sink.mapError((error) =>
|
|
157
|
+
TransportError.make({ cause: Cause.fail(error) }),
|
|
158
|
+
),
|
|
159
|
+
),
|
|
160
|
+
})
|
|
161
|
+
const identity = yield* Processes.identity(child.pid)
|
|
162
|
+
yield* Ref.update(peers, HashMap.set(handle.token, peer))
|
|
163
|
+
yield* Effect.addFinalizer(() =>
|
|
164
|
+
Ref.update(peers, HashMap.remove(handle.token)),
|
|
165
|
+
)
|
|
166
|
+
return {
|
|
167
|
+
code: resolved.identity,
|
|
168
|
+
start: send({ kind: "start" }),
|
|
169
|
+
identity,
|
|
170
|
+
exited,
|
|
171
|
+
interrupt: send({ kind: "stop" }),
|
|
172
|
+
kill: child.kill({ killSignal: "SIGKILL" }).pipe(
|
|
173
|
+
Effect.mapError((error) =>
|
|
174
|
+
ExecutionError.make({
|
|
175
|
+
detail: "Killing the child failed",
|
|
176
|
+
cause: Cause.fail(error),
|
|
177
|
+
}),
|
|
178
|
+
),
|
|
179
|
+
),
|
|
180
|
+
renew: (millis: number) => send({ kind: "renew", millis }),
|
|
181
|
+
}
|
|
182
|
+
}).pipe(
|
|
183
|
+
Effect.provideContext(platform),
|
|
184
|
+
Effect.catchTags({
|
|
185
|
+
PlatformError: (error) =>
|
|
186
|
+
Effect.fail(
|
|
187
|
+
ExecutionError.make({
|
|
188
|
+
detail: "Starting the child failed",
|
|
189
|
+
cause: Cause.fail(error),
|
|
190
|
+
}),
|
|
191
|
+
),
|
|
192
|
+
SchemaError: (error) =>
|
|
193
|
+
Effect.fail(
|
|
194
|
+
ExecutionError.make({
|
|
195
|
+
detail: "Encoding child bootstrap failed",
|
|
196
|
+
cause: Cause.fail(error),
|
|
197
|
+
}),
|
|
198
|
+
),
|
|
199
|
+
}),
|
|
200
|
+
),
|
|
201
|
+
})
|
|
202
|
+
return {
|
|
203
|
+
driver,
|
|
204
|
+
peer: (token: string) =>
|
|
205
|
+
Effect.acquireRelease(
|
|
206
|
+
Effect.gen(function* () {
|
|
207
|
+
const peer = yield* Ref.get(peers).pipe(
|
|
208
|
+
Effect.flatMap((all) => {
|
|
209
|
+
const peer = HashMap.get(all, token)
|
|
210
|
+
return peer._tag === "Some"
|
|
211
|
+
? Effect.succeed(peer.value)
|
|
212
|
+
: Effect.fail(
|
|
213
|
+
SessionStopped.make({
|
|
214
|
+
detail: "The execution channel is no longer available",
|
|
215
|
+
}),
|
|
216
|
+
)
|
|
217
|
+
}),
|
|
218
|
+
)
|
|
219
|
+
const occupied = yield* Ref.modify(
|
|
220
|
+
claimed,
|
|
221
|
+
(all) =>
|
|
222
|
+
[HashSet.has(all, token), HashSet.add(all, token)] as const,
|
|
223
|
+
)
|
|
224
|
+
if (occupied)
|
|
225
|
+
return yield* SessionConflict.make({
|
|
226
|
+
detail: "The instance channel is already attached",
|
|
227
|
+
})
|
|
228
|
+
return peer
|
|
229
|
+
}),
|
|
230
|
+
() => Ref.update(claimed, HashSet.remove(token)),
|
|
231
|
+
),
|
|
232
|
+
}
|
|
233
|
+
})
|
|
234
|
+
|
|
235
|
+
export const layer = (root: string) =>
|
|
236
|
+
Layer.effectContext(
|
|
237
|
+
make(root).pipe(
|
|
238
|
+
Effect.map((execution) =>
|
|
239
|
+
Context.make(Execution, execution.driver).pipe(
|
|
240
|
+
Context.add(Channels, { peer: execution.peer }),
|
|
241
|
+
),
|
|
242
|
+
),
|
|
243
|
+
),
|
|
244
|
+
)
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { Array, Effect, MutableRef, Option, Result, Schema } from "effect"
|
|
2
|
+
|
|
3
|
+
export class FrameError extends Schema.TaggedError<
|
|
4
|
+
FrameError,
|
|
5
|
+
{ readonly brand: unique symbol }
|
|
6
|
+
>()("FrameError", { detail: Schema.String }) {
|
|
7
|
+
override get message() {
|
|
8
|
+
return this.detail
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
/** A complete byte stream ended; malformed trailing bytes remain FrameError. */
|
|
12
|
+
export class EndOfStream extends Schema.TaggedError<
|
|
13
|
+
EndOfStream,
|
|
14
|
+
{ readonly brand: unique symbol }
|
|
15
|
+
>()("RunnerEndOfStream", {}) {
|
|
16
|
+
override get message() {
|
|
17
|
+
return "Runner byte channel ended"
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
export type Channel = 0 | 1
|
|
21
|
+
export interface Frame {
|
|
22
|
+
readonly channel: Channel
|
|
23
|
+
readonly payload: Uint8Array
|
|
24
|
+
}
|
|
25
|
+
export const headerSize = 8
|
|
26
|
+
export const maxPayload = 1024 * 1024
|
|
27
|
+
const invalid = (detail: string) => FrameError.make({ detail })
|
|
28
|
+
|
|
29
|
+
/** Version 2 carries only channel and length; request admission belongs to port. */
|
|
30
|
+
export const encode = (frame: Frame) =>
|
|
31
|
+
Effect.gen(function* () {
|
|
32
|
+
if (frame.payload.byteLength > maxPayload)
|
|
33
|
+
return yield* invalid("Runner frame exceeds the payload limit")
|
|
34
|
+
const bytes = new Uint8Array(headerSize + frame.payload.byteLength)
|
|
35
|
+
bytes.set([2, frame.channel, 0, 0])
|
|
36
|
+
new DataView(bytes.buffer).setUint32(4, frame.payload.byteLength)
|
|
37
|
+
bytes.set(frame.payload, headerSize)
|
|
38
|
+
return bytes
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
/** Check a message boundary; the decoder still validates the frame's contents. */
|
|
42
|
+
export const isCompleteFrame = (bytes: Uint8Array): boolean =>
|
|
43
|
+
bytes.byteLength >= headerSize &&
|
|
44
|
+
new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength).getUint32(
|
|
45
|
+
4,
|
|
46
|
+
) ===
|
|
47
|
+
bytes.byteLength - headerSize
|
|
48
|
+
|
|
49
|
+
/** The decoder owns its byte buffer. Results expose malformed frames without throwing through a generator. */
|
|
50
|
+
export const decoder = () => {
|
|
51
|
+
const pending = MutableRef.make<Result.Result<Uint8Array, FrameError>>(
|
|
52
|
+
Result.succeed(new Uint8Array(0)),
|
|
53
|
+
)
|
|
54
|
+
const decode = (
|
|
55
|
+
chunk: Uint8Array,
|
|
56
|
+
): Result.Result<ReadonlyArray<Frame>, FrameError> =>
|
|
57
|
+
Result.flatMap(MutableRef.get(pending), (previous) => {
|
|
58
|
+
const bytes = new Uint8Array(previous.length + chunk.length)
|
|
59
|
+
bytes.set(previous)
|
|
60
|
+
bytes.set(chunk, previous.length)
|
|
61
|
+
const frames = Array.unfold(
|
|
62
|
+
0,
|
|
63
|
+
(
|
|
64
|
+
offset,
|
|
65
|
+
): Option.Option<
|
|
66
|
+
readonly [Result.Result<Frame, FrameError>, number]
|
|
67
|
+
> => {
|
|
68
|
+
if (bytes.length - offset < headerSize) return Option.none()
|
|
69
|
+
const header = new DataView(bytes.buffer, offset, headerSize)
|
|
70
|
+
const channel = header.getUint8(1)
|
|
71
|
+
const length = header.getUint32(4)
|
|
72
|
+
if (
|
|
73
|
+
header.getUint8(0) !== 2 ||
|
|
74
|
+
(channel !== 0 && channel !== 1) ||
|
|
75
|
+
header.getUint16(2) !== 0 ||
|
|
76
|
+
length > maxPayload
|
|
77
|
+
)
|
|
78
|
+
return Option.some([
|
|
79
|
+
Result.fail(
|
|
80
|
+
invalid("Invalid runner frame header or payload length"),
|
|
81
|
+
),
|
|
82
|
+
bytes.length,
|
|
83
|
+
])
|
|
84
|
+
const end = offset + headerSize + length
|
|
85
|
+
if (end > bytes.length) return Option.none()
|
|
86
|
+
return Option.some([
|
|
87
|
+
Result.succeed({
|
|
88
|
+
channel,
|
|
89
|
+
payload: bytes.subarray(offset + headerSize, end),
|
|
90
|
+
}),
|
|
91
|
+
end,
|
|
92
|
+
])
|
|
93
|
+
},
|
|
94
|
+
)
|
|
95
|
+
const result = Result.all(frames)
|
|
96
|
+
MutableRef.set(
|
|
97
|
+
pending,
|
|
98
|
+
Result.map(result, (frames) =>
|
|
99
|
+
bytes.subarray(
|
|
100
|
+
Array.reduce(
|
|
101
|
+
frames,
|
|
102
|
+
0,
|
|
103
|
+
(used, frame) => used + headerSize + frame.payload.length,
|
|
104
|
+
),
|
|
105
|
+
),
|
|
106
|
+
),
|
|
107
|
+
)
|
|
108
|
+
return result
|
|
109
|
+
})
|
|
110
|
+
return {
|
|
111
|
+
decode,
|
|
112
|
+
finish: Effect.suspend(() =>
|
|
113
|
+
Effect.fromResult(
|
|
114
|
+
Result.flatMap(MutableRef.get(pending), (bytes) =>
|
|
115
|
+
bytes.length === 0
|
|
116
|
+
? Result.succeed(undefined)
|
|
117
|
+
: Result.fail(invalid("Runner connection ended inside a frame")),
|
|
118
|
+
),
|
|
119
|
+
),
|
|
120
|
+
),
|
|
121
|
+
}
|
|
122
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { Cause, Effect, Fiber, Option } from "effect"
|
|
2
|
+
import type { Execution } from "../directTransport"
|
|
3
|
+
import * as FailureGroup from "../FailureGroup"
|
|
4
|
+
import {
|
|
5
|
+
LocalTerminationTimeout,
|
|
6
|
+
ProcessExited,
|
|
7
|
+
TransportError,
|
|
8
|
+
} from "../../Errors"
|
|
9
|
+
import * as Port from "../port"
|
|
10
|
+
import { stdinExitTimeout } from "./ProcessObservation"
|
|
11
|
+
|
|
12
|
+
/** Direct execution observers stay alive until exit, including forced cleanup. */
|
|
13
|
+
export const connect = Effect.fnUntraced(function* (
|
|
14
|
+
execution: Execution,
|
|
15
|
+
options: Port.Options,
|
|
16
|
+
) {
|
|
17
|
+
const failures = yield* FailureGroup.make<ProcessExited | TransportError>()
|
|
18
|
+
const exited = yield* execution.exited.pipe(Effect.forkScoped)
|
|
19
|
+
const peer = yield* Port.make(execution.channel, options)
|
|
20
|
+
yield* failures.run(
|
|
21
|
+
Effect.raceFirst(
|
|
22
|
+
peer.await,
|
|
23
|
+
Fiber.join(exited).pipe(Effect.flatMap(Effect.fail)),
|
|
24
|
+
),
|
|
25
|
+
)
|
|
26
|
+
const report = (cause: Cause.Cause<unknown>) =>
|
|
27
|
+
Cause.hasInterruptsOnly(cause)
|
|
28
|
+
? Effect.void
|
|
29
|
+
: failures
|
|
30
|
+
.fail(TransportError.make({ cause }))
|
|
31
|
+
.pipe(Effect.andThen(peer.fail(TransportError.make({ cause }))))
|
|
32
|
+
yield* failures.run(execution.failure)
|
|
33
|
+
|
|
34
|
+
return {
|
|
35
|
+
...peer,
|
|
36
|
+
pid: execution.pid,
|
|
37
|
+
...(execution.threadId === undefined
|
|
38
|
+
? {}
|
|
39
|
+
: { threadId: execution.threadId }),
|
|
40
|
+
exited,
|
|
41
|
+
resources: yield* Effect.scope,
|
|
42
|
+
transport: { ...peer, failure: failures.await, report },
|
|
43
|
+
diagnostics: Fiber.await(exited).pipe(Effect.andThen(failures.cause)),
|
|
44
|
+
terminate: (graceful: boolean) =>
|
|
45
|
+
Effect.gen(function* () {
|
|
46
|
+
// Stop observation before intentionally ending stdin or killing the process.
|
|
47
|
+
yield* failures.close
|
|
48
|
+
if (!graceful) return
|
|
49
|
+
// If this fails, the execution scope forces termination and waits for exit.
|
|
50
|
+
const status = yield* peer.end.pipe(
|
|
51
|
+
// Peer EOF can interrupt the writer as its output ends. Still confirm the unit's exit.
|
|
52
|
+
Effect.catchCauseIf(Cause.hasInterruptsOnly, () => Effect.void),
|
|
53
|
+
Effect.andThen(Fiber.join(exited)),
|
|
54
|
+
Effect.interruptible,
|
|
55
|
+
Effect.timeoutOrElse({
|
|
56
|
+
duration: stdinExitTimeout,
|
|
57
|
+
orElse: () =>
|
|
58
|
+
LocalTerminationTimeout.make({
|
|
59
|
+
...Option.match(execution.pid, {
|
|
60
|
+
onNone: () => ({}),
|
|
61
|
+
onSome: (pid) => ({ pid }),
|
|
62
|
+
}),
|
|
63
|
+
...(execution.threadId === undefined
|
|
64
|
+
? {}
|
|
65
|
+
: { threadId: execution.threadId }),
|
|
66
|
+
}),
|
|
67
|
+
}),
|
|
68
|
+
)
|
|
69
|
+
if (status.exitCode !== 0 || status.statusCause.reasons.length > 0)
|
|
70
|
+
return yield* status
|
|
71
|
+
}),
|
|
72
|
+
}
|
|
73
|
+
})
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { Cause, Effect, FileSystem, Path } from "effect"
|
|
2
|
+
import { StateDirectoryLocked } from "./model"
|
|
3
|
+
|
|
4
|
+
/** SQLite owns the local directory lock for the enclosing scope, including failed startup. */
|
|
5
|
+
export const acquire = (directory: string) =>
|
|
6
|
+
Effect.gen(function* () {
|
|
7
|
+
const fs = yield* FileSystem.FileSystem
|
|
8
|
+
const path = yield* Path.Path
|
|
9
|
+
yield* fs
|
|
10
|
+
.makeDirectory(directory, { recursive: true })
|
|
11
|
+
.pipe(Effect.uninterruptible)
|
|
12
|
+
const { DatabaseSync } = yield* Effect.promise(
|
|
13
|
+
() => import("node:sqlite"),
|
|
14
|
+
).pipe(Effect.uninterruptible)
|
|
15
|
+
const locked = (cause: unknown) =>
|
|
16
|
+
StateDirectoryLocked.make({
|
|
17
|
+
detail:
|
|
18
|
+
"The runner state directory is already owned or cannot be locked",
|
|
19
|
+
cause: Cause.die(cause),
|
|
20
|
+
})
|
|
21
|
+
const db = yield* Effect.acquireRelease(
|
|
22
|
+
Effect.try({
|
|
23
|
+
try: () => new DatabaseSync(path.join(directory, "owner.sqlite")),
|
|
24
|
+
catch: locked,
|
|
25
|
+
}),
|
|
26
|
+
(db) => Effect.sync(() => db.close()),
|
|
27
|
+
)
|
|
28
|
+
yield* Effect.try({ try: () => db.exec("BEGIN EXCLUSIVE"), catch: locked })
|
|
29
|
+
})
|