@fungi.computer/computer 0.1.0 → 0.2.1
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/README.md +38 -10
- package/dist/computer-backends.d.ts +0 -1
- package/dist/computer-backends.js +0 -3
- package/dist/exec-output.d.ts +13 -0
- package/dist/exec-output.js +24 -0
- package/dist/execution-lifecycle.d.ts +35 -0
- package/dist/execution-lifecycle.js +78 -0
- package/dist/files-patch.d.ts +12 -4
- package/dist/files-patch.js +8 -14
- package/dist/files.d.ts +65 -15
- package/dist/files.js +164 -31
- package/dist/fleet/index.d.ts +1 -1
- package/dist/fleet/index.js +1 -1
- package/dist/fleet/inventory-wire.d.ts +6 -2
- package/dist/fleet/inventory-wire.js +7 -2
- package/dist/fleet/lifecycle.d.ts +7 -1
- package/dist/fleet/lifecycle.js +22 -0
- package/dist/fleet/wire.d.ts +12 -12
- package/dist/fleet/wire.js +1 -1
- package/dist/index.d.ts +33 -15
- package/dist/index.js +422 -105
- package/dist/internet-access.d.ts +4 -0
- package/dist/internet-access.js +14 -0
- package/dist/local-backend-replay.d.ts +10 -0
- package/dist/local-backend-replay.js +43 -0
- package/dist/local-backend.d.ts +2 -1
- package/dist/local-backend.js +4 -4
- package/dist/on-box-vm.d.ts +11 -0
- package/dist/on-box-vm.js +36 -13
- package/dist/operation-record.d.ts +1 -1
- package/dist/operation-record.js +1 -1
- package/dist/process-inventory.d.ts +3 -1
- package/dist/process-inventory.js +2 -2
- package/dist/team-candidate-builder.mjs +8375 -334
- package/package.json +17 -24
- package/dist/light-shell.d.ts +0 -44
- package/dist/light-shell.js +0 -376
- package/dist/operation-contract.d.ts +0 -456
- package/dist/operation-contract.js +0 -450
package/dist/files.js
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { posix } from "node:path";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { SearchVisibility } from "@fungi.computer/knapsack/files-search";
|
|
4
|
+
import { imageReadResult, imageSniffBytes, imageTooLargeMessage, maximumImageBytes, sniffImageMimeType, } from "@fungi.computer/knapsack/files-read";
|
|
5
|
+
import { replaceOutputSchema, } from "@fungi.computer/computer-contract";
|
|
2
6
|
const utf8 = new TextEncoder();
|
|
3
7
|
function entryType(entry) {
|
|
4
8
|
if (entry.isSymbolicLink)
|
|
@@ -52,7 +56,7 @@ export function readBoundedWorkspaceText(provider, path, maximumBytes) {
|
|
|
52
56
|
}
|
|
53
57
|
/** Adapt native Workspace.fs at the one real file boundary. */
|
|
54
58
|
export function createWorkspaceFilesPort(workspace, owner) {
|
|
55
|
-
|
|
59
|
+
const port = {
|
|
56
60
|
async read(input) {
|
|
57
61
|
const stat = await workspace.fs.stat(input.path);
|
|
58
62
|
if (!stat.isFile)
|
|
@@ -69,10 +73,22 @@ export function createWorkspaceFilesPort(workspace, owner) {
|
|
|
69
73
|
truncated: false,
|
|
70
74
|
};
|
|
71
75
|
}
|
|
72
|
-
|
|
73
|
-
|
|
76
|
+
// The first read covers the sniff window whatever the caller's limit.
|
|
77
|
+
const windowBytes = await readBytes(workspace, input.path, input.offset, Math.min(input.offset === 0
|
|
78
|
+
? Math.max(input.limit, imageSniffBytes)
|
|
79
|
+
: input.limit, stat.size - input.offset));
|
|
80
|
+
if (windowBytes.byteLength === 0) {
|
|
74
81
|
throw new Error(`Files path changed during read: ${input.path}`);
|
|
75
82
|
}
|
|
83
|
+
const mimeType = input.offset === 0 ? sniffImageMimeType(windowBytes) : undefined;
|
|
84
|
+
if (mimeType !== undefined) {
|
|
85
|
+
if (stat.size > maximumImageBytes)
|
|
86
|
+
throw new Error(imageTooLargeMessage(input.path, stat.size, maximumImageBytes));
|
|
87
|
+
return imageReadResult(input.path, mimeType, windowBytes.byteLength === stat.size
|
|
88
|
+
? windowBytes
|
|
89
|
+
: await readBytes(workspace, input.path, 0, stat.size));
|
|
90
|
+
}
|
|
91
|
+
const pageBytes = windowBytes.subarray(0, input.limit);
|
|
76
92
|
const page = decodeUtf8Page(pageBytes, input.path, input.offset + pageBytes.byteLength < stat.size);
|
|
77
93
|
const nextOffset = input.offset + page.byteLength;
|
|
78
94
|
return {
|
|
@@ -83,54 +99,171 @@ export function createWorkspaceFilesPort(workspace, owner) {
|
|
|
83
99
|
truncated: nextOffset < stat.size,
|
|
84
100
|
};
|
|
85
101
|
},
|
|
86
|
-
async list(input) {
|
|
87
|
-
const
|
|
88
|
-
limit: input.limit + 1,
|
|
89
|
-
});
|
|
90
|
-
const entries = rows.slice(0, input.limit).map((entry) => {
|
|
91
|
-
const path = childPath(input.path, entry.name);
|
|
92
|
-
return {
|
|
93
|
-
path,
|
|
94
|
-
name: entry.name,
|
|
95
|
-
type: entryType(entry),
|
|
96
|
-
size: entry.size,
|
|
97
|
-
modifiedAt: entry.mtime,
|
|
98
|
-
};
|
|
99
|
-
});
|
|
102
|
+
async list(input, context) {
|
|
103
|
+
const output = await searchWorkspaceDirectory(workspace, { ...input, offset: 0 }, context?.signal);
|
|
100
104
|
return {
|
|
101
105
|
path: input.path,
|
|
102
|
-
|
|
103
|
-
|
|
106
|
+
...output,
|
|
107
|
+
results: output.results.map((entry) => ({
|
|
108
|
+
path: entry.path,
|
|
109
|
+
type: entryType(entry),
|
|
110
|
+
})),
|
|
104
111
|
};
|
|
105
112
|
},
|
|
106
|
-
async glob(input) {
|
|
107
|
-
const
|
|
108
|
-
limit: input.limit + 1,
|
|
109
|
-
});
|
|
113
|
+
async glob(input, context) {
|
|
114
|
+
const output = await visibleSearch(workspace, workspacePath(input.path), input.limit, (options) => workspace.fs.find(workspacePath(input.path), input.pattern, options), (entry) => entry.type === "dir", context?.signal);
|
|
110
115
|
return {
|
|
111
116
|
path: input.path,
|
|
112
|
-
|
|
113
|
-
|
|
117
|
+
...output,
|
|
118
|
+
results: output.results.map((row) => ({
|
|
119
|
+
path: row.path,
|
|
120
|
+
type: row.type === "dir" ? "directory" : "file",
|
|
121
|
+
})),
|
|
114
122
|
};
|
|
115
123
|
},
|
|
116
|
-
async grep(input) {
|
|
117
|
-
const
|
|
124
|
+
async grep(input, context) {
|
|
125
|
+
const options = {
|
|
118
126
|
regex: input.regex,
|
|
119
127
|
ignoreCase: input.ignoreCase,
|
|
120
128
|
limit: input.limit + 1,
|
|
121
|
-
}
|
|
129
|
+
};
|
|
130
|
+
if (input.include !== undefined)
|
|
131
|
+
options.include = input.include;
|
|
132
|
+
const output = await visibleSearch(workspace, workspacePath(input.path), input.limit, (page) => workspace.fs.grep(input.pattern, workspacePath(input.path), {
|
|
133
|
+
...options,
|
|
134
|
+
...page,
|
|
135
|
+
}), () => false, context?.signal);
|
|
122
136
|
return {
|
|
123
137
|
path: input.path,
|
|
124
|
-
|
|
138
|
+
...output,
|
|
139
|
+
results: output.results.map((row) => ({
|
|
125
140
|
path: row.path,
|
|
126
141
|
line: row.line,
|
|
127
142
|
text: row.text,
|
|
128
143
|
})),
|
|
129
|
-
truncated: rows.length > input.limit,
|
|
130
144
|
};
|
|
131
145
|
},
|
|
132
146
|
async replace(input) {
|
|
133
147
|
return replaceOutputSchema.parse(await owner.replaceFileIfUnchanged(input));
|
|
134
148
|
},
|
|
149
|
+
};
|
|
150
|
+
return Object.freeze({
|
|
151
|
+
...port,
|
|
152
|
+
// The Computer REST contract keeps its own path and content searches for
|
|
153
|
+
// Apps and Hub routes; its matches/boolean results are not Agent results.
|
|
154
|
+
rest: Object.freeze({
|
|
155
|
+
list: (input) => listWorkspaceFiles(workspace, input),
|
|
156
|
+
async glob(input) {
|
|
157
|
+
const output = await port.glob(input);
|
|
158
|
+
return {
|
|
159
|
+
path: input.path,
|
|
160
|
+
matches: output.results.map((entry) => entry.path),
|
|
161
|
+
truncated: output.truncated !== null,
|
|
162
|
+
};
|
|
163
|
+
},
|
|
164
|
+
async grep(input) {
|
|
165
|
+
const output = await port.grep(input);
|
|
166
|
+
return {
|
|
167
|
+
path: input.path,
|
|
168
|
+
matches: output.results,
|
|
169
|
+
truncated: output.truncated !== null,
|
|
170
|
+
};
|
|
171
|
+
},
|
|
172
|
+
}),
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
function searchWorkspaceDirectory(workspace, input, signal) {
|
|
176
|
+
const path = workspacePath(input.path);
|
|
177
|
+
return visibleSearch(workspace, path, input.limit, async (options) => (await workspace.fs.readdir(path, options)).map((entry) => ({
|
|
178
|
+
...entry,
|
|
179
|
+
path: childPath(path, entry.name),
|
|
180
|
+
})), (entry) => entry.isDirectory, signal, input.offset);
|
|
181
|
+
}
|
|
182
|
+
/** Shared REST listing: offsets count visible entries, never ignored candidates. */
|
|
183
|
+
export async function listWorkspaceFiles(workspace, input) {
|
|
184
|
+
const output = await searchWorkspaceDirectory(workspace, input);
|
|
185
|
+
// The existing list wire has no scan-reason field. Do not disguise an
|
|
186
|
+
// exhausted scan as an empty finished page or a fabricated next offset.
|
|
187
|
+
if (output.truncated === "scan")
|
|
188
|
+
throw new Error("Files listing reached the search scan bound; narrow path");
|
|
189
|
+
return {
|
|
190
|
+
path: input.path,
|
|
191
|
+
entries: output.results.map((entry) => ({
|
|
192
|
+
name: entry.name,
|
|
193
|
+
size: entry.size,
|
|
194
|
+
modifiedAt: entry.mtime,
|
|
195
|
+
type: entryType(entry),
|
|
196
|
+
})),
|
|
197
|
+
nextOffset: output.truncated === null ? null : input.offset + output.results.length,
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
/** Computer wire paths are absolute within its Workspace, even for relative Files inputs. */
|
|
201
|
+
function workspacePath(path) {
|
|
202
|
+
if (path === ".")
|
|
203
|
+
return "/";
|
|
204
|
+
return path.startsWith("/") ? path : `/${path}`;
|
|
205
|
+
}
|
|
206
|
+
const missingSchema = z.object({ code: z.literal("ENOENT") });
|
|
207
|
+
/** A regular file's stat, or null when the path is absent or not a file. */
|
|
208
|
+
async function fileStat(workspace, path) {
|
|
209
|
+
try {
|
|
210
|
+
const stat = await workspace.fs.stat(path);
|
|
211
|
+
return stat.isFile ? stat : null;
|
|
212
|
+
}
|
|
213
|
+
catch (error) {
|
|
214
|
+
if (missingSchema.safeParse(error).success)
|
|
215
|
+
return null;
|
|
216
|
+
throw error;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
/** Storage adapter only; shared Files policy owns visibility and grammar. */
|
|
220
|
+
function workspaceSearchVisibility(workspace) {
|
|
221
|
+
let bytes = 0;
|
|
222
|
+
return new SearchVisibility(async (directory) => {
|
|
223
|
+
const path = posix.join("/", directory, ".gitignore");
|
|
224
|
+
const stat = await fileStat(workspace, path);
|
|
225
|
+
if (stat !== null) {
|
|
226
|
+
// Match Local's bounded text reads; never silently omit unreadable rules.
|
|
227
|
+
if (stat.size > 128 * 1_024 || bytes + stat.size > 64 * 1_024 * 1_024)
|
|
228
|
+
throw new Error(`Files ignore rules exceed the search byte bound: ${path}`);
|
|
229
|
+
bytes += stat.size;
|
|
230
|
+
return decodeUtf8(await readBytes(workspace, path, 0, stat.size), path);
|
|
231
|
+
}
|
|
232
|
+
return undefined;
|
|
135
233
|
});
|
|
136
234
|
}
|
|
235
|
+
function searchRelativePath(path) {
|
|
236
|
+
return posix.relative("/", posix.resolve("/", path));
|
|
237
|
+
}
|
|
238
|
+
/** Page the SDK's traversal; filtering must not spend the visible-result limit. */
|
|
239
|
+
async function visibleSearch(workspace, root, limit, page, isDirectory, signal, visibleOffset = 0) {
|
|
240
|
+
const rules = workspaceSearchVisibility(workspace);
|
|
241
|
+
const results = [];
|
|
242
|
+
signal?.throwIfAborted();
|
|
243
|
+
const metadata = await workspace.fs.stat(root);
|
|
244
|
+
if (!(await rules.visible(searchRelativePath(root), metadata.isDirectory)))
|
|
245
|
+
return { results, truncated: null };
|
|
246
|
+
const maximumCandidates = 50_000;
|
|
247
|
+
let offset = 0;
|
|
248
|
+
while (offset < maximumCandidates) {
|
|
249
|
+
signal?.throwIfAborted();
|
|
250
|
+
const count = Math.min(offset === 0 ? limit + 1 : Math.max(128, limit + 1), maximumCandidates - offset);
|
|
251
|
+
const rows = await page(offset === 0 ? { limit: count } : { limit: count, offset });
|
|
252
|
+
for (const row of rows) {
|
|
253
|
+
signal?.throwIfAborted();
|
|
254
|
+
if (!(await rules.visible(searchRelativePath(row.path), isDirectory(row))))
|
|
255
|
+
continue;
|
|
256
|
+
if (visibleOffset > 0) {
|
|
257
|
+
visibleOffset--;
|
|
258
|
+
continue;
|
|
259
|
+
}
|
|
260
|
+
if (results.length === limit)
|
|
261
|
+
return { results, truncated: "limit" };
|
|
262
|
+
results.push(row);
|
|
263
|
+
}
|
|
264
|
+
if (rows.length < count)
|
|
265
|
+
return { results, truncated: null };
|
|
266
|
+
offset += rows.length;
|
|
267
|
+
}
|
|
268
|
+
return { results, truncated: "scan" };
|
|
269
|
+
}
|
package/dist/fleet/index.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ export { computerActionSchema, computerProvenanceSchema, computerGrantSchema, co
|
|
|
5
5
|
export { ComputerRuntimeUsage } from "./runtime-usage.js";
|
|
6
6
|
export { computerUsageIntervalSchema, type ComputerUsageInterval, } from "./wire.js";
|
|
7
7
|
export { createFleetInventory } from "./inventory.js";
|
|
8
|
-
export { ComputerInventoryError, type FleetActor, type FleetComputer, } from "./inventory-wire.js";
|
|
8
|
+
export { ComputerInventoryError, computerCreateRequestSchema, computerPageSchema, type ComputerCreateRequest, type ComputerPage, type FleetActor, type FleetComputer, } from "./inventory-wire.js";
|
|
9
9
|
export { createFleetSlots } from "./slots.js";
|
|
10
10
|
export { FleetLifecycle, FleetGrantRefused, type FleetLifecyclePorts, type FleetLifecycleFact, } from "./lifecycle.js";
|
|
11
11
|
export { createFleetComputers } from "./computers.js";
|
package/dist/fleet/index.js
CHANGED
|
@@ -5,7 +5,7 @@ export { computerActionSchema, computerProvenanceSchema, computerGrantSchema, co
|
|
|
5
5
|
export { ComputerRuntimeUsage } from "./runtime-usage.js";
|
|
6
6
|
export { computerUsageIntervalSchema, } from "./wire.js";
|
|
7
7
|
export { createFleetInventory } from "./inventory.js";
|
|
8
|
-
export { ComputerInventoryError, } from "./inventory-wire.js";
|
|
8
|
+
export { ComputerInventoryError, computerCreateRequestSchema, computerPageSchema, } from "./inventory-wire.js";
|
|
9
9
|
export { createFleetSlots } from "./slots.js";
|
|
10
10
|
export { FleetLifecycle, FleetGrantRefused, } from "./lifecycle.js";
|
|
11
11
|
export { createFleetComputers } from "./computers.js";
|
|
@@ -12,15 +12,19 @@ export type FleetComputer = Readonly<{
|
|
|
12
12
|
updatedAt: number;
|
|
13
13
|
}>;
|
|
14
14
|
/** A durable create receipt binds the complete request to its actor. */
|
|
15
|
+
export declare const computerCreateRequestSchema: z.ZodObject<{
|
|
16
|
+
name: z.ZodOptional<z.ZodString>;
|
|
17
|
+
requestId: z.ZodString;
|
|
18
|
+
}, z.core.$strict>;
|
|
15
19
|
export type ComputerCreateRequest = Readonly<{
|
|
16
20
|
name?: string | undefined;
|
|
17
21
|
requestId: string;
|
|
18
22
|
}>;
|
|
19
23
|
/** Inventory pages retain stable ordering and a bounded response. */
|
|
20
|
-
export declare const computerPageSchema: z.
|
|
24
|
+
export declare const computerPageSchema: z.ZodObject<{
|
|
21
25
|
limit: z.ZodDefault<z.ZodNumber>;
|
|
22
26
|
offset: z.ZodDefault<z.ZodNumber>;
|
|
23
|
-
}, z.core.$strict
|
|
27
|
+
}, z.core.$strict>;
|
|
24
28
|
export type ComputerPage = z.input<typeof computerPageSchema>;
|
|
25
29
|
/** Inventory failures, independent of a host's public error mapping. */
|
|
26
30
|
export declare class ComputerInventoryError extends Error {
|
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
/** A durable create receipt binds the complete request to its actor. */
|
|
3
|
+
export const computerCreateRequestSchema = z.strictObject({
|
|
4
|
+
name: z.string().min(1).max(256).optional(),
|
|
5
|
+
requestId: z.string().min(1).max(256),
|
|
6
|
+
});
|
|
2
7
|
/** Inventory pages retain stable ordering and a bounded response. */
|
|
3
|
-
export const computerPageSchema = z.
|
|
8
|
+
export const computerPageSchema = z.strictObject({
|
|
4
9
|
limit: z.number().int().min(1).max(100).default(100),
|
|
5
10
|
offset: z.number().int().nonnegative().default(0),
|
|
6
|
-
})
|
|
11
|
+
});
|
|
7
12
|
/** Inventory failures, independent of a host's public error mapping. */
|
|
8
13
|
export class ComputerInventoryError extends Error {
|
|
9
14
|
code;
|
|
@@ -53,10 +53,16 @@ export declare class FleetLifecycle {
|
|
|
53
53
|
readonly usage: ComputerRuntimeUsage;
|
|
54
54
|
constructor(ports: FleetLifecyclePorts);
|
|
55
55
|
get recycleRuntimeId(): string | undefined;
|
|
56
|
+
/** A successful stop cannot race an admitted boot or in-flight operation. */
|
|
57
|
+
get hasPendingOperations(): boolean;
|
|
56
58
|
takeFacts(): FleetLifecycleFact[];
|
|
57
59
|
private diagnostic;
|
|
58
60
|
/** Keep pending starts visible while the host obtains admission and capacity. */
|
|
59
|
-
beginHostedStart
|
|
61
|
+
private beginHostedStart;
|
|
62
|
+
/** Start the selected hosted backend under the host's admission and slot policy. */
|
|
63
|
+
startHostedComputer(admitted: (backing: HostedBacking, run: () => Promise<void>) => Promise<void>): Promise<{
|
|
64
|
+
state: "running";
|
|
65
|
+
}>;
|
|
60
66
|
/** Read liveness without waking a sleeping backend. Queue state wins over booting. */
|
|
61
67
|
readComputerState(): Promise<{
|
|
62
68
|
state: "queued";
|
package/dist/fleet/lifecycle.js
CHANGED
|
@@ -40,6 +40,12 @@ export class FleetLifecycle {
|
|
|
40
40
|
get recycleRuntimeId() {
|
|
41
41
|
return this.#recycleRuntimeId;
|
|
42
42
|
}
|
|
43
|
+
/** A successful stop cannot race an admitted boot or in-flight operation. */
|
|
44
|
+
get hasPendingOperations() {
|
|
45
|
+
return (this.#hostedOperations > 0 ||
|
|
46
|
+
this.#startingHosted > 0 ||
|
|
47
|
+
this.#recycling !== undefined);
|
|
48
|
+
}
|
|
43
49
|
takeFacts() {
|
|
44
50
|
const facts = this.#facts;
|
|
45
51
|
this.#facts = [];
|
|
@@ -55,6 +61,22 @@ export class FleetLifecycle {
|
|
|
55
61
|
this.#startingHosted--;
|
|
56
62
|
};
|
|
57
63
|
}
|
|
64
|
+
/** Start the selected hosted backend under the host's admission and slot policy. */
|
|
65
|
+
async startHostedComputer(admitted) {
|
|
66
|
+
const hosted = this.ports.backend.requireHostedRoute();
|
|
67
|
+
const finish = this.beginHostedStart();
|
|
68
|
+
try {
|
|
69
|
+
await admitted(hosted.backing, async () => {
|
|
70
|
+
await this.ports.backend.workspace().push(hosted.id);
|
|
71
|
+
});
|
|
72
|
+
if (!(await this.ports.backend.containerRunning(hosted.backing)))
|
|
73
|
+
throw new Error("Hosted Computer disconnected while starting");
|
|
74
|
+
return { state: "running" };
|
|
75
|
+
}
|
|
76
|
+
finally {
|
|
77
|
+
finish();
|
|
78
|
+
}
|
|
79
|
+
}
|
|
58
80
|
/** Read liveness without waking a sleeping backend. Queue state wins over booting. */
|
|
59
81
|
async readComputerState() {
|
|
60
82
|
const backends = await this.readAttachedBackends();
|
package/dist/fleet/wire.d.ts
CHANGED
|
@@ -5,6 +5,12 @@ export declare const computerActionSchema: z.ZodEnum<{
|
|
|
5
5
|
exec: "exec";
|
|
6
6
|
start: "start";
|
|
7
7
|
delete: "delete";
|
|
8
|
+
mkdir: "mkdir";
|
|
9
|
+
backing: "backing";
|
|
10
|
+
bash: "bash";
|
|
11
|
+
exposePort: "exposePort";
|
|
12
|
+
listPorts: "listPorts";
|
|
13
|
+
unexposePort: "unexposePort";
|
|
8
14
|
"files.read": "files.read";
|
|
9
15
|
"files.readBytes": "files.readBytes";
|
|
10
16
|
"files.write": "files.write";
|
|
@@ -14,12 +20,6 @@ export declare const computerActionSchema: z.ZodEnum<{
|
|
|
14
20
|
"files.grep": "files.grep";
|
|
15
21
|
"files.edit": "files.edit";
|
|
16
22
|
"files.patch": "files.patch";
|
|
17
|
-
mkdir: "mkdir";
|
|
18
|
-
bash: "bash";
|
|
19
|
-
backing: "backing";
|
|
20
|
-
exposePort: "exposePort";
|
|
21
|
-
listPorts: "listPorts";
|
|
22
|
-
unexposePort: "unexposePort";
|
|
23
23
|
terminal: "terminal";
|
|
24
24
|
stop: "stop";
|
|
25
25
|
processStart: "processStart";
|
|
@@ -40,6 +40,12 @@ export declare const computerGrantSchema: z.ZodObject<{
|
|
|
40
40
|
exec: "exec";
|
|
41
41
|
start: "start";
|
|
42
42
|
delete: "delete";
|
|
43
|
+
mkdir: "mkdir";
|
|
44
|
+
backing: "backing";
|
|
45
|
+
bash: "bash";
|
|
46
|
+
exposePort: "exposePort";
|
|
47
|
+
listPorts: "listPorts";
|
|
48
|
+
unexposePort: "unexposePort";
|
|
43
49
|
"files.read": "files.read";
|
|
44
50
|
"files.readBytes": "files.readBytes";
|
|
45
51
|
"files.write": "files.write";
|
|
@@ -49,12 +55,6 @@ export declare const computerGrantSchema: z.ZodObject<{
|
|
|
49
55
|
"files.grep": "files.grep";
|
|
50
56
|
"files.edit": "files.edit";
|
|
51
57
|
"files.patch": "files.patch";
|
|
52
|
-
mkdir: "mkdir";
|
|
53
|
-
bash: "bash";
|
|
54
|
-
backing: "backing";
|
|
55
|
-
exposePort: "exposePort";
|
|
56
|
-
listPorts: "listPorts";
|
|
57
|
-
unexposePort: "unexposePort";
|
|
58
58
|
terminal: "terminal";
|
|
59
59
|
stop: "stop";
|
|
60
60
|
processStart: "processStart";
|
package/dist/fleet/wire.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { computerOperationSchema, computerValueSchema, pathSchema, } from "
|
|
2
|
+
import { computerOperationSchema, computerValueSchema, pathSchema, } from "@fungi.computer/computer-contract";
|
|
3
3
|
import { attachedBackendId, hostedBackingSchema, localBackendSchema, } from "../computer-backends.js";
|
|
4
4
|
/** Operations the host may admit on one Computer. */
|
|
5
5
|
export const computerActionSchema = z.enum([
|
package/dist/index.d.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
+
export { createLocalBackendReplayStore } from "./local-backend-replay.js";
|
|
1
2
|
import { Workspace, WorkspaceProxy, WorkspaceServiceProxy, type WorkspaceStubHost } from "@fungi.computer/cloudflare-computer";
|
|
2
3
|
import { type WorkerShellBackendOptions } from "@fungi.computer/cloudflare-computer/backends/worker-shell";
|
|
3
4
|
import { type IWorkspaceContainerAPI } from "@fungi.computer/cloudflare-computer/backends/container";
|
|
4
5
|
import { DurableObject } from "cloudflare:workers";
|
|
5
6
|
import type { FilesPort } from "@fungi.computer/knapsack";
|
|
6
7
|
import { z } from "zod";
|
|
7
|
-
import { type ComputerExecutionBudget, type ComputerExecRequest, type ComputerOperationResult, type ComputerFileReadInput, type ComputerFileByteReadInput, type ComputerFileReplacement, type ComputerFileReplacementResult, type ComputerValue, writeInputSchema, writeOutputSchema, type ComputerProcess } from "
|
|
8
|
+
import { type ComputerExecutionBudget, type ComputerExecutionHostHooks, type ComputerExecRequest, type ComputerOperationResult, type ComputerFileReadInput, type ComputerFileByteReadInput, type ComputerFileReplacement, type ComputerFileReplacementResult, type ComputerValue, writeInputSchema, writeOutputSchema, type ComputerProcess } from "@fungi.computer/computer-contract";
|
|
8
9
|
import { createWorkspaceFilesPort } from "./files.js";
|
|
9
10
|
import { OnBoxVmContainerHost, type OnBoxVmEnv, type OnBoxVmPlacement } from "./on-box-vm.js";
|
|
10
11
|
import { type AttachedBackend, type AttachedBackendView, type BackendNeed, type BackendRoute, type ContainerRuntimeStart, type HostedBacking } from "./computer-backends.js";
|
|
@@ -89,7 +90,7 @@ declare class ContainerComputer extends ContainerComputerBase {
|
|
|
89
90
|
* The hosted backend that terminals, processes and ports use: the first in
|
|
90
91
|
* the default order, or none when only local machines are attached.
|
|
91
92
|
*/
|
|
92
|
-
protected hostedRoute(): Readonly<{
|
|
93
|
+
protected hostedRoute(backend?: string): Readonly<{
|
|
93
94
|
id: string;
|
|
94
95
|
backing: HostedBacking;
|
|
95
96
|
}> | undefined;
|
|
@@ -114,9 +115,8 @@ declare class ContainerComputer extends ContainerComputerBase {
|
|
|
114
115
|
protected workspace(): Workspace;
|
|
115
116
|
/**
|
|
116
117
|
* The current Workspace once its mount index is ready and its guest mount
|
|
117
|
-
* root exists. Every backend mounts `/workspace`, so
|
|
118
|
-
*
|
|
119
|
-
* on a guest having booted first.
|
|
118
|
+
* root exists. Every backend mounts `/workspace`, so an ordinary host file
|
|
119
|
+
* write there must not depend on a guest having booted first.
|
|
120
120
|
*/
|
|
121
121
|
protected readyWorkspace(): Promise<Workspace>;
|
|
122
122
|
/** `getWorkspace(stub)` reaches the current generation through this door. */
|
|
@@ -132,6 +132,16 @@ declare class ContainerComputer extends ContainerComputerBase {
|
|
|
132
132
|
/** One upstream Cloudflare Workspace that advertises and runs its capabilities. */
|
|
133
133
|
export declare class ComputerDO extends ContainerComputer {
|
|
134
134
|
#private;
|
|
135
|
+
constructor(ctx: DurableObjectState, env: ComputerEnv);
|
|
136
|
+
/** Host-admitted settings write; egress reads the current durable value. */
|
|
137
|
+
internetAccess(enabled?: boolean): boolean;
|
|
138
|
+
/** Ordinary Computers have no credential host; resource hosts override it. */
|
|
139
|
+
protected executionHooks(): ComputerExecutionHostHooks | undefined;
|
|
140
|
+
/**
|
|
141
|
+
* Admission observes the actual native subscriber, never App completion,
|
|
142
|
+
* output silence, an accepted signal or this isolate's warm state.
|
|
143
|
+
*/
|
|
144
|
+
executionIsLive(invocationId: string): Promise<boolean>;
|
|
135
145
|
/** Persist the bounded set of ports admitted for preview on this Computer. */
|
|
136
146
|
exposePort(input: {
|
|
137
147
|
port: number;
|
|
@@ -201,17 +211,25 @@ export declare class ComputerDO extends ContainerComputer {
|
|
|
201
211
|
protected applyPatchAfterAcceptance(input: {
|
|
202
212
|
patch: string;
|
|
203
213
|
}, accept: () => void): Promise<{
|
|
204
|
-
status: "
|
|
214
|
+
status: "failed" | "completed" | "cancelled";
|
|
205
215
|
exitCode: number;
|
|
206
216
|
stdout: string;
|
|
207
217
|
stderr: string;
|
|
208
|
-
truncated:
|
|
218
|
+
truncated: false | {
|
|
219
|
+
stream: "stdout" | "stderr";
|
|
220
|
+
keptBytes: number;
|
|
221
|
+
totalBytes: number;
|
|
222
|
+
}[];
|
|
209
223
|
} | {
|
|
210
|
-
status: "
|
|
224
|
+
status: "failed" | "completed" | "cancelled";
|
|
211
225
|
exitCode: number;
|
|
212
226
|
stdout: string;
|
|
213
227
|
stderr: string;
|
|
214
|
-
truncated:
|
|
228
|
+
truncated: false | {
|
|
229
|
+
stream: "stdout" | "stderr";
|
|
230
|
+
keptBytes: number;
|
|
231
|
+
totalBytes: number;
|
|
232
|
+
}[];
|
|
215
233
|
error: {
|
|
216
234
|
kind: "mismatch";
|
|
217
235
|
};
|
|
@@ -220,14 +238,15 @@ export declare class ComputerDO extends ContainerComputer {
|
|
|
220
238
|
protected writeTextAfterAcceptance(input: z.output<typeof writeInputSchema>, accept: () => void): Promise<z.output<typeof writeOutputSchema>>;
|
|
221
239
|
call(capabilityId: ComputerValue, input: ComputerValue): Promise<ComputerValue>;
|
|
222
240
|
/** Resource hosts may accept authority once the Workspace is ready to dispatch. */
|
|
223
|
-
protected callAfterAcceptance(capabilityId: ComputerValue, input: ComputerValue, accept: () => void): Promise<ComputerValue>;
|
|
241
|
+
protected callAfterAcceptance(capabilityId: ComputerValue, input: ComputerValue, accept: () => void, executionContext?: ComputerValue): Promise<ComputerValue>;
|
|
224
242
|
/**
|
|
225
243
|
* Execute a host request once, after the host accepts current authority.
|
|
226
244
|
* `exec` runs on the Workspace's built-in Worker shell unless it names an
|
|
227
245
|
* attached backend; `bash` always runs on an attached backend, the named one
|
|
228
|
-
* or the first available in the default order.
|
|
246
|
+
* or the first available in the default order. Host-only `hosted` execution
|
|
247
|
+
* excludes local backends, including an explicitly named local backend.
|
|
229
248
|
*/
|
|
230
|
-
protected executeOperationAfterAcceptance(input: ComputerExecRequest, accept: () => void, kind: "exec" | "bash", budget?: ComputerExecutionBudget, signal?: AbortSignal): Promise<ComputerOperationResult>;
|
|
249
|
+
protected executeOperationAfterAcceptance(input: ComputerExecRequest, accept: () => void, kind: "exec" | "bash" | "hosted", budget?: ComputerExecutionBudget, signal?: AbortSignal, executionContext?: ComputerValue): Promise<ComputerOperationResult>;
|
|
231
250
|
/**
|
|
232
251
|
* Run one command on a routed backend. Owners admit and meter hosted
|
|
233
252
|
* backends here; a member's own machine is neither reserved nor metered.
|
|
@@ -238,7 +257,7 @@ export declare class ComputerDO extends ContainerComputer {
|
|
|
238
257
|
/** Start one long-running container process with a durable identity. */
|
|
239
258
|
startProcess(input: ComputerValue): Promise<ComputerProcess>;
|
|
240
259
|
/** The host accepts authority when the running slot is reserved. */
|
|
241
|
-
protected startProcessAfterAcceptance(input: ComputerValue, accept: () => void): Promise<ComputerProcess>;
|
|
260
|
+
protected startProcessAfterAcceptance(input: ComputerValue, accept: () => void, executionContext?: ComputerValue): Promise<ComputerProcess>;
|
|
242
261
|
/** List this Computer's processes, reconciling live ones with computerd. */
|
|
243
262
|
listProcesses(): Promise<{
|
|
244
263
|
processes: ComputerProcess[];
|
|
@@ -270,13 +289,12 @@ export declare class ComputerDO extends ContainerComputer {
|
|
|
270
289
|
protected replaceFileIfUnchangedAfterAcceptance(input: ComputerFileReplacement, accept: () => void): Promise<ComputerFileReplacementResult>;
|
|
271
290
|
}
|
|
272
291
|
export { WorkspaceProxy, WorkspaceServiceProxy };
|
|
273
|
-
export { OnBoxVmContainerHost, onBoxVmPlacement, onBoxVmIngress, type OnBoxVmEnv, type OnBoxVmState, } from "./on-box-vm.js";
|
|
292
|
+
export { OnBoxVmContainerHost, OnBoxVmTimeoutError, onBoxVmPlacement, onBoxVmIngress, type OnBoxVmEnv, type OnBoxVmOperation, type OnBoxVmState, } from "./on-box-vm.js";
|
|
274
293
|
export { computerContainerSleepAfterMs } from "./container-policy.js";
|
|
275
294
|
export * from "./computer-backends.js";
|
|
276
295
|
export { LocalBackendOfflineError, localBackendAudience, localBackendDialPath, verifyLocalBackendDial, } from "./local-backend.js";
|
|
277
296
|
/** Keep each native Workspace lease inside the operation that uses it. */
|
|
278
297
|
export declare function createComputerFilesPort(computer: ComputerRpc): FilesPort;
|
|
279
298
|
export { createWorkspaceFilesPort, type WorkspaceFilesOwner } from "./files.js";
|
|
280
|
-
export * from "./operation-contract.js";
|
|
281
299
|
export { ComputerOperationConflictError, ComputerOperationLimitError, ComputerRequestInvalidError, } from "./operation-record.js";
|
|
282
300
|
export type { ContainerComputer };
|