@fungi.computer/oyster 0.3.2 → 0.4.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/dist/agent-computer-source.d.ts +42 -0
- package/dist/agent-computer-source.js +88 -0
- package/dist/agent-computers.d.ts +104 -0
- package/dist/agent-computers.js +227 -0
- package/dist/agents.d.ts +10 -0
- package/dist/agents.js +33 -0
- package/dist/app-builds.d.ts +1 -1
- package/dist/app-builds.js +16 -6
- package/dist/boot-capability.d.ts +2 -1
- package/dist/boot-capability.js +7 -5
- package/dist/browser/boot.d.ts +2 -2
- package/dist/browser/boot.js +10 -3
- package/dist/browser/browser.d.ts +1 -1
- package/dist/browser/browser.js +2 -1
- package/dist/browser/cdp-client.d.ts +2 -0
- package/dist/browser/errors.d.ts +2 -2
- package/dist/browser/errors.js +2 -2
- package/dist/browser/index.d.ts +3 -2
- package/dist/browser/index.js +3 -2
- package/dist/browser/page.js +11 -4
- package/dist/browser/sessions.d.ts +7 -4
- package/dist/browser/sessions.js +159 -0
- package/dist/browser/url-policy.d.ts +4 -2
- package/dist/browser/url-policy.js +4 -2
- package/dist/build/build-jobs.d.ts +3 -3
- package/dist/build/build-jobs.js +10 -5
- package/dist/build/candidate-cli.js +5 -3
- package/dist/build/candidate-cli.mjs +223 -5
- package/dist/build/candidate-diagnostic.d.ts +2 -0
- package/dist/build/candidate-diagnostic.js +16 -0
- package/dist/computer-control-port.d.ts +26 -0
- package/dist/computer-control-port.js +1 -0
- package/dist/host.d.ts +3 -0
- package/dist/machine-link/delivery.d.ts +1 -1
- package/dist/machine-link/delivery.js +2 -3
- package/dist/machine-link/host.d.ts +37 -6
- package/dist/machine-link/host.js +209 -14
- package/dist/machine-link/link.d.ts +13 -1
- package/dist/machine-link/link.js +20 -7
- package/dist/mcp.d.ts +144 -10
- package/dist/mcp.js +130 -13
- package/dist/page/computer-handles.d.ts +3 -3
- package/dist/page-script.js +1 -1
- package/dist/standalone/agent-computers.d.ts +29 -0
- package/dist/standalone/agent-computers.js +101 -0
- package/dist/standalone/agent-mail.d.ts +41 -18
- package/dist/standalone/agent-model-choice.d.ts +29 -0
- package/dist/standalone/agent-model-choice.js +72 -0
- package/dist/standalone/agents.d.ts +44 -4
- package/dist/standalone/agents.js +502 -46
- package/dist/standalone/app-set.d.ts +8 -1
- package/dist/standalone/app-set.js +46 -31
- package/dist/standalone/computer-control.d.ts +4 -0
- package/dist/standalone/computer-control.js +62 -0
- package/dist/standalone/computer-fleet.d.ts +1 -0
- package/dist/standalone/computer-fleet.js +1 -1
- package/dist/standalone/computer.d.ts +21 -2
- package/dist/standalone/computer.js +12 -0
- package/dist/standalone/install-apps-bin.d.ts +2 -0
- package/dist/standalone/install-apps-bin.js +3 -0
- package/dist/standalone/install-apps-cli.d.ts +3 -1
- package/dist/standalone/install-apps-cli.js +27 -17
- package/dist/standalone/machine-link.d.ts +10 -0
- package/dist/standalone/machine-link.js +6 -0
- package/dist/standalone/registry.d.ts +10 -1
- package/dist/standalone/registry.js +9 -0
- package/dist/standalone/worker.js +12 -0
- package/dist/standalone/workflow-binding.d.ts +2 -2
- package/dist/standalone/workflow-host.d.ts +11 -11
- package/dist/standalone/workflows.d.ts +22 -22
- package/dist/storage-frame.d.ts +3 -3
- package/dist/workers-git.d.ts +2 -0
- package/dist/workers-git.js +2 -0
- package/dist/workers.d.ts +8 -8
- package/dist/workers.js +9 -1
- package/docs/api/agents.md +66 -0
- package/docs/api/apps.md +104 -104
- package/docs/api/build-contract.md +35 -35
- package/docs/api/build.md +12 -12
- package/docs/api/host.md +61 -61
- package/docs/api/mcp.md +127 -13
- package/docs/api/root-data.md +1 -1
- package/docs/api/tenancy.md +1 -1
- package/docs/api/workers-git.md +3 -2
- package/docs/api/workers.md +81 -83
- package/package.json +26 -18
|
@@ -60,6 +60,13 @@ export type InstalledStarterApp = Readonly<{
|
|
|
60
60
|
release: Schema.Json;
|
|
61
61
|
timings: StarterAppTimings;
|
|
62
62
|
}>;
|
|
63
|
+
export type StarterAppSetResult = Readonly<{
|
|
64
|
+
installed: readonly InstalledStarterApp[];
|
|
65
|
+
failed: readonly Readonly<{
|
|
66
|
+
installationId: string;
|
|
67
|
+
reason: string;
|
|
68
|
+
}>[];
|
|
69
|
+
}>;
|
|
63
70
|
/**
|
|
64
71
|
* Builds the installer runs at once by default. A build's compiler may take a
|
|
65
72
|
* 4 GB heap, so two fit the platform's 12 GiB Computer (standard-4) with room
|
|
@@ -91,4 +98,4 @@ export declare function installStandaloneAppSet(options: Readonly<{
|
|
|
91
98
|
/** The idle wait between build status reads; the real timer by default. */
|
|
92
99
|
idle?: (ms: number, signal: AbortSignal) => Promise<void>;
|
|
93
100
|
waitMs?: number;
|
|
94
|
-
}>): Promise<
|
|
101
|
+
}>): Promise<StarterAppSetResult>;
|
|
@@ -72,7 +72,13 @@ const publishedSchema = Schema.Struct({
|
|
|
72
72
|
});
|
|
73
73
|
const buildSchema = Schema.Struct({
|
|
74
74
|
jobId: Schema.String,
|
|
75
|
-
status: Schema.Literals([
|
|
75
|
+
status: Schema.Literals([
|
|
76
|
+
"running",
|
|
77
|
+
"succeeded",
|
|
78
|
+
"failed",
|
|
79
|
+
"interrupted",
|
|
80
|
+
"revoked",
|
|
81
|
+
]),
|
|
76
82
|
detail: Schema.NullOr(Schema.String),
|
|
77
83
|
/** Installed exactly as the host encoded it. */
|
|
78
84
|
release: Schema.NullOr(Schema.Json),
|
|
@@ -271,10 +277,15 @@ export async function installStandaloneAppSet(options) {
|
|
|
271
277
|
}
|
|
272
278
|
};
|
|
273
279
|
const installed = new Map();
|
|
274
|
-
|
|
275
|
-
const
|
|
276
|
-
|
|
277
|
-
|
|
280
|
+
const failures = new Map();
|
|
281
|
+
const fail = (app, cause) => {
|
|
282
|
+
const reason = cause instanceof Error ? cause.message : String(cause);
|
|
283
|
+
failures.set(app.installationId, reason);
|
|
284
|
+
options.report(`Failed ${app.installationId}: ${reason}`);
|
|
285
|
+
};
|
|
286
|
+
const reportFailure = ({ app, cause, }) => {
|
|
287
|
+
fail(app, cause);
|
|
288
|
+
};
|
|
278
289
|
const seededQueue = [];
|
|
279
290
|
const waiting = [];
|
|
280
291
|
const seeding = { finished: false };
|
|
@@ -318,21 +329,19 @@ export async function installStandaloneAppSet(options) {
|
|
|
318
329
|
const seedAll = async () => {
|
|
319
330
|
try {
|
|
320
331
|
for (const app of standaloneAppSet) {
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
332
|
+
try {
|
|
333
|
+
const seeded = await seedApp(app);
|
|
334
|
+
const waiter = waiting.shift();
|
|
335
|
+
if (waiter === undefined)
|
|
336
|
+
seededQueue.push(seeded);
|
|
337
|
+
else
|
|
338
|
+
waiter(seeded);
|
|
339
|
+
}
|
|
340
|
+
catch (cause) {
|
|
341
|
+
reportFailure({ app, cause });
|
|
342
|
+
}
|
|
329
343
|
}
|
|
330
344
|
}
|
|
331
|
-
catch (cause) {
|
|
332
|
-
stop.requested = true;
|
|
333
|
-
failures.push(cause);
|
|
334
|
-
throw cause;
|
|
335
|
-
}
|
|
336
345
|
finally {
|
|
337
346
|
seeding.finished = true;
|
|
338
347
|
for (const waiter of waiting.splice(0))
|
|
@@ -341,7 +350,7 @@ export async function installStandaloneAppSet(options) {
|
|
|
341
350
|
};
|
|
342
351
|
// `concurrency` builds at once, each installing as its build succeeds.
|
|
343
352
|
const slot = async () => {
|
|
344
|
-
for (let next = await takeSeeded(); next !== undefined
|
|
353
|
+
for (let next = await takeSeeded(); next !== undefined; next = await takeSeeded()) {
|
|
345
354
|
const name = next.app.installationId;
|
|
346
355
|
try {
|
|
347
356
|
const buildStarted = performance.now();
|
|
@@ -416,22 +425,28 @@ export async function installStandaloneAppSet(options) {
|
|
|
416
425
|
});
|
|
417
426
|
}
|
|
418
427
|
catch (cause) {
|
|
419
|
-
|
|
420
|
-
failures.push(cause);
|
|
421
|
-
throw cause;
|
|
428
|
+
reportFailure({ app: next.app, cause });
|
|
422
429
|
}
|
|
423
430
|
}
|
|
424
431
|
};
|
|
425
|
-
// Wait for every seed and build to settle before
|
|
426
|
-
|
|
427
|
-
await Promise.allSettled([
|
|
432
|
+
// Wait for every seed and build to settle before returning the full outcome.
|
|
433
|
+
const settled = await Promise.allSettled([
|
|
428
434
|
seedAll(),
|
|
429
435
|
...Array.from({ length: concurrency }, slot),
|
|
430
436
|
]);
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
+
for (const outcome of settled)
|
|
438
|
+
if (outcome.status === "rejected")
|
|
439
|
+
throw outcome.reason;
|
|
440
|
+
return {
|
|
441
|
+
installed: standaloneAppSet.flatMap((app) => {
|
|
442
|
+
const result = installed.get(app.installationId);
|
|
443
|
+
return result === undefined ? [] : [result];
|
|
444
|
+
}),
|
|
445
|
+
failed: standaloneAppSet.flatMap((app) => {
|
|
446
|
+
const reason = failures.get(app.installationId);
|
|
447
|
+
return reason === undefined
|
|
448
|
+
? []
|
|
449
|
+
: [{ installationId: app.installationId, reason }];
|
|
450
|
+
}),
|
|
451
|
+
};
|
|
437
452
|
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { ComputerControlPort } from "../mcp.js";
|
|
2
|
+
import type { StandaloneRegistryRpc } from "./registry.js";
|
|
3
|
+
/** MCP control speaks to the same owner-admitted durable fleet as HTTP. */
|
|
4
|
+
export declare function standaloneComputerControl(registry: Pick<StandaloneRegistryRpc, "computerFleet">, memberId: string): ComputerControlPort;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { computerStateSchema, } from "@fungi.computer/computer/fleet";
|
|
3
|
+
import { teamComputerDirectorySchema } from "@fungi.computer/nacre/workbench";
|
|
4
|
+
const inventoryComputer = z.strictObject({
|
|
5
|
+
computerId: z.string(),
|
|
6
|
+
name: z.string(),
|
|
7
|
+
createdAt: z.number(),
|
|
8
|
+
updatedAt: z.number(),
|
|
9
|
+
});
|
|
10
|
+
const inventoryPage = z.strictObject({
|
|
11
|
+
computers: z.array(inventoryComputer),
|
|
12
|
+
nextOffset: z.number().int().nonnegative().nullable(),
|
|
13
|
+
});
|
|
14
|
+
const inventoryError = z.strictObject({
|
|
15
|
+
error: z.string(),
|
|
16
|
+
});
|
|
17
|
+
/** MCP control speaks to the same owner-admitted durable fleet as HTTP. */
|
|
18
|
+
export function standaloneComputerControl(registry, memberId) {
|
|
19
|
+
const fleet = (request) => registry.computerFleet(memberId, request);
|
|
20
|
+
const available = (result) => {
|
|
21
|
+
if (result === null || inventoryError.safeParse(result).success)
|
|
22
|
+
throw new Error("Computer fleet refused the request");
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
return {
|
|
26
|
+
list: async (page) => teamComputerDirectorySchema.parse(inventoryPage.parse(available(await fleet({ operation: "list", page })))),
|
|
27
|
+
state: async (computerId) => computerStateSchema.parse(available(await fleet({ operation: "state", computerId }))),
|
|
28
|
+
start: async (computerId) => computerStateSchema.parse(available(await fleet({ operation: "start", computerId }))),
|
|
29
|
+
create: async (input) => {
|
|
30
|
+
let result;
|
|
31
|
+
try {
|
|
32
|
+
result = await fleet({ operation: "create", request: input });
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
// Creation may have persisted before the RPC failed. Surface its
|
|
36
|
+
// identity so the caller can retry, never silently mint another one.
|
|
37
|
+
return { kind: "uncertain" };
|
|
38
|
+
}
|
|
39
|
+
if (result === null)
|
|
40
|
+
return { kind: "denied" };
|
|
41
|
+
const error = inventoryError.safeParse(result);
|
|
42
|
+
if (error.success) {
|
|
43
|
+
switch (error.data.error) {
|
|
44
|
+
case "owner_required":
|
|
45
|
+
return { kind: "denied" };
|
|
46
|
+
case "name_taken":
|
|
47
|
+
case "request_conflict":
|
|
48
|
+
return { kind: "conflict" };
|
|
49
|
+
case "partial_init":
|
|
50
|
+
return { kind: "uncertain" };
|
|
51
|
+
default:
|
|
52
|
+
return { kind: "invalid" };
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
const computer = inventoryComputer.parse(result);
|
|
56
|
+
return {
|
|
57
|
+
kind: "created",
|
|
58
|
+
computer: { computerId: computer.computerId, name: computer.name },
|
|
59
|
+
};
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
}
|
|
@@ -15,7 +15,7 @@ export const standaloneFleetRequestSchema = z.discriminatedUnion("operation", [
|
|
|
15
15
|
page: computerPageSchema,
|
|
16
16
|
}),
|
|
17
17
|
z.strictObject({
|
|
18
|
-
operation: z.enum(["read", "resolve", "start", "select"]),
|
|
18
|
+
operation: z.enum(["read", "resolve", "state", "start", "select"]),
|
|
19
19
|
computerId: fleetComputerId,
|
|
20
20
|
}),
|
|
21
21
|
z.strictObject({
|
|
@@ -4,7 +4,8 @@ import { type PreviewSelector } from "./preview.js";
|
|
|
4
4
|
import { type StandaloneTenancyEnv } from "./tenancy.js";
|
|
5
5
|
import * as z from "zod";
|
|
6
6
|
import { computerProcessSchema, computerToolResultSchema, readOutputSchema } from "@fungi.computer/computer-contract";
|
|
7
|
-
import { ComputerDO as NativeComputerDO, type AttachedBackend, type ComputerEnv } from "@fungi.computer/computer";
|
|
7
|
+
import { ComputerDO as NativeComputerDO, type AttachedBackend, type ComputerEnv, type ComputerFilesRpc } from "@fungi.computer/computer";
|
|
8
|
+
import { patchInputSchema, patchOutputSchema } from "@fungi.computer/knapsack/files-patch";
|
|
8
9
|
import { listOutputSchema, writeOutputSchema, mkdirOutputSchema, computerPortsOutputSchema, computerPortOutputSchema, computerPortRemovalOutputSchema, processListOutputSchema, processKillOutputSchema, computerOperationResultSchema, type ComputerExecutionHostHooks, type ComputerValue } from "@fungi.computer/computer-contract";
|
|
9
10
|
import { type WorkflowGitCredential, type WorkflowGitPassGrant } from "@fungi.computer/braid/runtime";
|
|
10
11
|
import { type ComputerAction, type ComputerGrant } from "@fungi.computer/computer/fleet";
|
|
@@ -66,8 +67,13 @@ export type StandaloneWorkflowGitIssuer = Readonly<{
|
|
|
66
67
|
export type ComputerFetch = {
|
|
67
68
|
fetch(request: Request): Promise<Response>;
|
|
68
69
|
};
|
|
70
|
+
/** The additional native doors an attached Agent may use on this Computer. */
|
|
71
|
+
export type StandaloneAgentComputerRpc = ComputerFilesRpc & Readonly<{
|
|
72
|
+
tool(authority: ComputerGrant, capabilityId: string, input: ComputerValue): Promise<StandaloneComputerResult>;
|
|
73
|
+
filePatch(authority: ComputerGrant, input: z.output<typeof patchInputSchema>): Promise<z.output<typeof patchOutputSchema>>;
|
|
74
|
+
}>;
|
|
69
75
|
/** Durable workspace and existing backings, with fleet checks at native acceptance. */
|
|
70
|
-
export declare class ComputerDO extends NativeComputerDO implements ComputerFetch {
|
|
76
|
+
export declare class ComputerDO extends NativeComputerDO implements ComputerFetch, StandaloneAgentComputerRpc {
|
|
71
77
|
#private;
|
|
72
78
|
private readonly previewBindings;
|
|
73
79
|
constructor(state: DurableObjectState, previewBindings: StandaloneComputerOwnerEnv);
|
|
@@ -77,6 +83,17 @@ export declare class ComputerDO extends NativeComputerDO implements ComputerFetc
|
|
|
77
83
|
startHostedComputer(authority: ComputerGrant): Promise<{
|
|
78
84
|
state: "running";
|
|
79
85
|
}>;
|
|
86
|
+
/** Observe an admitted fleet Computer without starting its backend. */
|
|
87
|
+
readHostedComputerState(authority: ComputerGrant): Promise<{
|
|
88
|
+
state: "queued";
|
|
89
|
+
position: number;
|
|
90
|
+
} | {
|
|
91
|
+
state: "running" | "starting";
|
|
92
|
+
position?: never;
|
|
93
|
+
} | {
|
|
94
|
+
state: "running" | "sleeping";
|
|
95
|
+
position?: never;
|
|
96
|
+
}>;
|
|
80
97
|
/** A read-only preflight over the maintained hosted-route selector. */
|
|
81
98
|
buildBackend(): OysterBuildBackend;
|
|
82
99
|
/**
|
|
@@ -120,6 +137,8 @@ export declare class ComputerDO extends NativeComputerDO implements ComputerFetc
|
|
|
120
137
|
/** Binary files share the same native filesystem as commands and PTYs. */
|
|
121
138
|
fileRead(authority: ComputerGrant, path: string): Promise<Uint8Array | null>;
|
|
122
139
|
fileWrite(authority: ComputerGrant, path: string, bytes: Uint8Array): Promise<void>;
|
|
140
|
+
/** Patch is handled by the Computer's pinned native engine, not by the Agent. */
|
|
141
|
+
filePatch(authority: ComputerGrant, input: z.output<typeof patchInputSchema>): Promise<z.output<typeof patchOutputSchema>>;
|
|
123
142
|
fileReplace(authority: ComputerGrant, path: string, expectedContent: string, content: string): Promise<{
|
|
124
143
|
path: string;
|
|
125
144
|
bytes: number;
|
|
@@ -11,6 +11,7 @@ import { Base64 } from "effect/encoding";
|
|
|
11
11
|
import * as z from "zod";
|
|
12
12
|
import { ComputerPortConflictError, computerByteReadReplySchema, computerProcessSchema, installationProcessScope, processStartInputSchema, processStartToolInputSchema, computerCapabilities, computerToolResultSchema, readOutputSchema, } from "@fungi.computer/computer-contract";
|
|
13
13
|
import { ComputerDO as NativeComputerDO, ComputerOperationConflictError, } from "@fungi.computer/computer";
|
|
14
|
+
import { patchInputSchema, patchOutputSchema, } from "@fungi.computer/knapsack/files-patch";
|
|
14
15
|
import { ComputerFileConflict, computerExecutionRequestSchema, computerValueSchema, computerPortInputSchema, listInputSchema, listOutputSchema, readBytesInputSchema, writeInputSchema, replaceInputSchema, writeOutputSchema, mkdirOutputSchema, computerPortsOutputSchema, computerPortOutputSchema, computerPortRemovalOutputSchema, processListOutputSchema, processKillOutputSchema, computerOperationResultSchema, mintComputerRequestId, processIdInputSchema, } from "@fungi.computer/computer-contract";
|
|
15
16
|
import { workflowGitExecutionEnvironment, workflowGitPassGrantSchema, } from "@fungi.computer/braid/runtime";
|
|
16
17
|
import { checkComputerGrant, computerActionSchema, computerGrantSchema, createFleetBacking, FleetGrantRefused, FleetLifecycle, } from "@fungi.computer/computer/fleet";
|
|
@@ -173,6 +174,11 @@ export class ComputerDO extends NativeComputerDO {
|
|
|
173
174
|
await run();
|
|
174
175
|
});
|
|
175
176
|
}
|
|
177
|
+
/** Observe an admitted fleet Computer without starting its backend. */
|
|
178
|
+
readHostedComputerState(authority) {
|
|
179
|
+
this.#accept(authority, "start");
|
|
180
|
+
return this.#fleetLifecycle().readComputerState();
|
|
181
|
+
}
|
|
176
182
|
#previewGrant(ownerId) {
|
|
177
183
|
return {
|
|
178
184
|
computerId: this.#id(),
|
|
@@ -455,6 +461,12 @@ export class ComputerDO extends NativeComputerDO {
|
|
|
455
461
|
this.#accept(authority, "files.writeBytes");
|
|
456
462
|
});
|
|
457
463
|
}
|
|
464
|
+
/** Patch is handled by the Computer's pinned native engine, not by the Agent. */
|
|
465
|
+
async filePatch(authority, input) {
|
|
466
|
+
return this.applyPatchAfterAcceptance(input, () => {
|
|
467
|
+
this.#accept(authority, "files.edit");
|
|
468
|
+
});
|
|
469
|
+
}
|
|
458
470
|
async fileReplace(authority, path, expectedContent, content) {
|
|
459
471
|
try {
|
|
460
472
|
const result = await this.replaceFileIfUnchangedAfterAcceptance({ path, expectedContent, content }, () => {
|
|
@@ -1,2 +1,4 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
2
|
+
import { installStandaloneAppSet } from "./app-set.js";
|
|
3
|
+
export declare function installApps(args: readonly string[], install?: typeof installStandaloneAppSet): Promise<void>;
|
|
4
|
+
export declare function runInstallAppsCli(args?: readonly string[]): Promise<void>;
|
|
@@ -76,7 +76,7 @@ function timingTable(results) {
|
|
|
76
76
|
];
|
|
77
77
|
return [header, ...rows].map((row) => row.join("\t")).join("\n");
|
|
78
78
|
}
|
|
79
|
-
async function installApps(args) {
|
|
79
|
+
export async function installApps(args, install = installStandaloneAppSet) {
|
|
80
80
|
const { values } = parseArgs({
|
|
81
81
|
args: [...args],
|
|
82
82
|
options: {
|
|
@@ -121,7 +121,7 @@ async function installApps(args) {
|
|
|
121
121
|
source = { kind: "checkout", directory, revision: `git:${revision}` };
|
|
122
122
|
}
|
|
123
123
|
const started = performance.now();
|
|
124
|
-
const
|
|
124
|
+
const { installed, failed } = await install({
|
|
125
125
|
source,
|
|
126
126
|
url,
|
|
127
127
|
key,
|
|
@@ -131,33 +131,43 @@ async function installApps(args) {
|
|
|
131
131
|
},
|
|
132
132
|
});
|
|
133
133
|
const elapsed = performance.now() - started;
|
|
134
|
-
process.stdout.write(`\n${timingTable(
|
|
135
|
-
process.stdout.write(
|
|
134
|
+
process.stdout.write(`\n${timingTable(installed)}\n`);
|
|
135
|
+
process.stdout.write("\nApp\tResult\n");
|
|
136
|
+
for (const app of installed)
|
|
137
|
+
process.stdout.write(`${app.installationId}\tinstalled\n`);
|
|
138
|
+
for (const app of failed)
|
|
139
|
+
process.stdout.write(`${app.installationId}\tfailed: ${app.reason}\n`);
|
|
140
|
+
process.stdout.write(`\nInstalled ${String(installed.length)} Apps; failed ${String(failed.length)} in ${seconds(elapsed)}.${installed.length > 0 ? ` Open ${url}/ to use the installed Apps.` : ""}\n`);
|
|
136
141
|
if (values.timings !== undefined)
|
|
137
142
|
await writeFile(values.timings, JSON.stringify({
|
|
138
143
|
concurrency,
|
|
139
144
|
elapsedMs: elapsed,
|
|
140
|
-
apps:
|
|
145
|
+
apps: installed.map(({ installationId, jobId, timings }) => ({
|
|
141
146
|
installationId,
|
|
142
147
|
jobId,
|
|
143
148
|
timings,
|
|
144
149
|
})),
|
|
150
|
+
failed,
|
|
145
151
|
}, null, 2) + "\n");
|
|
152
|
+
if (failed.length > 0)
|
|
153
|
+
process.exitCode = 1;
|
|
146
154
|
}
|
|
147
155
|
finally {
|
|
148
156
|
await rm(scratch, { recursive: true, force: true });
|
|
149
157
|
}
|
|
150
158
|
}
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
159
|
+
export async function runInstallAppsCli(args = process.argv.slice(2)) {
|
|
160
|
+
const [command, ...rest] = args;
|
|
161
|
+
if (command === "--help" || command === "-h") {
|
|
162
|
+
process.stdout.write(usage + "\n" + installReleaseUsage + "\n");
|
|
163
|
+
}
|
|
164
|
+
else if (command !== "install-apps" && command !== "install") {
|
|
165
|
+
process.stderr.write(`${usage}\n`);
|
|
166
|
+
process.exitCode = 2;
|
|
167
|
+
}
|
|
168
|
+
else
|
|
169
|
+
await (command === "install" ? installReleaseCommand(rest) : installApps(rest)).catch((cause) => {
|
|
170
|
+
process.stderr.write(`${cause instanceof Error ? cause.message : String(cause)}\n`);
|
|
171
|
+
process.exitCode = 1;
|
|
172
|
+
});
|
|
158
173
|
}
|
|
159
|
-
else
|
|
160
|
-
await (command === "install" ? installReleaseCommand(rest) : installApps(rest)).catch((cause) => {
|
|
161
|
-
process.stderr.write(`${cause instanceof Error ? cause.message : String(cause)}\n`);
|
|
162
|
-
process.exitCode = 1;
|
|
163
|
-
});
|
|
@@ -265,6 +265,7 @@ export declare function standaloneMachineLinkRoutes(env: StandaloneMachineLinkEn
|
|
|
265
265
|
/** One device's link; directory, credentials and owner state remain elsewhere. */
|
|
266
266
|
export declare class MachineLinkDO extends DurableObject<StandaloneMachineLinkEnv> {
|
|
267
267
|
private readonly link;
|
|
268
|
+
alarm(): Promise<void>;
|
|
268
269
|
fetch(request: Request): Promise<Response>;
|
|
269
270
|
deliver(id: MachineLinkIdentity, caller: MachineLinkCaller, agentId: string, envelope: PostEnvelope): Promise<PostDeliveryResult>;
|
|
270
271
|
operation(id: MachineLinkIdentity, caller: MachineLinkCaller, agentId: string, operation: MachineAgentOperation): Promise<import("effect/Schema").Json>;
|
|
@@ -276,6 +277,15 @@ export declare class MachineLinkDO extends DurableObject<StandaloneMachineLinkEn
|
|
|
276
277
|
readErasure(): Promise<{
|
|
277
278
|
erased: boolean;
|
|
278
279
|
}>;
|
|
280
|
+
deadLetters(id: MachineLinkIdentity, afterKey?: string): Promise<Readonly<{
|
|
281
|
+
letters: readonly Readonly<{
|
|
282
|
+
key: string;
|
|
283
|
+
reason: "refused" | "exhausted" | "expired";
|
|
284
|
+
envelope: PostEnvelope;
|
|
285
|
+
attempts: number;
|
|
286
|
+
}>[];
|
|
287
|
+
nextCursor: string | undefined;
|
|
288
|
+
}>>;
|
|
279
289
|
webSocketMessage(socket: WebSocket, message: string | ArrayBuffer): Promise<void>;
|
|
280
290
|
webSocketClose(socket: WebSocket, code: number, reason: string): void;
|
|
281
291
|
webSocketError(socket: WebSocket): void;
|
|
@@ -176,6 +176,9 @@ export class MachineLinkDO extends DurableObject {
|
|
|
176
176
|
console.error(operation, cause);
|
|
177
177
|
},
|
|
178
178
|
});
|
|
179
|
+
alarm() {
|
|
180
|
+
return this.link.alarm();
|
|
181
|
+
}
|
|
179
182
|
fetch(request) {
|
|
180
183
|
return this.link.fetch(request);
|
|
181
184
|
}
|
|
@@ -197,6 +200,9 @@ export class MachineLinkDO extends DurableObject {
|
|
|
197
200
|
readErasure() {
|
|
198
201
|
return this.link.readErasure();
|
|
199
202
|
}
|
|
203
|
+
deadLetters(id, afterKey) {
|
|
204
|
+
return this.link.deadLetters(id, afterKey);
|
|
205
|
+
}
|
|
200
206
|
webSocketMessage(socket, message) {
|
|
201
207
|
return this.link.webSocketMessage(socket, message);
|
|
202
208
|
}
|
|
@@ -61,6 +61,15 @@ export declare abstract class StandaloneRegistry extends OysterRegistry implemen
|
|
|
61
61
|
}>[];
|
|
62
62
|
readonly nextOffset: number | null;
|
|
63
63
|
} | ({
|
|
64
|
+
state: "queued";
|
|
65
|
+
position: number;
|
|
66
|
+
} & Disposable) | ({
|
|
67
|
+
state: "running" | "starting";
|
|
68
|
+
position?: never | undefined;
|
|
69
|
+
} & Disposable) | ({
|
|
70
|
+
state: "running" | "sleeping";
|
|
71
|
+
position?: never | undefined;
|
|
72
|
+
} & Disposable) | ({
|
|
64
73
|
state: "running";
|
|
65
74
|
} & Disposable) | {
|
|
66
75
|
error: string;
|
|
@@ -178,7 +187,7 @@ export declare abstract class StandaloneRegistry extends OysterRegistry implemen
|
|
|
178
187
|
appId: string;
|
|
179
188
|
releaseId: string;
|
|
180
189
|
sourceRevision: string;
|
|
181
|
-
status: "running" | "succeeded" | "failed" | "revoked";
|
|
190
|
+
status: "running" | "succeeded" | "failed" | "interrupted" | "revoked";
|
|
182
191
|
detail: string | null;
|
|
183
192
|
release: Release | null;
|
|
184
193
|
installation: import("../app-builds.js").OysterBuildInstallation | null;
|
|
@@ -129,6 +129,15 @@ export class StandaloneRegistry extends OysterRegistry {
|
|
|
129
129
|
await this.#fleetStorage.put("selected-computer", input.computerId);
|
|
130
130
|
return computer;
|
|
131
131
|
}
|
|
132
|
+
case "state":
|
|
133
|
+
if (this.#computers.read(input.computerId) === null)
|
|
134
|
+
return null;
|
|
135
|
+
return this.#env.COMPUTER.getByName(input.computerId).readHostedComputerState({
|
|
136
|
+
computerId: input.computerId,
|
|
137
|
+
actions: new Set(["start"]),
|
|
138
|
+
expiresAt: Date.now() + 60_000,
|
|
139
|
+
provenance: { actor: memberId },
|
|
140
|
+
});
|
|
132
141
|
case "start":
|
|
133
142
|
if (this.#computers.read(input.computerId) === null)
|
|
134
143
|
return null;
|
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import { createOysterGitWorker, createOysterGitMcpOwner, oysterGitRoot, } from "../workers-git.js";
|
|
7
7
|
import { defaultTenant, tenantScope } from "../tenant.js";
|
|
8
|
+
import { oysterPrincipalIsOwner } from "../host.js";
|
|
9
|
+
import { standaloneComputerControl } from "./computer-control.js";
|
|
8
10
|
import { onBoxVmIngress } from "@fungi.computer/computer";
|
|
9
11
|
import { WorkerEntrypoint } from "cloudflare:workers";
|
|
10
12
|
import { installationWorkerEntrypoint } from "../installation-execution.js";
|
|
@@ -28,6 +30,8 @@ import { Hono } from "hono";
|
|
|
28
30
|
import { HTTPException } from "hono/http-exception";
|
|
29
31
|
import { standaloneBuildRoutes } from "./builds.js";
|
|
30
32
|
import { standaloneComputerFleetRoutes, standaloneComputerSelection, } from "./computer-fleet.js";
|
|
33
|
+
import { standaloneAgentComputerRoutes } from "./agent-computers.js";
|
|
34
|
+
import { agentDirectoryForScope } from "./agent-directory.js";
|
|
31
35
|
import { serveOysterComputerEgress, } from "./computer-egress.js";
|
|
32
36
|
import { appSourceOwnerRoutes, } from "./app-source.js";
|
|
33
37
|
export { StandaloneGraphWorkflow } from "./workflows.js";
|
|
@@ -65,6 +69,9 @@ const standaloneRoot = (selectComputer) => {
|
|
|
65
69
|
homeInstallationId: (env) => env.OYSTER_HOME_INSTALLATION,
|
|
66
70
|
agents: standaloneAgents,
|
|
67
71
|
workflows: standaloneWorkflowControl,
|
|
72
|
+
computerControl: (env, scope) => (principal) => oysterPrincipalIsOwner(principal)
|
|
73
|
+
? standaloneComputerControl(tenantRegistry(env, scope), principal.actor.memberId)
|
|
74
|
+
: null,
|
|
68
75
|
workspace: async (env, keysLive, scope, request) => standaloneWorkspace(env, keysLive, await selectComputer(tenantRegistry(env, scope), request)),
|
|
69
76
|
capabilities: (env, workspace, keysLive) => [
|
|
70
77
|
...standaloneComputerCapabilities(env, workspace, keysLive),
|
|
@@ -97,6 +104,11 @@ const standaloneRoot = (selectComputer) => {
|
|
|
97
104
|
ownerRoutes: (env, scope) => appSourceOwnerRoutes(env, scope)
|
|
98
105
|
.route("/", standaloneBuildRoutes(() => tenantRegistry(env, scope)))
|
|
99
106
|
.route("/", standaloneComputerFleetRoutes(() => tenantRegistry(env, scope)))
|
|
107
|
+
.route("/", standaloneAgentComputerRoutes({
|
|
108
|
+
attach: (principal, command) => agentDirectoryForScope(env, scope).attachComputer(command, principal, scope.tenantId),
|
|
109
|
+
detach: (principal, command) => agentDirectoryForScope(env, scope).detachComputer(command, principal, scope.tenantId),
|
|
110
|
+
read: (principal, agentId) => agentDirectoryForScope(env, scope).readAgentComputers(agentId, principal, scope.tenantId),
|
|
111
|
+
}))
|
|
100
112
|
.route("/", standaloneWorkflowWebhookControls(env, () => root.hostFor(env, scope))),
|
|
101
113
|
});
|
|
102
114
|
return root;
|
|
@@ -72,10 +72,10 @@ export declare const workflowFrameMethods: {
|
|
|
72
72
|
readonly input: {
|
|
73
73
|
readonly [x: string]: S.Json;
|
|
74
74
|
};
|
|
75
|
+
readonly requestId: string;
|
|
75
76
|
readonly bindings: Record<string, string | {
|
|
76
77
|
kind: "team-source";
|
|
77
78
|
}>;
|
|
78
|
-
readonly requestId: string;
|
|
79
79
|
readonly definition: unknown;
|
|
80
80
|
readonly delivery?: {
|
|
81
81
|
readonly agentId: string;
|
|
@@ -86,10 +86,10 @@ export declare const workflowFrameMethods: {
|
|
|
86
86
|
readonly input: {
|
|
87
87
|
readonly [x: string]: S.Json;
|
|
88
88
|
};
|
|
89
|
+
readonly requestId: string;
|
|
89
90
|
readonly bindings: Record<string, string | {
|
|
90
91
|
kind: "team-source";
|
|
91
92
|
}>;
|
|
92
|
-
readonly requestId: string;
|
|
93
93
|
readonly definition: S.Json;
|
|
94
94
|
readonly published: {
|
|
95
95
|
workflowId: string;
|