@fungi.computer/oyster 0.2.0 → 0.3.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.
Files changed (287) hide show
  1. package/README.md +376 -72
  2. package/dist/agents.d.ts +3 -2
  3. package/dist/agents.js +4 -2
  4. package/dist/app-builds.d.ts +81 -8
  5. package/dist/app-builds.js +210 -60
  6. package/dist/app-storage.d.ts +3 -3
  7. package/dist/approval-capability.d.ts +5 -0
  8. package/dist/approval-capability.js +46 -0
  9. package/dist/approval-home.d.ts +51 -0
  10. package/dist/approval-home.js +226 -0
  11. package/dist/apps/catalog.d.ts +335 -0
  12. package/dist/apps/catalog.js +46 -0
  13. package/dist/apps/gate.d.ts +20 -6
  14. package/dist/apps/gate.js +42 -16
  15. package/dist/apps/index.d.ts +3 -1
  16. package/dist/apps/index.js +3 -1
  17. package/dist/apps/installation.d.ts +132 -51
  18. package/dist/apps/installation.js +176 -70
  19. package/dist/apps/permissions.d.ts +247 -0
  20. package/dist/apps/permissions.js +255 -0
  21. package/dist/boot-capability-route.d.ts +15 -0
  22. package/dist/boot-capability-route.js +196 -0
  23. package/dist/boot-capability.d.ts +103 -0
  24. package/dist/boot-capability.js +141 -0
  25. package/dist/boot-construction.d.ts +12 -0
  26. package/dist/boot-construction.js +175 -0
  27. package/dist/boot-gate.d.ts +267 -0
  28. package/dist/boot-gate.js +236 -0
  29. package/dist/boot-host-script.d.ts +2 -0
  30. package/dist/boot-host-script.js +1 -0
  31. package/dist/boot-mount.d.ts +21 -0
  32. package/dist/boot-mount.js +65 -0
  33. package/dist/browser/boot.d.ts +73 -0
  34. package/dist/browser/boot.js +502 -0
  35. package/dist/browser/browser.d.ts +104 -0
  36. package/dist/browser/browser.js +374 -0
  37. package/dist/browser/capability-source.d.ts +76 -0
  38. package/dist/browser/capability-source.js +338 -0
  39. package/dist/browser/cdp-client.d.ts +56 -0
  40. package/dist/browser/cdp-client.js +184 -0
  41. package/dist/browser/errors.d.ts +79 -0
  42. package/dist/browser/errors.js +32 -0
  43. package/dist/browser/index.d.ts +16 -0
  44. package/dist/browser/index.js +14 -0
  45. package/dist/browser/lease-registry.d.ts +61 -0
  46. package/dist/browser/lease-registry.js +101 -0
  47. package/dist/browser/operations.d.ts +225 -0
  48. package/dist/browser/operations.js +137 -0
  49. package/dist/browser/page.d.ts +25 -0
  50. package/dist/browser/page.js +172 -0
  51. package/dist/browser/quick-reads.d.ts +29 -0
  52. package/dist/browser/quick-reads.js +160 -0
  53. package/dist/browser/sessions.d.ts +52 -0
  54. package/dist/browser/sessions.js +118 -0
  55. package/dist/browser/url-policy.d.ts +18 -0
  56. package/dist/browser/url-policy.js +58 -0
  57. package/dist/build/app-imports.d.ts +12 -0
  58. package/dist/build/app-imports.js +105 -0
  59. package/dist/build/build-policy.d.ts +5 -3
  60. package/dist/build/build-policy.js +5 -3
  61. package/dist/build/candidate-builder.d.ts +1 -1
  62. package/dist/build/candidate-cli.mjs +8375 -334
  63. package/dist/build/local-build.d.ts +2 -0
  64. package/dist/build/local-build.js +28 -6
  65. package/dist/build/snapshot-package-store.d.ts +1 -1
  66. package/dist/build/snapshot-package-store.js +7 -2
  67. package/dist/build/workspace-build.d.ts +92 -5
  68. package/dist/build/workspace-build.js +93 -19
  69. package/dist/build.d.ts +1 -1
  70. package/dist/build.js +1 -1
  71. package/dist/catalog-frame.js +1 -1
  72. package/dist/cloudflare.d.ts +13 -3
  73. package/dist/cloudflare.js +102 -4
  74. package/dist/frame-binding-wire-script.js +1 -1
  75. package/dist/frame-binding-wire.js +4 -2
  76. package/dist/git-auth.d.ts +0 -2
  77. package/dist/git-auth.js +0 -2
  78. package/dist/git.d.ts +0 -1
  79. package/dist/git.js +0 -2
  80. package/dist/host.d.ts +39 -13
  81. package/dist/host.js +268 -142
  82. package/dist/index.d.ts +25 -15
  83. package/dist/index.js +39 -16
  84. package/dist/install-release-cli.d.ts +2 -0
  85. package/dist/install-release-cli.js +46 -0
  86. package/dist/installation-client.d.ts +43 -0
  87. package/dist/installation-client.js +178 -0
  88. package/dist/installation-execution.d.ts +11 -0
  89. package/dist/installation-execution.js +184 -0
  90. package/dist/installation-operations.d.ts +1437 -0
  91. package/dist/installation-operations.js +664 -0
  92. package/dist/installation-store-generation.d.ts +3 -0
  93. package/dist/installation-store-generation.js +16 -0
  94. package/dist/instance-id.d.ts +10 -0
  95. package/dist/instance-id.js +10 -0
  96. package/dist/instance-refusal.d.ts +11 -0
  97. package/dist/instance-refusal.js +21 -0
  98. package/dist/internal/delivery.js +4 -2
  99. package/dist/internal/host-gate.js +7 -1
  100. package/dist/internal/registry-installations.js +4 -4
  101. package/dist/keys.d.ts +5 -1
  102. package/dist/keys.js +10 -2
  103. package/dist/machine-link/authority.d.ts +89 -0
  104. package/dist/machine-link/authority.js +37 -0
  105. package/dist/machine-link/client.d.ts +66 -0
  106. package/dist/machine-link/client.js +94 -0
  107. package/dist/machine-link/delivery.d.ts +18 -0
  108. package/dist/machine-link/delivery.js +55 -0
  109. package/dist/machine-link/enrollment.d.ts +111 -0
  110. package/dist/machine-link/enrollment.js +286 -0
  111. package/dist/machine-link/fences.d.ts +37 -0
  112. package/dist/machine-link/fences.js +166 -0
  113. package/dist/machine-link/home.d.ts +20 -0
  114. package/dist/machine-link/home.js +24 -0
  115. package/dist/machine-link/host.d.ts +43 -0
  116. package/dist/machine-link/host.js +189 -0
  117. package/dist/machine-link/index.d.ts +15 -0
  118. package/dist/machine-link/index.js +15 -0
  119. package/dist/machine-link/link.d.ts +48 -0
  120. package/dist/machine-link/link.js +223 -0
  121. package/dist/machine-link/presence.d.ts +10 -0
  122. package/dist/machine-link/presence.js +25 -0
  123. package/dist/machine-link/workers.d.ts +6 -0
  124. package/dist/machine-link/workers.js +6 -0
  125. package/dist/mcp-owner.d.ts +2 -2
  126. package/dist/mcp-owner.js +41 -33
  127. package/dist/mcp.d.ts +15 -3
  128. package/dist/mcp.js +146 -25
  129. package/dist/pack.d.ts +14 -3
  130. package/dist/pack.js +44 -19
  131. package/dist/page/app-access.d.ts +7 -0
  132. package/dist/page/app-access.js +75 -0
  133. package/dist/page/boot-host.js +57 -0
  134. package/dist/page/capabilities.d.ts +3 -3
  135. package/dist/page/capabilities.js +16 -12
  136. package/dist/page/computer-handles.d.ts +3 -3
  137. package/dist/page/computer-target.d.ts +3 -0
  138. package/dist/page/computer-target.js +9 -0
  139. package/dist/page/mount.d.ts +23 -3
  140. package/dist/page/mount.js +152 -16
  141. package/dist/page-assets.d.ts +22 -0
  142. package/dist/page-assets.js +28 -0
  143. package/dist/page-script.d.ts +3 -0
  144. package/dist/page-script.js +5 -4
  145. package/dist/release.d.ts +13 -6
  146. package/dist/release.js +10 -7
  147. package/dist/root-data.d.ts +16 -6
  148. package/dist/root-data.js +2 -2
  149. package/dist/source/index.d.ts +1 -1
  150. package/dist/source/team-source.d.ts +20 -8
  151. package/dist/source/team-source.js +39 -10
  152. package/dist/source/template-seed.d.ts +2 -2
  153. package/dist/standalone/agent-mail.d.ts +24 -0
  154. package/dist/standalone/agent-mail.js +62 -0
  155. package/dist/standalone/agent-store.d.ts +9 -0
  156. package/dist/standalone/agent-store.js +30 -0
  157. package/dist/standalone/agents.d.ts +68 -9
  158. package/dist/standalone/agents.js +428 -56
  159. package/dist/standalone/app-builds-binding.d.ts +20 -2
  160. package/dist/standalone/app-builds-binding.js +147 -23
  161. package/dist/standalone/app-set.d.ts +5 -2
  162. package/dist/standalone/app-set.js +174 -143
  163. package/dist/standalone/browser.d.ts +11 -0
  164. package/dist/standalone/browser.js +72 -0
  165. package/dist/standalone/builds.d.ts +13 -4
  166. package/dist/standalone/builds.js +28 -44
  167. package/dist/standalone/computer-egress.d.ts +2 -0
  168. package/dist/standalone/computer-egress.js +13 -4
  169. package/dist/standalone/computer-fleet.d.ts +43 -0
  170. package/dist/standalone/computer-fleet.js +200 -0
  171. package/dist/standalone/computer.d.ts +44 -9
  172. package/dist/standalone/computer.js +129 -22
  173. package/dist/standalone/install-apps-cli.js +43 -6
  174. package/dist/standalone/instance.d.ts +2 -2
  175. package/dist/standalone/instance.js +8 -11
  176. package/dist/standalone/machine-link.d.ts +354 -0
  177. package/dist/standalone/machine-link.js +368 -0
  178. package/dist/standalone/media.d.ts +2 -2
  179. package/dist/standalone/media.js +19 -16
  180. package/dist/standalone/preview.d.ts +2 -2
  181. package/dist/standalone/preview.js +37 -10
  182. package/dist/standalone/registry.d.ts +149 -9
  183. package/dist/standalone/registry.js +299 -8
  184. package/dist/standalone/worker.d.ts +19 -5
  185. package/dist/standalone/worker.js +90 -54
  186. package/dist/standalone/workflow-binding.d.ts +18 -22
  187. package/dist/standalone/workflow-binding.js +18 -18
  188. package/dist/standalone/workflow-control.d.ts +4 -0
  189. package/dist/standalone/workflow-control.js +40 -0
  190. package/dist/standalone/workflow-events.d.ts +2 -2
  191. package/dist/standalone/workflow-events.js +9 -10
  192. package/dist/standalone/workflow-git-capture.js +4 -3
  193. package/dist/standalone/workflow-host.d.ts +162 -161
  194. package/dist/standalone/workflow-host.js +101 -21
  195. package/dist/standalone/workflow-node-capture.js +11 -4
  196. package/dist/standalone/workflow-schedules.d.ts +5 -2
  197. package/dist/standalone/workflow-schedules.js +9 -10
  198. package/dist/standalone/workflow-trigger-host.d.ts +65 -5
  199. package/dist/standalone/workflow-trigger-host.js +2 -2
  200. package/dist/standalone/workflow-webhooks.d.ts +22 -0
  201. package/dist/standalone/workflow-webhooks.js +39 -0
  202. package/dist/standalone/workflows.d.ts +140 -43
  203. package/dist/standalone/workflows.js +198 -39
  204. package/dist/storage-frame.d.ts +6 -6
  205. package/dist/testing/capabilities.js +14 -3
  206. package/dist/testing/installation.js +26 -2
  207. package/dist/testing/worker.js +1 -1
  208. package/dist/testing/workers-runtime.d.ts +8 -2
  209. package/dist/testing/workers-runtime.js +20 -2
  210. package/dist/webhook-abuse.d.ts +25 -0
  211. package/dist/webhook-abuse.js +54 -0
  212. package/dist/webhook-custody.d.ts +144 -0
  213. package/dist/webhook-custody.js +222 -0
  214. package/dist/webhook-ingress.d.ts +36 -0
  215. package/dist/webhook-ingress.js +146 -0
  216. package/dist/webhooks.d.ts +111 -0
  217. package/dist/webhooks.js +189 -0
  218. package/dist/workers-git.d.ts +12 -17
  219. package/dist/workers-git.js +60 -52
  220. package/dist/workers.d.ts +224 -13
  221. package/dist/workers.js +351 -51
  222. package/dist/workflow-agent-authority.d.ts +8 -0
  223. package/dist/workflow-agent-authority.js +42 -0
  224. package/dist/workflows.d.ts +66 -0
  225. package/dist/workflows.js +97 -0
  226. package/docs/api/README.md +63 -37
  227. package/docs/api/agents.md +17 -0
  228. package/docs/api/app-storage.md +1 -1
  229. package/docs/api/apps.md +1394 -543
  230. package/docs/api/browser/boot.md +132 -0
  231. package/docs/api/browser.md +1440 -0
  232. package/docs/api/build-contract.md +38 -25
  233. package/docs/api/build.md +263 -84
  234. package/docs/api/cloudflare.md +93 -7
  235. package/docs/api/git.md +3 -13
  236. package/docs/api/host.md +944 -118
  237. package/docs/api/index.md +320 -18
  238. package/docs/api/keys.md +24 -1
  239. package/docs/api/mcp.md +59 -12
  240. package/docs/api/modules.md +33 -40
  241. package/docs/api/pack.md +57 -8
  242. package/docs/api/page/mount.md +92 -4
  243. package/docs/api/workers-git.md +103 -24
  244. package/docs/api/workers.md +1581 -244
  245. package/docs/site/agent-quickstart.mdx +28 -14
  246. package/examples/standalone/worker.ts +3 -1
  247. package/examples/standalone/wrangler.jsonc +2 -0
  248. package/package.json +73 -71
  249. package/dist/installation-consent.d.ts +0 -81
  250. package/dist/installation-consent.js +0 -84
  251. package/dist/shell/app-directory.d.ts +0 -542
  252. package/dist/shell/app-directory.js +0 -196
  253. package/dist/shell/browser-actions.d.ts +0 -7
  254. package/dist/shell/browser-actions.js +0 -56
  255. package/dist/shell/command-host.d.ts +0 -112
  256. package/dist/shell/command-host.js +0 -502
  257. package/dist/shell/commands.d.ts +0 -25
  258. package/dist/shell/commands.js +0 -78
  259. package/dist/shell/index.d.ts +0 -8
  260. package/dist/shell/index.js +0 -8
  261. package/dist/shell/keys.d.ts +0 -41
  262. package/dist/shell/keys.js +0 -134
  263. package/dist/shell/shell-module.d.ts +0 -55
  264. package/dist/shell/shell-module.js +0 -86
  265. package/dist/shell/window-manager.d.ts +0 -128
  266. package/dist/shell/window-manager.js +0 -730
  267. package/dist/shell/workbench-contract.d.ts +0 -68
  268. package/dist/shell/workbench-directory.d.ts +0 -332
  269. package/dist/shell/workbench-directory.js +0 -35
  270. package/dist/shell/workbench-host-types.d.ts +0 -96
  271. package/dist/shell/workbench-host-types.js +0 -1
  272. package/dist/shell/workbench-host.d.ts +0 -34
  273. package/dist/shell/workbench-host.js +0 -352
  274. package/dist/shell/workbench-model.d.ts +0 -194
  275. package/dist/shell/workbench-model.js +0 -53
  276. package/dist/shell/workbench.d.ts +0 -4
  277. package/dist/shell/workbench.js +0 -2
  278. package/docs/api/shell/app-directory.md +0 -599
  279. package/docs/api/shell/command-host.md +0 -224
  280. package/docs/api/shell/commands.md +0 -197
  281. package/docs/api/shell/keys.md +0 -183
  282. package/docs/api/shell/shell-module.md +0 -204
  283. package/docs/api/shell/window-manager.md +0 -267
  284. package/docs/api/shell/workbench-directory.md +0 -119
  285. package/docs/api/shell/workbench.md +0 -205
  286. package/docs/api/shell.md +0 -1915
  287. /package/dist/{shell/workbench-contract.js → page/boot-host.d.ts} +0 -0
