@fungi.computer/oyster 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +1134 -2
- package/dist/agents.d.ts +114 -0
- package/dist/agents.js +322 -0
- package/dist/app-builds.d.ts +210 -0
- package/dist/app-builds.js +499 -0
- package/dist/app-sandbox.d.ts +2 -0
- package/dist/app-sandbox.js +2 -0
- package/dist/app-sources.d.ts +31 -0
- package/dist/app-sources.js +370 -0
- package/dist/app-storage.d.ts +141 -0
- package/dist/app-storage.js +281 -0
- package/dist/apps/gate.d.ts +105 -0
- package/dist/apps/gate.js +273 -0
- package/dist/apps/index.d.ts +5 -0
- package/dist/apps/index.js +5 -0
- package/dist/apps/installation.d.ts +248 -0
- package/dist/apps/installation.js +307 -0
- package/dist/apps/operations.d.ts +8 -0
- package/dist/apps/operations.js +98 -0
- package/dist/apps/tools.d.ts +33 -0
- package/dist/apps/tools.js +17 -0
- package/dist/auth.d.ts +11 -0
- package/dist/auth.js +93 -0
- package/dist/backend-request.d.ts +3 -0
- package/dist/backend-request.js +53 -0
- package/dist/build/artifact-writer.d.ts +40 -0
- package/dist/build/artifact-writer.js +181 -0
- package/dist/build/build-jobs.d.ts +128 -0
- package/dist/build/build-jobs.js +203 -0
- package/dist/build/build-policy.d.ts +10 -0
- package/dist/build/build-policy.js +10 -0
- package/dist/build/build-step.d.ts +37 -0
- package/dist/build/build-step.js +28 -0
- package/dist/build/bundled-html.d.ts +7 -0
- package/dist/build/bundled-html.js +64 -0
- package/dist/build/candidate-builder.d.ts +68 -0
- package/dist/build/candidate-builder.js +91 -0
- package/dist/build/candidate-cli.d.ts +1 -0
- package/dist/build/candidate-cli.js +119 -0
- package/dist/build/candidate-cli.mjs +28170 -0
- package/dist/build/content-objects.d.ts +37 -0
- package/dist/build/content-objects.js +53 -0
- package/dist/build/definition-emission.d.ts +34 -0
- package/dist/build/definition-emission.js +201 -0
- package/dist/build/effect.d.ts +249 -0
- package/dist/build/effect.js +436 -0
- package/dist/build/git-environment.d.ts +9 -0
- package/dist/build/git-environment.js +60 -0
- package/dist/build/html-recipe.d.ts +20 -0
- package/dist/build/html-recipe.js +51 -0
- package/dist/build/local-build.d.ts +67 -0
- package/dist/build/local-build.js +1128 -0
- package/dist/build/snapshot-package-store.d.ts +12 -0
- package/dist/build/snapshot-package-store.js +36 -0
- package/dist/build/snapshot-program-scripts.d.ts +6 -0
- package/dist/build/snapshot-program-scripts.js +2 -0
- package/dist/build/snapshot-programs/snapshot-path.d.ts +2 -0
- package/dist/build/snapshot-programs/snapshot-path.js +6 -0
- package/dist/build/submission-contract.d.ts +828 -0
- package/dist/build/submission-contract.js +180 -0
- package/dist/build/workspace-build.d.ts +55 -0
- package/dist/build/workspace-build.js +200 -0
- package/dist/build-contract.d.ts +7 -0
- package/dist/build-contract.js +7 -0
- package/dist/build-egress.d.ts +62 -0
- package/dist/build-egress.js +148 -0
- package/dist/build-jobs.d.ts +2 -0
- package/dist/build-jobs.js +2 -0
- package/dist/build.d.ts +12 -0
- package/dist/build.js +14 -0
- package/dist/capability-descriptor.d.ts +46 -0
- package/dist/capability-descriptor.js +51 -0
- package/dist/catalog-frame.d.ts +39 -0
- package/dist/catalog-frame.js +72 -0
- package/dist/cause-class.d.ts +19 -0
- package/dist/cause-class.js +54 -0
- package/dist/cloudflare.d.ts +123 -0
- package/dist/cloudflare.js +391 -0
- package/dist/computer-terminal.d.ts +38 -0
- package/dist/computer-terminal.js +410 -0
- package/dist/computer.d.ts +60 -0
- package/dist/computer.js +33 -0
- package/dist/event-recipients.d.ts +57 -0
- package/dist/event-recipients.js +144 -0
- package/dist/facet-invocation.d.ts +4 -0
- package/dist/facet-invocation.js +45 -0
- package/dist/files.d.ts +42 -0
- package/dist/files.js +123 -0
- package/dist/frame-binding-wire-script.d.ts +2 -0
- package/dist/frame-binding-wire-script.js +1 -0
- package/dist/frame-binding-wire.d.ts +28 -0
- package/dist/frame-binding-wire.js +54 -0
- package/dist/git-admission.d.ts +52 -0
- package/dist/git-admission.js +188 -0
- package/dist/git-auth.d.ts +11 -0
- package/dist/git-auth.js +27 -0
- package/dist/git-door.d.ts +35 -0
- package/dist/git-door.js +108 -0
- package/dist/git-journal.d.ts +31 -0
- package/dist/git-journal.js +188 -0
- package/dist/git.d.ts +31 -0
- package/dist/git.js +64 -0
- package/dist/guest-policy.d.ts +9 -0
- package/dist/guest-policy.js +13 -0
- package/dist/host.d.ts +244 -0
- package/dist/host.js +1043 -0
- package/dist/index.d.ts +166 -0
- package/dist/index.js +320 -0
- package/dist/installation-consent.d.ts +79 -0
- package/dist/installation-consent.js +82 -0
- package/dist/internal/background-origins.d.ts +3 -0
- package/dist/internal/background-origins.js +8 -0
- package/dist/internal/delivery.d.ts +12 -0
- package/dist/internal/delivery.js +167 -0
- package/dist/internal/host-gate.d.ts +14 -0
- package/dist/internal/host-gate.js +62 -0
- package/dist/internal/registry-installations.d.ts +6 -0
- package/dist/internal/registry-installations.js +12 -0
- package/dist/invocation.d.ts +251 -0
- package/dist/invocation.js +377 -0
- package/dist/keys.d.ts +48 -0
- package/dist/keys.js +52 -0
- package/dist/mcp-owner.d.ts +27 -0
- package/dist/mcp-owner.js +155 -0
- package/dist/mcp.d.ts +89 -0
- package/dist/mcp.js +429 -0
- package/dist/media.d.ts +23 -0
- package/dist/media.js +12 -0
- package/dist/pack.d.ts +79 -0
- package/dist/pack.js +151 -0
- package/dist/page/capabilities.d.ts +23 -0
- package/dist/page/capabilities.js +256 -0
- package/dist/page/computer-handles.d.ts +79 -0
- package/dist/page/computer-handles.js +44 -0
- package/dist/page/gill-observation.d.ts +4 -0
- package/dist/page/gill-observation.js +31 -0
- package/dist/page/lifetime.d.ts +6 -0
- package/dist/page/lifetime.js +19 -0
- package/dist/page/mount.d.ts +163 -0
- package/dist/page/mount.js +390 -0
- package/dist/page/storage.d.ts +3 -0
- package/dist/page/storage.js +82 -0
- package/dist/page/terminal.d.ts +5 -0
- package/dist/page/terminal.js +7 -0
- package/dist/page/window-launch.d.ts +18 -0
- package/dist/page/window-launch.js +11 -0
- package/dist/page-script.d.ts +14 -0
- package/dist/page-script.js +5 -0
- package/dist/r2-files.d.ts +3 -0
- package/dist/r2-files.js +79 -0
- package/dist/release.d.ts +83 -0
- package/dist/release.js +167 -0
- package/dist/root-data.d.ts +86 -0
- package/dist/root-data.js +69 -0
- package/dist/shell/app-directory.d.ts +542 -0
- package/dist/shell/app-directory.js +199 -0
- package/dist/shell/browser-actions.d.ts +7 -0
- package/dist/shell/browser-actions.js +56 -0
- package/dist/shell/command-host.d.ts +112 -0
- package/dist/shell/command-host.js +502 -0
- package/dist/shell/commands.d.ts +25 -0
- package/dist/shell/commands.js +78 -0
- package/dist/shell/index.d.ts +8 -0
- package/dist/shell/index.js +8 -0
- package/dist/shell/keys.d.ts +41 -0
- package/dist/shell/keys.js +134 -0
- package/dist/shell/shell-module.d.ts +55 -0
- package/dist/shell/shell-module.js +86 -0
- package/dist/shell/window-manager.d.ts +124 -0
- package/dist/shell/window-manager.js +711 -0
- package/dist/shell/workbench-contract.d.ts +68 -0
- package/dist/shell/workbench-contract.js +1 -0
- package/dist/shell/workbench-directory.d.ts +332 -0
- package/dist/shell/workbench-directory.js +35 -0
- package/dist/shell/workbench-host-types.d.ts +96 -0
- package/dist/shell/workbench-host-types.js +1 -0
- package/dist/shell/workbench-host.d.ts +34 -0
- package/dist/shell/workbench-host.js +347 -0
- package/dist/shell/workbench-model.d.ts +192 -0
- package/dist/shell/workbench-model.js +53 -0
- package/dist/shell/workbench.d.ts +4 -0
- package/dist/shell/workbench.js +2 -0
- package/dist/source/fork.d.ts +27 -0
- package/dist/source/fork.js +46 -0
- package/dist/source/index.d.ts +10 -0
- package/dist/source/index.js +10 -0
- package/dist/source/seed-receipt.d.ts +53 -0
- package/dist/source/seed-receipt.js +18 -0
- package/dist/source/team-source.d.ts +32 -0
- package/dist/source/team-source.js +42 -0
- package/dist/source/template-push.d.ts +33 -0
- package/dist/source/template-push.js +223 -0
- package/dist/source/template-repository.d.ts +4 -0
- package/dist/source/template-repository.js +21 -0
- package/dist/source/template-seed.d.ts +68 -0
- package/dist/source/template-seed.js +80 -0
- package/dist/standalone/agent-directory.d.ts +4 -0
- package/dist/standalone/agent-directory.js +4 -0
- package/dist/standalone/agents.d.ts +220 -0
- package/dist/standalone/agents.js +795 -0
- package/dist/standalone/app-builds-binding.d.ts +36 -0
- package/dist/standalone/app-builds-binding.js +119 -0
- package/dist/standalone/app-set.d.ts +91 -0
- package/dist/standalone/app-set.js +395 -0
- package/dist/standalone/app-source.d.ts +41 -0
- package/dist/standalone/app-source.js +59 -0
- package/dist/standalone/builds.d.ts +18 -0
- package/dist/standalone/builds.js +101 -0
- package/dist/standalone/computer-egress.d.ts +37 -0
- package/dist/standalone/computer-egress.js +111 -0
- package/dist/standalone/computer.d.ts +122 -0
- package/dist/standalone/computer.js +714 -0
- package/dist/standalone/install-apps-cli.d.ts +2 -0
- package/dist/standalone/install-apps-cli.js +126 -0
- package/dist/standalone/instance.d.ts +35 -0
- package/dist/standalone/instance.js +74 -0
- package/dist/standalone/media.d.ts +76 -0
- package/dist/standalone/media.js +227 -0
- package/dist/standalone/preview.d.ts +51 -0
- package/dist/standalone/preview.js +167 -0
- package/dist/standalone/registry.d.ts +102 -0
- package/dist/standalone/registry.js +193 -0
- package/dist/standalone/tenancy.d.ts +42 -0
- package/dist/standalone/tenancy.js +241 -0
- package/dist/standalone/worker.d.ts +55 -0
- package/dist/standalone/worker.js +147 -0
- package/dist/standalone/workflow-authority-readers.d.ts +16 -0
- package/dist/standalone/workflow-authority-readers.js +35 -0
- package/dist/standalone/workflow-binding.d.ts +216 -0
- package/dist/standalone/workflow-binding.js +217 -0
- package/dist/standalone/workflow-events.d.ts +46 -0
- package/dist/standalone/workflow-events.js +39 -0
- package/dist/standalone/workflow-git-capture.d.ts +7 -0
- package/dist/standalone/workflow-git-capture.js +62 -0
- package/dist/standalone/workflow-host.d.ts +931 -0
- package/dist/standalone/workflow-host.js +751 -0
- package/dist/standalone/workflow-node-capture.d.ts +13 -0
- package/dist/standalone/workflow-node-capture.js +72 -0
- package/dist/standalone/workflow-root-authority.d.ts +27 -0
- package/dist/standalone/workflow-root-authority.js +87 -0
- package/dist/standalone/workflow-schedules.d.ts +52 -0
- package/dist/standalone/workflow-schedules.js +40 -0
- package/dist/standalone/workflow-trigger-host.d.ts +1694 -0
- package/dist/standalone/workflow-trigger-host.js +52 -0
- package/dist/standalone/workflows.d.ts +657 -0
- package/dist/standalone/workflows.js +554 -0
- package/dist/storage-frame.d.ts +64 -0
- package/dist/storage-frame.js +28 -0
- package/dist/tenancy/auth0.d.ts +64 -0
- package/dist/tenancy/auth0.js +198 -0
- package/dist/tenancy/cloudflare/rate-limit.d.ts +8 -0
- package/dist/tenancy/cloudflare/rate-limit.js +19 -0
- package/dist/tenancy/cloudflare/registry.d.ts +50 -0
- package/dist/tenancy/cloudflare/registry.js +301 -0
- package/dist/tenancy/cloudflare.d.ts +2 -0
- package/dist/tenancy/cloudflare.js +2 -0
- package/dist/tenancy/cold-directory.d.ts +13 -0
- package/dist/tenancy/cold-directory.js +43 -0
- package/dist/tenancy/contracts.d.ts +195 -0
- package/dist/tenancy/contracts.js +1 -0
- package/dist/tenancy/index.d.ts +19 -0
- package/dist/tenancy/index.js +156 -0
- package/dist/tenancy/owner-key.d.ts +26 -0
- package/dist/tenancy/owner-key.js +63 -0
- package/dist/tenancy/oyster.d.ts +4 -0
- package/dist/tenancy/oyster.js +25 -0
- package/dist/tenancy/registry-record.d.ts +7 -0
- package/dist/tenancy/registry-record.js +44 -0
- package/dist/tenancy/registry.d.ts +27 -0
- package/dist/tenancy/registry.js +138 -0
- package/dist/tenancy/resolve.d.ts +7 -0
- package/dist/tenancy/resolve.js +64 -0
- package/dist/tenant.d.ts +81 -0
- package/dist/tenant.js +71 -0
- package/dist/testing/capabilities.d.ts +29 -0
- package/dist/testing/capabilities.js +130 -0
- package/dist/testing/catalog.d.ts +3 -0
- package/dist/testing/catalog.js +53 -0
- package/dist/testing/index.d.ts +10 -0
- package/dist/testing/index.js +10 -0
- package/dist/testing/installation.d.ts +11 -0
- package/dist/testing/installation.js +25 -0
- package/dist/testing/native-worker.d.ts +21 -0
- package/dist/testing/native-worker.js +106 -0
- package/dist/testing/worker.d.ts +18 -0
- package/dist/testing/worker.js +67 -0
- package/dist/testing/workers-runtime.d.ts +24 -0
- package/dist/testing/workers-runtime.js +33 -0
- package/dist/webhook-delivery.d.ts +14 -0
- package/dist/webhook-delivery.js +17 -0
- package/dist/workers-git.d.ts +285 -0
- package/dist/workers-git.js +1174 -0
- package/dist/workers.d.ts +352 -0
- package/dist/workers.js +868 -0
- package/docs/api/README.md +82 -0
- package/docs/api/agents.md +332 -0
- package/docs/api/app-storage.md +209 -0
- package/docs/api/apps.md +1375 -0
- package/docs/api/auth.md +63 -0
- package/docs/api/build-contract.md +1304 -0
- package/docs/api/build.md +1712 -0
- package/docs/api/cloudflare.md +421 -0
- package/docs/api/computer.md +203 -0
- package/docs/api/files.md +117 -0
- package/docs/api/git.md +470 -0
- package/docs/api/host.md +1328 -0
- package/docs/api/index.md +430 -0
- package/docs/api/invocation.md +738 -0
- package/docs/api/keys.md +175 -0
- package/docs/api/mcp.md +301 -0
- package/docs/api/media.md +40 -0
- package/docs/api/modules.md +50 -0
- package/docs/api/pack.md +141 -0
- package/docs/api/page/mount.md +298 -0
- package/docs/api/root-data.md +111 -0
- package/docs/api/shell/app-directory.md +599 -0
- package/docs/api/shell/command-host.md +224 -0
- package/docs/api/shell/commands.md +197 -0
- package/docs/api/shell/keys.md +183 -0
- package/docs/api/shell/shell-module.md +204 -0
- package/docs/api/shell/window-manager.md +267 -0
- package/docs/api/shell/workbench-directory.md +119 -0
- package/docs/api/shell/workbench.md +205 -0
- package/docs/api/shell.md +1915 -0
- package/docs/api/tenancy/auth0.md +110 -0
- package/docs/api/tenancy/cloudflare.md +221 -0
- package/docs/api/tenancy/cold-directory.md +46 -0
- package/docs/api/tenancy/owner-key.md +63 -0
- package/docs/api/tenancy/registry.md +77 -0
- package/docs/api/tenancy/resolve.md +69 -0
- package/docs/api/tenancy.md +430 -0
- package/docs/api/tenant.md +194 -0
- package/docs/api/workers-git.md +1646 -0
- package/docs/api/workers.md +1691 -0
- package/docs/site/agent-quickstart.mdx +346 -0
- package/examples/standalone/worker.ts +7 -0
- package/examples/standalone/wrangler.jsonc +82 -0
- package/package.json +300 -3
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { Hono } from "hono";
|
|
2
|
+
import { bodyLimit } from "hono/body-limit";
|
|
3
|
+
import * as Schema from "effect/Schema";
|
|
4
|
+
import { AppInstanceError } from "../index.js";
|
|
5
|
+
import { catalogFrameMethods, oysterAppCatalog, appForkInputSchema, appForkResultSchema, } from "../catalog-frame.js";
|
|
6
|
+
import { parseTemplateSeedClaim, TEMPLATE_SEED_CLAIM_MAX_BYTES, } from "../source/index.js";
|
|
7
|
+
/** `catalogFrameMethods.APPS`'s descriptors already decode to
|
|
8
|
+
* `OysterBindingResult` at their owner (`catalog-frame.ts`); no re-typing
|
|
9
|
+
* is needed here. */
|
|
10
|
+
export const appSourceFrameMethods = catalogFrameMethods;
|
|
11
|
+
/** Serve `APPS.catalog`/`APPS.fork`; `fork` is admitted only for the tenant owner,
|
|
12
|
+
* re-checked here (never trusted from a stale captured frame). */
|
|
13
|
+
export async function dispatchAppSourceBinding(env, scope, frame, method, args) {
|
|
14
|
+
const registry = env.REGISTRY.getByName(scope.durableName("registry"));
|
|
15
|
+
if (method === "catalog")
|
|
16
|
+
return oysterAppCatalog(await registry.directory());
|
|
17
|
+
if (method !== "fork")
|
|
18
|
+
throw new AppInstanceError("revoked");
|
|
19
|
+
if (frame.facts.subject.kind !== "call" ||
|
|
20
|
+
frame.facts.subject.context.actor.kind !== "member" ||
|
|
21
|
+
frame.facts.subject.context.role !== "owner")
|
|
22
|
+
throw new AppInstanceError("revoked");
|
|
23
|
+
const [input] = Schema.decodeUnknownSync(Schema.Tuple([appForkInputSchema]))(args);
|
|
24
|
+
const installationId = frame.facts.selection.owner.id;
|
|
25
|
+
const result = await registry.forkApp({
|
|
26
|
+
teamId: scope.tenantId,
|
|
27
|
+
platformAppId: input.platformAppId,
|
|
28
|
+
installationId,
|
|
29
|
+
repositoryPrefix: scope.repositoryPrefix(installationId),
|
|
30
|
+
name: input.name,
|
|
31
|
+
});
|
|
32
|
+
return Schema.decodeUnknownSync(appForkResultSchema)(result);
|
|
33
|
+
}
|
|
34
|
+
/** Operator-only: seed one App's editable source template (`install-apps.mjs`).
|
|
35
|
+
* Mounted under `host.ownerRoutes`, already owner-key-gated for every `/api/*`
|
|
36
|
+
* path before this handler runs (`host.ts`'s `oysterPrincipalIsOwner` check).
|
|
37
|
+
* The operator `git push`es the projected source to
|
|
38
|
+
* `/api/apps/:appId/source.git`, then claims that head here. */
|
|
39
|
+
export function appSourceOwnerRoutes(env, scope) {
|
|
40
|
+
const routes = new Hono();
|
|
41
|
+
const registry = () => env.REGISTRY.getByName(scope.durableName("registry"));
|
|
42
|
+
routes.on(["GET", "POST"], "/api/apps/:appId/source.git/:suffix{info/refs|git-upload-pack|git-receive-pack}", (context) => registry().exchangeTemplateSource(context.req.param("appId"), context.req.param("suffix"), context.req.raw));
|
|
43
|
+
routes.put("/api/apps/:appId/source", bodyLimit({
|
|
44
|
+
maxSize: TEMPLATE_SEED_CLAIM_MAX_BYTES,
|
|
45
|
+
onError: (context) => context.json({ kind: "invalid" }, 413),
|
|
46
|
+
}), async (context) => {
|
|
47
|
+
const claim = parseTemplateSeedClaim(await context.req.text());
|
|
48
|
+
if (claim === null)
|
|
49
|
+
return context.json({ kind: "invalid" }, 400);
|
|
50
|
+
const result = await registry().publishSource(context.req.param("appId"), claim, scope.tenantId);
|
|
51
|
+
const status = result.kind === "recorded" || result.kind === "existing"
|
|
52
|
+
? 200
|
|
53
|
+
: result.kind === "conflict"
|
|
54
|
+
? 409
|
|
55
|
+
: 503;
|
|
56
|
+
return Response.json(result, { status });
|
|
57
|
+
});
|
|
58
|
+
return routes;
|
|
59
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Owner routes for App builds on the standalone's Computer. They run after
|
|
3
|
+
* the host's owner guard; the build owner is the registry.
|
|
4
|
+
*
|
|
5
|
+
* - `PUT /api/apps/:appId/build-source` records an installation repository
|
|
6
|
+
* and recipe an App builds from (the recipe is normalized and pinned here).
|
|
7
|
+
* Publishing an App's source (`PUT /api/apps/:appId/source`) instead makes
|
|
8
|
+
* its template the build source, buildable at the returned seed revision.
|
|
9
|
+
* - `POST /api/builds` starts a build of that source at one exact commit.
|
|
10
|
+
* - `GET /api/builds/:jobId` reads it; a finished build carries its Release,
|
|
11
|
+
* which the owner installs or selects through the installation routes.
|
|
12
|
+
*/
|
|
13
|
+
import { Hono } from "hono";
|
|
14
|
+
import { type OysterEnv } from "../host.js";
|
|
15
|
+
import type { StandaloneRegistry } from "./registry.js";
|
|
16
|
+
/** The build routes over the tenant registry that owns builds, resolved per
|
|
17
|
+
* request so mounting the routes reaches no registry. */
|
|
18
|
+
export declare function standaloneBuildRoutes(registryFor: () => DurableObjectStub<StandaloneRegistry>): Hono<OysterEnv>;
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Owner routes for App builds on the standalone's Computer. They run after
|
|
3
|
+
* the host's owner guard; the build owner is the registry.
|
|
4
|
+
*
|
|
5
|
+
* - `PUT /api/apps/:appId/build-source` records an installation repository
|
|
6
|
+
* and recipe an App builds from (the recipe is normalized and pinned here).
|
|
7
|
+
* Publishing an App's source (`PUT /api/apps/:appId/source`) instead makes
|
|
8
|
+
* its template the build source, buildable at the returned seed revision.
|
|
9
|
+
* - `POST /api/builds` starts a build of that source at one exact commit.
|
|
10
|
+
* - `GET /api/builds/:jobId` reads it; a finished build carries its Release,
|
|
11
|
+
* which the owner installs or selects through the installation routes.
|
|
12
|
+
*/
|
|
13
|
+
import { Hono } from "hono";
|
|
14
|
+
import * as Schema from "effect/Schema";
|
|
15
|
+
import { capturedPinnedRecipeSchema, normalizeAppPublisherRecipe, pinnedRecipeSchema, } from "../build-contract.js";
|
|
16
|
+
import { oysterBuildSourceSchema } from "../app-builds.js";
|
|
17
|
+
import { oysterPrincipalIsOwner } from "../host.js";
|
|
18
|
+
import { appIdSchema, releaseSchema, sourceRevisionSchema, } from "@fungi.computer/puffball/effect";
|
|
19
|
+
const sourceInput = Schema.Struct({
|
|
20
|
+
installationId: Schema.String,
|
|
21
|
+
repository: Schema.String,
|
|
22
|
+
recipe: pinnedRecipeSchema,
|
|
23
|
+
});
|
|
24
|
+
const buildInput = Schema.Struct({
|
|
25
|
+
appId: appIdSchema,
|
|
26
|
+
sourceRevision: sourceRevisionSchema,
|
|
27
|
+
});
|
|
28
|
+
/** The build routes over the tenant registry that owns builds, resolved per
|
|
29
|
+
* request so mounting the routes reaches no registry. */
|
|
30
|
+
export function standaloneBuildRoutes(registryFor) {
|
|
31
|
+
const routes = new Hono();
|
|
32
|
+
routes.use("/api/builds/*", async (context, next) => oysterPrincipalIsOwner(context.get("principal"))
|
|
33
|
+
? next()
|
|
34
|
+
: Response.json({ error: "Owner only" }, { status: 403 }));
|
|
35
|
+
routes.put("/api/apps/:appId/build-source", async (context) => {
|
|
36
|
+
if (!oysterPrincipalIsOwner(context.get("principal")))
|
|
37
|
+
return Response.json({ error: "Owner only" }, { status: 403 });
|
|
38
|
+
let source;
|
|
39
|
+
try {
|
|
40
|
+
const appId = Schema.decodeUnknownSync(appIdSchema)(context.req.param("appId"));
|
|
41
|
+
const input = Schema.decodeUnknownSync(sourceInput, {
|
|
42
|
+
onExcessProperty: "error",
|
|
43
|
+
})(await context.req.json());
|
|
44
|
+
const pinned = normalizeAppPublisherRecipe({
|
|
45
|
+
appId,
|
|
46
|
+
recipe: Schema.encodeSync(pinnedRecipeSchema)(input.recipe),
|
|
47
|
+
});
|
|
48
|
+
source = Schema.decodeUnknownSync(oysterBuildSourceSchema)({
|
|
49
|
+
appId,
|
|
50
|
+
location: {
|
|
51
|
+
kind: "installation",
|
|
52
|
+
installationId: input.installationId,
|
|
53
|
+
repository: input.repository,
|
|
54
|
+
},
|
|
55
|
+
recipe: Schema.encodeSync(capturedPinnedRecipeSchema)(pinned.recipe),
|
|
56
|
+
buildRecipeDigest: pinned.buildRecipeDigest,
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
return Response.json({ error: "Invalid build source" }, { status: 400 });
|
|
61
|
+
}
|
|
62
|
+
await registryFor().registerBuildSource(source);
|
|
63
|
+
return new Response(null, { status: 204 });
|
|
64
|
+
});
|
|
65
|
+
routes.post("/api/builds", async (context) => {
|
|
66
|
+
const principal = context.get("principal");
|
|
67
|
+
if (!oysterPrincipalIsOwner(principal))
|
|
68
|
+
return Response.json({ error: "Owner only" }, { status: 403 });
|
|
69
|
+
let input;
|
|
70
|
+
try {
|
|
71
|
+
input = Schema.decodeUnknownSync(buildInput, {
|
|
72
|
+
onExcessProperty: "error",
|
|
73
|
+
})(await context.req.json());
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
return Response.json({ error: "Invalid build" }, { status: 400 });
|
|
77
|
+
}
|
|
78
|
+
try {
|
|
79
|
+
const build = await registryFor().startBuild({
|
|
80
|
+
...input,
|
|
81
|
+
requestedBy: principal.actor.memberId,
|
|
82
|
+
});
|
|
83
|
+
return Response.json(build, { status: 202 });
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
return Response.json({ error: "App has no build source" }, { status: 409 });
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
routes.get("/api/builds/:jobId", async (context) => {
|
|
90
|
+
const build = await registryFor().readBuild(context.req.param("jobId"));
|
|
91
|
+
return build === null
|
|
92
|
+
? Response.json({ error: "Not found" }, { status: 404 })
|
|
93
|
+
: Response.json({
|
|
94
|
+
...build,
|
|
95
|
+
release: build.release === null
|
|
96
|
+
? null
|
|
97
|
+
: Schema.encodeUnknownSync(releaseSchema)(build.release),
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
return routes;
|
|
101
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { type BuildGitEvent } from "../build-egress.js";
|
|
2
|
+
import type { CandidateObject, CandidateObjectMediaType, TeamAppCandidateManifest } from "../build-contract.js";
|
|
3
|
+
import { type GitDoorTarget } from "../git.js";
|
|
4
|
+
/** Loopback props the ComputerDO sets; a guest cannot choose them. */
|
|
5
|
+
export type OysterComputerEgressProps = Readonly<{
|
|
6
|
+
computerObjectId: string;
|
|
7
|
+
}>;
|
|
8
|
+
/** The registry's build admission, for the one Computer this egress serves. */
|
|
9
|
+
export type OysterComputerBuildPort = Readonly<{
|
|
10
|
+
gitPassFor(target: GitDoorTarget): Promise<string | null>;
|
|
11
|
+
/** One read-only exchange with a template repository a live job names. */
|
|
12
|
+
exchangeTemplate(repository: string, request: Request): Promise<Response>;
|
|
13
|
+
admitBuild(jobId: string): Promise<Readonly<{
|
|
14
|
+
credentialId: string;
|
|
15
|
+
}> | null>;
|
|
16
|
+
/** One object upload for a live job's grant, and its size bound. */
|
|
17
|
+
admitObject(jobId: string, grant: string): Promise<Readonly<{
|
|
18
|
+
maxObjectBytes: number;
|
|
19
|
+
}> | null>;
|
|
20
|
+
/** Stream one admitted object into the host's Release store. */
|
|
21
|
+
storeObject(object: CandidateObject, body: ReadableStream<Uint8Array> | null, mediaType: CandidateObjectMediaType): Promise<"stored" | "refused">;
|
|
22
|
+
submitCandidate(jobId: string, manifest: TeamAppCandidateManifest, grant: string, credentialId: string): Promise<Response>;
|
|
23
|
+
record(event: BuildGitEvent): Promise<void>;
|
|
24
|
+
}>;
|
|
25
|
+
/**
|
|
26
|
+
* Admit one intercepted guest request. Oyster Git (on `origin`, or a build
|
|
27
|
+
* clone on the internal Git host) is handed to `forward`, the Oyster
|
|
28
|
+
* Worker's own fetch, whose door authenticates it as for any other client.
|
|
29
|
+
*/
|
|
30
|
+
export declare function serveOysterComputerEgress(request: Request, options: Readonly<{
|
|
31
|
+
origin: string;
|
|
32
|
+
computerId: string;
|
|
33
|
+
/** `COMPUTER_PUBLIC_EGRESS`; only `disabled` closes the public lane. */
|
|
34
|
+
publicEgress: string | undefined;
|
|
35
|
+
build: OysterComputerBuildPort;
|
|
36
|
+
forward(request: Request): Promise<Response>;
|
|
37
|
+
}>): Promise<Response>;
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Standalone Oyster's Computer egress: the same host policy Hub's
|
|
3
|
+
* ComputerEgress decides with. It serves three lanes:
|
|
4
|
+
*
|
|
5
|
+
* - Oyster's own origin Git, carrying only the guest's own credential;
|
|
6
|
+
* - the anonymous public download lane (package registries and release hosts
|
|
7
|
+
* the policy classifies), so a build can install its dependencies, behind
|
|
8
|
+
* the same `COMPUTER_PUBLIC_EGRESS=disabled` kill switch as Hub;
|
|
9
|
+
* - the internal build hosts: a live App build job's read-only clone on
|
|
10
|
+
* `git.team.internal` (an installation repository through the Git door,
|
|
11
|
+
* presented with a host-held job pass the guest never sees, or the App's
|
|
12
|
+
* template through the registry's read-only template exchange), and its
|
|
13
|
+
* `/jobs/:id/*` reports and object uploads on `build.team.internal`, each
|
|
14
|
+
* object streamed into the Release store without passing the registry.
|
|
15
|
+
*
|
|
16
|
+
* Team Git is refused, because this host serves no such lane.
|
|
17
|
+
*/
|
|
18
|
+
import { anonymousRequest, anonymousResponse, computerEgressDenied, decideComputerEgress, decidePublicLane, originGitRequest, publicEgressEnabled, recordComputerEgress, } from "@fungi.computer/computer/egress";
|
|
19
|
+
import { createBuildEgress } from "../build-egress.js";
|
|
20
|
+
import { oysterBuildGitHost, oysterBuildReportHost } from "../app-builds.js";
|
|
21
|
+
import { gitDoorRoute, smartHttpExchange } from "../git.js";
|
|
22
|
+
const templateRoute = /^\/([a-zA-Z0-9._-]{1,128}?)\.git\/(info\/refs|git-upload-pack|git-receive-pack)$/u;
|
|
23
|
+
/**
|
|
24
|
+
* Admit one intercepted guest request. Oyster Git (on `origin`, or a build
|
|
25
|
+
* clone on the internal Git host) is handed to `forward`, the Oyster
|
|
26
|
+
* Worker's own fetch, whose door authenticates it as for any other client.
|
|
27
|
+
*/
|
|
28
|
+
export async function serveOysterComputerEgress(request, options) {
|
|
29
|
+
const deny = (host, decision) => computerEgressDenied({
|
|
30
|
+
computerId: options.computerId,
|
|
31
|
+
teamId: null,
|
|
32
|
+
host,
|
|
33
|
+
method: request.method,
|
|
34
|
+
status: 403,
|
|
35
|
+
decision,
|
|
36
|
+
});
|
|
37
|
+
let url;
|
|
38
|
+
try {
|
|
39
|
+
url = new URL(request.url);
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
return deny("invalid", "invalid-url");
|
|
43
|
+
}
|
|
44
|
+
const decision = decideComputerEgress(request, url, {
|
|
45
|
+
origin: options.origin,
|
|
46
|
+
route: (request, url) => gitDoorRoute(request.method, url),
|
|
47
|
+
});
|
|
48
|
+
if (decision.kind === "denied")
|
|
49
|
+
return deny(url.hostname, decision.reason);
|
|
50
|
+
let response;
|
|
51
|
+
if (decision.kind === "origin-git")
|
|
52
|
+
response = await options.forward(originGitRequest(request, url));
|
|
53
|
+
else if (decision.kind === "public") {
|
|
54
|
+
const lane = decidePublicLane({
|
|
55
|
+
publicEgressEnabled: publicEgressEnabled(options.publicEgress),
|
|
56
|
+
teamDisabled: false,
|
|
57
|
+
computerInternetAccessEnabled: true,
|
|
58
|
+
});
|
|
59
|
+
if (lane.kind === "denied")
|
|
60
|
+
return deny(url.hostname, lane.reason);
|
|
61
|
+
response = anonymousResponse(await fetch(anonymousRequest(request, url)));
|
|
62
|
+
}
|
|
63
|
+
else if (decision.kind === "internal-git" &&
|
|
64
|
+
url.hostname === oysterBuildReportHost)
|
|
65
|
+
response = await createBuildEgress({
|
|
66
|
+
admitBuild: (jobId) => options.build.admitBuild(jobId),
|
|
67
|
+
admitObject: (jobId, grant) => options.build.admitObject(jobId, grant),
|
|
68
|
+
storeObject: (object, body, mediaType) => options.build.storeObject(object, body, mediaType),
|
|
69
|
+
submitCandidate: (jobId, manifest, grant, credentialId) => options.build.submitCandidate(jobId, manifest, grant, credentialId),
|
|
70
|
+
record: (event) => options.build.record(event),
|
|
71
|
+
}).fetch(request, url);
|
|
72
|
+
else if (decision.kind === "internal-git" &&
|
|
73
|
+
url.hostname === oysterBuildGitHost) {
|
|
74
|
+
const target = gitDoorRoute(request.method, url);
|
|
75
|
+
const template = target === null ? templateRoute.exec(url.pathname) : null;
|
|
76
|
+
if (template !== null) {
|
|
77
|
+
const [, repository = "", suffix = ""] = template;
|
|
78
|
+
if (smartHttpExchange(request.method, url, suffix)?.scope !== "read" ||
|
|
79
|
+
url.username ||
|
|
80
|
+
url.password)
|
|
81
|
+
return deny(url.hostname, "build-git-route");
|
|
82
|
+
response = await options.build.exchangeTemplate(repository, request);
|
|
83
|
+
if (response.status === 403)
|
|
84
|
+
return deny(url.hostname, "no-live-build");
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
if (target?.scope !== "read")
|
|
88
|
+
return deny(url.hostname, "build-git-route");
|
|
89
|
+
const pass = await options.build.gitPassFor(target);
|
|
90
|
+
if (pass === null)
|
|
91
|
+
return deny(url.hostname, "no-live-build");
|
|
92
|
+
const forwarded = originGitRequest(request, new URL(url.pathname + url.search, options.origin));
|
|
93
|
+
forwarded.headers.set("Authorization", `Bearer ${pass}`);
|
|
94
|
+
response = await options.forward(forwarded);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
else
|
|
98
|
+
return deny(url.hostname, `lane-not-offered:${decision.kind}`);
|
|
99
|
+
recordComputerEgress({
|
|
100
|
+
event: "computer.egress",
|
|
101
|
+
computerId: options.computerId,
|
|
102
|
+
teamId: null,
|
|
103
|
+
lane: decision.kind,
|
|
104
|
+
host: url.hostname,
|
|
105
|
+
method: request.method,
|
|
106
|
+
status: response.status,
|
|
107
|
+
decision: response.status < 400 ? "allowed" : "denied",
|
|
108
|
+
contentLength: response.headers.get("content-length"),
|
|
109
|
+
});
|
|
110
|
+
return response;
|
|
111
|
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { type AppMethodRefusedReply } from "@fungi.computer/agar/backend";
|
|
2
|
+
import { type WorkflowGitPassResult } from "@fungi.computer/agar/computer-contracts";
|
|
3
|
+
import { type ComputerPorts } from "@fungi.computer/agar/computer";
|
|
4
|
+
import { type PreviewSelector } from "./preview.js";
|
|
5
|
+
import { type StandaloneTenancyEnv } from "./tenancy.js";
|
|
6
|
+
import * as z from "zod";
|
|
7
|
+
import { computerProcessSchema, computerToolResultSchema, readOutputSchema } from "@fungi.computer/computer/operations";
|
|
8
|
+
import { ComputerDO as NativeComputerDO, listOutputSchema, writeOutputSchema, mkdirOutputSchema, computerPortsOutputSchema, computerPortOutputSchema, computerPortRemovalOutputSchema, processListOutputSchema, processKillOutputSchema, computerOperationResultSchema, type AttachedBackend, type ComputerEnv, type ComputerValue } from "@fungi.computer/computer";
|
|
9
|
+
import { type ComputerAction, type ComputerGrant } from "@fungi.computer/computer/fleet";
|
|
10
|
+
import type { InvocationFrame } from "../invocation.js";
|
|
11
|
+
import { type OysterCapability } from "../host.js";
|
|
12
|
+
import type { OysterBindingRow, OysterInvocationHost, OysterWorkerEnv, OysterWorkspace } from "../workers.js";
|
|
13
|
+
import type { OysterKeys, OysterKeysLivePort } from "../workers-git.js";
|
|
14
|
+
import type { StandaloneRegistry } from "./registry.js";
|
|
15
|
+
/** Native bindings selected by the standalone composition, never by an App. */
|
|
16
|
+
export type StandaloneComputerEnv = ComputerEnv & Pick<OysterWorkerEnv, "RELEASES"> & Readonly<{
|
|
17
|
+
OYSTER_COMPUTER_ID: string;
|
|
18
|
+
COMPUTER: DurableObjectNamespace<ComputerDO>;
|
|
19
|
+
/** The registry owns installations and this Computer's App build jobs. */
|
|
20
|
+
REGISTRY: DurableObjectNamespace<StandaloneRegistry>;
|
|
21
|
+
/** This single-tenant standalone's own named keys, for the `computer.use` grant check. */
|
|
22
|
+
KEYS: DurableObjectNamespace<OysterKeys>;
|
|
23
|
+
OYSTER_ORIGIN?: string;
|
|
24
|
+
OYSTER_PREVIEW_ORIGIN?: string;
|
|
25
|
+
OYSTER_OWNER_NAME?: string;
|
|
26
|
+
}>;
|
|
27
|
+
/** Finite native results cross the Workers RPC mapping without recursive JSON types. */
|
|
28
|
+
export type StandaloneComputerResult = WorkflowGitPassResult | z.output<typeof listOutputSchema> | Readonly<{
|
|
29
|
+
path: string;
|
|
30
|
+
byteOffset: number;
|
|
31
|
+
content: string;
|
|
32
|
+
encoding: "base64";
|
|
33
|
+
truncated: boolean;
|
|
34
|
+
}> | Readonly<{
|
|
35
|
+
error: "conflict";
|
|
36
|
+
}> | z.output<typeof writeOutputSchema> | z.output<typeof mkdirOutputSchema> | z.output<typeof computerPortsOutputSchema> | z.output<typeof computerPortOutputSchema> | z.output<typeof computerPortRemovalOutputSchema> | z.output<typeof processListOutputSchema> | z.output<typeof processKillOutputSchema> | z.output<typeof computerProcessSchema> | ComputerPorts["ports"][number] | z.output<typeof computerOperationResultSchema> | z.output<typeof readOutputSchema> | z.output<typeof computerToolResultSchema>;
|
|
37
|
+
/**
|
|
38
|
+
* A port-preview listing, or the typed refusal the owner route presents with
|
|
39
|
+
* {@link appMethodRefusalStatus}. A refusal never escapes as a 500.
|
|
40
|
+
*/
|
|
41
|
+
export type ComputerPortPreviewsReply = Readonly<{
|
|
42
|
+
kind: "ok";
|
|
43
|
+
value: ComputerPorts;
|
|
44
|
+
}> | AppMethodRefusedReply;
|
|
45
|
+
/**
|
|
46
|
+
* The Computer owner's bindings: the standalone Computer env plus the tenancy
|
|
47
|
+
* registry that names the live canonical owner a port preview must belong to.
|
|
48
|
+
*/
|
|
49
|
+
export type StandaloneComputerOwnerEnv = StandaloneComputerEnv & StandaloneTenancyEnv;
|
|
50
|
+
/** Durable workspace and existing backings, with fleet checks at native acceptance. */
|
|
51
|
+
export declare class ComputerDO extends NativeComputerDO {
|
|
52
|
+
#private;
|
|
53
|
+
private readonly previewBindings;
|
|
54
|
+
constructor(state: DurableObjectState, previewBindings: StandaloneComputerOwnerEnv);
|
|
55
|
+
/**
|
|
56
|
+
* Run one App build job's fixed command, under the build budget. The
|
|
57
|
+
* registry that minted the job re-admits it for this Computer first.
|
|
58
|
+
*/
|
|
59
|
+
runBuild(input: {
|
|
60
|
+
jobId: string;
|
|
61
|
+
command: string;
|
|
62
|
+
timeoutMs: number;
|
|
63
|
+
}): Promise<Readonly<{
|
|
64
|
+
output: {
|
|
65
|
+
exitCode: number | null;
|
|
66
|
+
stderr: string;
|
|
67
|
+
} | null;
|
|
68
|
+
}>>;
|
|
69
|
+
/** Issue preview tickets for every exposed port, or refuse typed. */
|
|
70
|
+
portPreviews(authority: ComputerGrant, selectorInput: PreviewSelector): Promise<ComputerPortPreviewsReply>;
|
|
71
|
+
portPreview(signature: string, port: number, request: Request): Promise<Response>;
|
|
72
|
+
protected attachedBackends(): readonly AttachedBackend[];
|
|
73
|
+
/** One advertised Computer tool, accepted against the host-issued grant. */
|
|
74
|
+
tool(authority: ComputerGrant, capabilityId: string, input: ComputerValue): Promise<StandaloneComputerResult>;
|
|
75
|
+
/** JSON operations keep the Computer's maintained parsers and execution engine. */
|
|
76
|
+
value(authority: ComputerGrant, action: ComputerAction, input: ComputerValue): Promise<StandaloneComputerResult>;
|
|
77
|
+
fileList(authority: ComputerGrant, path: string, limit: number, offset: number): Promise<{
|
|
78
|
+
path: string;
|
|
79
|
+
entries: {
|
|
80
|
+
name: string;
|
|
81
|
+
size: number;
|
|
82
|
+
modifiedAt: number;
|
|
83
|
+
type: "file" | "directory" | "symlink";
|
|
84
|
+
}[];
|
|
85
|
+
nextOffset: number | null;
|
|
86
|
+
} | null>;
|
|
87
|
+
/** Binary files share the same native filesystem as commands and PTYs. */
|
|
88
|
+
fileRead(authority: ComputerGrant, path: string): Promise<Uint8Array | null>;
|
|
89
|
+
fileWrite(authority: ComputerGrant, path: string, bytes: Uint8Array): Promise<void>;
|
|
90
|
+
fileReplace(authority: ComputerGrant, path: string, expectedContent: string, content: string): Promise<{
|
|
91
|
+
path: string;
|
|
92
|
+
bytes: number;
|
|
93
|
+
kind: "replaced";
|
|
94
|
+
} | {
|
|
95
|
+
kind: "conflict";
|
|
96
|
+
}>;
|
|
97
|
+
/** WebSocket upgrades use native Fetch, which can return a 101 response. */
|
|
98
|
+
fetch(request: Request): Promise<Response>;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Select the fixed native Computer once for the HTTP/MCP host and its
|
|
102
|
+
* adapters. `keysLive` is the host's own owner-checked `host.keys.live`
|
|
103
|
+
* port, threaded in by `oysterGitWorkerHost` before this workspace exists;
|
|
104
|
+
* a caller that only needs `.files` (the Media binding's own dispatch) may
|
|
105
|
+
* omit it, since a team-key principal then simply never reaches the
|
|
106
|
+
* Computer, rather than reading `env.KEYS` directly and skipping the check.
|
|
107
|
+
*/
|
|
108
|
+
export declare function standaloneWorkspace(env: StandaloneComputerEnv, keysLive?: OysterKeysLivePort): OysterWorkspace;
|
|
109
|
+
/**
|
|
110
|
+
* Owner/key capability routes over the selected Computer's native
|
|
111
|
+
* namespace. `keysLive` is the same owner-checked port `standaloneWorkspace`
|
|
112
|
+
* takes; a caller with none fails every team-key request closed.
|
|
113
|
+
*/
|
|
114
|
+
export declare function standaloneComputerCapabilities(env: StandaloneComputerEnv, workspace: OysterWorkspace, keysLive?: OysterKeysLivePort): readonly OysterCapability[];
|
|
115
|
+
/** The COMPUTER row's codecs, as the standalone Instance joins them. */
|
|
116
|
+
export type ComputerFrameMethods = Readonly<{
|
|
117
|
+
COMPUTER: OysterBindingRow<OysterWorkerEnv>["methods"];
|
|
118
|
+
}>;
|
|
119
|
+
/** Closed COMPUTER binding codecs, joined into the standalone Instance. */
|
|
120
|
+
export declare const computerFrameMethods: ComputerFrameMethods;
|
|
121
|
+
/** Dispatch one admitted COMPUTER operation under its captured frame. */
|
|
122
|
+
export declare function computerInvocation(env: StandaloneComputerEnv, frame: InvocationFrame<OysterInvocationHost>, method: string, args: readonly unknown[]): Promise<StandaloneComputerResult>;
|