@fungi.computer/oyster 0.0.0-stage → 0.0.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/LICENSE +21 -0
- package/README.md +1082 -2
- package/dist/agents.d.ts +114 -0
- package/dist/agents.js +322 -0
- package/dist/app-builds.d.ts +151 -0
- package/dist/app-builds.js +355 -0
- package/dist/app-sandbox.d.ts +2 -0
- package/dist/app-sandbox.js +2 -0
- package/dist/app-sources.d.ts +46 -0
- package/dist/app-sources.js +335 -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/build-jobs.d.ts +128 -0
- package/dist/build/build-jobs.js +197 -0
- package/dist/build/build-step.d.ts +26 -0
- package/dist/build/build-step.js +20 -0
- package/dist/build/bundled-html.d.ts +7 -0
- package/dist/build/bundled-html.js +64 -0
- package/dist/build/definition-emission.d.ts +34 -0
- package/dist/build/definition-emission.js +201 -0
- package/dist/build/effect.d.ts +231 -0
- package/dist/build/effect.js +396 -0
- package/dist/build/git-environment.d.ts +6 -0
- package/dist/build/git-environment.js +48 -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 +57 -0
- package/dist/build/local-build.js +1078 -0
- package/dist/build/r2-artifact-writer.d.ts +31 -0
- package/dist/build/r2-artifact-writer.js +179 -0
- package/dist/build/snapshot-package-store.d.ts +2 -0
- package/dist/build/snapshot-package-store.js +21 -0
- package/dist/build/snapshot-program-scripts.d.ts +6 -0
- package/dist/build/snapshot-program-scripts.js +2 -0
- package/dist/build/submission-contract.d.ts +384 -0
- package/dist/build/submission-contract.js +70 -0
- package/dist/build/workspace-build.d.ts +50 -0
- package/dist/build/workspace-build.js +156 -0
- package/dist/build-contract.d.ts +4 -0
- package/dist/build-contract.js +4 -0
- package/dist/build-jobs.d.ts +2 -0
- package/dist/build-jobs.js +2 -0
- package/dist/build.d.ts +11 -0
- package/dist/build.js +13 -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 +143 -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 +76 -0
- package/dist/pack.js +134 -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/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-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 +72 -0
- package/dist/release.js +164 -0
- package/dist/root-data.d.ts +86 -0
- package/dist/root-data.js +69 -0
- package/dist/shell/app-directory.d.ts +537 -0
- package/dist/shell/app-directory.js +195 -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 +31 -0
- package/dist/shell/shell-module.js +37 -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-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 +11 -0
- package/dist/source/index.js +11 -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-repository.d.ts +2 -0
- package/dist/source/template-repository.js +8 -0
- package/dist/source/template-write.d.ts +69 -0
- package/dist/source/template-write.js +344 -0
- package/dist/storage-frame.d.ts +64 -0
- package/dist/storage-frame.js +28 -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 +866 -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 +507 -0
- package/docs/api/build.md +1476 -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 +42 -0
- package/docs/api/pack.md +138 -0
- package/docs/api/page/mount.md +298 -0
- package/docs/api/root-data.md +111 -0
- package/docs/api/shell/app-directory.md +332 -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 +143 -0
- package/docs/api/shell/window-manager.md +267 -0
- package/docs/api/shell/workbench.md +186 -0
- package/docs/api/shell.md +1788 -0
- package/docs/api/tenant.md +194 -0
- package/docs/api/workers-git.md +1527 -0
- package/docs/api/workers.md +1678 -0
- package/docs/media/architecture.mmd +32 -0
- package/docs/media/architecture.png +0 -0
- package/docs/media/bonsai-network.png +0 -0
- package/docs/media/bonsai-repository.png +0 -0
- package/docs/site/agent-quickstart.mdx +349 -0
- package/examples/git/worker.ts +39 -0
- package/examples/git/wrangler.jsonc +32 -0
- package/examples/standalone/worker.ts +14 -0
- package/examples/standalone/wrangler.jsonc +21 -0
- package/package.json +249 -3
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import { type BackgroundSyncRefusal } from "@fungi.computer/puffball/background-replay";
|
|
2
|
+
import { type AppGitPushFact, type GitPushQuestion, type GitPushVerdict } from "@fungi.computer/agar/git";
|
|
3
|
+
import { type CairnId } from "@fungi.computer/cairn/id";
|
|
4
|
+
import * as Effect from "effect/Effect";
|
|
5
|
+
import * as Schema from "effect/Schema";
|
|
6
|
+
import type { DurableBackend } from "@fungi.computer/puffball/backend";
|
|
7
|
+
import type { Release, Sha256Digest } from "@fungi.computer/puffball";
|
|
8
|
+
import type { AppMethodReply } from "@fungi.computer/agar/backend";
|
|
9
|
+
import type { AppApi } from "@fungi.computer/puffball";
|
|
10
|
+
import type { InvocationFrame } from "./invocation.js";
|
|
11
|
+
import type { Installation } from "./apps/installation.js";
|
|
12
|
+
import { type GillCall, type GillReply } from "@fungi.computer/gills";
|
|
13
|
+
/** The Cairn ID family that mints and recognizes App Instance IDs (`instance_…`). */
|
|
14
|
+
export declare const instanceIds: Readonly<{
|
|
15
|
+
readonly instance: import("@fungi.computer/cairn").IdCodec<"app-instance", "instance", 1>;
|
|
16
|
+
}>;
|
|
17
|
+
/** A stable App Instance ID minted by {@link instanceIds}. */
|
|
18
|
+
export type InstanceId = CairnId<"app-instance", "instance">;
|
|
19
|
+
/** Decodes a string into an {@link InstanceId}, rejecting any other ID family. */
|
|
20
|
+
export declare const instanceIdSchema: Schema.Codec<InstanceId, string>;
|
|
21
|
+
/**
|
|
22
|
+
* The owner that issues authority for an Instance: an `installation` of an App
|
|
23
|
+
* or a `deployment`. Owner identity never changes for the life of an Instance.
|
|
24
|
+
*/
|
|
25
|
+
export declare const ownerSchema: Schema.Struct<{
|
|
26
|
+
readonly kind: Schema.Literals<readonly ["installation", "deployment"]>;
|
|
27
|
+
readonly id: Schema.NonEmptyString;
|
|
28
|
+
}>;
|
|
29
|
+
/**
|
|
30
|
+
* An owner-issued selection: which Instance, owner, App and Release a request
|
|
31
|
+
* runs against, at a positive authority version.
|
|
32
|
+
*/
|
|
33
|
+
export declare const selectionSchema: Schema.Struct<{
|
|
34
|
+
readonly instanceId: Schema.Codec<InstanceId, string, never, never>;
|
|
35
|
+
readonly owner: Schema.Struct<{
|
|
36
|
+
readonly kind: Schema.Literals<readonly ["installation", "deployment"]>;
|
|
37
|
+
readonly id: Schema.NonEmptyString;
|
|
38
|
+
}>;
|
|
39
|
+
readonly appId: Schema.refine<import("@fungi.computer/puffball").AppId, Schema.String>;
|
|
40
|
+
readonly releaseId: Schema.refine<import("@fungi.computer/puffball").ReleaseId, Schema.String>;
|
|
41
|
+
readonly authorityVersion: Schema.Number;
|
|
42
|
+
}>;
|
|
43
|
+
/** A complete owner-issued state: a selection plus `enabled`, `disabled` or `deleted`. */
|
|
44
|
+
export declare const ownerStateSchema: Schema.Struct<{
|
|
45
|
+
readonly status: Schema.Literals<readonly ["enabled", "disabled", "deleted"]>;
|
|
46
|
+
readonly instanceId: Schema.Codec<InstanceId, string, never, never>;
|
|
47
|
+
readonly owner: Schema.Struct<{
|
|
48
|
+
readonly kind: Schema.Literals<readonly ["installation", "deployment"]>;
|
|
49
|
+
readonly id: Schema.NonEmptyString;
|
|
50
|
+
}>;
|
|
51
|
+
readonly appId: Schema.refine<import("@fungi.computer/puffball").AppId, Schema.String>;
|
|
52
|
+
readonly releaseId: Schema.refine<import("@fungi.computer/puffball").ReleaseId, Schema.String>;
|
|
53
|
+
readonly authorityVersion: Schema.Number;
|
|
54
|
+
}>;
|
|
55
|
+
/** A decoded {@link ownerStateSchema} value, the input to `apply`. */
|
|
56
|
+
export type OwnerState = Schema.Schema.Type<typeof ownerStateSchema>;
|
|
57
|
+
/**
|
|
58
|
+
* The persisted Instance record: the last applied selection, its state-schema
|
|
59
|
+
* version, the owner's desired status and the runtime's own status.
|
|
60
|
+
*/
|
|
61
|
+
export declare const instanceSchema: Schema.Struct<{
|
|
62
|
+
readonly stateSchema: Schema.NullOr<Schema.Number>;
|
|
63
|
+
readonly desiredStatus: Schema.Literals<readonly ["enabled", "disabled", "deleted"]>;
|
|
64
|
+
readonly status: Schema.Literals<readonly ["active", "disabled", "deleting", "deleted"]>;
|
|
65
|
+
readonly instanceId: Schema.Codec<InstanceId, string, never, never>;
|
|
66
|
+
readonly owner: Schema.Struct<{
|
|
67
|
+
readonly kind: Schema.Literals<readonly ["installation", "deployment"]>;
|
|
68
|
+
readonly id: Schema.NonEmptyString;
|
|
69
|
+
}>;
|
|
70
|
+
readonly appId: Schema.refine<import("@fungi.computer/puffball").AppId, Schema.String>;
|
|
71
|
+
readonly releaseId: Schema.refine<import("@fungi.computer/puffball").ReleaseId, Schema.String>;
|
|
72
|
+
readonly authorityVersion: Schema.Number;
|
|
73
|
+
}>;
|
|
74
|
+
/** A decoded {@link selectionSchema} value, the authority for `fetch` and `call`. */
|
|
75
|
+
export type Selection = Schema.Schema.Type<typeof selectionSchema>;
|
|
76
|
+
/** The admitted selection for an installation's Instance. */
|
|
77
|
+
export declare function selectionOf(installation: Installation): Selection;
|
|
78
|
+
/** A decoded {@link instanceSchema} record, as returned by `apply` and `inspect`. */
|
|
79
|
+
export type Instance = Schema.Schema.Type<typeof instanceSchema>;
|
|
80
|
+
/** A resolved, verified durable backend: its Release, artifact digest and code. */
|
|
81
|
+
export type Backend = Readonly<{
|
|
82
|
+
release: Release;
|
|
83
|
+
digest: Sha256Digest;
|
|
84
|
+
backend: DurableBackend;
|
|
85
|
+
}>;
|
|
86
|
+
/** The runtime owns the bounded rejection grammar, including across RPC. */
|
|
87
|
+
export declare const appInstanceErrorCodeSchema: Schema.Literals<readonly ["invalid_owner_state", "invalid_invocation_facts", "revoked", "binding_not_granted", "binding_unavailable", "expired", "reentrant", "instance_identity_mismatch", "instance_owner_mismatch", "conflicting_owner_state", "stale_authority", "instance_deleted", "release_app_mismatch", "state_schema_migration_unsupported", "instance_inactive", "instance_authority_version_mismatch", "release_no_longer_matches_instance", "instance_release_mismatch", "installation_tenant_mismatch"]>;
|
|
88
|
+
/** One of the bounded {@link appInstanceErrorCodeSchema} rejection codes. */
|
|
89
|
+
export type AppInstanceErrorCode = typeof appInstanceErrorCodeSchema.Type;
|
|
90
|
+
/** Stable runtime failure identity; message remains presentation only. */
|
|
91
|
+
export declare class AppInstanceError extends Error {
|
|
92
|
+
readonly code: AppInstanceErrorCode;
|
|
93
|
+
readonly name = "AppInstanceError";
|
|
94
|
+
constructor(code: AppInstanceErrorCode, options?: ErrorOptions);
|
|
95
|
+
}
|
|
96
|
+
/** Supervisor schema checks do not open guest authority; execution carries a frame. */
|
|
97
|
+
export type BackendResolution<Host> = Readonly<{
|
|
98
|
+
kind: "metadata";
|
|
99
|
+
}> | Readonly<{
|
|
100
|
+
kind: "invocation";
|
|
101
|
+
frame: InvocationFrame<Host>;
|
|
102
|
+
}>;
|
|
103
|
+
/** Host storage, verified artifact resolution, and frame-bound execution ports. */
|
|
104
|
+
export type InstancePorts<Host> = Readonly<{
|
|
105
|
+
/** The stable ID every owner state and selection must name. */
|
|
106
|
+
instanceId: string;
|
|
107
|
+
/** Read the persisted Instance record, or `null` before the first apply. */
|
|
108
|
+
read: () => Effect.Effect<Instance | null, Error>;
|
|
109
|
+
/** Persist the Instance record. */
|
|
110
|
+
write: (instance: Instance) => Effect.Effect<void, Error>;
|
|
111
|
+
/** Resolve a Release ID to its verified durable backend. */
|
|
112
|
+
resolve: (release: Selection["releaseId"], scope: BackendResolution<Host>) => Effect.Effect<Backend, Error>;
|
|
113
|
+
/** Abort in-flight backend work and invalidate existing connections. */
|
|
114
|
+
abort: () => Effect.Effect<void, Error>;
|
|
115
|
+
/** Delete the backend's own state. */
|
|
116
|
+
purge: () => Effect.Effect<void, Error>;
|
|
117
|
+
/**
|
|
118
|
+
* Acquire a connection to the backend under admission. A later `abort` must
|
|
119
|
+
* invalidate connections acquired before it.
|
|
120
|
+
*/
|
|
121
|
+
connect: (backend: Backend, frame: InvocationFrame<Host>) => Effect.Effect<AppBackendConnection, Error>;
|
|
122
|
+
}>;
|
|
123
|
+
/** One admitted connection to a running App backend. */
|
|
124
|
+
export type AppBackendConnection = Readonly<{
|
|
125
|
+
/** Ask the App about the whole external push under this admitted connection. */
|
|
126
|
+
authorizeGitPush?: (question: GitPushQuestion) => Effect.Effect<GitPushVerdict, Error>;
|
|
127
|
+
/** Handle an accepted Git push under the same selected Instance authority. */
|
|
128
|
+
onGitPush?: (fact: AppGitPushFact) => Effect.Effect<void, Error>;
|
|
129
|
+
/** Settle the refused member lane, without binding authority. */
|
|
130
|
+
syncRefused?: (refusal: BackgroundSyncRefusal) => Effect.Effect<void, Error>;
|
|
131
|
+
/** Send an HTTP request to the backend. */
|
|
132
|
+
fetch: (request: Request) => Effect.Effect<Response, Error>;
|
|
133
|
+
/** Invoke a declared backend method under the connection's immutable frame;
|
|
134
|
+
* the App's typed refusal is a reply, not a failure. */
|
|
135
|
+
call: (name: string, input: Schema.Json) => Effect.Effect<AppMethodReply, Error>;
|
|
136
|
+
/** Invoke the generated Gill entry under the connection's immutable frame. */
|
|
137
|
+
gill: (cargo: Schema.Json) => Effect.Effect<Schema.Json, Error>;
|
|
138
|
+
}>;
|
|
139
|
+
/** The lifecycle and admission operations of one App Instance. */
|
|
140
|
+
export type AppInstance<Host> = Readonly<{
|
|
141
|
+
/**
|
|
142
|
+
* Apply a complete owner state. Exact retries return the recorded result;
|
|
143
|
+
* changed input at the same version conflicts and older versions are stale.
|
|
144
|
+
*/
|
|
145
|
+
apply: (input: OwnerState) => Effect.Effect<Instance, Error>;
|
|
146
|
+
/** Admit the frame's selection and forward an HTTP request to the active backend. */
|
|
147
|
+
fetch: (frame: InvocationFrame<Host>, request: Request) => Effect.Effect<Response, Error>;
|
|
148
|
+
/** Admit the frame and invoke a method declared by the Release's App API. */
|
|
149
|
+
call: (frame: InvocationFrame<Host>, api: AppApi, name: string, input: Schema.Json) => Effect.Effect<AppMethodReply, Error>;
|
|
150
|
+
/** Invoke a Gill's generated entry under its declared source read's frame. */
|
|
151
|
+
gill: (frame: InvocationFrame<Host>, api: AppApi, source: string, input: GillCall) => Effect.Effect<GillReply, Error>;
|
|
152
|
+
/** Authorize a whole external push through the same frame-bound connection. */
|
|
153
|
+
authorizeGitPush: (frame: InvocationFrame<Host>, question: GitPushQuestion) => Effect.Effect<GitPushVerdict, Error>;
|
|
154
|
+
/** Deliver a host-admitted Git push under exact live Instance selection. */
|
|
155
|
+
onGitPush: (frame: InvocationFrame<Host>, fact: AppGitPushFact) => Effect.Effect<void, Error>;
|
|
156
|
+
/** Deliver a zero-grant refusal to an active, freshly selected backend. */
|
|
157
|
+
syncRefused: (frame: InvocationFrame<Host>, refusal: BackgroundSyncRefusal) => Effect.Effect<void, Error>;
|
|
158
|
+
/** Read the current Instance record, resuming an interrupted purge. */
|
|
159
|
+
inspect: () => Effect.Effect<Instance | null, Error>;
|
|
160
|
+
}>;
|
|
161
|
+
/**
|
|
162
|
+
* Create the resource-local lifecycle for one App Instance over host ports.
|
|
163
|
+
* This is not admission: the host must obtain real authority from its owner
|
|
164
|
+
* before calling `apply`, `fetch` or `call`.
|
|
165
|
+
*/
|
|
166
|
+
export declare function createAppInstance<Host>(ports: InstancePorts<Host>): AppInstance<Host>;
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
import { backgroundSyncRefusalSchema, } from "@fungi.computer/puffball/background-replay";
|
|
2
|
+
import { appGitPushFactSchema, gitPushQuestionSchema, gitPushVerdictSchema, } from "@fungi.computer/agar/git";
|
|
3
|
+
import * as Semaphore from "effect/Semaphore";
|
|
4
|
+
import { defineIdFamily } from "@fungi.computer/cairn/id";
|
|
5
|
+
import * as Effect from "effect/Effect";
|
|
6
|
+
import * as Schema from "effect/Schema";
|
|
7
|
+
import { releaseIdSchema, appIdSchema } from "@fungi.computer/puffball/effect";
|
|
8
|
+
import { appFacetRequest, appFacetResponse } from "./backend-request.js";
|
|
9
|
+
import { appApiSchema } from "@fungi.computer/puffball/app";
|
|
10
|
+
import { gillCallSchema, gillReplySchema, } from "@fungi.computer/gills";
|
|
11
|
+
/** The Cairn ID family that mints and recognizes App Instance IDs (`instance_…`). */
|
|
12
|
+
export const instanceIds = defineIdFamily({
|
|
13
|
+
family: "app-instance",
|
|
14
|
+
version: 1,
|
|
15
|
+
kinds: { instance: { prefix: "instance" } },
|
|
16
|
+
});
|
|
17
|
+
/** Decodes a string into an {@link InstanceId}, rejecting any other ID family. */
|
|
18
|
+
export const instanceIdSchema = Schema.String.pipe(Schema.refine((value) => instanceIds.instance.is(value)));
|
|
19
|
+
/**
|
|
20
|
+
* The owner that issues authority for an Instance: an `installation` of an App
|
|
21
|
+
* or a `deployment`. Owner identity never changes for the life of an Instance.
|
|
22
|
+
*/
|
|
23
|
+
export const ownerSchema = Schema.Struct({
|
|
24
|
+
kind: Schema.Literals(["installation", "deployment"]),
|
|
25
|
+
id: Schema.NonEmptyString,
|
|
26
|
+
});
|
|
27
|
+
/**
|
|
28
|
+
* An owner-issued selection: which Instance, owner, App and Release a request
|
|
29
|
+
* runs against, at a positive authority version.
|
|
30
|
+
*/
|
|
31
|
+
export const selectionSchema = Schema.Struct({
|
|
32
|
+
instanceId: instanceIdSchema,
|
|
33
|
+
owner: ownerSchema,
|
|
34
|
+
appId: appIdSchema,
|
|
35
|
+
releaseId: releaseIdSchema,
|
|
36
|
+
authorityVersion: Schema.Number.pipe(Schema.check(Schema.isInt()), Schema.check(Schema.isGreaterThan(0))),
|
|
37
|
+
});
|
|
38
|
+
/** A complete owner-issued state: a selection plus `enabled`, `disabled` or `deleted`. */
|
|
39
|
+
export const ownerStateSchema = Schema.Struct({
|
|
40
|
+
...selectionSchema.fields,
|
|
41
|
+
status: Schema.Literals(["enabled", "disabled", "deleted"]),
|
|
42
|
+
});
|
|
43
|
+
/**
|
|
44
|
+
* The persisted Instance record: the last applied selection, its state-schema
|
|
45
|
+
* version, the owner's desired status and the runtime's own status.
|
|
46
|
+
*/
|
|
47
|
+
export const instanceSchema = Schema.Struct({
|
|
48
|
+
...selectionSchema.fields,
|
|
49
|
+
stateSchema: Schema.NullOr(Schema.Number),
|
|
50
|
+
desiredStatus: ownerStateSchema.fields.status,
|
|
51
|
+
status: Schema.Literals(["active", "disabled", "deleting", "deleted"]),
|
|
52
|
+
});
|
|
53
|
+
/** The admitted selection for an installation's Instance. */
|
|
54
|
+
export function selectionOf(installation) {
|
|
55
|
+
if (installation.instanceId === null)
|
|
56
|
+
throw new Error("Installation has no Instance");
|
|
57
|
+
return {
|
|
58
|
+
instanceId: installation.instanceId,
|
|
59
|
+
owner: { kind: "installation", id: installation.installationId },
|
|
60
|
+
appId: installation.release.appId,
|
|
61
|
+
releaseId: installation.release.releaseId,
|
|
62
|
+
authorityVersion: installation.authorityVersion,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
/** The runtime owns the bounded rejection grammar, including across RPC. */
|
|
66
|
+
export const appInstanceErrorCodeSchema = Schema.Literals([
|
|
67
|
+
"invalid_owner_state",
|
|
68
|
+
"invalid_invocation_facts",
|
|
69
|
+
"revoked",
|
|
70
|
+
"binding_not_granted",
|
|
71
|
+
"binding_unavailable",
|
|
72
|
+
"expired",
|
|
73
|
+
"reentrant",
|
|
74
|
+
"instance_identity_mismatch",
|
|
75
|
+
"instance_owner_mismatch",
|
|
76
|
+
"conflicting_owner_state",
|
|
77
|
+
"stale_authority",
|
|
78
|
+
"instance_deleted",
|
|
79
|
+
"release_app_mismatch",
|
|
80
|
+
"state_schema_migration_unsupported",
|
|
81
|
+
"instance_inactive",
|
|
82
|
+
"instance_authority_version_mismatch",
|
|
83
|
+
"release_no_longer_matches_instance",
|
|
84
|
+
"instance_release_mismatch",
|
|
85
|
+
"installation_tenant_mismatch",
|
|
86
|
+
]);
|
|
87
|
+
const APP_INSTANCE_ERROR_MESSAGES = {
|
|
88
|
+
invalid_owner_state: "Invalid owner state",
|
|
89
|
+
invalid_invocation_facts: "Invalid invocation facts",
|
|
90
|
+
revoked: "Invocation authority revoked",
|
|
91
|
+
binding_not_granted: "Binding operation not granted",
|
|
92
|
+
binding_unavailable: "Binding operation unavailable on this host",
|
|
93
|
+
expired: "Invocation deadline exceeded",
|
|
94
|
+
reentrant: "Invocation would re-enter an active Instance",
|
|
95
|
+
instance_identity_mismatch: "Instance identity mismatch",
|
|
96
|
+
instance_owner_mismatch: "Instance owner mismatch",
|
|
97
|
+
conflicting_owner_state: "Conflicting owner state",
|
|
98
|
+
stale_authority: "Stale authority",
|
|
99
|
+
instance_deleted: "Instance deleted",
|
|
100
|
+
release_app_mismatch: "Release belongs to another App",
|
|
101
|
+
state_schema_migration_unsupported: "State schema migration not supported",
|
|
102
|
+
instance_inactive: "Instance inactive",
|
|
103
|
+
instance_authority_version_mismatch: "Instance authority version mismatch",
|
|
104
|
+
release_no_longer_matches_instance: "Release no longer matches Instance",
|
|
105
|
+
instance_release_mismatch: "Instance release mismatch",
|
|
106
|
+
installation_tenant_mismatch: "Installation tenant mismatch",
|
|
107
|
+
};
|
|
108
|
+
/** Stable runtime failure identity; message remains presentation only. */
|
|
109
|
+
export class AppInstanceError extends Error {
|
|
110
|
+
code;
|
|
111
|
+
name = "AppInstanceError";
|
|
112
|
+
constructor(code, options) {
|
|
113
|
+
super(APP_INSTANCE_ERROR_MESSAGES[code], options);
|
|
114
|
+
this.code = code;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
const failure = (code) => Effect.fail(new AppInstanceError(code));
|
|
118
|
+
const sameOwner = (a, b) => a.instanceId === b.instanceId &&
|
|
119
|
+
a.owner.kind === b.owner.kind &&
|
|
120
|
+
a.owner.id === b.owner.id &&
|
|
121
|
+
a.appId === b.appId;
|
|
122
|
+
/**
|
|
123
|
+
* Create the resource-local lifecycle for one App Instance over host ports.
|
|
124
|
+
* This is not admission: the host must obtain real authority from its owner
|
|
125
|
+
* before calling `apply`, `fetch` or `call`.
|
|
126
|
+
*/
|
|
127
|
+
export function createAppInstance(ports) {
|
|
128
|
+
const lock = Semaphore.makeUnsafe(1);
|
|
129
|
+
const read = Effect.gen(function* () {
|
|
130
|
+
const current = yield* ports.read();
|
|
131
|
+
if (current?.status === "deleting") {
|
|
132
|
+
yield* ports.purge();
|
|
133
|
+
const deleted = { ...current, status: "deleted" };
|
|
134
|
+
yield* ports.write(deleted);
|
|
135
|
+
return deleted;
|
|
136
|
+
}
|
|
137
|
+
return current;
|
|
138
|
+
});
|
|
139
|
+
const apply = (input) => lock.withPermits(1)(Effect.gen(function* () {
|
|
140
|
+
const desired = yield* Schema.decodeUnknownEffect(ownerStateSchema)(input).pipe(Effect.mapError(() => new AppInstanceError("invalid_owner_state")));
|
|
141
|
+
if (desired.instanceId !== ports.instanceId)
|
|
142
|
+
return yield* failure("instance_identity_mismatch");
|
|
143
|
+
const current = yield* read;
|
|
144
|
+
if (current && !sameOwner(current, desired))
|
|
145
|
+
return yield* failure("instance_owner_mismatch");
|
|
146
|
+
const retry = current !== null &&
|
|
147
|
+
current.authorityVersion === desired.authorityVersion;
|
|
148
|
+
if (retry &&
|
|
149
|
+
(current.releaseId !== desired.releaseId ||
|
|
150
|
+
current.desiredStatus !== desired.status))
|
|
151
|
+
return yield* failure("conflicting_owner_state");
|
|
152
|
+
if (current && desired.authorityVersion < current.authorityVersion)
|
|
153
|
+
return yield* failure("stale_authority");
|
|
154
|
+
if (current?.status === "deleted") {
|
|
155
|
+
if (retry && desired.status === "deleted")
|
|
156
|
+
return current;
|
|
157
|
+
return yield* failure("instance_deleted");
|
|
158
|
+
}
|
|
159
|
+
if (retry && current.status === "active")
|
|
160
|
+
return current;
|
|
161
|
+
let stateSchema = current?.stateSchema ?? null;
|
|
162
|
+
if (desired.status === "enabled") {
|
|
163
|
+
const resolved = yield* ports.resolve(desired.releaseId, {
|
|
164
|
+
kind: "metadata",
|
|
165
|
+
});
|
|
166
|
+
if (resolved.release.appId !== desired.appId)
|
|
167
|
+
return yield* failure("release_app_mismatch");
|
|
168
|
+
if (stateSchema !== null &&
|
|
169
|
+
stateSchema !== resolved.backend.runtime.stateSchema)
|
|
170
|
+
return yield* failure("state_schema_migration_unsupported");
|
|
171
|
+
stateSchema = resolved.backend.runtime.stateSchema;
|
|
172
|
+
}
|
|
173
|
+
const next = {
|
|
174
|
+
...desired,
|
|
175
|
+
desiredStatus: desired.status,
|
|
176
|
+
stateSchema,
|
|
177
|
+
status: desired.status === "deleted"
|
|
178
|
+
? "deleting"
|
|
179
|
+
: "disabled",
|
|
180
|
+
};
|
|
181
|
+
// Persist the exact owner fact before stopping runtime; no resource-issued version.
|
|
182
|
+
yield* ports.write(next);
|
|
183
|
+
yield* ports.abort();
|
|
184
|
+
if (desired.status === "deleted")
|
|
185
|
+
yield* ports.purge();
|
|
186
|
+
const receipt = {
|
|
187
|
+
...next,
|
|
188
|
+
status: desired.status === "enabled"
|
|
189
|
+
? "active"
|
|
190
|
+
: desired.status === "deleted"
|
|
191
|
+
? "deleted"
|
|
192
|
+
: "disabled",
|
|
193
|
+
};
|
|
194
|
+
yield* ports.write(receipt);
|
|
195
|
+
return receipt;
|
|
196
|
+
}));
|
|
197
|
+
const connect = (frame) => lock.withPermits(1)(Effect.gen(function* () {
|
|
198
|
+
yield* Effect.try({
|
|
199
|
+
try: () => {
|
|
200
|
+
frame.signal.throwIfAborted();
|
|
201
|
+
},
|
|
202
|
+
catch: () => new AppInstanceError("revoked"),
|
|
203
|
+
});
|
|
204
|
+
if (frame.facts.deadlineAt <= Date.now())
|
|
205
|
+
return yield* failure("expired");
|
|
206
|
+
const authority = frame.facts.selection;
|
|
207
|
+
if (frame.facts.instanceId !== ports.instanceId)
|
|
208
|
+
return yield* failure("instance_identity_mismatch");
|
|
209
|
+
const current = yield* read;
|
|
210
|
+
if (current?.status !== "active")
|
|
211
|
+
return yield* failure("instance_inactive");
|
|
212
|
+
if (!sameOwner(current, authority))
|
|
213
|
+
return yield* failure("instance_owner_mismatch");
|
|
214
|
+
if (current.authorityVersion !== authority.authorityVersion)
|
|
215
|
+
return yield* failure("instance_authority_version_mismatch");
|
|
216
|
+
const scope = { kind: "invocation", frame };
|
|
217
|
+
const storedBackend = yield* ports.resolve(current.releaseId, scope);
|
|
218
|
+
if (storedBackend.release.appId !== current.appId ||
|
|
219
|
+
storedBackend.backend.runtime.stateSchema !== current.stateSchema)
|
|
220
|
+
return yield* failure("release_no_longer_matches_instance");
|
|
221
|
+
const backend = authority.releaseId === current.releaseId
|
|
222
|
+
? storedBackend
|
|
223
|
+
: yield* ports.resolve(authority.releaseId, scope);
|
|
224
|
+
if (backend.release.appId !== authority.appId ||
|
|
225
|
+
backend.backend.runtime.stateSchema !== current.stateSchema)
|
|
226
|
+
return yield* failure("release_no_longer_matches_instance");
|
|
227
|
+
if (backend.digest !== storedBackend.digest)
|
|
228
|
+
return yield* failure("instance_release_mismatch");
|
|
229
|
+
if (backend.digest !== frame.facts.artifactDigest)
|
|
230
|
+
return yield* failure("instance_release_mismatch");
|
|
231
|
+
yield* Effect.try({
|
|
232
|
+
try: () => {
|
|
233
|
+
frame.signal.throwIfAborted();
|
|
234
|
+
},
|
|
235
|
+
catch: () => new AppInstanceError("revoked"),
|
|
236
|
+
});
|
|
237
|
+
if (frame.facts.deadlineAt <= Date.now())
|
|
238
|
+
return yield* failure("expired");
|
|
239
|
+
return yield* ports.connect(backend, frame);
|
|
240
|
+
}));
|
|
241
|
+
const fetch = (frame, request) => connect(frame).pipe(Effect.flatMap((connection) => connection
|
|
242
|
+
.fetch(appFacetRequest(request))
|
|
243
|
+
.pipe(Effect.map(appFacetResponse))));
|
|
244
|
+
const call = (frame, api, name, input) => frame.facts.subject.kind !== "call"
|
|
245
|
+
? failure("invalid_invocation_facts")
|
|
246
|
+
: Schema.decodeUnknownEffect(appApiSchema)(api).pipe(Effect.mapError((cause) => new Error("App method declaration invalid", { cause })), Effect.flatMap((declaration) => {
|
|
247
|
+
if (!Object.hasOwn(declaration.methods, name))
|
|
248
|
+
return Effect.fail(new Error("App method not declared"));
|
|
249
|
+
const subject = frame.facts.subject;
|
|
250
|
+
if (subject.kind !== "call" ||
|
|
251
|
+
(subject.method !== undefined &&
|
|
252
|
+
(subject.method.name !== name ||
|
|
253
|
+
subject.method.effect !== declaration.methods[name]?.effect)))
|
|
254
|
+
return failure("invalid_invocation_facts");
|
|
255
|
+
return connect(frame).pipe(Effect.flatMap((connection) => connection.call(name, input)));
|
|
256
|
+
}), Effect.mapError((cause) => cause instanceof Error
|
|
257
|
+
? cause
|
|
258
|
+
: new Error("App method context invalid", { cause })));
|
|
259
|
+
const gill = (frame, api, source, input) => Effect.gen(function* () {
|
|
260
|
+
if (frame.facts.subject.kind !== "call")
|
|
261
|
+
return yield* failure("invalid_invocation_facts");
|
|
262
|
+
const declaration = yield* Schema.decodeUnknownEffect(appApiSchema)(api);
|
|
263
|
+
if (!Object.hasOwn(declaration.methods, source) ||
|
|
264
|
+
declaration.methods[source]?.effect !== "read")
|
|
265
|
+
return yield* Effect.fail(new Error("Gill source must be a declared read method"));
|
|
266
|
+
const selected = yield* Schema.decodeUnknownEffect(gillCallSchema)(input, { onExcessProperty: "error" });
|
|
267
|
+
const cargo = yield* Schema.decodeUnknownEffect(Schema.Json)(selected);
|
|
268
|
+
const connection = yield* connect(frame);
|
|
269
|
+
const result = yield* connection.gill(cargo);
|
|
270
|
+
return yield* Schema.decodeUnknownEffect(gillReplySchema)(result, {
|
|
271
|
+
onExcessProperty: "error",
|
|
272
|
+
});
|
|
273
|
+
}).pipe(Effect.mapError((cause) => cause instanceof Error
|
|
274
|
+
? cause
|
|
275
|
+
: new Error("App Gill context invalid", { cause })));
|
|
276
|
+
const authorizeGitPush = (frame, raw) => Effect.try({
|
|
277
|
+
try: () => gitPushQuestionSchema.parse(raw),
|
|
278
|
+
catch: (cause) => new Error("Git push question invalid", { cause }),
|
|
279
|
+
}).pipe(Effect.flatMap((question) => connect(frame).pipe(Effect.flatMap((connection) => connection.authorizeGitPush === undefined
|
|
280
|
+
? Effect.fail(new Error("App Git push authorizer unavailable"))
|
|
281
|
+
: connection.authorizeGitPush(question)))), Effect.flatMap((verdict) => Effect.try({
|
|
282
|
+
try: () => gitPushVerdictSchema.parse(verdict),
|
|
283
|
+
catch: (cause) => new Error("Git push verdict invalid", { cause }),
|
|
284
|
+
})));
|
|
285
|
+
const onGitPush = (frame, raw) => Effect.try({
|
|
286
|
+
try: () => appGitPushFactSchema.parse(raw),
|
|
287
|
+
catch: (cause) => new Error("Git push fact invalid", { cause }),
|
|
288
|
+
}).pipe(Effect.flatMap((fact) => frame.facts.subject.kind !== "lifecycle" ||
|
|
289
|
+
frame.facts.subject.name !== "onGitPush" ||
|
|
290
|
+
frame.facts.subject.eventId !== fact.eventId
|
|
291
|
+
? failure("invalid_invocation_facts")
|
|
292
|
+
: connect(frame).pipe(Effect.flatMap((connection) => connection.onGitPush === undefined
|
|
293
|
+
? Effect.fail(new Error("App Git push handler unavailable"))
|
|
294
|
+
: connection.onGitPush(fact)))));
|
|
295
|
+
const syncRefused = (frame, raw) => Effect.try({
|
|
296
|
+
try: () => backgroundSyncRefusalSchema.parse(raw),
|
|
297
|
+
catch: (cause) => new Error("Background refusal invalid", { cause }),
|
|
298
|
+
}).pipe(Effect.flatMap((refusal) => {
|
|
299
|
+
const subject = frame.facts.subject;
|
|
300
|
+
if (subject.kind !== "lifecycle" ||
|
|
301
|
+
subject.name !== "syncRefused" ||
|
|
302
|
+
frame.facts.grants.length !== 0 ||
|
|
303
|
+
subject.lane !== refusal.lane ||
|
|
304
|
+
subject.code !== refusal.code)
|
|
305
|
+
return failure("invalid_invocation_facts");
|
|
306
|
+
return connect(frame).pipe(Effect.flatMap((connection) => connection.syncRefused === undefined
|
|
307
|
+
? Effect.fail(new Error("App background refusal handler unavailable"))
|
|
308
|
+
: connection.syncRefused(refusal)));
|
|
309
|
+
}));
|
|
310
|
+
return {
|
|
311
|
+
syncRefused,
|
|
312
|
+
apply,
|
|
313
|
+
fetch,
|
|
314
|
+
call,
|
|
315
|
+
gill,
|
|
316
|
+
authorizeGitPush,
|
|
317
|
+
onGitPush,
|
|
318
|
+
inspect: () => lock.withPermits(1)(read),
|
|
319
|
+
};
|
|
320
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import * as Schema from "effect/Schema";
|
|
2
|
+
/** Private view projection. The installation reducer decodes all submitted grants. */
|
|
3
|
+
export declare const installationConsentSchema: Schema.Struct<{
|
|
4
|
+
readonly installationId: Schema.String;
|
|
5
|
+
readonly appId: Schema.String;
|
|
6
|
+
readonly appName: Schema.String;
|
|
7
|
+
readonly authorityVersion: Schema.Int;
|
|
8
|
+
readonly artifactDigest: Schema.refine<import("@fungi.computer/puffball").Sha256Digest, Schema.String>;
|
|
9
|
+
readonly capabilities: Schema.$Array<Schema.Struct<{
|
|
10
|
+
readonly request: Schema.Struct<{
|
|
11
|
+
readonly capability: Schema.Literals<readonly ["agent.list", "agent.manage", "agent.rename", "agent.toolPolicy", "agent.read", "agent.send", "computer.list", "computer.readFile", "computer.writeFile", "computer.execute", "llm.generation", "media.generation", "games:servers", "git.repository", "apps.open", "apps.call", "app.storage", "app.events.emit", "team.workbenchHost", "marketplace.browse", "marketplace.purchase", "workflow.run", "workflow.publish", "workflow.arm", "world", "channels.read", "channels.personal.manage"]>;
|
|
12
|
+
readonly operations: Schema.$Array<Schema.String>;
|
|
13
|
+
readonly resources: Schema.Codec<Schema.Json, Schema.Json, never, never>;
|
|
14
|
+
}>;
|
|
15
|
+
readonly approved: Schema.Boolean;
|
|
16
|
+
readonly description: Schema.String;
|
|
17
|
+
readonly scope: Schema.String;
|
|
18
|
+
}>>;
|
|
19
|
+
}>;
|
|
20
|
+
/** Human consent copy; these labels never decide authority or operation mappings. */
|
|
21
|
+
export declare const capabilityLabels: {
|
|
22
|
+
"agent.list": string;
|
|
23
|
+
"agent.manage": string;
|
|
24
|
+
"agent.rename": string;
|
|
25
|
+
"agent.toolPolicy": string;
|
|
26
|
+
"agent.read": string;
|
|
27
|
+
"agent.send": string;
|
|
28
|
+
"computer.list": string;
|
|
29
|
+
"computer.readFile": string;
|
|
30
|
+
"computer.writeFile": string;
|
|
31
|
+
"computer.execute": string;
|
|
32
|
+
"llm.generation": string;
|
|
33
|
+
"media.generation": string;
|
|
34
|
+
"games:servers": string;
|
|
35
|
+
"git.repository": string;
|
|
36
|
+
"apps.open": string;
|
|
37
|
+
"apps.call": string;
|
|
38
|
+
"app.storage": string;
|
|
39
|
+
"app.events.emit": string;
|
|
40
|
+
"team.workbenchHost": string;
|
|
41
|
+
"marketplace.browse": string;
|
|
42
|
+
"marketplace.purchase": string;
|
|
43
|
+
"workflow.run": string;
|
|
44
|
+
"workflow.publish": string;
|
|
45
|
+
"workflow.arm": string;
|
|
46
|
+
world: string;
|
|
47
|
+
"channels.read": string;
|
|
48
|
+
"channels.personal.manage": string;
|
|
49
|
+
};
|
|
50
|
+
/** Plain descriptions of the declared capability; copy never infers extra access. */
|
|
51
|
+
export declare const capabilityDescriptions: {
|
|
52
|
+
"agent.list": string;
|
|
53
|
+
"agent.manage": string;
|
|
54
|
+
"agent.rename": string;
|
|
55
|
+
"agent.toolPolicy": string;
|
|
56
|
+
"agent.read": string;
|
|
57
|
+
"agent.send": string;
|
|
58
|
+
"computer.list": string;
|
|
59
|
+
"computer.readFile": string;
|
|
60
|
+
"computer.writeFile": string;
|
|
61
|
+
"computer.execute": string;
|
|
62
|
+
"llm.generation": string;
|
|
63
|
+
"media.generation": string;
|
|
64
|
+
"games:servers": string;
|
|
65
|
+
"git.repository": string;
|
|
66
|
+
"apps.open": string;
|
|
67
|
+
"apps.call": string;
|
|
68
|
+
"app.storage": string;
|
|
69
|
+
"app.events.emit": string;
|
|
70
|
+
"team.workbenchHost": string;
|
|
71
|
+
"marketplace.browse": string;
|
|
72
|
+
"marketplace.purchase": string;
|
|
73
|
+
"workflow.run": string;
|
|
74
|
+
"workflow.publish": string;
|
|
75
|
+
"workflow.arm": string;
|
|
76
|
+
world: string;
|
|
77
|
+
"channels.read": string;
|
|
78
|
+
"channels.personal.manage": string;
|
|
79
|
+
};
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import * as Schema from "effect/Schema";
|
|
2
|
+
import { sha256DigestSchema } from "@fungi.computer/puffball/effect";
|
|
3
|
+
import { appCapabilityIdSchema, } from "@fungi.computer/puffball/app";
|
|
4
|
+
/** Private view projection. The installation reducer decodes all submitted grants. */
|
|
5
|
+
export const installationConsentSchema = Schema.Struct({
|
|
6
|
+
installationId: Schema.String,
|
|
7
|
+
appId: Schema.String,
|
|
8
|
+
appName: Schema.String,
|
|
9
|
+
authorityVersion: Schema.Int,
|
|
10
|
+
artifactDigest: sha256DigestSchema,
|
|
11
|
+
capabilities: Schema.Array(Schema.Struct({
|
|
12
|
+
request: Schema.Struct({
|
|
13
|
+
capability: appCapabilityIdSchema,
|
|
14
|
+
operations: Schema.Array(Schema.String),
|
|
15
|
+
// Preserve the owner's resource selector as JSON; this view cannot mint authority.
|
|
16
|
+
resources: Schema.Json,
|
|
17
|
+
}),
|
|
18
|
+
approved: Schema.Boolean,
|
|
19
|
+
description: Schema.String,
|
|
20
|
+
scope: Schema.String,
|
|
21
|
+
})),
|
|
22
|
+
});
|
|
23
|
+
/** Human consent copy; these labels never decide authority or operation mappings. */
|
|
24
|
+
export const capabilityLabels = {
|
|
25
|
+
"agent.list": "List Agents",
|
|
26
|
+
"agent.manage": "Create and manage Agents",
|
|
27
|
+
"agent.rename": "Rename Agents",
|
|
28
|
+
"agent.toolPolicy": "Allow Agents to use Apps",
|
|
29
|
+
"agent.read": "Read Agent conversations",
|
|
30
|
+
"agent.send": "Send messages to Agents",
|
|
31
|
+
"computer.list": "List Computers",
|
|
32
|
+
"computer.readFile": "Read files",
|
|
33
|
+
"computer.writeFile": "Change files",
|
|
34
|
+
"computer.execute": "Run commands",
|
|
35
|
+
"llm.generation": "Generate text",
|
|
36
|
+
"media.generation": "Generate images and videos",
|
|
37
|
+
"games:servers": "Use game servers",
|
|
38
|
+
"git.repository": "Use Git repositories",
|
|
39
|
+
"apps.open": "Open Apps",
|
|
40
|
+
"apps.call": "Call declared App methods",
|
|
41
|
+
"app.storage": "Store App data",
|
|
42
|
+
"app.events.emit": "Send events",
|
|
43
|
+
"team.workbenchHost": "Arrange the workbench",
|
|
44
|
+
"marketplace.browse": "Browse the marketplace",
|
|
45
|
+
"marketplace.purchase": "Purchase from the marketplace",
|
|
46
|
+
"workflow.run": "Run Workflows",
|
|
47
|
+
"workflow.publish": "Publish Workflows",
|
|
48
|
+
"workflow.arm": "Arm Workflow triggers",
|
|
49
|
+
world: "Connect to external services",
|
|
50
|
+
"channels.read": "Read channels",
|
|
51
|
+
"channels.personal.manage": "Manage personal channels",
|
|
52
|
+
};
|
|
53
|
+
/** Plain descriptions of the declared capability; copy never infers extra access. */
|
|
54
|
+
export const capabilityDescriptions = {
|
|
55
|
+
"agent.list": "see which Agents are available.",
|
|
56
|
+
"agent.manage": "create Agents and manage their sessions.",
|
|
57
|
+
"agent.rename": "rename Agents you created.",
|
|
58
|
+
"agent.toolPolicy": "approve Agents to use your installed Apps.",
|
|
59
|
+
"agent.read": "read Agent conversations.",
|
|
60
|
+
"agent.send": "send messages to Agents.",
|
|
61
|
+
"computer.list": "see which Computers are available.",
|
|
62
|
+
"computer.readFile": "read files on your Computers.",
|
|
63
|
+
"computer.writeFile": "create and change files on your Computers.",
|
|
64
|
+
"computer.execute": "run commands on your Computers.",
|
|
65
|
+
"llm.generation": "ask AI models to generate text.",
|
|
66
|
+
"media.generation": "ask AI models to generate images and videos.",
|
|
67
|
+
"games:servers": "connect to game servers.",
|
|
68
|
+
"git.repository": "work with Git repositories.",
|
|
69
|
+
"apps.open": "open other Apps.",
|
|
70
|
+
"apps.call": "call the listed methods in other Apps as you. Renamed target methods need approval again.",
|
|
71
|
+
"app.storage": "save its App data on your Oyster.",
|
|
72
|
+
"app.events.emit": "tell your Workflows when something happens.",
|
|
73
|
+
"team.workbenchHost": "arrange Apps on your workbench.",
|
|
74
|
+
"marketplace.browse": "browse Apps in the marketplace.",
|
|
75
|
+
"marketplace.purchase": "purchase Apps from the marketplace.",
|
|
76
|
+
"workflow.run": "start Workflows.",
|
|
77
|
+
"workflow.publish": "publish Workflow definitions.",
|
|
78
|
+
"workflow.arm": "enable Workflow triggers.",
|
|
79
|
+
world: "connect to external services.",
|
|
80
|
+
"channels.read": "read messages from channels.",
|
|
81
|
+
"channels.personal.manage": "manage personal channels.",
|
|
82
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { admitMethodMetadata } from "../apps/gate.js";
|
|
2
|
+
/** Project only write origins admitted by this frame's fresh canonical gate. */
|
|
3
|
+
export function admittedBackgroundOrigins(gate) {
|
|
4
|
+
return Object.entries(gate.api?.methods ?? {})
|
|
5
|
+
.filter(([name, method]) => method.effect === "write" &&
|
|
6
|
+
admitMethodMetadata(gate, name).kind === "admitted")
|
|
7
|
+
.map(([name]) => name);
|
|
8
|
+
}
|