@@ -7,9 +7,9 @@ import { createOysterGitWorker, createOysterGitMcpOwner, oysterGitRoot, } from "
7
7
  import { defaultTenant, tenantScope } from "../tenant.js";
8
8
  import { onBoxVmIngress } from "@fungi.computer/computer";
9
9
  import { WorkerEntrypoint } from "cloudflare:workers";
10
+ import { installationWorkerEntrypoint } from "../installation-execution.js";
10
11
  import { putContentObject } from "../build-contract.js";
11
- import { workflowGitPassPrefix } from "../git.js";
12
- import { z } from "zod";
12
+ import { workflowGitPassTokenSchema } from "@fungi.computer/braid/runtime";
13
13
  import { standaloneComputerCapabilities, standaloneWorkspace, } from "./computer.js";
14
14
  import { StandaloneAgentBase, standaloneAgents, } from "./agents.js";
15
15
  import { workersMedia } from "./media.js";
@@ -20,70 +20,87 @@ import { StandaloneWorkflowRunsBase, } from "./workflows.js";
20
20
  import { standaloneTenancy } from "./tenancy.js";
21
21
  export { StandaloneTenants } from "./tenancy.js";
22
22
  import { standalonePreviewRequest } from "./preview.js";
23
+ import { standaloneMachineLinkRoutes, } from "./machine-link.js";
24
+ export { MachineLinkDO } from "./machine-link.js";
25
+ import { standaloneWorkflowControl } from "./workflow-control.js";
26
+ import { standaloneWorkflowWebhookRoutes, standaloneWorkflowWebhookControls, } from "./workflow-webhooks.js";
27
+ import { Hono } from "hono";
23
28
  import { standaloneBuildRoutes } from "./builds.js";
