@graftland/plugin-remote 0.3.1 → 0.3.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.
- package/dist/Auth.d.ts +2 -2
- package/dist/Auth.d.ts.map +1 -1
- package/dist/Auth.js.map +1 -1
- package/dist/{Bootstrap-Dr6sX7el.js → Bootstrap-H9gDsgsC.js} +2 -2
- package/dist/{Contract-DL2mHvaT.js → Contract-DsJtisvL.js} +2 -2
- package/dist/Contract.js +1 -1
- package/dist/{Errors-BMKmu5Y7.js → Errors-DYhsGZcZ.js} +3 -1
- package/dist/Errors.d.ts +7 -0
- package/dist/Errors.d.ts.map +1 -1
- package/dist/Errors.js +3 -1
- package/dist/Errors.js.map +1 -1
- package/dist/{FailureGroup-BcH0XOBi.js → FailureGroup-BgT73AaR.js} +14 -4
- package/dist/{Framing-zaYdOJnT.js → Framing-G9eSaeLb.js} +1 -1
- package/dist/{Graft-CVyueNOf.js → Graft-B71CPA2Y.js} +20 -16
- package/dist/{Packages-JNL34JpS.js → Packages-AgoCGotL.js} +23 -26
- package/dist/{Peer-BT7eAGmh.js → Peer-DXeVrGBh.js} +5 -5
- package/dist/{ProcessObservation-DEGrEMfP.js → ProcessObservation-gLgGirYv.js} +1 -1
- package/dist/{ServeLifecycle-CJM0gG5B.js → ServeLifecycle-Bd1FPJ6e.js} +1 -1
- package/dist/Transport.d.ts +2 -2
- package/dist/Transport.d.ts.map +1 -1
- package/dist/Transport.js.map +1 -1
- package/dist/bin.js +48 -47
- package/dist/bin.js.map +1 -1
- package/dist/{childRuntime-An0HsIzl.js → childRuntime-Bm3iSg_N.js} +29 -26
- package/dist/{connectionEnded-DCpFCjMX.js → connectionEnded-BwcxQ7W8.js} +4 -4
- package/dist/{entry-B89sepMU.js → entry-psOpX3Bj.js} +72 -35
- package/dist/host/auth/bearer.js +1 -1
- package/dist/host/auth/jwt.js +1 -1
- package/dist/host/auth/mtls.js +2 -2
- package/dist/host/auth/none.js +1 -1
- package/dist/host/command.js +4 -4
- package/dist/host/local.js +3 -3
- package/dist/host/unix.js +3 -3
- package/dist/host/websocket.js +3 -3
- package/dist/host/worker.js +4 -5
- package/dist/{importer-Bk6U_sn4.js → importer-Cvf88eAY.js} +5 -8
- package/dist/index.js +9 -9
- package/dist/index.js.map +1 -1
- package/dist/internal/Certificates.js +4 -4
- package/dist/internal/Certificates.js.map +1 -1
- package/dist/internal/FailureGroup.js +3 -2
- package/dist/internal/FailureGroup.js.map +1 -1
- package/dist/internal/assembly.js +9 -2
- package/dist/internal/assembly.js.map +1 -1
- package/dist/internal/auth/mtls.js +1 -1
- package/dist/internal/auth/mtls.js.map +1 -1
- package/dist/internal/child.js +20 -17
- package/dist/internal/child.js.map +1 -1
- package/dist/internal/childRuntime.js +4 -4
- package/dist/internal/childRuntime.js.map +1 -1
- package/dist/internal/directTransport.d.ts +1 -1
- package/dist/internal/entry.d.ts.map +1 -1
- package/dist/internal/entry.js +1 -1
- package/dist/internal/entry.js.map +1 -1
- package/dist/internal/execution.js +40 -5
- package/dist/internal/execution.js.map +1 -1
- package/dist/internal/host/runner.js +7 -6
- package/dist/internal/host/runner.js.map +1 -1
- package/dist/internal/host.js +5 -5
- package/dist/internal/host.js.map +1 -1
- package/dist/internal/port.js +5 -5
- package/dist/internal/port.js.map +1 -1
- package/dist/internal/release.js +1 -1
- package/dist/internal/release.js.map +1 -1
- package/dist/internal/runner/Connection.d.ts +1 -1
- package/dist/internal/runner/Connection.js +4 -4
- package/dist/internal/runner/Connection.js.map +1 -1
- package/dist/internal/runner/Execution.js +3 -3
- package/dist/internal/runner/Execution.js.map +1 -1
- package/dist/internal/runner/Framing.js +1 -1
- package/dist/internal/runner/Framing.js.map +1 -1
- package/dist/internal/runner/Local.js +1 -1
- package/dist/internal/runner/Local.js.map +1 -1
- package/dist/internal/runner/Lock.js +1 -1
- package/dist/internal/runner/Lock.js.map +1 -1
- package/dist/internal/runner/Packages.d.ts +1 -1
- package/dist/internal/runner/Packages.js +22 -25
- package/dist/internal/runner/Packages.js.map +1 -1
- package/dist/internal/runner/Processes.js +7 -9
- package/dist/internal/runner/Processes.js.map +1 -1
- package/dist/internal/runner/Store.js +3 -3
- package/dist/internal/runner/Store.js.map +1 -1
- package/dist/internal/runner/Supervisor.js +46 -47
- package/dist/internal/runner/Supervisor.js.map +1 -1
- package/dist/internal/runner/accept.js +3 -3
- package/dist/internal/runner/accept.js.map +1 -1
- package/dist/internal/runner/model.js +1 -1
- package/dist/internal/runner/model.js.map +1 -1
- package/dist/internal/serve.js +1 -1
- package/dist/internal/serve.js.map +1 -1
- package/dist/internal/supervisorPlugin.js +1 -1
- package/dist/internal/watchdogMain.js +5 -5
- package/dist/internal/watchdogMain.js.map +1 -1
- package/dist/internal/websocketServer.js +3 -1
- package/dist/internal/websocketServer.js.map +1 -1
- package/dist/internal/workerMain.js +3 -3
- package/dist/{port-DSVUY8Bq.js → port--3x2zJ4D.js} +8 -8
- package/dist/{release-BY4Mlluy.js → release-DfZPuIcK.js} +1 -1
- package/dist/runner/auth/bearer.js +2 -2
- package/dist/runner/auth/jwt.js +4 -4
- package/dist/runner/auth/jwt.js.map +1 -1
- package/dist/runner/auth/mtls.js +3 -3
- package/dist/runner/auth/none.js +1 -1
- package/dist/runner/index.js +5 -5
- package/dist/runner/stdio.js +3 -3
- package/dist/runner/unix.js +3 -3
- package/dist/runner/websocket.js +4 -4
- package/dist/{socketChannel-C_ppdSzz.js → socketChannel-CJvL2DiB.js} +2 -2
- package/dist/{supervisorPlugin-CivhM7x5.js → supervisorPlugin-t6Z6PrhD.js} +72 -76
- package/dist/{token-Cc_1CTbE.js → token-CYBNNlhS.js} +1 -1
- package/dist/{tokenCredentials-CI6mMRRd.js → tokenCredentials-CbCuk-Rt.js} +3 -3
- package/dist/{websocketServer-D2Hk7wsA.js → websocketServer-NQhODlEt.js} +5 -3
- package/dist/{workerChannel-cmLc13vm.js → workerChannel-C8hhBJzA.js} +1 -1
- package/package.json +3 -3
- package/src/Auth.ts +9 -2
- package/src/Errors.ts +8 -0
- package/src/Transport.ts +6 -2
- package/src/bin.ts +28 -30
- package/src/index.ts +5 -3
- package/src/internal/Certificates.ts +10 -13
- package/src/internal/FailureGroup.ts +128 -121
- package/src/internal/assembly.ts +19 -10
- package/src/internal/auth/mtls.ts +19 -20
- package/src/internal/child.ts +248 -239
- package/src/internal/childRuntime.ts +81 -80
- package/src/internal/entry.ts +10 -11
- package/src/internal/execution.ts +69 -12
- package/src/internal/host/runner.ts +153 -153
- package/src/internal/host.ts +116 -120
- package/src/internal/port.ts +100 -103
- package/src/internal/release.ts +3 -2
- package/src/internal/runner/Connection.ts +52 -60
- package/src/internal/runner/Execution.ts +163 -164
- package/src/internal/runner/Framing.ts +9 -10
- package/src/internal/runner/Local.ts +26 -27
- package/src/internal/runner/Lock.ts +23 -25
- package/src/internal/runner/Packages.ts +67 -70
- package/src/internal/runner/Processes.ts +89 -92
- package/src/internal/runner/Store.ts +110 -114
- package/src/internal/runner/Supervisor.ts +392 -405
- package/src/internal/runner/accept.ts +76 -84
- package/src/internal/runner/model.ts +20 -21
- package/src/internal/serve.ts +15 -13
- package/src/internal/watchdogMain.ts +16 -11
- package/src/internal/websocketServer.ts +18 -3
- package/src/runner/auth/jwt.ts +10 -6
- package/dist/cleanup-CXLa1Wd9.js +0 -13
|
@@ -45,192 +45,191 @@ export class Channels extends Context.Service<
|
|
|
45
45
|
}
|
|
46
46
|
>()("@graftland/plugin-remote/internal/runner/Execution/Channels") {}
|
|
47
47
|
|
|
48
|
-
export const make = (root: string)
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
).pipe(Context.add(FileSystem.FileSystem, fs), Context.add(Path.Path, path))
|
|
48
|
+
export const make = Effect.fnUntraced(function* (root: string) {
|
|
49
|
+
const spawner = yield* ChildProcessSpawner.ChildProcessSpawner
|
|
50
|
+
const fs = yield* FileSystem.FileSystem
|
|
51
|
+
const path = yield* Path.Path
|
|
52
|
+
const platform = Context.make(
|
|
53
|
+
ChildProcessSpawner.ChildProcessSpawner,
|
|
54
|
+
spawner,
|
|
55
|
+
).pipe(Context.add(FileSystem.FileSystem, fs), Context.add(Path.Path, path))
|
|
57
56
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
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" },
|
|
57
|
+
const peers = yield* Ref.make(HashMap.empty<string, Multiplex.Peer>())
|
|
58
|
+
const claimed = yield* Ref.make(HashSet.empty<string>())
|
|
59
|
+
const driver = Execution.of({
|
|
60
|
+
recover: (child) =>
|
|
61
|
+
Processes.recover(child).pipe(Effect.provideContext(platform)),
|
|
62
|
+
spawn: Effect.fnUntraced(
|
|
63
|
+
function* (handle, request, limits) {
|
|
64
|
+
const resolved = yield* Packages.resolve(
|
|
65
|
+
root,
|
|
66
|
+
request.specifier,
|
|
67
|
+
request.digest,
|
|
68
|
+
)
|
|
69
|
+
const argv = yield* command()
|
|
70
|
+
const child = yield* spawner.spawn(
|
|
71
|
+
ChildProcess.make(
|
|
72
|
+
argv[0],
|
|
73
|
+
[
|
|
74
|
+
...Array.drop(argv, 1),
|
|
75
|
+
"child",
|
|
76
|
+
resolved.entry,
|
|
77
|
+
handle.token,
|
|
78
|
+
yield* Schema.encodeEffect(Schema.fromJsonString(Bootstrap))({
|
|
79
|
+
identity: {
|
|
80
|
+
id: handle.key.instance,
|
|
81
|
+
generation: handle.generation,
|
|
94
82
|
},
|
|
95
|
-
|
|
83
|
+
limits,
|
|
84
|
+
}),
|
|
85
|
+
],
|
|
86
|
+
{
|
|
87
|
+
stdin: "pipe",
|
|
88
|
+
stdout: "pipe",
|
|
89
|
+
stderr: "pipe",
|
|
90
|
+
additionalFds: {
|
|
91
|
+
fd3: { type: "output" },
|
|
92
|
+
fd4: { type: "input" },
|
|
96
93
|
},
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
94
|
+
forceKillAfter: "1 second",
|
|
95
|
+
},
|
|
96
|
+
),
|
|
97
|
+
)
|
|
98
|
+
const observation = yield* ProcessObservation.observe(child)
|
|
99
|
+
const exited = observation.exited.pipe(
|
|
100
|
+
Effect.mapError((error) =>
|
|
101
|
+
ExecutionError.make({
|
|
102
|
+
detail: "Observing child exit failed",
|
|
103
|
+
cause: Cause.fail(error),
|
|
104
|
+
}),
|
|
105
|
+
),
|
|
106
|
+
)
|
|
107
|
+
const failures = yield* FailureGroup.make<ExecutionError>()
|
|
108
|
+
yield* failures.run(
|
|
109
|
+
observation.failure.pipe(
|
|
101
110
|
Effect.mapError((error) =>
|
|
102
111
|
ExecutionError.make({
|
|
103
|
-
detail: "
|
|
112
|
+
detail: "Reading child output failed",
|
|
104
113
|
cause: Cause.fail(error),
|
|
105
114
|
}),
|
|
106
115
|
),
|
|
107
|
-
)
|
|
108
|
-
|
|
109
|
-
|
|
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)
|
|
116
|
+
),
|
|
117
|
+
)
|
|
118
|
+
const controls = yield* Queue.bounded<string>(4)
|
|
120
119
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
120
|
+
yield* failures.run(
|
|
121
|
+
Stream.fromQueue(controls).pipe(
|
|
122
|
+
Stream.map((line) => new TextEncoder().encode(line + "\n")),
|
|
123
|
+
Stream.run(child.getInputFd(4)),
|
|
124
|
+
Effect.mapError((error) =>
|
|
125
|
+
ExecutionError.make({
|
|
126
|
+
detail: "Child supervision channel failed",
|
|
127
|
+
cause: Cause.fail(error),
|
|
128
|
+
}),
|
|
129
|
+
),
|
|
130
|
+
),
|
|
131
|
+
)
|
|
132
|
+
const send = (value: typeof Command.Type) =>
|
|
133
|
+
Effect.raceFirst(
|
|
134
|
+
Schema.encodeEffect(CommandJson)(value).pipe(
|
|
135
|
+
Effect.flatMap((line) => Queue.offer(controls, line)),
|
|
136
|
+
Effect.asVoid,
|
|
125
137
|
Effect.mapError((error) =>
|
|
126
138
|
ExecutionError.make({
|
|
127
|
-
detail: "
|
|
139
|
+
detail: "Encoding watchdog command failed",
|
|
128
140
|
cause: Cause.fail(error),
|
|
129
141
|
}),
|
|
130
142
|
),
|
|
131
143
|
),
|
|
144
|
+
failures.await,
|
|
132
145
|
)
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
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) =>
|
|
146
|
+
const peer = yield* Multiplex.make({
|
|
147
|
+
input: child
|
|
148
|
+
.getOutputFd(3)
|
|
149
|
+
.pipe(
|
|
150
|
+
Stream.mapError((error) =>
|
|
157
151
|
TransportError.make({ cause: Cause.fail(error) }),
|
|
158
152
|
),
|
|
159
153
|
),
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
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
|
-
),
|
|
154
|
+
output: child.stdin.pipe(
|
|
155
|
+
Sink.mapError((error) =>
|
|
156
|
+
TransportError.make({ cause: Cause.fail(error) }),
|
|
179
157
|
),
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
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
|
-
)
|
|
158
|
+
),
|
|
159
|
+
})
|
|
160
|
+
const identity = yield* Processes.identity(child.pid)
|
|
161
|
+
yield* Ref.update(peers, HashMap.set(handle.token, peer))
|
|
162
|
+
yield* Effect.addFinalizer(() =>
|
|
163
|
+
Ref.update(peers, HashMap.remove(handle.token)),
|
|
164
|
+
)
|
|
165
|
+
return {
|
|
166
|
+
code: resolved.identity,
|
|
167
|
+
start: send({ kind: "start" }),
|
|
168
|
+
identity,
|
|
169
|
+
exited,
|
|
170
|
+
interrupt: send({ kind: "stop" }),
|
|
171
|
+
kill: child.kill({ killSignal: "SIGKILL" }).pipe(
|
|
172
|
+
Effect.mapError((error) =>
|
|
173
|
+
ExecutionError.make({
|
|
174
|
+
detail: "Killing the child failed",
|
|
175
|
+
cause: Cause.fail(error),
|
|
217
176
|
}),
|
|
218
|
-
)
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
177
|
+
),
|
|
178
|
+
),
|
|
179
|
+
renew: (millis: number) => send({ kind: "renew", millis }),
|
|
180
|
+
}
|
|
181
|
+
},
|
|
182
|
+
Effect.provideContext(platform),
|
|
183
|
+
Effect.catchTags({
|
|
184
|
+
PlatformError: (error) =>
|
|
185
|
+
Effect.fail(
|
|
186
|
+
ExecutionError.make({
|
|
187
|
+
detail: "Starting the child failed",
|
|
188
|
+
cause: Cause.fail(error),
|
|
189
|
+
}),
|
|
190
|
+
),
|
|
191
|
+
SchemaError: (error) =>
|
|
192
|
+
Effect.fail(
|
|
193
|
+
ExecutionError.make({
|
|
194
|
+
detail: "Encoding child bootstrap failed",
|
|
195
|
+
cause: Cause.fail(error),
|
|
196
|
+
}),
|
|
197
|
+
),
|
|
198
|
+
}),
|
|
199
|
+
),
|
|
233
200
|
})
|
|
201
|
+
return {
|
|
202
|
+
driver,
|
|
203
|
+
peer: (token: string) =>
|
|
204
|
+
Effect.acquireRelease(
|
|
205
|
+
Effect.gen(function* () {
|
|
206
|
+
const peer = yield* Ref.get(peers).pipe(
|
|
207
|
+
Effect.flatMap((all) => {
|
|
208
|
+
const peer = HashMap.get(all, token)
|
|
209
|
+
return peer._tag === "Some"
|
|
210
|
+
? Effect.succeed(peer.value)
|
|
211
|
+
: Effect.fail(
|
|
212
|
+
SessionStopped.make({
|
|
213
|
+
detail: "The execution channel is no longer available",
|
|
214
|
+
}),
|
|
215
|
+
)
|
|
216
|
+
}),
|
|
217
|
+
)
|
|
218
|
+
const occupied = yield* Ref.modify(
|
|
219
|
+
claimed,
|
|
220
|
+
(all) =>
|
|
221
|
+
[HashSet.has(all, token), HashSet.add(all, token)] as const,
|
|
222
|
+
)
|
|
223
|
+
if (occupied)
|
|
224
|
+
return yield* SessionConflict.make({
|
|
225
|
+
detail: "The instance channel is already attached",
|
|
226
|
+
})
|
|
227
|
+
return peer
|
|
228
|
+
}),
|
|
229
|
+
() => Ref.update(claimed, HashSet.remove(token)),
|
|
230
|
+
),
|
|
231
|
+
}
|
|
232
|
+
})
|
|
234
233
|
|
|
235
234
|
export const layer = (root: string) =>
|
|
236
235
|
Layer.effectContext(
|
|
@@ -27,16 +27,15 @@ export const maxPayload = 1024 * 1024
|
|
|
27
27
|
const invalid = (detail: string) => FrameError.make({ detail })
|
|
28
28
|
|
|
29
29
|
/** Version 2 carries only channel and length; request admission belongs to port. */
|
|
30
|
-
export const encode = (frame: Frame)
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
})
|
|
30
|
+
export const encode = Effect.fnUntraced(function* (frame: Frame) {
|
|
31
|
+
if (frame.payload.byteLength > maxPayload)
|
|
32
|
+
return yield* invalid("Runner frame exceeds the payload limit")
|
|
33
|
+
const bytes = new Uint8Array(headerSize + frame.payload.byteLength)
|
|
34
|
+
bytes.set([2, frame.channel, 0, 0])
|
|
35
|
+
new DataView(bytes.buffer).setUint32(4, frame.payload.byteLength)
|
|
36
|
+
bytes.set(frame.payload, headerSize)
|
|
37
|
+
return bytes
|
|
38
|
+
})
|
|
40
39
|
|
|
41
40
|
/** Check a message boundary; the decoder still validates the frame's contents. */
|
|
42
41
|
export const isCompleteFrame = (bytes: Uint8Array): boolean =>
|
|
@@ -41,33 +41,32 @@ export const connect = Effect.fnUntraced(function* (
|
|
|
41
41
|
resources: yield* Effect.scope,
|
|
42
42
|
transport: { ...peer, failure: failures.await, report },
|
|
43
43
|
diagnostics: Fiber.await(exited).pipe(Effect.andThen(failures.cause)),
|
|
44
|
-
terminate: (graceful: boolean)
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
onSome: (pid) => ({ pid }),
|
|
62
|
-
}),
|
|
63
|
-
...(execution.threadId === undefined
|
|
64
|
-
? {}
|
|
65
|
-
: { threadId: execution.threadId }),
|
|
44
|
+
terminate: Effect.fnUntraced(function* (graceful: boolean) {
|
|
45
|
+
// Stop observation before intentionally ending stdin or killing the process.
|
|
46
|
+
yield* failures.close
|
|
47
|
+
if (!graceful) return
|
|
48
|
+
// If this fails, the execution scope forces termination and waits for exit.
|
|
49
|
+
const status = yield* peer.end.pipe(
|
|
50
|
+
// Peer EOF can interrupt the writer as its output ends. Still confirm the unit's exit.
|
|
51
|
+
Effect.catchCauseIf(Cause.hasInterruptsOnly, () => Effect.void),
|
|
52
|
+
Effect.andThen(Fiber.join(exited)),
|
|
53
|
+
Effect.interruptible,
|
|
54
|
+
Effect.timeoutOrElse({
|
|
55
|
+
duration: stdinExitTimeout,
|
|
56
|
+
orElse: () =>
|
|
57
|
+
LocalTerminationTimeout.make({
|
|
58
|
+
...Option.match(execution.pid, {
|
|
59
|
+
onNone: () => ({}),
|
|
60
|
+
onSome: (pid) => ({ pid }),
|
|
66
61
|
}),
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
62
|
+
...(execution.threadId === undefined
|
|
63
|
+
? {}
|
|
64
|
+
: { threadId: execution.threadId }),
|
|
65
|
+
}),
|
|
66
|
+
}),
|
|
67
|
+
)
|
|
68
|
+
if (status.exitCode !== 0 || status.statusCause.reasons.length > 0)
|
|
69
|
+
return yield* status
|
|
70
|
+
}),
|
|
72
71
|
}
|
|
73
72
|
})
|
|
@@ -2,28 +2,26 @@ import { Cause, Effect, FileSystem, Path } from "effect"
|
|
|
2
2
|
import { StateDirectoryLocked } from "./model"
|
|
3
3
|
|
|
4
4
|
/** SQLite owns the local directory lock for the enclosing scope, including failed startup. */
|
|
5
|
-
export const acquire = (directory: string)
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
yield* Effect.try({ try: () => db.exec("BEGIN EXCLUSIVE"), catch: locked })
|
|
29
|
-
})
|
|
5
|
+
export const acquire = Effect.fnUntraced(function* (directory: string) {
|
|
6
|
+
const fs = yield* FileSystem.FileSystem
|
|
7
|
+
const path = yield* Path.Path
|
|
8
|
+
yield* fs
|
|
9
|
+
.makeDirectory(directory, { recursive: true })
|
|
10
|
+
.pipe(Effect.uninterruptible)
|
|
11
|
+
const { DatabaseSync } = yield* Effect.promise(
|
|
12
|
+
() => import("node:sqlite"),
|
|
13
|
+
).pipe(Effect.uninterruptible)
|
|
14
|
+
const locked = (cause: unknown) =>
|
|
15
|
+
StateDirectoryLocked.make({
|
|
16
|
+
detail: "The runner state directory is already owned or cannot be locked",
|
|
17
|
+
cause: Cause.die(cause),
|
|
18
|
+
})
|
|
19
|
+
const db = yield* Effect.acquireRelease(
|
|
20
|
+
Effect.try({
|
|
21
|
+
try: () => new DatabaseSync(path.join(directory, "owner.sqlite")),
|
|
22
|
+
catch: locked,
|
|
23
|
+
}),
|
|
24
|
+
(db) => Effect.sync(() => db.close()),
|
|
25
|
+
)
|
|
26
|
+
yield* Effect.try({ try: () => db.exec("BEGIN EXCLUSIVE"), catch: locked })
|
|
27
|
+
})
|
|
@@ -44,39 +44,38 @@ export const digest = Effect.fnUntraced(function* (directory: string) {
|
|
|
44
44
|
const hash = yield* Effect.sync(() =>
|
|
45
45
|
createHash("sha256").update("graft-package-sha256-v1\0"),
|
|
46
46
|
)
|
|
47
|
-
const visit = (
|
|
47
|
+
const visit = Effect.fnUntraced(function* (
|
|
48
48
|
relative: string,
|
|
49
|
-
): Effect.
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
detail: `Unsupported package entry: ${relative}`,
|
|
56
|
-
})
|
|
57
|
-
const name = new TextEncoder().encode(relative)
|
|
58
|
-
yield* Effect.sync(() => {
|
|
59
|
-
hash.update(
|
|
60
|
-
`${info.type}:${name.byteLength}:${info.type === "File" ? info.size : 0}:`,
|
|
61
|
-
)
|
|
62
|
-
hash.update(name)
|
|
49
|
+
): Effect.fn.Return<void, PackageError | PlatformError.PlatformError> {
|
|
50
|
+
const filename = path.join(directory, relative)
|
|
51
|
+
const info = yield* fs.stat(filename)
|
|
52
|
+
if (info.type !== "File" && info.type !== "Directory")
|
|
53
|
+
return yield* PackageError.make({
|
|
54
|
+
detail: `Unsupported package entry: ${relative}`,
|
|
63
55
|
})
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
),
|
|
71
|
-
)
|
|
72
|
-
} else {
|
|
73
|
-
const entries = yield* fs.readDirectory(filename)
|
|
74
|
-
for (const entry of Array.sort(entries, Order.String)) {
|
|
75
|
-
if (entry === "node_modules") continue
|
|
76
|
-
yield* visit(relative === "" ? entry : `${relative}/${entry}`)
|
|
77
|
-
}
|
|
78
|
-
}
|
|
56
|
+
const name = new TextEncoder().encode(relative)
|
|
57
|
+
yield* Effect.sync(() => {
|
|
58
|
+
hash.update(
|
|
59
|
+
`${info.type}:${name.byteLength}:${info.type === "File" ? info.size : 0}:`,
|
|
60
|
+
)
|
|
61
|
+
hash.update(name)
|
|
79
62
|
})
|
|
63
|
+
if (info.type === "File") {
|
|
64
|
+
yield* fs.stream(filename).pipe(
|
|
65
|
+
Stream.runForEach((bytes) =>
|
|
66
|
+
Effect.sync(() => {
|
|
67
|
+
hash.update(bytes)
|
|
68
|
+
}),
|
|
69
|
+
),
|
|
70
|
+
)
|
|
71
|
+
} else {
|
|
72
|
+
const entries = yield* fs.readDirectory(filename)
|
|
73
|
+
for (const entry of Array.sort(entries, Order.String)) {
|
|
74
|
+
if (entry === "node_modules") continue
|
|
75
|
+
yield* visit(relative === "" ? entry : `${relative}/${entry}`)
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
})
|
|
80
79
|
yield* visit("")
|
|
81
80
|
return yield* Effect.sync(() => hash.digest("hex"))
|
|
82
81
|
})
|
|
@@ -136,43 +135,41 @@ export const resolve = Effect.fnUntraced(
|
|
|
136
135
|
}),
|
|
137
136
|
)
|
|
138
137
|
|
|
139
|
-
export const resolveEntry = (
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
)
|
|
178
|
-
})
|
|
138
|
+
export const resolveEntry = Effect.fnUntraced(function* (
|
|
139
|
+
installation: string,
|
|
140
|
+
specifier: string,
|
|
141
|
+
) {
|
|
142
|
+
const spawner = yield* ChildProcessSpawner.ChildProcessSpawner
|
|
143
|
+
const child = yield* spawner.spawn(
|
|
144
|
+
ChildProcess.make(
|
|
145
|
+
process.execPath,
|
|
146
|
+
[
|
|
147
|
+
"--input-type=module",
|
|
148
|
+
"--eval",
|
|
149
|
+
"process.stdout.write(import.meta.resolve(process.argv[1]))",
|
|
150
|
+
"--",
|
|
151
|
+
specifier,
|
|
152
|
+
],
|
|
153
|
+
{
|
|
154
|
+
cwd: installation,
|
|
155
|
+
env: {},
|
|
156
|
+
stdin: "ignore",
|
|
157
|
+
stdout: "pipe",
|
|
158
|
+
stderr: "pipe",
|
|
159
|
+
},
|
|
160
|
+
),
|
|
161
|
+
)
|
|
162
|
+
const [code, stdout, stderr] = yield* Effect.all(
|
|
163
|
+
[
|
|
164
|
+
child.exitCode,
|
|
165
|
+
child.stdout.pipe(Stream.decodeText(), Stream.mkString),
|
|
166
|
+
child.stderr.pipe(Stream.decodeText(), Stream.mkString),
|
|
167
|
+
],
|
|
168
|
+
{ concurrency: "unbounded" },
|
|
169
|
+
)
|
|
170
|
+
if (code !== 0)
|
|
171
|
+
return yield* PackageError.make({
|
|
172
|
+
detail: `Package resolution failed: ${stderr}`,
|
|
173
|
+
})
|
|
174
|
+
return stdout
|
|
175
|
+
}, Effect.scoped)
|