effect-cf 0.25.3 → 0.27.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/CHANGELOG.md +16 -0
- package/README.md +213 -1
- package/dist/Artifacts-BhWrdyqX.mjs +241 -0
- package/dist/Artifacts-DcoXcRP5.d.mts +231 -0
- package/dist/ComputerArtifacts.d.mts +55 -0
- package/dist/ComputerArtifacts.mjs +95 -0
- package/dist/ComputerWorkspace.d.mts +279 -0
- package/dist/ComputerWorkspace.mjs +545 -0
- package/dist/ComputerWorkspaceHost.d.mts +71 -0
- package/dist/ComputerWorkspaceHost.mjs +74 -0
- package/dist/ComputerWorkspaceHostRegistry-CY5Fv4Ng.mjs +8 -0
- package/dist/{DurableObjectState-CEh0DVw7.mjs → DurableObjectState-Dux8KfjG.mjs} +3 -48
- package/dist/{WorkflowBinding-D5iWzCUa.d.mts → DurableObjectState-biHrHwrh.d.mts} +7 -81
- package/dist/ErrorMessage-_lg53p3W.mjs +49 -0
- package/dist/Vitest.d.mts +2 -1
- package/dist/Vitest.mjs +2 -1
- package/dist/WorkflowBinding-zlRPCXVf.d.mts +79 -0
- package/dist/index.d.mts +80 -2
- package/dist/index.mjs +72 -123
- package/package.json +23 -1
- package/src/Artifacts.ts +519 -0
- package/src/ComputerArtifacts.ts +255 -0
- package/src/ComputerWorkspace.ts +1098 -0
- package/src/ComputerWorkspaceHost.ts +216 -0
- package/src/WebTransport.ts +123 -0
- package/src/index.ts +2 -0
- package/src/internal/ComputerWorkspaceHostRegistry.ts +10 -0
|
@@ -0,0 +1,545 @@
|
|
|
1
|
+
import { t as DurableObjectState } from "./DurableObjectState-Dux8KfjG.mjs";
|
|
2
|
+
import { fromClient } from "./ComputerArtifacts.mjs";
|
|
3
|
+
import { t as lookup } from "./ComputerWorkspaceHostRegistry-CY5Fv4Ng.mjs";
|
|
4
|
+
import { Context, Effect, Layer, Schema, Stream } from "effect";
|
|
5
|
+
//#region src/ComputerWorkspace.ts
|
|
6
|
+
/**
|
|
7
|
+
* Workspace errors use `Schema.TaggedError`, unlike most local binding errors,
|
|
8
|
+
* because workspace operations commonly sit behind Durable Object RPC methods.
|
|
9
|
+
* The schema form preserves the operation-family tags across that boundary.
|
|
10
|
+
*/
|
|
11
|
+
var WorkspaceFsError = class extends Schema.TaggedError()("WorkspaceFsError", {
|
|
12
|
+
operation: Schema.String,
|
|
13
|
+
path: Schema.String,
|
|
14
|
+
message: Schema.String,
|
|
15
|
+
code: Schema.optionalKey(Schema.String),
|
|
16
|
+
cause: Schema.optionalKey(Schema.Defect({ includeStack: true }))
|
|
17
|
+
}) {};
|
|
18
|
+
/** Schema-serializable error for a workspace Git operation. */
|
|
19
|
+
var WorkspaceGitError = class extends Schema.TaggedError()("WorkspaceGitError", {
|
|
20
|
+
operation: Schema.String,
|
|
21
|
+
message: Schema.String,
|
|
22
|
+
code: Schema.optionalKey(Schema.String),
|
|
23
|
+
cause: Schema.optionalKey(Schema.Defect({ includeStack: true }))
|
|
24
|
+
}) {};
|
|
25
|
+
/** Schema-serializable error for a workspace runtime operation. */
|
|
26
|
+
var WorkspaceExecError = class extends Schema.TaggedError()("WorkspaceExecError", {
|
|
27
|
+
operation: Schema.String,
|
|
28
|
+
message: Schema.String,
|
|
29
|
+
code: Schema.optionalKey(Schema.String),
|
|
30
|
+
cause: Schema.optionalKey(Schema.Defect({ includeStack: true }))
|
|
31
|
+
}) {};
|
|
32
|
+
/** Schema-serializable error for a workspace Assets operation. */
|
|
33
|
+
var WorkspaceAssetsError = class extends Schema.TaggedError()("WorkspaceAssetsError", {
|
|
34
|
+
operation: Schema.String,
|
|
35
|
+
path: Schema.String,
|
|
36
|
+
message: Schema.String,
|
|
37
|
+
code: Schema.optionalKey(Schema.String),
|
|
38
|
+
cause: Schema.optionalKey(Schema.Defect({ includeStack: true }))
|
|
39
|
+
}) {};
|
|
40
|
+
const errorMessage = (cause) => cause instanceof Error ? cause.message : String(cause);
|
|
41
|
+
const errorCode = (cause) => {
|
|
42
|
+
if (typeof cause !== "object" || cause === null) return;
|
|
43
|
+
const code = Reflect.get(cause, "code");
|
|
44
|
+
return typeof code === "string" ? code : void 0;
|
|
45
|
+
};
|
|
46
|
+
const fsError = (operation, path, cause) => WorkspaceFsError.make({
|
|
47
|
+
operation,
|
|
48
|
+
path,
|
|
49
|
+
message: errorMessage(cause),
|
|
50
|
+
cause,
|
|
51
|
+
...errorCode(cause) === void 0 ? {} : { code: errorCode(cause) }
|
|
52
|
+
});
|
|
53
|
+
const gitError = (operation, cause) => WorkspaceGitError.make({
|
|
54
|
+
operation,
|
|
55
|
+
message: errorMessage(cause),
|
|
56
|
+
cause,
|
|
57
|
+
...errorCode(cause) === void 0 ? {} : { code: errorCode(cause) }
|
|
58
|
+
});
|
|
59
|
+
const execError = (operation, cause) => WorkspaceExecError.make({
|
|
60
|
+
operation,
|
|
61
|
+
message: errorMessage(cause),
|
|
62
|
+
cause,
|
|
63
|
+
...errorCode(cause) === void 0 ? {} : { code: errorCode(cause) }
|
|
64
|
+
});
|
|
65
|
+
const assetsError = (operation, path, cause) => WorkspaceAssetsError.make({
|
|
66
|
+
operation,
|
|
67
|
+
path,
|
|
68
|
+
message: errorMessage(cause),
|
|
69
|
+
cause,
|
|
70
|
+
...errorCode(cause) === void 0 ? {} : { code: errorCode(cause) }
|
|
71
|
+
});
|
|
72
|
+
const tryFs = (operation, path, evaluate) => Effect.tryPromise({
|
|
73
|
+
try: evaluate,
|
|
74
|
+
catch: (cause) => fsError(operation, path, cause)
|
|
75
|
+
});
|
|
76
|
+
const isNotFound = (error) => error.code === "ENOENT" || /ENOENT|no such/i.test(error.message);
|
|
77
|
+
const makeFilesystem = (fsClient) => {
|
|
78
|
+
const readFile = Effect.fn("ComputerWorkspace.fs.readFile", { attributes: {
|
|
79
|
+
family: "fs",
|
|
80
|
+
operation: "readFile"
|
|
81
|
+
} })(function* (path, options = {}) {
|
|
82
|
+
yield* Effect.annotateCurrentSpan("path", path);
|
|
83
|
+
const { encoding = "utf8", ...readOptions } = options;
|
|
84
|
+
if (encoding === "stream") return yield* tryFs("readFile", path, () => fsClient.readFile(path, readOptions));
|
|
85
|
+
return yield* tryFs("readFile", path, () => fsClient.readFile(path, {
|
|
86
|
+
...readOptions,
|
|
87
|
+
encoding: "utf8"
|
|
88
|
+
}));
|
|
89
|
+
});
|
|
90
|
+
const stat = Effect.fn("ComputerWorkspace.fs.stat", { attributes: {
|
|
91
|
+
family: "fs",
|
|
92
|
+
operation: "stat"
|
|
93
|
+
} })(function* (path) {
|
|
94
|
+
yield* Effect.annotateCurrentSpan("path", path);
|
|
95
|
+
return yield* tryFs("stat", path, () => fsClient.stat(path));
|
|
96
|
+
});
|
|
97
|
+
return {
|
|
98
|
+
readFile,
|
|
99
|
+
stat,
|
|
100
|
+
lstat: Effect.fn("ComputerWorkspace.fs.lstat", { attributes: {
|
|
101
|
+
family: "fs",
|
|
102
|
+
operation: "lstat"
|
|
103
|
+
} })(function* (path) {
|
|
104
|
+
yield* Effect.annotateCurrentSpan("path", path);
|
|
105
|
+
return yield* tryFs("lstat", path, () => fsClient.lstat(path));
|
|
106
|
+
}),
|
|
107
|
+
exists: Effect.fn("ComputerWorkspace.fs.exists", { attributes: {
|
|
108
|
+
family: "fs",
|
|
109
|
+
operation: "exists"
|
|
110
|
+
} })(function* (path) {
|
|
111
|
+
yield* Effect.annotateCurrentSpan("path", path);
|
|
112
|
+
return yield* stat(path).pipe(Effect.as(true), Effect.catch((error) => isNotFound(error) ? Effect.succeed(false) : Effect.fail(error)));
|
|
113
|
+
}),
|
|
114
|
+
readlink: Effect.fn("ComputerWorkspace.fs.readlink", { attributes: {
|
|
115
|
+
family: "fs",
|
|
116
|
+
operation: "readlink"
|
|
117
|
+
} })(function* (path) {
|
|
118
|
+
yield* Effect.annotateCurrentSpan("path", path);
|
|
119
|
+
return yield* tryFs("readlink", path, () => fsClient.readlink(path));
|
|
120
|
+
}),
|
|
121
|
+
readdir: Effect.fn("ComputerWorkspace.fs.readdir", { attributes: {
|
|
122
|
+
family: "fs",
|
|
123
|
+
operation: "readdir"
|
|
124
|
+
} })(function* (path, options) {
|
|
125
|
+
yield* Effect.annotateCurrentSpan("path", path);
|
|
126
|
+
return yield* tryFs("readdir", path, () => fsClient.readdir(path, options));
|
|
127
|
+
}),
|
|
128
|
+
find: Effect.fn("ComputerWorkspace.fs.find", { attributes: {
|
|
129
|
+
family: "fs",
|
|
130
|
+
operation: "find"
|
|
131
|
+
} })(function* (directory, pattern, options) {
|
|
132
|
+
yield* Effect.annotateCurrentSpan({
|
|
133
|
+
path: directory,
|
|
134
|
+
pattern
|
|
135
|
+
});
|
|
136
|
+
return yield* tryFs("find", directory, () => fsClient.find(directory, pattern, options));
|
|
137
|
+
}),
|
|
138
|
+
ls: Effect.fn("ComputerWorkspace.fs.ls", { attributes: {
|
|
139
|
+
family: "fs",
|
|
140
|
+
operation: "ls"
|
|
141
|
+
} })(function* (prefix) {
|
|
142
|
+
yield* Effect.annotateCurrentSpan("path", prefix);
|
|
143
|
+
return yield* tryFs("ls", prefix, () => fsClient.ls(prefix));
|
|
144
|
+
}),
|
|
145
|
+
grep: Effect.fn("ComputerWorkspace.fs.grep", { attributes: {
|
|
146
|
+
family: "fs",
|
|
147
|
+
operation: "grep"
|
|
148
|
+
} })(function* (pattern, path, options) {
|
|
149
|
+
yield* Effect.annotateCurrentSpan({
|
|
150
|
+
path,
|
|
151
|
+
pattern
|
|
152
|
+
});
|
|
153
|
+
return yield* tryFs("grep", path, () => fsClient.grep(pattern, path, options));
|
|
154
|
+
}),
|
|
155
|
+
writeFile: Effect.fn("ComputerWorkspace.fs.writeFile", { attributes: {
|
|
156
|
+
family: "fs",
|
|
157
|
+
operation: "writeFile"
|
|
158
|
+
} })(function* (path, content, options) {
|
|
159
|
+
yield* Effect.annotateCurrentSpan("path", path);
|
|
160
|
+
yield* tryFs("writeFile", path, () => fsClient.writeFile(path, content, options));
|
|
161
|
+
}),
|
|
162
|
+
mkdir: Effect.fn("ComputerWorkspace.fs.mkdir", { attributes: {
|
|
163
|
+
family: "fs",
|
|
164
|
+
operation: "mkdir"
|
|
165
|
+
} })(function* (path, options) {
|
|
166
|
+
yield* Effect.annotateCurrentSpan("path", path);
|
|
167
|
+
yield* tryFs("mkdir", path, () => fsClient.mkdir(path, options));
|
|
168
|
+
}),
|
|
169
|
+
rm: Effect.fn("ComputerWorkspace.fs.rm", { attributes: {
|
|
170
|
+
family: "fs",
|
|
171
|
+
operation: "rm"
|
|
172
|
+
} })(function* (path, options) {
|
|
173
|
+
yield* Effect.annotateCurrentSpan("path", path);
|
|
174
|
+
yield* tryFs("rm", path, () => fsClient.rm(path, options));
|
|
175
|
+
}),
|
|
176
|
+
chmod: Effect.fn("ComputerWorkspace.fs.chmod", { attributes: {
|
|
177
|
+
family: "fs",
|
|
178
|
+
operation: "chmod"
|
|
179
|
+
} })(function* (path, mode) {
|
|
180
|
+
yield* Effect.annotateCurrentSpan({
|
|
181
|
+
path,
|
|
182
|
+
mode
|
|
183
|
+
});
|
|
184
|
+
yield* tryFs("chmod", path, () => fsClient.chmod(path, mode));
|
|
185
|
+
}),
|
|
186
|
+
symlink: Effect.fn("ComputerWorkspace.fs.symlink", { attributes: {
|
|
187
|
+
family: "fs",
|
|
188
|
+
operation: "symlink"
|
|
189
|
+
} })(function* (target, path) {
|
|
190
|
+
yield* Effect.annotateCurrentSpan({
|
|
191
|
+
path,
|
|
192
|
+
target
|
|
193
|
+
});
|
|
194
|
+
yield* tryFs("symlink", path, () => fsClient.symlink(target, path));
|
|
195
|
+
})
|
|
196
|
+
};
|
|
197
|
+
};
|
|
198
|
+
const gitPerson = (person) => ({
|
|
199
|
+
...person,
|
|
200
|
+
timestampSeconds: person.timestamp
|
|
201
|
+
});
|
|
202
|
+
const gitCommit = (commit) => ({
|
|
203
|
+
oid: commit.oid,
|
|
204
|
+
message: commit.message,
|
|
205
|
+
tree: commit.tree,
|
|
206
|
+
parent: commit.parent,
|
|
207
|
+
parents: commit.parent,
|
|
208
|
+
author: gitPerson(commit.author),
|
|
209
|
+
committer: gitPerson(commit.committer)
|
|
210
|
+
});
|
|
211
|
+
const makeGit = (gitClient) => {
|
|
212
|
+
const method = (operation, evaluate) => Effect.fn(`ComputerWorkspace.git.${operation}`, { attributes: {
|
|
213
|
+
family: "git",
|
|
214
|
+
operation
|
|
215
|
+
} })(function* (...args) {
|
|
216
|
+
return yield* Effect.tryPromise({
|
|
217
|
+
try: () => evaluate(...args),
|
|
218
|
+
catch: (cause) => gitError(operation, cause)
|
|
219
|
+
});
|
|
220
|
+
});
|
|
221
|
+
const log = Effect.fn("ComputerWorkspace.git.log", { attributes: {
|
|
222
|
+
family: "git",
|
|
223
|
+
operation: "log"
|
|
224
|
+
} })(function* (options) {
|
|
225
|
+
const { maxCount, ...upstreamOptions } = options ?? {};
|
|
226
|
+
return (yield* Effect.tryPromise({
|
|
227
|
+
try: () => gitClient.log({
|
|
228
|
+
...upstreamOptions,
|
|
229
|
+
...upstreamOptions.depth === void 0 && maxCount !== void 0 ? { depth: maxCount } : {}
|
|
230
|
+
}),
|
|
231
|
+
catch: (cause) => gitError("log", cause)
|
|
232
|
+
})).map(gitCommit);
|
|
233
|
+
});
|
|
234
|
+
const revParse = Effect.fn("ComputerWorkspace.git.revParse", { attributes: {
|
|
235
|
+
family: "git",
|
|
236
|
+
operation: "revParse"
|
|
237
|
+
} })(function* (input) {
|
|
238
|
+
const options = typeof input === "string" ? { ref: input } : input;
|
|
239
|
+
return yield* Effect.tryPromise({
|
|
240
|
+
try: () => gitClient.revParse(options),
|
|
241
|
+
catch: (cause) => gitError("revParse", cause)
|
|
242
|
+
});
|
|
243
|
+
});
|
|
244
|
+
return {
|
|
245
|
+
clone: method("clone", (options) => gitClient.clone(options)),
|
|
246
|
+
diff: method("diff", (options) => gitClient.diff(options)),
|
|
247
|
+
diffSummary: method("diffSummary", (options) => gitClient.diffSummary(options)),
|
|
248
|
+
init: method("init", (options) => gitClient.init(options)),
|
|
249
|
+
status: method("status", (options) => gitClient.status(options)),
|
|
250
|
+
add: method("add", (options) => gitClient.add(options)),
|
|
251
|
+
rm: method("rm", (options) => gitClient.rm(options)),
|
|
252
|
+
commit: method("commit", (options) => gitClient.commit(options)),
|
|
253
|
+
log,
|
|
254
|
+
show: method("show", (options) => gitClient.show(options)),
|
|
255
|
+
revParse,
|
|
256
|
+
repoRoot: method("repoRoot", (options) => gitClient.repoRoot(options)),
|
|
257
|
+
currentBranch: method("currentBranch", (options) => gitClient.currentBranch(options)),
|
|
258
|
+
lsFiles: method("lsFiles", (options) => gitClient.lsFiles(options)),
|
|
259
|
+
lsTree: method("lsTree", (options) => gitClient.lsTree(options)),
|
|
260
|
+
branch: method("branch", (options) => gitClient.branch(options)),
|
|
261
|
+
branchDelete: method("branchDelete", (options) => gitClient.branchDelete(options)),
|
|
262
|
+
branchList: method("branchList", (options) => gitClient.branchList(options)),
|
|
263
|
+
tag: method("tag", (options) => gitClient.tag(options)),
|
|
264
|
+
tagDelete: method("tagDelete", (options) => gitClient.tagDelete(options)),
|
|
265
|
+
tagList: method("tagList", (options) => gitClient.tagList(options)),
|
|
266
|
+
checkout: method("checkout", (options) => gitClient.checkout(options)),
|
|
267
|
+
fetch: method("fetch", (options) => gitClient.fetch(options)),
|
|
268
|
+
push: method("push", (options) => gitClient.push(options)),
|
|
269
|
+
pull: method("pull", (options) => gitClient.pull(options)),
|
|
270
|
+
merge: method("merge", (options) => gitClient.merge(options)),
|
|
271
|
+
remoteAdd: method("remoteAdd", (options) => gitClient.remoteAdd(options)),
|
|
272
|
+
remoteRemove: method("remoteRemove", (options) => gitClient.remoteRemove(options)),
|
|
273
|
+
remoteList: method("remoteList", (options) => gitClient.remoteList(options)),
|
|
274
|
+
hashObject: method("hashObject", (options) => gitClient.hashObject(options)),
|
|
275
|
+
catFile: method("catFile", (options) => gitClient.catFile(options)),
|
|
276
|
+
updateRef: method("updateRef", (options) => gitClient.updateRef(options)),
|
|
277
|
+
configGet: method("configGet", (options) => gitClient.configGet(options)),
|
|
278
|
+
configSet: method("configSet", (options) => gitClient.configSet(options)),
|
|
279
|
+
stashPush: method("stashPush", (options) => gitClient.stashPush(options)),
|
|
280
|
+
stashList: method("stashList", (options) => gitClient.stashList(options)),
|
|
281
|
+
stashPop: method("stashPop", (options) => gitClient.stashPop(options)),
|
|
282
|
+
reset: method("reset", (options) => gitClient.reset(options)),
|
|
283
|
+
clean: method("clean", (options) => gitClient.clean(options)),
|
|
284
|
+
cli: method("cli", (input) => gitClient.cli(input))
|
|
285
|
+
};
|
|
286
|
+
};
|
|
287
|
+
const runtimeExecOptions = (options) => {
|
|
288
|
+
const { encoding = "utf8", timeoutMillis, timeoutMs, ...rest } = options ?? {};
|
|
289
|
+
return {
|
|
290
|
+
...rest,
|
|
291
|
+
...encoding === "utf8" ? { encoding: "utf8" } : {},
|
|
292
|
+
...timeoutMs === void 0 && timeoutMillis === void 0 ? {} : { timeoutMs: timeoutMs ?? timeoutMillis }
|
|
293
|
+
};
|
|
294
|
+
};
|
|
295
|
+
const runtimeGetOptions = (options) => {
|
|
296
|
+
const { encoding = "utf8", ...rest } = options ?? {};
|
|
297
|
+
return {
|
|
298
|
+
...rest,
|
|
299
|
+
...encoding === "utf8" ? { encoding: "utf8" } : {}
|
|
300
|
+
};
|
|
301
|
+
};
|
|
302
|
+
const wrapRuntimeResult = (result) => ({
|
|
303
|
+
status: result.status,
|
|
304
|
+
exitCode: result.exitCode,
|
|
305
|
+
stdout: result.stdout,
|
|
306
|
+
stderr: result.stderr,
|
|
307
|
+
...result.value === void 0 ? {} : { value: result.value },
|
|
308
|
+
pushed: result.pushed,
|
|
309
|
+
pulled: result.pulled,
|
|
310
|
+
skipped: result.skipped,
|
|
311
|
+
sync: result.sync
|
|
312
|
+
});
|
|
313
|
+
const wrapRuntimeEvent = (event) => {
|
|
314
|
+
switch (event.name) {
|
|
315
|
+
case "stdout": {
|
|
316
|
+
const chunk = event.value;
|
|
317
|
+
return typeof chunk === "string" ? {
|
|
318
|
+
_tag: "Stdout",
|
|
319
|
+
id: event.id,
|
|
320
|
+
sequence: event.seq,
|
|
321
|
+
chunk,
|
|
322
|
+
text: chunk
|
|
323
|
+
} : {
|
|
324
|
+
_tag: "Stdout",
|
|
325
|
+
id: event.id,
|
|
326
|
+
sequence: event.seq,
|
|
327
|
+
chunk,
|
|
328
|
+
bytes: chunk
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
case "stderr": {
|
|
332
|
+
const chunk = event.value;
|
|
333
|
+
return typeof chunk === "string" ? {
|
|
334
|
+
_tag: "Stderr",
|
|
335
|
+
id: event.id,
|
|
336
|
+
sequence: event.seq,
|
|
337
|
+
chunk,
|
|
338
|
+
text: chunk
|
|
339
|
+
} : {
|
|
340
|
+
_tag: "Stderr",
|
|
341
|
+
id: event.id,
|
|
342
|
+
sequence: event.seq,
|
|
343
|
+
chunk,
|
|
344
|
+
bytes: chunk
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
case "exit": return {
|
|
348
|
+
_tag: "Exit",
|
|
349
|
+
id: event.id,
|
|
350
|
+
sequence: event.seq,
|
|
351
|
+
exitCode: event.code,
|
|
352
|
+
...event.result === void 0 ? {} : { value: event.result }
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
};
|
|
356
|
+
const wrapRun = (handle) => {
|
|
357
|
+
const events = Stream.fromReadableStream({
|
|
358
|
+
evaluate: () => handle,
|
|
359
|
+
onError: (cause) => execError("events", cause)
|
|
360
|
+
}).pipe(Stream.map(wrapRuntimeEvent));
|
|
361
|
+
const result = Effect.tryPromise({
|
|
362
|
+
try: () => handle.result(),
|
|
363
|
+
catch: (cause) => execError("result", cause)
|
|
364
|
+
}).pipe(Effect.map(wrapRuntimeResult));
|
|
365
|
+
const kill = Effect.fn("ComputerWorkspace.runtime.run.kill", { attributes: {
|
|
366
|
+
family: "runtime",
|
|
367
|
+
operation: "run.kill",
|
|
368
|
+
runId: handle.id
|
|
369
|
+
} })(function* (signal) {
|
|
370
|
+
yield* Effect.tryPromise({
|
|
371
|
+
try: () => handle.kill(signal),
|
|
372
|
+
catch: (cause) => execError("run.kill", cause)
|
|
373
|
+
});
|
|
374
|
+
});
|
|
375
|
+
return {
|
|
376
|
+
id: handle.id,
|
|
377
|
+
backend: handle.backend,
|
|
378
|
+
events,
|
|
379
|
+
result,
|
|
380
|
+
kill
|
|
381
|
+
};
|
|
382
|
+
};
|
|
383
|
+
const releaseRun = (handle) => Effect.sync(() => handle[Symbol.dispose]());
|
|
384
|
+
const makeRuntime = (client) => {
|
|
385
|
+
const execImplementation = Effect.fn("ComputerWorkspace.runtime.exec", { attributes: {
|
|
386
|
+
family: "runtime",
|
|
387
|
+
operation: "exec"
|
|
388
|
+
} })(function* (source, optionsOrValue, ...remainingValues) {
|
|
389
|
+
const options = typeof source === "string" ? optionsOrValue : void 0;
|
|
390
|
+
yield* Effect.annotateCurrentSpan({
|
|
391
|
+
backend: options?.backend,
|
|
392
|
+
runId: options?.id
|
|
393
|
+
});
|
|
394
|
+
const handle = yield* Effect.acquireRelease(Effect.tryPromise({
|
|
395
|
+
try: () => {
|
|
396
|
+
if (typeof source === "string") return client.runtime.exec(source, runtimeExecOptions(options));
|
|
397
|
+
const values = optionsOrValue === void 0 ? remainingValues : [optionsOrValue, ...remainingValues];
|
|
398
|
+
return client.runtime.exec(source, ...values);
|
|
399
|
+
},
|
|
400
|
+
catch: (cause) => execError("exec", cause)
|
|
401
|
+
}), releaseRun);
|
|
402
|
+
return wrapRun(handle);
|
|
403
|
+
});
|
|
404
|
+
const exec = execImplementation;
|
|
405
|
+
const getExec = Effect.fn("ComputerWorkspace.runtime.getExec", { attributes: {
|
|
406
|
+
family: "runtime",
|
|
407
|
+
operation: "getExec"
|
|
408
|
+
} })(function* (id, options) {
|
|
409
|
+
yield* Effect.annotateCurrentSpan({
|
|
410
|
+
backend: options?.backend,
|
|
411
|
+
runId: id
|
|
412
|
+
});
|
|
413
|
+
const handle = yield* Effect.acquireRelease(Effect.tryPromise({
|
|
414
|
+
try: () => client.runtime.getExec(id, runtimeGetOptions(options)),
|
|
415
|
+
catch: (cause) => execError("getExec", cause)
|
|
416
|
+
}), releaseRun);
|
|
417
|
+
return wrapRun(handle);
|
|
418
|
+
});
|
|
419
|
+
const killExec = Effect.fn("ComputerWorkspace.runtime.killExec", { attributes: {
|
|
420
|
+
family: "runtime",
|
|
421
|
+
operation: "killExec"
|
|
422
|
+
} })(function* (id, options) {
|
|
423
|
+
yield* Effect.annotateCurrentSpan({
|
|
424
|
+
backend: options?.backend,
|
|
425
|
+
runId: id
|
|
426
|
+
});
|
|
427
|
+
yield* Effect.tryPromise({
|
|
428
|
+
try: () => client.runtime.killExec(id, options),
|
|
429
|
+
catch: (cause) => execError("killExec", cause)
|
|
430
|
+
});
|
|
431
|
+
});
|
|
432
|
+
const disposeExec = Effect.fn("ComputerWorkspace.runtime.disposeExec", { attributes: {
|
|
433
|
+
family: "runtime",
|
|
434
|
+
operation: "disposeExec"
|
|
435
|
+
} })(function* (id, options) {
|
|
436
|
+
yield* Effect.annotateCurrentSpan({
|
|
437
|
+
backend: options?.backend,
|
|
438
|
+
runId: id
|
|
439
|
+
});
|
|
440
|
+
yield* Effect.tryPromise({
|
|
441
|
+
try: () => client.runtime.disposeExec(id, options),
|
|
442
|
+
catch: (cause) => execError("disposeExec", cause)
|
|
443
|
+
});
|
|
444
|
+
});
|
|
445
|
+
return {
|
|
446
|
+
exec,
|
|
447
|
+
execCollect: Effect.fn("ComputerWorkspace.runtime.execCollect", { attributes: {
|
|
448
|
+
family: "runtime",
|
|
449
|
+
operation: "execCollect"
|
|
450
|
+
} })(function* (source, optionsOrValue, ...remainingValues) {
|
|
451
|
+
return yield* Effect.scoped(Effect.gen(function* () {
|
|
452
|
+
return yield* (yield* execImplementation(source, optionsOrValue, ...remainingValues)).result;
|
|
453
|
+
}));
|
|
454
|
+
}),
|
|
455
|
+
getExec,
|
|
456
|
+
killExec,
|
|
457
|
+
disposeExec
|
|
458
|
+
};
|
|
459
|
+
};
|
|
460
|
+
const makeAssets = (client) => client === void 0 ? void 0 : { share: Effect.fn("ComputerWorkspace.assets.share", { attributes: {
|
|
461
|
+
family: "assets",
|
|
462
|
+
operation: "share"
|
|
463
|
+
} })(function* (path, options) {
|
|
464
|
+
yield* Effect.annotateCurrentSpan("path", path);
|
|
465
|
+
return yield* Effect.tryPromise({
|
|
466
|
+
try: () => client.share(path, options),
|
|
467
|
+
catch: (cause) => assetsError("share", path, cause)
|
|
468
|
+
});
|
|
469
|
+
}) };
|
|
470
|
+
const makeThink = (client) => {
|
|
471
|
+
if (client.readFile === void 0 || client.readFileBytes === void 0 || client.writeFile === void 0 || client.readDir === void 0 || client.rm === void 0 || client.glob === void 0 || client.mkdir === void 0 || client.stat === void 0) return;
|
|
472
|
+
const method = (operation, pathOf, evaluate) => Effect.fn(`ComputerWorkspace.think.${operation}`, { attributes: {
|
|
473
|
+
family: "think",
|
|
474
|
+
operation
|
|
475
|
+
} })(function* (...args) {
|
|
476
|
+
const path = pathOf(...args);
|
|
477
|
+
yield* Effect.annotateCurrentSpan("path", path);
|
|
478
|
+
return yield* tryFs(`think.${operation}`, path, () => evaluate(...args));
|
|
479
|
+
});
|
|
480
|
+
return {
|
|
481
|
+
readFile: method("readFile", (path) => path, (path) => client.readFile(path)),
|
|
482
|
+
readFileBytes: method("readFileBytes", (path) => path, (path) => client.readFileBytes(path)),
|
|
483
|
+
writeFile: method("writeFile", (path, _content) => path, (path, content) => client.writeFile(path, content)),
|
|
484
|
+
readDir: method("readDir", (path, _options) => path, (path, options) => client.readDir(path, options)),
|
|
485
|
+
rm: method("rm", (path, _options) => path, (path, options) => client.rm(path, options)),
|
|
486
|
+
glob: method("glob", (pattern) => pattern, (pattern) => client.glob(pattern)),
|
|
487
|
+
mkdir: method("mkdir", (path, _options) => path, (path, options) => client.mkdir(path, options)),
|
|
488
|
+
stat: method("stat", (path) => path, (path) => client.stat(path))
|
|
489
|
+
};
|
|
490
|
+
};
|
|
491
|
+
/**
|
|
492
|
+
* Wraps a client whose lifetime is owned by the caller. Prefer
|
|
493
|
+
* {@link ComputerWorkspace.layer} in Durable Objects so the parent client and
|
|
494
|
+
* every runtime handle are disposed by Effect `Scope`.
|
|
495
|
+
*/
|
|
496
|
+
const fromWorkspaceClient = (client) => {
|
|
497
|
+
const fs = makeFilesystem(client.fs);
|
|
498
|
+
const git = makeGit(client.git);
|
|
499
|
+
const runtime = makeRuntime(client);
|
|
500
|
+
return {
|
|
501
|
+
fs,
|
|
502
|
+
git,
|
|
503
|
+
runtime,
|
|
504
|
+
artifacts: fromClient(client.artifacts, { binding: "ComputerWorkspace.artifacts" }),
|
|
505
|
+
assets: makeAssets(client.assets),
|
|
506
|
+
think: makeThink(client),
|
|
507
|
+
readFile: fs.readFile,
|
|
508
|
+
stat: fs.stat,
|
|
509
|
+
lstat: fs.lstat,
|
|
510
|
+
exists: fs.exists,
|
|
511
|
+
readlink: fs.readlink,
|
|
512
|
+
readdir: fs.readdir,
|
|
513
|
+
listDirectory: fs.readdir,
|
|
514
|
+
find: fs.find,
|
|
515
|
+
ls: fs.ls,
|
|
516
|
+
grep: fs.grep,
|
|
517
|
+
writeFile: fs.writeFile,
|
|
518
|
+
mkdir: fs.mkdir,
|
|
519
|
+
rm: fs.rm,
|
|
520
|
+
removePath: fs.rm,
|
|
521
|
+
chmod: fs.chmod,
|
|
522
|
+
symlink: fs.symlink,
|
|
523
|
+
exec: runtime.exec,
|
|
524
|
+
execCollect: runtime.execCollect,
|
|
525
|
+
getExec: runtime.getExec,
|
|
526
|
+
killExec: runtime.killExec,
|
|
527
|
+
disposeExec: runtime.disposeExec
|
|
528
|
+
};
|
|
529
|
+
};
|
|
530
|
+
/** Effect service for the workspace owned by the enclosing Durable Object. */
|
|
531
|
+
var ComputerWorkspace = class ComputerWorkspace extends Context.Service()("effect-cf/ComputerWorkspace") {
|
|
532
|
+
/**
|
|
533
|
+
* Acquires the single workspace registered by `withComputerWorkspace` and
|
|
534
|
+
* releases its RPC client when the layer scope closes.
|
|
535
|
+
*/
|
|
536
|
+
static layer = Layer.effect(ComputerWorkspace, Effect.gen(function* () {
|
|
537
|
+
const host = lookup((yield* DurableObjectState).raw);
|
|
538
|
+
if (host === void 0) return yield* Effect.die("ComputerWorkspace.layer requires a Durable Object class built with withComputerWorkspace");
|
|
539
|
+
const computer = yield* Effect.promise(() => import("@cloudflare/computer"));
|
|
540
|
+
const client = yield* Effect.acquireRelease(Effect.promise(() => computer.getWorkspace(host)), (workspace) => Effect.sync(() => workspace[Symbol.dispose]()));
|
|
541
|
+
return ComputerWorkspace.of(fromWorkspaceClient(client));
|
|
542
|
+
}));
|
|
543
|
+
};
|
|
544
|
+
//#endregion
|
|
545
|
+
export { ComputerWorkspace, WorkspaceAssetsError, WorkspaceExecError, WorkspaceFsError, WorkspaceGitError, fromWorkspaceClient };
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { t as ArtifactsBinding } from "./Artifacts-DcoXcRP5.mjs";
|
|
2
|
+
import { GitClientFactory } from "@cloudflare/computer/git";
|
|
3
|
+
import { DurableObjectStorageLike, SyncRetryIntent, SyncRetryOptions, SyncRetryScheduler, WithWorkspaceCtor, WorkspaceAttributeValue, WorkspaceAttributes, WorkspaceObserver, WorkspaceOptions, WorkspaceRegisteredBackend, WorkspaceRetryPendingSyncResult, WorkspaceServiceProxy, WorkspaceSpan } from "@cloudflare/computer";
|
|
4
|
+
import { ShellModuleGroup, WorkerShellBackendOptions, WorkerShellLoader, WorkspaceEgressPolicy } from "@cloudflare/computer/backends/worker-shell";
|
|
5
|
+
//#region src/ComputerWorkspaceHost.d.ts
|
|
6
|
+
/** The value of a `worker_loaders` binding. */
|
|
7
|
+
type DynamicWorkerLoader = WorkerShellLoader;
|
|
8
|
+
/** Default author and committer identity for workspace Git operations. */
|
|
9
|
+
interface ComputerWorkspaceGitIdentity {
|
|
10
|
+
readonly name: string;
|
|
11
|
+
readonly email: string;
|
|
12
|
+
}
|
|
13
|
+
/** Session-scoped Artifacts configuration passed through to `Workspace`. */
|
|
14
|
+
interface ComputerWorkspaceArtifactsOptions {
|
|
15
|
+
readonly binding: ArtifactsBinding;
|
|
16
|
+
/**
|
|
17
|
+
* Session prefix for repository names. When omitted, `sessionId` from the
|
|
18
|
+
* enclosing host configuration is used.
|
|
19
|
+
*/
|
|
20
|
+
readonly sessionId?: string;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Worker-shell backend options with the loopback workspace wiring named in
|
|
24
|
+
* Durable Object terms.
|
|
25
|
+
*/
|
|
26
|
+
interface ComputerWorkspaceShellOptions extends Omit<WorkerShellBackendOptions, "source" | "loader" | "workspace" | "ctx" | "egress"> {
|
|
27
|
+
readonly loader: DynamicWorkerLoader;
|
|
28
|
+
/** Durable Object namespace binding that points back to the wrapped class. */
|
|
29
|
+
readonly hostBinding: string;
|
|
30
|
+
/** Current Durable Object id, normally `ctx.id.toString()`. */
|
|
31
|
+
readonly hostId: string;
|
|
32
|
+
/** Current Durable Object execution context, normally `ctx`. */
|
|
33
|
+
readonly executionContext: unknown;
|
|
34
|
+
/** Dynamic Worker egress policy. Defaults to `{ mode: "none" }`. */
|
|
35
|
+
readonly egress?: WorkspaceEgressPolicy;
|
|
36
|
+
}
|
|
37
|
+
/** Configuration accepted by {@link withComputerWorkspace}. */
|
|
38
|
+
interface ComputerWorkspaceHostConfig {
|
|
39
|
+
/** Native SQLite-backed Durable Object storage (`ctx.storage`). */
|
|
40
|
+
readonly storage: globalThis.DurableObjectStorage | DurableObjectStorageLike;
|
|
41
|
+
/** Stable workspace id used by mounts and as the default Artifacts session. */
|
|
42
|
+
readonly sessionId?: string;
|
|
43
|
+
readonly gitIdentity?: ComputerWorkspaceGitIdentity;
|
|
44
|
+
/** Override the default `createGitClient()` factory. */
|
|
45
|
+
readonly git?: GitClientFactory;
|
|
46
|
+
/** Additional container, Worker JavaScript, shell, or custom backends. */
|
|
47
|
+
readonly backends?: ReadonlyArray<WorkspaceRegisteredBackend>;
|
|
48
|
+
/** Convenience wiring for one Worker Shell backend. */
|
|
49
|
+
readonly shell?: ComputerWorkspaceShellOptions;
|
|
50
|
+
readonly mounts?: WorkspaceOptions["mounts"];
|
|
51
|
+
readonly observer?: WorkspaceOptions["observer"];
|
|
52
|
+
readonly retryScheduler?: WorkspaceOptions["retryScheduler"];
|
|
53
|
+
readonly retry?: WorkspaceOptions["retry"];
|
|
54
|
+
readonly assets?: WorkspaceOptions["assets"];
|
|
55
|
+
readonly artifacts?: ComputerWorkspaceArtifactsOptions;
|
|
56
|
+
readonly now?: WorkspaceOptions["now"];
|
|
57
|
+
readonly useThink?: WorkspaceOptions["useThink"];
|
|
58
|
+
}
|
|
59
|
+
type DurableObjectConstructor = new (...args: Array<any>) => object;
|
|
60
|
+
/**
|
|
61
|
+
* Wraps a Durable Object class with one SQLite-backed Cloudflare Computer
|
|
62
|
+
* workspace and registers that local workspace for {@link ComputerWorkspace}
|
|
63
|
+
* to acquire through its Effect layer.
|
|
64
|
+
*
|
|
65
|
+
* The class must use a `new_sqlite_classes` migration. If `shell` is present,
|
|
66
|
+
* its Worker must also configure a `worker_loaders` binding, enable the
|
|
67
|
+
* `experimental` compatibility flag, and export `WorkspaceServiceProxy`.
|
|
68
|
+
*/
|
|
69
|
+
declare const withComputerWorkspace: <TBase extends DurableObjectConstructor>(Base: TBase, config: (self: InstanceType<TBase>, state: globalThis.DurableObjectState, env: ConstructorParameters<TBase>[1]) => ComputerWorkspaceHostConfig) => WithWorkspaceCtor<TBase>;
|
|
70
|
+
//#endregion
|
|
71
|
+
export { ComputerWorkspaceArtifactsOptions, ComputerWorkspaceGitIdentity, ComputerWorkspaceHostConfig, ComputerWorkspaceShellOptions, DynamicWorkerLoader, type ShellModuleGroup, type SyncRetryIntent, type SyncRetryOptions, type SyncRetryScheduler, type WorkspaceAttributeValue, type WorkspaceAttributes, type WorkspaceEgressPolicy, type WorkspaceObserver, type WorkspaceRetryPendingSyncResult, WorkspaceServiceProxy, type WorkspaceSpan, withComputerWorkspace };
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { n as register } from "./ComputerWorkspaceHostRegistry-CY5Fv4Ng.mjs";
|
|
2
|
+
import { createGitClient } from "@cloudflare/computer/git";
|
|
3
|
+
import { WorkspaceServiceProxy, withWorkspace } from "@cloudflare/computer";
|
|
4
|
+
import { WorkerShellBackend } from "@cloudflare/computer/backends/worker-shell";
|
|
5
|
+
//#region src/ComputerWorkspaceHost.ts
|
|
6
|
+
const toWorkspaceOptions = (config) => {
|
|
7
|
+
const backends = [...config.backends ?? []];
|
|
8
|
+
if (config.shell !== void 0) {
|
|
9
|
+
const { egress, executionContext, hostBinding, hostId, loader, ...shellOptions } = config.shell;
|
|
10
|
+
backends.push(new WorkerShellBackend({
|
|
11
|
+
...shellOptions,
|
|
12
|
+
loader,
|
|
13
|
+
workspace: {
|
|
14
|
+
binding: hostBinding,
|
|
15
|
+
id: hostId
|
|
16
|
+
},
|
|
17
|
+
ctx: executionContext,
|
|
18
|
+
egress: egress ?? { mode: "none" }
|
|
19
|
+
}));
|
|
20
|
+
}
|
|
21
|
+
return {
|
|
22
|
+
storage: config.storage,
|
|
23
|
+
git: config.git ?? createGitClient(),
|
|
24
|
+
...config.gitIdentity === void 0 ? {} : { defaultGitIdentity: config.gitIdentity },
|
|
25
|
+
...backends.length === 0 ? {} : { backends },
|
|
26
|
+
...config.sessionId === void 0 ? {} : { sessionId: config.sessionId },
|
|
27
|
+
...config.mounts === void 0 ? {} : { mounts: config.mounts },
|
|
28
|
+
...config.observer === void 0 ? {} : { observer: config.observer },
|
|
29
|
+
...config.retryScheduler === void 0 ? {} : { retryScheduler: config.retryScheduler },
|
|
30
|
+
...config.retry === void 0 ? {} : { retry: config.retry },
|
|
31
|
+
...config.assets === void 0 ? {} : { assets: config.assets },
|
|
32
|
+
...config.artifacts === void 0 ? {} : { artifacts: {
|
|
33
|
+
binding: config.artifacts.binding,
|
|
34
|
+
...config.artifacts.sessionId === void 0 ? {} : { sessionId: config.artifacts.sessionId }
|
|
35
|
+
} },
|
|
36
|
+
...config.now === void 0 ? {} : { now: config.now },
|
|
37
|
+
...config.useThink === void 0 ? {} : { useThink: config.useThink }
|
|
38
|
+
};
|
|
39
|
+
};
|
|
40
|
+
const HostArguments = Symbol("effect-cf/ComputerWorkspaceHost/arguments");
|
|
41
|
+
/**
|
|
42
|
+
* Wraps a Durable Object class with one SQLite-backed Cloudflare Computer
|
|
43
|
+
* workspace and registers that local workspace for {@link ComputerWorkspace}
|
|
44
|
+
* to acquire through its Effect layer.
|
|
45
|
+
*
|
|
46
|
+
* The class must use a `new_sqlite_classes` migration. If `shell` is present,
|
|
47
|
+
* its Worker must also configure a `worker_loaders` binding, enable the
|
|
48
|
+
* `experimental` compatibility flag, and export `WorkspaceServiceProxy`.
|
|
49
|
+
*/
|
|
50
|
+
const withComputerWorkspace = (Base, config) => {
|
|
51
|
+
const CapturedBase = class extends Base {
|
|
52
|
+
[HostArguments];
|
|
53
|
+
constructor(...args) {
|
|
54
|
+
super(...args);
|
|
55
|
+
this[HostArguments] = {
|
|
56
|
+
state: args[0],
|
|
57
|
+
env: args[1]
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
const WithWorkspace = withWorkspace(CapturedBase, (self) => {
|
|
62
|
+
const { env, state } = self[HostArguments];
|
|
63
|
+
return toWorkspaceOptions(config(self, state, env));
|
|
64
|
+
});
|
|
65
|
+
class WithComputerWorkspace extends WithWorkspace {
|
|
66
|
+
constructor(...args) {
|
|
67
|
+
super(...args);
|
|
68
|
+
register(args[0], this);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return WithComputerWorkspace;
|
|
72
|
+
};
|
|
73
|
+
//#endregion
|
|
74
|
+
export { WorkspaceServiceProxy, withComputerWorkspace };
|