29
+ import { standaloneComputerFleetRoutes, standaloneComputerSelection, } from "./computer-fleet.js";
24
30
  import { serveOysterComputerEgress, } from "./computer-egress.js";
25
31
  import { appSourceOwnerRoutes, } from "./app-source.js";
26
32
  export { StandaloneGraphWorkflow } from "./workflows.js";
27
33
  export { MediaOperationDO } from "@fungi.computer/darkroom/cloudflare";
28
34
  /** A tenant's registry: it owns that tenant's installations and App builds. */
29
35
  const tenantRegistry = (env, scope) => env.REGISTRY.getByName(scope.durableName("registry"));
30
- /** The standalone's one Computer belongs to its default tenant, whose
31
- * registry minted every build job that Computer runs. */
36
+ /** The standalone fleet belongs to its default tenant, whose registry also
37
+ * owns build jobs on the fixed build Computer. */
32
38
  const computerRegistry = (env) => tenantRegistry(env, tenantScope(defaultTenant, env.OYSTER_ORIGIN));
33
39
  export { ComputerDO } from "./computer.js";
34
40
  export { WorkspaceProxy, WorkspaceServiceProxy, } from "@fungi.computer/computer";
35
- export { StandaloneRegistry as OysterRegistry } from "./registry.js";
41
+ import { StandaloneRegistry as BaseRegistry, } from "./registry.js";
42
+ /** The registry uses the same root, with its own in-process Computer selection. */
43
+ export class OysterRegistry extends BaseRegistry {
44
+ #rootEnv;
45
+ #root = standaloneRoot(() => this.selectedComputer());
46
+ constructor(state, env, git = {}) {
47
+ super(state, env, git);
48
+ this.#rootEnv = env;
49
+ }
50
+ hostFor(scope) {
51
+ return this.#root.hostFor(this.#rootEnv, scope);
52
+ }
53
+ }
36
54
  export { OysterKeys } from "../workers-git.js";
37
55
  // WEBHOOKS keeps its deployed class name; the standalone owner also delivers to Workflows.
38
56
  export { StandaloneEventDelivery as OysterWebhooks } from "./instance.js";
39
57
  export { AgentDirectory } from "./agents.js";
40
- /** After Oyster's bearer prefix: `<library>.<pass>`, as the library minted it. */
41
- const workflowGitPassToken = z.lazy(() => z
42
- .string()
43
- .regex(/^([a-zA-Z0-9_-]{1,256})\.([0-9a-f]{64})$/u)
44
- .transform((token) => {
45
- const [libraryId = "", passId = ""] = token.split(".");
46
- return [libraryId, passId];
47
- }));
48
58
  /** The one composition root: every door reaches a tenant's host through `hostFor`. */
49
- const oysterRoot = oysterGitRoot({
50
- tenancy: (env) => ({
51
- resolve: async (request) => (await standaloneTenancy(env)).oyster.resolve(request),
52
- }),
53
- homeInstallationId: (env) => env.OYSTER_HOME_INSTALLATION,
54
- agents: standaloneAgents,
55
- workspace: standaloneWorkspace,
56
- capabilities: (env, workspace, keysLive) => [
57
- ...standaloneComputerCapabilities(env, workspace, keysLive),
58
- ...workersMedia(env, workspace),
59
- ],
60
- // A presented pass names the library that minted it; only that library's
61
- // run owner admits it, for one installation repository and scope.
62
- workflowGitPass: async (env, _scope, pass, target) => {
63
- const parsed = workflowGitPassToken.safeParse(pass.slice(workflowGitPassPrefix.length));
64
- if (!parsed.success)
65
- return null;
66
- const [libraryId, passId] = parsed.data;
67
- return env.WORKFLOW_RUNS.getByName(libraryId).admitWorkflowGitPass(passId, {
68
- kind: "installation",
69
- installationId: target.installationId,
70
- repository: target.repository,
71
- }, target.scope);
72
- },
73
- // A build job's clone reads one repository as the owner who started it.
74
- buildGitPass: async (env, scope, pass, target) => {
75
- const admitted = await tenantRegistry(env, scope).admitBuildGitPass(pass, target);
76
- return admitted === null
77
- ? null
78
- : {
79
- kind: "actor",
80
- actor: { kind: "member", memberId: admitted.requestedBy },
81
- role: "owner",
82
- };
83
- },
84
- // Operator source seeding and owner builds share the one owner guard.
85
- ownerRoutes: (env, scope) => appSourceOwnerRoutes(env, scope).route("/", standaloneBuildRoutes(() => tenantRegistry(env, scope))),
86
- });
59
+ const standaloneRoot = (selectComputer) => {
60
+ const root = oysterGitRoot({
61
+ tenancy: (env) => ({
62
+ resolve: async (request) => (await standaloneTenancy(env)).oyster.resolve(request),
63
+ }),
64
+ homeInstallationId: (env) => env.OYSTER_HOME_INSTALLATION,
65
+ agents: standaloneAgents,
66
+ workflows: standaloneWorkflowControl,
67
+ workspace: async (env, keysLive, scope, request) => standaloneWorkspace(env, keysLive, await selectComputer(tenantRegistry(env, scope), request)),
68
+ capabilities: (env, workspace, keysLive) => [
69
+ ...standaloneComputerCapabilities(env, workspace, keysLive),
70
+ ...workersMedia(env, workspace),
71
+ ],
72
+ // A presented bearer names the library that issued it; only that
73
+ // library's run owner admits it, for one installation repository and scope.
74
+ workflowGitPass: async (env, _scope, bearer, target) => {
75
+ const parsed = workflowGitPassTokenSchema.safeParse(bearer);
76
+ if (!parsed.success)
77
+ return null;
78
+ return env.WORKFLOW_RUNS.getByName(parsed.data.ownerId).admitWorkflowGitPass(bearer, {
79
+ kind: "installation",
80
+ installationId: target.installationId,
81
+ repository: target.repository,
82
+ }, target.scope);
83
+ },
84
+ // A build job's clone reads one repository as the owner who started it.
85
+ buildGitPass: async (env, scope, pass, target) => {
86
+ const admitted = await tenantRegistry(env, scope).admitBuildGitPass(pass, target);
87
+ return admitted === null
88
+ ? null
89
+ : {
90
+ kind: "actor",
91
+ actor: { kind: "member", memberId: admitted.requestedBy },
92
+ role: "owner",
93
+ };
94
+ },
95
+ // Operator source seeding and owner builds share the one owner guard.
96
+ ownerRoutes: (env, scope) => appSourceOwnerRoutes(env, scope)
97
+ .route("/", standaloneBuildRoutes(() => tenantRegistry(env, scope)))
98
+ .route("/", standaloneComputerFleetRoutes(() => tenantRegistry(env, scope)))
99
+ .route("/", standaloneWorkflowWebhookControls(env, () => root.hostFor(env, scope))),
100
+ });
101
+ return root;
102
+ };
103
+ const oysterRoot = standaloneRoot(standaloneComputerSelection);
87
104
  export class OysterMcp extends createOysterGitMcpOwner(oysterRoot) {
88
105
  }
89
106
  /** Background wakes readmit members through this root's tenancy, as HTTP and MCP do. */
@@ -105,6 +122,9 @@ export class StandaloneWorkflowRuns extends StandaloneWorkflowRunsBase {
105
122
  }
106
123
  }
107
124
  const oyster = createOysterGitWorker(oysterRoot);
125
+ /** Private loopback execution uses this root's ordinary host composition. */
126
+ export class OysterInstallationExecutor extends installationWorkerEntrypoint((env, scope) => oysterRoot.hostFor(env, scope)) {
127
+ }
108
128
  /** The Computer's egress gateway: its own HTTPS Git back into this Worker. */
109
129
  export class OysterComputerEgress extends WorkerEntrypoint {
110
130
  fetch(request) {
@@ -114,6 +134,7 @@ export class OysterComputerEgress extends WorkerEntrypoint {
114
134
  origin: this.env.OYSTER_ORIGIN,
115
135
  computerId: computerObjectId,
116
136
  publicEgress: this.env.COMPUTER_PUBLIC_EGRESS,
137
+ internetAccess: () => this.env.COMPUTER.get(this.env.COMPUTER.idFromString(computerObjectId)).internetAccess(),
117
138
  build: {
118
139
  gitPassFor: (target) => registry.buildGitPassFor(computerObjectId, target),
119
140
  exchangeTemplate: (repository, request) => registry.exchangeBuildTemplate(computerObjectId, repository, request),
@@ -134,14 +155,29 @@ export class OysterComputerEgress extends WorkerEntrypoint {
134
155
  }
135
156
  export default {
136
157
  async fetch(request, env, context) {
137
- const preview = await standalonePreviewRequest(request, env, env.OYSTER_COMPUTER_ID, (signature, port, service) => env.COMPUTER.getByName(env.OYSTER_COMPUTER_ID).portPreview(signature, port, service));
158
+ const path = new URL(request.url).pathname;
159
+ if (path === "/local/mesh/join" ||
160
+ new URLPattern({
161
+ pathname: "/api/teams/:teamId/local/mesh/:deviceId/connect",
162
+ }).test(request.url))
163
+ return standaloneMachineLinkRoutes(env).fetch(request);
164
+ const preview = await standalonePreviewRequest(request, env, (computerId) => computerRegistry(env).ownsComputer(computerId), (computerId, signature, port, service) => env.COMPUTER.getByName(computerId).portPreview(signature, port, service));
138
165
  if (preview !== null)
139
166
  return preview;
140
167
  const ingress = onBoxVmIngress(request, env, env.COMPUTER);
141
168
  if (ingress !== undefined)
142
169
  return ingress;
143
- if (oyster.fetch === undefined)
170
+ const fetchOyster = oyster.fetch;
171
+ if (fetchOyster === undefined)
144
172
  throw new Error("Oyster fetch handler is missing");
145
- return oyster.fetch(request, env, context);
173
+ return new Hono()
174
+ .route("/", standaloneWorkflowWebhookRoutes(env, async (incoming) => {
175
+ const admitted = await oysterRoot.admit(env, incoming);
176
+ return admitted === null
177
+ ? null
178
+ : oysterRoot.hostFor(env, admitted.scope);
179
+ }))
180
+ .all("*", () => fetchOyster(request, env, context))
181
+ .fetch(request);
146
182
  },
147
183
  };
@@ -1,8 +1,8 @@
1
1
  import * as S from "effect/Schema";
2
2
  import { z } from "zod";
3
+ import { type WorkflowGitPassGrant } from "@fungi.computer/braid/runtime";
3
4
  import { eventDisarmInputSchema, scheduleDisarmInputSchema } from "@fungi.computer/workflows-wire";
4
5
  import type { Installation } from "../apps/index.js";
5
- import type { WorkflowGitPassResult } from "@fungi.computer/agar/computer-contracts";
6
6
  import { type AppEventFact } from "@fungi.computer/puffball/app-event-facts";
7
7
  import type { HostRecipientEvent } from "../workers-git.js";
8
8
  import type { FrameBindingProps } from "../cloudflare.js";
@@ -34,6 +34,8 @@ export declare const triggerArmTargetSchema: z.ZodObject<{
34
34
  kind: z.ZodLiteral<"team-source">;
35
35
  }, z.core.$strict>>]>>;
36
36
  workflowId: z.ZodString;
37
+ commit: z.ZodString;
38
+ triggerId: z.ZodString;
37
39
  delivery: z.ZodOptional<z.ZodCustom<{
38
40
  readonly agentId: string;
39
41
  readonly sessionId: string;
@@ -41,14 +43,12 @@ export declare const triggerArmTargetSchema: z.ZodObject<{
41
43
  readonly agentId: string;
42
44
  readonly sessionId: string;
43
45
  }>>;
44
- triggerId: z.ZodString;
45
- commit: z.ZodString;
46
46
  }, z.core.$strict>;
47
47
  export type TriggerArmTarget = z.output<typeof triggerArmTargetSchema>;
48
48
  export type ScheduleDisarmInput = z.output<typeof scheduleDisarmInputSchema>;
49
49
  export type EventDisarmInput = z.output<typeof eventDisarmInputSchema>;
50
50
  /** The frame gate needs only these run-owner calls; it re-decodes each reply with the method's owner schema. */
51
- export type WorkflowRunCalls = Pick<StandaloneWorkflowRunsBase, "catalog" | "start" | "status" | "cancel" | "answerReview" | "armSchedule" | "disarmSchedule" | "schedules" | "armEventTrigger" | "disarmEventTrigger" | "eventTriggers" | "runs">;
51
+ export type WorkflowRunCalls = Pick<StandaloneWorkflowRunsBase, "admitWorkflowGit" | "catalog" | "start" | "status" | "cancel" | "answerReview" | "armSchedule" | "disarmSchedule" | "schedules" | "armEventTrigger" | "disarmEventTrigger" | "eventTriggers" | "runs">;
52
52
  export type WorkflowBindingEnv = Readonly<{
53
53
  WORKFLOW_RUNS: Readonly<{
54
54
  getByName(installationId: string): WorkflowRunCalls;
@@ -90,6 +90,7 @@ export declare const workflowFrameMethods: {
90
90
  kind: "team-source";
91
91
  }>;
92
92
  readonly requestId: string;
93
+ readonly definition: S.Json;
93
94
  readonly published: {
94
95
  workflowId: string;
95
96
  commit: string;
@@ -97,7 +98,6 @@ export declare const workflowFrameMethods: {
97
98
  readonly appReleaseKind: "Sha256Digest";
98
99
  };
99
100
  };
100
- readonly definition: S.Json;
101
101
  readonly delivery?: {
102
102
  readonly agentId: string;
103
103
  readonly sessionId: string;
@@ -122,8 +122,8 @@ export declare const workflowFrameMethods: {
122
122
  kind: "team-source";
123
123
  }>;
124
124
  workflowId: string;
125
- triggerId: string;
126
125
  commit: string;
126
+ triggerId: string;
127
127
  delivery?: {
128
128
  readonly agentId: string;
129
129
  readonly sessionId: string;
@@ -138,8 +138,8 @@ export declare const workflowFrameMethods: {
138
138
  kind: "team-source";
139
139
  }>;
140
140
  workflowId: string;
141
- triggerId: string;
142
141
  commit: string;
142
+ triggerId: string;
143
143
  delivery?: {
144
144
  readonly agentId: string;
145
145
  readonly sessionId: string;
@@ -160,7 +160,7 @@ export declare const workflowFrameMethods: {
160
160
  * delivery per library and returns; the emit never waits on a library.
161
161
  */
162
162
  export declare function workflowEventRecipients(installations: Readonly<{
163
- list(): Promise<readonly Pick<Installation, "installationId" | "status" | "approvedGrants">[]>;
163
+ list(): Promise<readonly Pick<Installation, "installationId" | "status" | "authority">[]>;
164
164
  }>): Promise<readonly string[]>;
165
165
  /**
166
166
  * One recorded delivery to one library: the host-attested fact reaches its
@@ -168,7 +168,7 @@ export declare function workflowEventRecipients(installations: Readonly<{
168
168
  * so the delivery owner's retries never start a run twice.
169
169
  */
170
170
  export declare function deliverWorkflowEvent(env: WorkflowEventEnv, libraryId: string, event: HostRecipientEvent): Promise<void>;
171
- /** What a Workflow node frame attests when it asks for its Git step's pass. */
171
+ /** What a Workflow node frame attests when its Git step is admitted. */
172
172
  export declare const workflowGitPassRequestSchema: z.ZodLazy<z.ZodObject<{
173
173
  installationId: z.ZodString;
174
174
  idempotencyKey: z.ZodString;
@@ -191,26 +191,22 @@ export declare const workflowGitPassRequestSchema: z.ZodLazy<z.ZodObject<{
191
191
  computerId: z.ZodString;
192
192
  }, z.core.$strict>>;
193
193
  export type WorkflowGitPassRequest = z.output<typeof workflowGitPassRequestSchema>;
194
- /** The library run owners a node frame's pass request may reach. */
195
- export type WorkflowGitPassEnv = Readonly<{
196
- OYSTER_COMPUTER_ID: string;
194
+ /** Each run library's per-step Workflow Git admission. */
195
+ export type WorkflowGitStepEnv = Readonly<{
197
196
  WORKFLOW_RUNS: Readonly<{
198
- getByName(installationId: string): Readonly<{
199
- openWorkflowGitPass(request: WorkflowGitPassRequest): Promise<WorkflowGitPassResult | null>;
200
- }>;
197
+ getByName(libraryId: string): Pick<WorkflowRunCalls, "admitWorkflowGit">;
201
198
  }>;
202
199
  }>;
203
200
  /**
204
- * COMPUTER.workflowGitPass from a published Workflow node frame. Only the
205
- * frame's host-attested Workflow, call key, member and pinned Computer cross;
206
- * the library that dispatched that Git step derives the scope from its own
207
- * stored run and mints or renews the pass.
201
+ * The public grant for the Git step a member Workflow node frame is running,
202
+ * from the run library that dispatched it; null for any other frame. Every
203
+ * native command the step starts carries this grant, never a credential.
208
204
  */
209
- export declare function openWorkflowGitPass(env: WorkflowGitPassEnv, installations: Readonly<{
210
- list(): Promise<readonly Pick<Installation, "installationId" | "status" | "approvedGrants">[]>;
205
+ export declare function workflowGitStepGrant(env: WorkflowGitStepEnv, installations: Readonly<{
206
+ list(): Promise<readonly Pick<Installation, "installationId" | "status" | "authority">[]>;
211
207
  }>, frame: Readonly<{
212
208
  facts: InvocationFacts;
213
209
  signal: AbortSignal;
214
- }>, args: readonly unknown[]): Promise<WorkflowGitPassResult>;
210
+ }>): Promise<WorkflowGitPassGrant | null>;
215
211
  /** Called only after the Instance's shared gate resolves this live frame. */
216
212
  export declare function dispatchWorkflowBinding(env: WorkflowBindingEnv, frame: InvocationFrame, props: FrameBindingProps, method: string, args: readonly unknown[]): Promise<OysterJson>;
@@ -79,7 +79,7 @@ export const workflowFrameMethods = {
79
79
  export async function workflowEventRecipients(installations) {
80
80
  return (await installations.list())
81
81
  .filter((installation) => installation.status === "enabled" &&
82
- installation.approvedGrants.some((grant) => grant.capability === "workflow.arm"))
82
+ installation.authority.approvedGrants.some((grant) => grant.capability === "workflow.arm"))
83
83
  .map((installation) => installation.installationId);
84
84
  }
85
85
  /**
@@ -98,7 +98,7 @@ export async function deliverWorkflowEvent(env, libraryId, event) {
98
98
  });
99
99
  await env.WORKFLOW_RUNS.getByName(libraryId).deliverEvent(fact);
100
100
  }
101
- /** What a Workflow node frame attests when it asks for its Git step's pass. */
101
+ /** What a Workflow node frame attests when its Git step is admitted. */
102
102
  export const workflowGitPassRequestSchema = z.lazy(() => z.strictObject({
103
103
  installationId: z.string().min(1).max(256),
104
104
  idempotencyKey: z.string().min(1).max(128),
@@ -107,21 +107,19 @@ export const workflowGitPassRequestSchema = z.lazy(() => z.strictObject({
107
107
  computerId: z.string().min(1).max(256),
108
108
  }));
109
109
  /**
110
- * COMPUTER.workflowGitPass from a published Workflow node frame. Only the
111
- * frame's host-attested Workflow, call key, member and pinned Computer cross;
112
- * the library that dispatched that Git step derives the scope from its own
113
- * stored run and mints or renews the pass.
110
+ * The public grant for the Git step a member Workflow node frame is running,
111
+ * from the run library that dispatched it; null for any other frame. Every
112
+ * native command the step starts carries this grant, never a credential.
114
113
  */
115
- export async function openWorkflowGitPass(env, installations, frame, args) {
116
- z.tuple([z.strictObject({})]).parse(args);
114
+ export async function workflowGitStepGrant(env, installations, frame) {
117
115
  const { subject, selection } = frame.facts;
118
116
  if (subject.kind !== "call" ||
119
117
  subject.context.workflow === undefined ||
120
118
  subject.context.idempotencyKey === undefined ||
121
119
  subject.context.actor.kind !== "member" ||
122
- subject.context.computerId !== env.OYSTER_COMPUTER_ID ||
120
+ subject.context.computerId === null ||
123
121
  selection.owner.kind !== "installation")
124
- throw new AppInstanceError("revoked");
122
+ return null;
125
123
  const request = workflowGitPassRequestSchema.parse({
126
124
  installationId: selection.owner.id,
127
125
  idempotencyKey: subject.context.idempotencyKey,
@@ -129,21 +127,23 @@ export async function openWorkflowGitPass(env, installations, frame, args) {
129
127
  memberId: subject.context.actor.memberId,
130
128
  computerId: subject.context.computerId,
131
129
  });
132
- const libraries = (await installations.list()).filter((installation) => installation.status === "enabled" &&
133
- installation.approvedGrants.some((grant) => grant.capability === "workflow.run"));
134
- for (const library of libraries) {
130
+ for (const library of await installations.list()) {
131
+ if (library.status !== "enabled" ||
132
+ !library.authority.approvedGrants.some((grant) => grant.capability === "workflow.run"))
133
+ continue;
135
134
  frame.signal.throwIfAborted();
136
- const pass = await env.WORKFLOW_RUNS.getByName(library.installationId).openWorkflowGitPass(request);
137
- if (pass !== null)
138
- return pass;
135
+ const grant = await env.WORKFLOW_RUNS.getByName(library.installationId).admitWorkflowGit(request);
136
+ if (grant !== null)
137
+ return grant;
139
138
  }
140
- throw new AppInstanceError("revoked");
139
+ return null;
141
140
  }
142
141
  /** Called only after the Instance's shared gate resolves this live frame. */
143
142
  export async function dispatchWorkflowBinding(env, frame, props, method, args) {
144
143
  frame.signal.throwIfAborted();
145
144
  if (props.binding !== "WORKFLOWS" ||
146
- frame.facts.subject.kind !== "call" ||
145
+ (frame.facts.subject.kind !== "call" &&
146
+ !(method === "runGraph" && frame.facts.subject.name === "onGitPush")) ||
147
147
  frame.facts.selection.owner.kind !== "installation")
148
148
  throw new AppInstanceError("revoked");
149
149
  const runs = env.WORKFLOW_RUNS.getByName(frame.facts.selection.owner.id);
@@ -0,0 +1,4 @@
1
+ import type { OysterHost } from "../host.js";
2
+ import type { StandaloneWorkflowEnv } from "./workflows.js";
3
+ /** Locate runs in the existing library owners; never create a second run index. */
4
+ export declare function standaloneWorkflowControl(env: Pick<StandaloneWorkflowEnv, "WORKFLOW_RUNS">, host: Pick<OysterHost, "auth" | "installations">): NonNullable<OysterHost["workflows"]>;
@@ -0,0 +1,40 @@
1
+ import { cancelReplySchema, statusReply } from "@fungi.computer/workflows-wire";
2
+ import { AppInstanceError } from "../index.js";
3
+ /** Locate runs in the existing library owners; never create a second run index. */
4
+ export function standaloneWorkflowControl(env, host) {
5
+ return async (principal) => {
6
+ if (principal.actor.kind !== "member")
7
+ return null;
8
+ const actor = principal.actor;
9
+ const requireOwner = async () => {
10
+ const current = await host.auth.resolve(actor);
11
+ if (current === null || !("role" in current) || current.role !== "owner")
12
+ throw new AppInstanceError("revoked");
13
+ };
14
+ await requireOwner();
15
+ const call = async (operation, runId) => {
16
+ await requireOwner();
17
+ for (const installation of await host.installations.list()) {
18
+ if (installation.status !== "enabled" ||
19
+ !installation.authority.approvedGrants.some((grant) => grant.capability === "workflow.run"))
20
+ continue;
21
+ const result = await env.WORKFLOW_RUNS.getByName(installation.installationId).control(principal, operation, runId);
22
+ if (result !== null)
23
+ return result;
24
+ }
25
+ return null;
26
+ };
27
+ return {
28
+ status: async (runId) => {
29
+ const snapshot = await call("status", runId);
30
+ return snapshot === null
31
+ ? null
32
+ : statusReply.parse(JSON.parse(JSON.stringify(snapshot)));
33
+ },
34
+ cancel: async (runId) => {
35
+ const result = await call("cancel", runId);
36
+ return cancelReplySchema.parse(result ?? { kind: "refused", reason: "denied" });
37
+ },
38
+ };
39
+ };
40
+ }
@@ -19,7 +19,7 @@ export declare function createStandaloneWorkflowEvents<Frame>(options: Parameter
19
19
  } | {
20
20
  kind: "refused";
21
21
  reason: string;
22
- readonly error?: undefined;
22
+ error?: undefined;
23
23
  }>;
24
24
  disarm(frame: Frame, raw: EventDisarmInput): Promise<{
25
25
  kind: "disarmed";
@@ -41,6 +41,6 @@ export declare function createStandaloneWorkflowEvents<Frame>(options: Parameter
41
41
  state: "active" | "paused";
42
42
  lastError: string | null;
43
43
  }[];
44
- readonly error?: undefined;
44
+ error?: undefined;
45
45
  }>;
46
46
  };
@@ -9,30 +9,29 @@ export function createStandaloneWorkflowEvents(options) {
9
9
  deliver: (fact) => router.deliver(fact),
10
10
  async arm(frame, raw) {
11
11
  const target = triggerArmTargetSchema.parse(raw);
12
- const { libraryId, context, installationAuthority } = await options.admitInvocation(frame, "armEventTrigger");
13
- if (context.actor.kind !== "member")
12
+ const { libraryId, context, installationAuthority, authority } = await options.admitInvocation(frame, "armEventTrigger");
13
+ if (authority === null)
14
14
  return { error: "not_granted" };
15
15
  if (context.computerId === null)
16
16
  return {
17
17
  kind: "refused",
18
18
  reason: "Select a Computer before arming an event trigger",
19
19
  };
20
- // Standalone has no Agent arming source: the owner arms as themself.
21
- return router.arm({ armedBy: context.actor.memberId, armedVia: null }, { ...target, installationId: libraryId }, context.computerId, installationAuthority);
20
+ return router.arm(authority.armer, { ...target, installationId: libraryId }, context.computerId, installationAuthority);
22
21
  },
23
22
  async disarm(frame, raw) {
24
23
  const { eventTriggerId } = eventDisarmInputSchema.parse(raw);
25
- const { libraryId, context } = await options.admitInvocation(frame, "disarmEventTrigger");
26
- if (context.actor.kind !== "member")
24
+ const { libraryId, authority } = await options.admitInvocation(frame, "disarmEventTrigger");
25
+ if (authority === null)
27
26
  return { error: "not_granted" };
28
- return router.disarm({ kind: "member", memberId: context.actor.memberId }, libraryId, eventTriggerId);
27
+ return router.disarm(authority.principal, libraryId, eventTriggerId);
29
28
  },
30
29
  async list(frame) {
31
- const { libraryId, context } = await options.admitInvocation(frame, "eventTriggers");
32
- if (context.actor.kind !== "member")
30
+ const { libraryId, authority } = await options.admitInvocation(frame, "eventTriggers");
31
+ if (authority === null)
33
32
  return { error: "not_granted" };
34
33
  return {
35
- eventTriggers: await router.list({ kind: "member", memberId: context.actor.memberId }, libraryId),
34
+ eventTriggers: await router.list(authority.principal, libraryId),
36
35
  };
37
36
  },
38
37
  };
@@ -30,9 +30,10 @@ export function createStandaloneWorkflowInstallationGitCapture(options) {
30
30
  if (current?.status !== "enabled" ||
31
31
  current.installationId !== installation.installationId ||
32
32
  current.instanceId !== installation.instanceId ||
33
- current.authorityVersion !== installation.authorityVersion ||
34
- current.declaredOperationsDigest !==
35
- installation.declaredOperationsDigest ||
33
+ current.authority.authorityVersion !==
34
+ installation.authority.authorityVersion ||
35
+ current.authority.declaredOperationsDigest !==
36
+ installation.authority.declaredOperationsDigest ||
36
37
  current.release.appId !== installation.release.appId ||
37
38
  current.release.releaseId !== installation.release.releaseId ||
38
39
  current.release.artifact.artifactRef !==