@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.
Files changed (339) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +1134 -2
  3. package/dist/agents.d.ts +114 -0
  4. package/dist/agents.js +322 -0
  5. package/dist/app-builds.d.ts +210 -0
  6. package/dist/app-builds.js +499 -0
  7. package/dist/app-sandbox.d.ts +2 -0
  8. package/dist/app-sandbox.js +2 -0
  9. package/dist/app-sources.d.ts +31 -0
  10. package/dist/app-sources.js +370 -0
  11. package/dist/app-storage.d.ts +141 -0
  12. package/dist/app-storage.js +281 -0
  13. package/dist/apps/gate.d.ts +105 -0
  14. package/dist/apps/gate.js +273 -0
  15. package/dist/apps/index.d.ts +5 -0
  16. package/dist/apps/index.js +5 -0
  17. package/dist/apps/installation.d.ts +248 -0
  18. package/dist/apps/installation.js +307 -0
  19. package/dist/apps/operations.d.ts +8 -0
  20. package/dist/apps/operations.js +98 -0
  21. package/dist/apps/tools.d.ts +33 -0
  22. package/dist/apps/tools.js +17 -0
  23. package/dist/auth.d.ts +11 -0
  24. package/dist/auth.js +93 -0
  25. package/dist/backend-request.d.ts +3 -0
  26. package/dist/backend-request.js +53 -0
  27. package/dist/build/artifact-writer.d.ts +40 -0
  28. package/dist/build/artifact-writer.js +181 -0
  29. package/dist/build/build-jobs.d.ts +128 -0
  30. package/dist/build/build-jobs.js +203 -0
  31. package/dist/build/build-policy.d.ts +10 -0
  32. package/dist/build/build-policy.js +10 -0
  33. package/dist/build/build-step.d.ts +37 -0
  34. package/dist/build/build-step.js +28 -0
  35. package/dist/build/bundled-html.d.ts +7 -0
  36. package/dist/build/bundled-html.js +64 -0
  37. package/dist/build/candidate-builder.d.ts +68 -0
  38. package/dist/build/candidate-builder.js +91 -0
  39. package/dist/build/candidate-cli.d.ts +1 -0
  40. package/dist/build/candidate-cli.js +119 -0
  41. package/dist/build/candidate-cli.mjs +28170 -0
  42. package/dist/build/content-objects.d.ts +37 -0
  43. package/dist/build/content-objects.js +53 -0
  44. package/dist/build/definition-emission.d.ts +34 -0
  45. package/dist/build/definition-emission.js +201 -0
  46. package/dist/build/effect.d.ts +249 -0
  47. package/dist/build/effect.js +436 -0
  48. package/dist/build/git-environment.d.ts +9 -0
  49. package/dist/build/git-environment.js +60 -0
  50. package/dist/build/html-recipe.d.ts +20 -0
  51. package/dist/build/html-recipe.js +51 -0
  52. package/dist/build/local-build.d.ts +67 -0
  53. package/dist/build/local-build.js +1128 -0
  54. package/dist/build/snapshot-package-store.d.ts +12 -0
  55. package/dist/build/snapshot-package-store.js +36 -0
  56. package/dist/build/snapshot-program-scripts.d.ts +6 -0
  57. package/dist/build/snapshot-program-scripts.js +2 -0
  58. package/dist/build/snapshot-programs/snapshot-path.d.ts +2 -0
  59. package/dist/build/snapshot-programs/snapshot-path.js +6 -0
  60. package/dist/build/submission-contract.d.ts +828 -0
  61. package/dist/build/submission-contract.js +180 -0
  62. package/dist/build/workspace-build.d.ts +55 -0
  63. package/dist/build/workspace-build.js +200 -0
  64. package/dist/build-contract.d.ts +7 -0
  65. package/dist/build-contract.js +7 -0
  66. package/dist/build-egress.d.ts +62 -0
  67. package/dist/build-egress.js +148 -0
  68. package/dist/build-jobs.d.ts +2 -0
  69. package/dist/build-jobs.js +2 -0
  70. package/dist/build.d.ts +12 -0
  71. package/dist/build.js +14 -0
  72. package/dist/capability-descriptor.d.ts +46 -0
  73. package/dist/capability-descriptor.js +51 -0
  74. package/dist/catalog-frame.d.ts +39 -0
  75. package/dist/catalog-frame.js +72 -0
  76. package/dist/cause-class.d.ts +19 -0
  77. package/dist/cause-class.js +54 -0
  78. package/dist/cloudflare.d.ts +123 -0
  79. package/dist/cloudflare.js +391 -0
  80. package/dist/computer-terminal.d.ts +38 -0
  81. package/dist/computer-terminal.js +410 -0
  82. package/dist/computer.d.ts +60 -0
  83. package/dist/computer.js +33 -0
  84. package/dist/event-recipients.d.ts +57 -0
  85. package/dist/event-recipients.js +144 -0
  86. package/dist/facet-invocation.d.ts +4 -0
  87. package/dist/facet-invocation.js +45 -0
  88. package/dist/files.d.ts +42 -0
  89. package/dist/files.js +123 -0
  90. package/dist/frame-binding-wire-script.d.ts +2 -0
  91. package/dist/frame-binding-wire-script.js +1 -0
  92. package/dist/frame-binding-wire.d.ts +28 -0
  93. package/dist/frame-binding-wire.js +54 -0
  94. package/dist/git-admission.d.ts +52 -0
  95. package/dist/git-admission.js +188 -0
  96. package/dist/git-auth.d.ts +11 -0
  97. package/dist/git-auth.js +27 -0
  98. package/dist/git-door.d.ts +35 -0
  99. package/dist/git-door.js +108 -0
  100. package/dist/git-journal.d.ts +31 -0
  101. package/dist/git-journal.js +188 -0
  102. package/dist/git.d.ts +31 -0
  103. package/dist/git.js +64 -0
  104. package/dist/guest-policy.d.ts +9 -0
  105. package/dist/guest-policy.js +13 -0
  106. package/dist/host.d.ts +244 -0
  107. package/dist/host.js +1043 -0
  108. package/dist/index.d.ts +166 -0
  109. package/dist/index.js +320 -0
  110. package/dist/installation-consent.d.ts +79 -0
  111. package/dist/installation-consent.js +82 -0
  112. package/dist/internal/background-origins.d.ts +3 -0
  113. package/dist/internal/background-origins.js +8 -0
  114. package/dist/internal/delivery.d.ts +12 -0
  115. package/dist/internal/delivery.js +167 -0
  116. package/dist/internal/host-gate.d.ts +14 -0
  117. package/dist/internal/host-gate.js +62 -0
  118. package/dist/internal/registry-installations.d.ts +6 -0
  119. package/dist/internal/registry-installations.js +12 -0
  120. package/dist/invocation.d.ts +251 -0
  121. package/dist/invocation.js +377 -0
  122. package/dist/keys.d.ts +48 -0
  123. package/dist/keys.js +52 -0
  124. package/dist/mcp-owner.d.ts +27 -0
  125. package/dist/mcp-owner.js +155 -0
  126. package/dist/mcp.d.ts +89 -0
  127. package/dist/mcp.js +429 -0
  128. package/dist/media.d.ts +23 -0
  129. package/dist/media.js +12 -0
  130. package/dist/pack.d.ts +79 -0
  131. package/dist/pack.js +151 -0
  132. package/dist/page/capabilities.d.ts +23 -0
  133. package/dist/page/capabilities.js +256 -0
  134. package/dist/page/computer-handles.d.ts +79 -0
  135. package/dist/page/computer-handles.js +44 -0
  136. package/dist/page/gill-observation.d.ts +4 -0
  137. package/dist/page/gill-observation.js +31 -0
  138. package/dist/page/lifetime.d.ts +6 -0
  139. package/dist/page/lifetime.js +19 -0
  140. package/dist/page/mount.d.ts +163 -0
  141. package/dist/page/mount.js +390 -0
  142. package/dist/page/storage.d.ts +3 -0
  143. package/dist/page/storage.js +82 -0
  144. package/dist/page/terminal.d.ts +5 -0
  145. package/dist/page/terminal.js +7 -0
  146. package/dist/page/window-launch.d.ts +18 -0
  147. package/dist/page/window-launch.js +11 -0
  148. package/dist/page-script.d.ts +14 -0
  149. package/dist/page-script.js +5 -0
  150. package/dist/r2-files.d.ts +3 -0
  151. package/dist/r2-files.js +79 -0
  152. package/dist/release.d.ts +83 -0
  153. package/dist/release.js +167 -0
  154. package/dist/root-data.d.ts +86 -0
  155. package/dist/root-data.js +69 -0
  156. package/dist/shell/app-directory.d.ts +542 -0
  157. package/dist/shell/app-directory.js +199 -0
  158. package/dist/shell/browser-actions.d.ts +7 -0
  159. package/dist/shell/browser-actions.js +56 -0
  160. package/dist/shell/command-host.d.ts +112 -0
  161. package/dist/shell/command-host.js +502 -0
  162. package/dist/shell/commands.d.ts +25 -0
  163. package/dist/shell/commands.js +78 -0
  164. package/dist/shell/index.d.ts +8 -0
  165. package/dist/shell/index.js +8 -0
  166. package/dist/shell/keys.d.ts +41 -0
  167. package/dist/shell/keys.js +134 -0
  168. package/dist/shell/shell-module.d.ts +55 -0
  169. package/dist/shell/shell-module.js +86 -0
  170. package/dist/shell/window-manager.d.ts +124 -0
  171. package/dist/shell/window-manager.js +711 -0
  172. package/dist/shell/workbench-contract.d.ts +68 -0
  173. package/dist/shell/workbench-contract.js +1 -0
  174. package/dist/shell/workbench-directory.d.ts +332 -0
  175. package/dist/shell/workbench-directory.js +35 -0
  176. package/dist/shell/workbench-host-types.d.ts +96 -0
  177. package/dist/shell/workbench-host-types.js +1 -0
  178. package/dist/shell/workbench-host.d.ts +34 -0
  179. package/dist/shell/workbench-host.js +347 -0
  180. package/dist/shell/workbench-model.d.ts +192 -0
  181. package/dist/shell/workbench-model.js +53 -0
  182. package/dist/shell/workbench.d.ts +4 -0
  183. package/dist/shell/workbench.js +2 -0
  184. package/dist/source/fork.d.ts +27 -0
  185. package/dist/source/fork.js +46 -0
  186. package/dist/source/index.d.ts +10 -0
  187. package/dist/source/index.js +10 -0
  188. package/dist/source/seed-receipt.d.ts +53 -0
  189. package/dist/source/seed-receipt.js +18 -0
  190. package/dist/source/team-source.d.ts +32 -0
  191. package/dist/source/team-source.js +42 -0
  192. package/dist/source/template-push.d.ts +33 -0
  193. package/dist/source/template-push.js +223 -0
  194. package/dist/source/template-repository.d.ts +4 -0
  195. package/dist/source/template-repository.js +21 -0
  196. package/dist/source/template-seed.d.ts +68 -0
  197. package/dist/source/template-seed.js +80 -0
  198. package/dist/standalone/agent-directory.d.ts +4 -0
  199. package/dist/standalone/agent-directory.js +4 -0
  200. package/dist/standalone/agents.d.ts +220 -0
  201. package/dist/standalone/agents.js +795 -0
  202. package/dist/standalone/app-builds-binding.d.ts +36 -0
  203. package/dist/standalone/app-builds-binding.js +119 -0
  204. package/dist/standalone/app-set.d.ts +91 -0
  205. package/dist/standalone/app-set.js +395 -0
  206. package/dist/standalone/app-source.d.ts +41 -0
  207. package/dist/standalone/app-source.js +59 -0
  208. package/dist/standalone/builds.d.ts +18 -0
  209. package/dist/standalone/builds.js +101 -0
  210. package/dist/standalone/computer-egress.d.ts +37 -0
  211. package/dist/standalone/computer-egress.js +111 -0
  212. package/dist/standalone/computer.d.ts +122 -0
  213. package/dist/standalone/computer.js +714 -0
  214. package/dist/standalone/install-apps-cli.d.ts +2 -0
  215. package/dist/standalone/install-apps-cli.js +126 -0
  216. package/dist/standalone/instance.d.ts +35 -0
  217. package/dist/standalone/instance.js +74 -0
  218. package/dist/standalone/media.d.ts +76 -0
  219. package/dist/standalone/media.js +227 -0
  220. package/dist/standalone/preview.d.ts +51 -0
  221. package/dist/standalone/preview.js +167 -0
  222. package/dist/standalone/registry.d.ts +102 -0
  223. package/dist/standalone/registry.js +193 -0
  224. package/dist/standalone/tenancy.d.ts +42 -0
  225. package/dist/standalone/tenancy.js +241 -0
  226. package/dist/standalone/worker.d.ts +55 -0
  227. package/dist/standalone/worker.js +147 -0
  228. package/dist/standalone/workflow-authority-readers.d.ts +16 -0
  229. package/dist/standalone/workflow-authority-readers.js +35 -0
  230. package/dist/standalone/workflow-binding.d.ts +216 -0
  231. package/dist/standalone/workflow-binding.js +217 -0
  232. package/dist/standalone/workflow-events.d.ts +46 -0
  233. package/dist/standalone/workflow-events.js +39 -0
  234. package/dist/standalone/workflow-git-capture.d.ts +7 -0
  235. package/dist/standalone/workflow-git-capture.js +62 -0
  236. package/dist/standalone/workflow-host.d.ts +931 -0
  237. package/dist/standalone/workflow-host.js +751 -0
  238. package/dist/standalone/workflow-node-capture.d.ts +13 -0
  239. package/dist/standalone/workflow-node-capture.js +72 -0
  240. package/dist/standalone/workflow-root-authority.d.ts +27 -0
  241. package/dist/standalone/workflow-root-authority.js +87 -0
  242. package/dist/standalone/workflow-schedules.d.ts +52 -0
  243. package/dist/standalone/workflow-schedules.js +40 -0
  244. package/dist/standalone/workflow-trigger-host.d.ts +1694 -0
  245. package/dist/standalone/workflow-trigger-host.js +52 -0
  246. package/dist/standalone/workflows.d.ts +657 -0
  247. package/dist/standalone/workflows.js +554 -0
  248. package/dist/storage-frame.d.ts +64 -0
  249. package/dist/storage-frame.js +28 -0
  250. package/dist/tenancy/auth0.d.ts +64 -0
  251. package/dist/tenancy/auth0.js +198 -0
  252. package/dist/tenancy/cloudflare/rate-limit.d.ts +8 -0
  253. package/dist/tenancy/cloudflare/rate-limit.js +19 -0
  254. package/dist/tenancy/cloudflare/registry.d.ts +50 -0
  255. package/dist/tenancy/cloudflare/registry.js +301 -0
  256. package/dist/tenancy/cloudflare.d.ts +2 -0
  257. package/dist/tenancy/cloudflare.js +2 -0
  258. package/dist/tenancy/cold-directory.d.ts +13 -0
  259. package/dist/tenancy/cold-directory.js +43 -0
  260. package/dist/tenancy/contracts.d.ts +195 -0
  261. package/dist/tenancy/contracts.js +1 -0
  262. package/dist/tenancy/index.d.ts +19 -0
  263. package/dist/tenancy/index.js +156 -0
  264. package/dist/tenancy/owner-key.d.ts +26 -0
  265. package/dist/tenancy/owner-key.js +63 -0
  266. package/dist/tenancy/oyster.d.ts +4 -0
  267. package/dist/tenancy/oyster.js +25 -0
  268. package/dist/tenancy/registry-record.d.ts +7 -0
  269. package/dist/tenancy/registry-record.js +44 -0
  270. package/dist/tenancy/registry.d.ts +27 -0
  271. package/dist/tenancy/registry.js +138 -0
  272. package/dist/tenancy/resolve.d.ts +7 -0
  273. package/dist/tenancy/resolve.js +64 -0
  274. package/dist/tenant.d.ts +81 -0
  275. package/dist/tenant.js +71 -0
  276. package/dist/testing/capabilities.d.ts +29 -0
  277. package/dist/testing/capabilities.js +130 -0
  278. package/dist/testing/catalog.d.ts +3 -0
  279. package/dist/testing/catalog.js +53 -0
  280. package/dist/testing/index.d.ts +10 -0
  281. package/dist/testing/index.js +10 -0
  282. package/dist/testing/installation.d.ts +11 -0
  283. package/dist/testing/installation.js +25 -0
  284. package/dist/testing/native-worker.d.ts +21 -0
  285. package/dist/testing/native-worker.js +106 -0
  286. package/dist/testing/worker.d.ts +18 -0
  287. package/dist/testing/worker.js +67 -0
  288. package/dist/testing/workers-runtime.d.ts +24 -0
  289. package/dist/testing/workers-runtime.js +33 -0
  290. package/dist/webhook-delivery.d.ts +14 -0
  291. package/dist/webhook-delivery.js +17 -0
  292. package/dist/workers-git.d.ts +285 -0
  293. package/dist/workers-git.js +1174 -0
  294. package/dist/workers.d.ts +352 -0
  295. package/dist/workers.js +868 -0
  296. package/docs/api/README.md +82 -0
  297. package/docs/api/agents.md +332 -0
  298. package/docs/api/app-storage.md +209 -0
  299. package/docs/api/apps.md +1375 -0
  300. package/docs/api/auth.md +63 -0
  301. package/docs/api/build-contract.md +1304 -0
  302. package/docs/api/build.md +1712 -0
  303. package/docs/api/cloudflare.md +421 -0
  304. package/docs/api/computer.md +203 -0
  305. package/docs/api/files.md +117 -0
  306. package/docs/api/git.md +470 -0
  307. package/docs/api/host.md +1328 -0
  308. package/docs/api/index.md +430 -0
  309. package/docs/api/invocation.md +738 -0
  310. package/docs/api/keys.md +175 -0
  311. package/docs/api/mcp.md +301 -0
  312. package/docs/api/media.md +40 -0
  313. package/docs/api/modules.md +50 -0
  314. package/docs/api/pack.md +141 -0
  315. package/docs/api/page/mount.md +298 -0
  316. package/docs/api/root-data.md +111 -0
  317. package/docs/api/shell/app-directory.md +599 -0
  318. package/docs/api/shell/command-host.md +224 -0
  319. package/docs/api/shell/commands.md +197 -0
  320. package/docs/api/shell/keys.md +183 -0
  321. package/docs/api/shell/shell-module.md +204 -0
  322. package/docs/api/shell/window-manager.md +267 -0
  323. package/docs/api/shell/workbench-directory.md +119 -0
  324. package/docs/api/shell/workbench.md +205 -0
  325. package/docs/api/shell.md +1915 -0
  326. package/docs/api/tenancy/auth0.md +110 -0
  327. package/docs/api/tenancy/cloudflare.md +221 -0
  328. package/docs/api/tenancy/cold-directory.md +46 -0
  329. package/docs/api/tenancy/owner-key.md +63 -0
  330. package/docs/api/tenancy/registry.md +77 -0
  331. package/docs/api/tenancy/resolve.md +69 -0
  332. package/docs/api/tenancy.md +430 -0
  333. package/docs/api/tenant.md +194 -0
  334. package/docs/api/workers-git.md +1646 -0
  335. package/docs/api/workers.md +1691 -0
  336. package/docs/site/agent-quickstart.mdx +346 -0
  337. package/examples/standalone/worker.ts +7 -0
  338. package/examples/standalone/wrangler.jsonc +82 -0
  339. package/package.json +300 -3
@@ -0,0 +1,241 @@
1
+ import { TenancyRegistryDO, admittedRegistry, durableObjectRegistry, } from "../tenancy/cloudflare.js";
2
+ import { createTenancy, } from "../tenancy/index.js";
3
+ import { ownerKey } from "../tenancy/owner-key.js";
4
+ import { hosts } from "../tenancy/resolve.js";
5
+ import { ownerOrKeyAuth } from "../auth.js";
6
+ import { defaultTenant, tenantScope, } from "../tenant.js";
7
+ import * as Schema from "effect/Schema";
8
+ const seedSchema = Schema.Struct({
9
+ origin: Schema.String,
10
+ owner: Schema.Struct({
11
+ provider: Schema.NonEmptyString,
12
+ subject: Schema.NonEmptyString,
13
+ }),
14
+ });
15
+ const seedKey = "standalone-owner-seed";
16
+ /** A fresh registry namespace with one host-provisioned standalone tenant. */
17
+ export class StandaloneTenants extends TenancyRegistryDO {
18
+ #state;
19
+ constructor(state) {
20
+ super(state);
21
+ this.#state = state;
22
+ }
23
+ /** Reuse only the exact configured tenant and its current canonical owner. */
24
+ seed(input) {
25
+ return this.#state.blockConcurrencyWhile(async () => {
26
+ input = Schema.decodeUnknownSync(seedSchema)(input);
27
+ const scope = tenantScope(defaultTenant, input.origin);
28
+ const domain = new URL(scope.origin).host;
29
+ const raw = await this.#state.storage.get(seedKey);
30
+ if (raw !== undefined) {
31
+ const marker = Schema.decodeUnknownSync(seedSchema)(raw);
32
+ if (marker.origin !== scope.origin ||
33
+ marker.owner.provider !== input.owner.provider ||
34
+ marker.owner.subject !== input.owner.subject)
35
+ throw new Error("Standalone seed configuration changed");
36
+ }
37
+ let tenant = await this.get(defaultTenant);
38
+ if (tenant === null) {
39
+ if (raw !== undefined)
40
+ throw new Error("Previously seeded standalone tenant is missing");
41
+ // Persist the attempt first: a crash must never turn removal into creation.
42
+ await this.#state.storage.put(seedKey, {
43
+ origin: scope.origin,
44
+ owner: input.owner,
45
+ });
46
+ tenant = await this.create({
47
+ handle: defaultTenant,
48
+ origin: scope.origin,
49
+ domains: [domain],
50
+ owner: input.owner,
51
+ });
52
+ }
53
+ const owner = await this.membershipOf(defaultTenant, input.owner);
54
+ if (tenant.handle !== defaultTenant ||
55
+ tenant.origin !== scope.origin ||
56
+ tenant.domains.length !== 1 ||
57
+ tenant.domains[0] !== domain ||
58
+ tenant.status !== "active" ||
59
+ owner?.role !== "owner" ||
60
+ tenant.owners.length !== 1 ||
61
+ tenant.owners[0] !== owner.memberId)
62
+ throw new Error("Standalone tenant does not match its configured owner");
63
+ if (raw === undefined)
64
+ await this.#state.storage.put(seedKey, {
65
+ origin: scope.origin,
66
+ owner: input.owner,
67
+ });
68
+ return tenant;
69
+ });
70
+ }
71
+ }
72
+ const environments = new WeakMap();
73
+ /** Seed once per environment, then admit every entry from one fresh registry read. */
74
+ export function standaloneTenancy(env) {
75
+ let pending = environments.get(env);
76
+ if (pending === undefined) {
77
+ pending = composeTenancy(env).catch((cause) => {
78
+ if (environments.get(env) === pending)
79
+ environments.delete(env);
80
+ throw cause;
81
+ });
82
+ environments.set(env, pending);
83
+ }
84
+ return pending;
85
+ }
86
+ async function composeTenancy(env) {
87
+ const scope = tenantScope(defaultTenant, env.OYSTER_ORIGIN);
88
+ const domain = new URL(scope.origin).host;
89
+ const identity = {
90
+ provider: "standalone-owner",
91
+ subject: env.OYSTER_OWNER_NAME ?? "owner",
92
+ };
93
+ await env.TENANTS.getByName("registry").seed({
94
+ origin: scope.origin,
95
+ owner: identity,
96
+ });
97
+ const registry = durableObjectRegistry(env.TENANTS, { readonly: true });
98
+ /** The canonical owner, only while the one admitted record still proves it. */
99
+ const ownerOf = ({ tenant, membership, }) => tenant.id !== defaultTenant ||
100
+ tenant.status !== "active" ||
101
+ tenant.origin !== scope.origin ||
102
+ tenant.handle !== defaultTenant ||
103
+ tenant.domains.length !== 1 ||
104
+ tenant.domains[0] !== domain ||
105
+ membership?.role !== "owner" ||
106
+ tenant.owners.length !== 1 ||
107
+ tenant.owners[0] !== membership.memberId
108
+ ? null
109
+ : {
110
+ actor: { kind: "member", memberId: membership.memberId },
111
+ role: "owner",
112
+ membershipVersion: membership.membershipVersion,
113
+ };
114
+ const admitted = (admission) => {
115
+ const view = admittedRegistry(admission);
116
+ const owner = ownerOf(admission);
117
+ const ownerAuth = ownerKey({
118
+ registry: view,
119
+ keys: {
120
+ get: () => Promise.resolve(owner === null
121
+ ? null
122
+ : { key: env.OYSTER_OWNER_KEY, memberId: owner.actor.memberId }),
123
+ },
124
+ login: { resolveTenant: (request) => tenancy.resolve(request) },
125
+ });
126
+ const resolver = hosts(view);
127
+ const tenancy = createTenancy({
128
+ registry: view,
129
+ resolve: {
130
+ kind: "origin",
131
+ resolve: (url, current) => url.origin === scope.origin
132
+ ? resolver.resolve(url, current)
133
+ : Promise.resolve(null),
134
+ },
135
+ auth: [
136
+ ownerAuth,
137
+ {
138
+ id: "standalone-named-key",
139
+ async authenticate(request) {
140
+ if (owner === null)
141
+ return { kind: "refused", status: 403 };
142
+ const admission = await ownerOrKeyAuth(env.OYSTER_OWNER_KEY, owner.actor.memberId, () => env.KEYS.getByName(scope.durableName("keys"))).authenticate(request);
143
+ // This entry admits only named keys; the owner key has its own entry.
144
+ return admission.kind === "admitted" &&
145
+ admission.principal.actor.kind === "team-key"
146
+ ? { kind: "principal", principal: admission.principal }
147
+ : { kind: "refused", status: 401 };
148
+ },
149
+ },
150
+ ],
151
+ });
152
+ const routes = ownerAuth.routes;
153
+ return {
154
+ tenancy,
155
+ owner,
156
+ login: async (request) => routes === undefined
157
+ ? new Response("Sign-in is not available", { status: 404 })
158
+ : await routes.fetch(request),
159
+ };
160
+ };
161
+ /**
162
+ * The registry only ever stores a tenant's creation-time owner membership,
163
+ * and `ownerOf` proved this admission names exactly one owner. So the
164
+ * admitted owner is the tenant's only member: attest it on that principal.
165
+ */
166
+ const soleOwner = (current, principal) => principal.actor.kind === "member" &&
167
+ current.owner?.actor.memberId === principal.actor.memberId
168
+ ? { ...principal, soleMember: true }
169
+ : principal;
170
+ /** The one TENANTS read for an entry at `url`; another origin never reads. */
171
+ const admit = async (url) => {
172
+ if (url.origin !== scope.origin)
173
+ return null;
174
+ const admission = await registry.admission(url.host, identity);
175
+ return admission === null ? null : admitted(admission);
176
+ };
177
+ const atRoot = () => admit(new URL(scope.origin));
178
+ const owner = async () => (await atRoot())?.owner ?? null;
179
+ const tenancy = {
180
+ resolve: async (request) => (await (await admit(new URL(request.url)))?.tenancy.resolve(request)) ??
181
+ null,
182
+ authenticate: async (request, tenant) => {
183
+ const current = await admit(new URL(request.url));
184
+ return current === null
185
+ ? { kind: "refused", status: 404 }
186
+ : current.tenancy.authenticate(request, tenant);
187
+ },
188
+ member: async (tenantId, member) => (await (await atRoot())?.tenancy.member(tenantId, member)) ?? null,
189
+ memberById: async (tenantId, memberId) => (await (await atRoot())?.tenancy.memberById(tenantId, memberId)) ?? null,
190
+ middleware: (options) => async (context, next) => {
191
+ const current = await admit(new URL(context.req.url));
192
+ return current === null
193
+ ? context.json({ error: "not_found" }, 404)
194
+ : current.tenancy.middleware(options)(context, next);
195
+ },
196
+ oyster: () => oyster,
197
+ };
198
+ const oyster = {
199
+ async resolve(request) {
200
+ const current = await admit(new URL(request.url));
201
+ const tenant = (await current?.tenancy.resolve(request)) ?? null;
202
+ if (current === null || tenant === null)
203
+ return null;
204
+ // This request's authentication consumes the admission read above.
205
+ // Any other request is its own entry and is admitted afresh.
206
+ const forRequest = (incoming) => incoming === request
207
+ ? Promise.resolve(current)
208
+ : admit(new URL(incoming.url));
209
+ return {
210
+ scope: tenantScope(tenant.id, tenant.origin),
211
+ auth: {
212
+ resolve: async (actor) => {
213
+ if (actor.kind !== "member")
214
+ return null;
215
+ const current = await atRoot();
216
+ const member = (await current?.tenancy.memberById(tenant.id, actor.memberId)) ??
217
+ null;
218
+ return current === null || member === null
219
+ ? null
220
+ : soleOwner(current, member);
221
+ },
222
+ authenticate: async (incoming) => {
223
+ const admittedFor = await forRequest(incoming);
224
+ if (admittedFor === null)
225
+ return { kind: "refused", status: 404 };
226
+ const result = await admittedFor.tenancy.authenticate(incoming, tenant);
227
+ return result.kind === "admitted"
228
+ ? {
229
+ kind: "admitted",
230
+ principal: soleOwner(admittedFor, result.principal),
231
+ }
232
+ : result;
233
+ },
234
+ login: async (incoming) => (await (await forRequest(incoming))?.login(incoming)) ??
235
+ new Response("Not found", { status: 404 }),
236
+ },
237
+ };
238
+ },
239
+ };
240
+ return { tenantId: defaultTenant, tenancy, oyster, owner };
241
+ }
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Bonsai on a standalone Oyster: the whole Worker is Oyster's own git-enabled
3
+ * entry, plus agents on Workers AI. Bonsai is installed into it as an App
4
+ * Release (scripts/release.mjs); nothing here is specific to Bonsai.
5
+ */
6
+ import { type OysterGitWorkerEnv } from "../workers-git.js";
7
+ import { type TenantScope } from "../tenant.js";
8
+ import { WorkerEntrypoint } from "cloudflare:workers";
9
+ import { type StandaloneComputerEnv } from "./computer.js";
10
+ import { StandaloneAgentBase, type StandaloneAgentsEnv } from "./agents.js";
11
+ import { type OysterMediaEnv } from "./media.js";
12
+ import { StandaloneInstance as ComputerInstance } from "./instance.js";
13
+ export { OysterBinding } from "./instance.js";
14
+ import { StandaloneWorkflowRunsBase, type StandaloneWorkflowEnv } from "./workflows.js";
15
+ import { type StandaloneTenancyEnv } from "./tenancy.js";
16
+ export { StandaloneTenants } from "./tenancy.js";
17
+ import { type OysterComputerEgressProps } from "./computer-egress.js";
18
+ import { type AppSourceBindingEnv } from "./app-source.js";
19
+ export { StandaloneGraphWorkflow } from "./workflows.js";
20
+ export { MediaOperationDO } from "@fungi.computer/darkroom/cloudflare";
21
+ /** Every binding the standalone Bonsai root receives. */
22
+ export type StandaloneEnv = OysterGitWorkerEnv & StandaloneTenancyEnv & StandaloneComputerEnv & StandaloneAgentsEnv & OysterMediaEnv & StandaloneWorkflowEnv & AppSourceBindingEnv & {
23
+ OYSTER_HOME_INSTALLATION: string;
24
+ /** `disabled` closes the Computer's public download lane. */
25
+ COMPUTER_PUBLIC_EGRESS?: string;
26
+ };
27
+ export { ComputerDO } from "./computer.js";
28
+ export { WorkspaceProxy, WorkspaceServiceProxy, } from "@fungi.computer/computer";
29
+ export { StandaloneRegistry as OysterRegistry } from "./registry.js";
30
+ export { OysterKeys } from "../workers-git.js";
31
+ export { StandaloneEventDelivery as OysterWebhooks } from "./instance.js";
32
+ export { AgentDirectory } from "./agents.js";
33
+ declare const OysterMcp_base: new (ctx: DurableObjectState, env: StandaloneEnv) => import("../mcp-owner.js").OysterMcpOwner<StandaloneEnv>;
34
+ export declare class OysterMcp extends OysterMcp_base {
35
+ }
36
+ /** Background wakes readmit members through this root's tenancy, as HTTP and MCP do. */
37
+ export declare class StandaloneInstance extends ComputerInstance<StandaloneEnv> {
38
+ protected hostFor(scope: TenantScope): import("../workers-git.js").OysterGitWorkerHost;
39
+ }
40
+ /** Agent App tools derive the same catalog as HTTP and MCP for the Agent's pinned tenant. */
41
+ export declare class StandaloneAgent extends StandaloneAgentBase<StandaloneEnv> {
42
+ protected hostFor(scope: TenantScope): import("../workers-git.js").OysterGitWorkerHost;
43
+ }
44
+ /** Workflow runs admit through the same root composition as HTTP. */
45
+ export declare class StandaloneWorkflowRuns extends StandaloneWorkflowRunsBase<StandaloneEnv> {
46
+ protected hostFor(scope: TenantScope): import("../workers-git.js").OysterGitWorkerHost;
47
+ }
48
+ /** The Computer's egress gateway: its own HTTPS Git back into this Worker. */
49
+ export declare class OysterComputerEgress extends WorkerEntrypoint<StandaloneEnv, OysterComputerEgressProps> {
50
+ fetch(request: Request): Promise<Response>;
51
+ }
52
+ declare const _default: {
53
+ fetch(request: Request<unknown, IncomingRequestCfProperties<unknown>>, env: StandaloneEnv, context: ExecutionContext<unknown>): Promise<Response>;
54
+ };
55
+ export default _default;
@@ -0,0 +1,147 @@
1
+ /**
2
+ * Bonsai on a standalone Oyster: the whole Worker is Oyster's own git-enabled
3
+ * entry, plus agents on Workers AI. Bonsai is installed into it as an App
4
+ * Release (scripts/release.mjs); nothing here is specific to Bonsai.
5
+ */
6
+ import { createOysterGitWorker, createOysterGitMcpOwner, oysterGitRoot, } from "../workers-git.js";
7
+ import { defaultTenant, tenantScope } from "../tenant.js";
8
+ import { onBoxVmIngress } from "@fungi.computer/computer";
9
+ import { WorkerEntrypoint } from "cloudflare:workers";
10
+ import { putContentObject } from "../build-contract.js";
11
+ import { workflowGitPassPrefix } from "../git.js";
12
+ import { z } from "zod";
13
+ import { standaloneComputerCapabilities, standaloneWorkspace, } from "./computer.js";
14
+ import { StandaloneAgentBase, standaloneAgents, } from "./agents.js";
15
+ import { workersMedia } from "./media.js";
16
+ import { StandaloneInstance as ComputerInstance } from "./instance.js";
17
+ // Every guest binding in every frame is this one entrypoint, routed by props.
18
+ export { OysterBinding } from "./instance.js";
19
+ import { StandaloneWorkflowRunsBase, } from "./workflows.js";
20
+ import { standaloneTenancy } from "./tenancy.js";
21
+ export { StandaloneTenants } from "./tenancy.js";
22
+ import { standalonePreviewRequest } from "./preview.js";
23
+ import { standaloneBuildRoutes } from "./builds.js";
24
+ import { serveOysterComputerEgress, } from "./computer-egress.js";
25
+ import { appSourceOwnerRoutes, } from "./app-source.js";
26
+ export { StandaloneGraphWorkflow } from "./workflows.js";
27
+ export { MediaOperationDO } from "@fungi.computer/darkroom/cloudflare";
28
+ /** A tenant's registry: it owns that tenant's installations and App builds. */
29
+ 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. */
32
+ const computerRegistry = (env) => tenantRegistry(env, tenantScope(defaultTenant, env.OYSTER_ORIGIN));
33
+ export { ComputerDO } from "./computer.js";
34
+ export { WorkspaceProxy, WorkspaceServiceProxy, } from "@fungi.computer/computer";
35
+ export { StandaloneRegistry as OysterRegistry } from "./registry.js";
36
+ export { OysterKeys } from "../workers-git.js";
37
+ // WEBHOOKS keeps its deployed class name; the standalone owner also delivers to Workflows.
38
+ export { StandaloneEventDelivery as OysterWebhooks } from "./instance.js";
39
+ export { AgentDirectory } from "./agents.js";
40
+ /** After Oyster's bearer prefix: `<library>.<pass>`, as the library minted it. */
41
+ const workflowGitPassToken = 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
+ /** 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
+ });
87
+ export class OysterMcp extends createOysterGitMcpOwner(oysterRoot) {
88
+ }
89
+ /** Background wakes readmit members through this root's tenancy, as HTTP and MCP do. */
90
+ export class StandaloneInstance extends ComputerInstance {
91
+ hostFor(scope) {
92
+ return oysterRoot.hostFor(this.env, scope);
93
+ }
94
+ }
95
+ /** Agent App tools derive the same catalog as HTTP and MCP for the Agent's pinned tenant. */
96
+ export class StandaloneAgent extends StandaloneAgentBase {
97
+ hostFor(scope) {
98
+ return oysterRoot.hostFor(this.env, scope);
99
+ }
100
+ }
101
+ /** Workflow runs admit through the same root composition as HTTP. */
102
+ export class StandaloneWorkflowRuns extends StandaloneWorkflowRunsBase {
103
+ hostFor(scope) {
104
+ return oysterRoot.hostFor(this.env, scope);
105
+ }
106
+ }
107
+ const oyster = createOysterGitWorker(oysterRoot);
108
+ /** The Computer's egress gateway: its own HTTPS Git back into this Worker. */
109
+ export class OysterComputerEgress extends WorkerEntrypoint {
110
+ fetch(request) {
111
+ const computerObjectId = this.ctx.props.computerObjectId;
112
+ const registry = computerRegistry(this.env);
113
+ return serveOysterComputerEgress(request, {
114
+ origin: this.env.OYSTER_ORIGIN,
115
+ computerId: computerObjectId,
116
+ publicEgress: this.env.COMPUTER_PUBLIC_EGRESS,
117
+ build: {
118
+ gitPassFor: (target) => registry.buildGitPassFor(computerObjectId, target),
119
+ exchangeTemplate: (repository, request) => registry.exchangeBuildTemplate(computerObjectId, repository, request),
120
+ admitBuild: (jobId) => registry.admitBuildReport(jobId, computerObjectId),
121
+ admitObject: (jobId, grant) => registry.admitBuildObject(jobId, computerObjectId, grant),
122
+ // The bytes stream from the Computer to R2, which checks the digest;
123
+ // the registry only admits the upload and later reads the facts.
124
+ storeObject: (object, body, mediaType) => putContentObject(this.env.RELEASES, object,
125
+ // R2 takes a stream of known length; the stated length is exact.
126
+ body?.pipeThrough(new FixedLengthStream(object.byteLength)) ?? null, mediaType),
127
+ submitCandidate: (jobId, manifest, grant, credentialId) => registry.submitBuildCandidate(jobId, manifest, grant, credentialId),
128
+ record: (event) => registry.recordBuildEvent(event),
129
+ },
130
+ // Back through this Worker's own default export, the way every other client arrives.
131
+ forward: (admitted) => this.ctx.exports.default.fetch(admitted),
132
+ });
133
+ }
134
+ }
135
+ export default {
136
+ 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));
138
+ if (preview !== null)
139
+ return preview;
140
+ const ingress = onBoxVmIngress(request, env, env.COMPUTER);
141
+ if (ingress !== undefined)
142
+ return ingress;
143
+ if (oyster.fetch === undefined)
144
+ throw new Error("Oyster fetch handler is missing");
145
+ return oyster.fetch(request, env, context);
146
+ },
147
+ };
@@ -0,0 +1,16 @@
1
+ import type { ReadonlyTenancy } from "../tenancy/index.js";
2
+ import type { TenantId } from "../tenant.js";
3
+ import type { OysterHost } from "../host.js";
4
+ import type { WorkflowRunAuthorityPorts } from "@fungi.computer/braid/runtime";
5
+ import type { createStandaloneWorkflowHost } from "./workflow-host.js";
6
+ /** Host-selected tenant, Computer and library; none come from Workflow input. */
7
+ export type StandaloneWorkflowAuthorityReaderOptions = Readonly<{
8
+ tenantId: TenantId;
9
+ tenancy: Pick<ReadonlyTenancy, "memberById">;
10
+ host: Pick<OysterHost, "computerId">;
11
+ libraryInstallationId: string;
12
+ catalogHost: Pick<ReturnType<typeof createStandaloneWorkflowHost>, "admitLibrary" | "catalogForLibrary">;
13
+ }>;
14
+ /** Live authority readers only: catalog entries do not establish node grants. */
15
+ export type StandaloneWorkflowAuthorityReaders = Pick<WorkflowRunAuthorityPorts, "member" | "memberComputer" | "catalog">;
16
+ export declare function createStandaloneWorkflowAuthorityReaders(options: StandaloneWorkflowAuthorityReaderOptions): StandaloneWorkflowAuthorityReaders;
@@ -0,0 +1,35 @@
1
+ import { AppInstanceError } from "../index.js";
2
+ export function createStandaloneWorkflowAuthorityReaders(options) {
3
+ const member = async (id) => {
4
+ const current = await options.tenancy.memberById(options.tenantId, id);
5
+ if (current?.actor.kind !== "member" ||
6
+ current.actor.memberId !== id ||
7
+ !("role" in current) ||
8
+ current.role !== "owner")
9
+ return null;
10
+ return { membershipVersion: current.membershipVersion };
11
+ };
12
+ return {
13
+ member,
14
+ async memberComputer(id, computerId) {
15
+ const before = await member(id);
16
+ if (before === null || computerId !== options.host.computerId)
17
+ return false;
18
+ const after = await member(id);
19
+ return after?.membershipVersion === before.membershipVersion;
20
+ },
21
+ async catalog(id) {
22
+ const before = await member(id);
23
+ if (before === null)
24
+ throw new AppInstanceError("revoked");
25
+ const selected = await options.catalogHost.admitLibrary(options.libraryInstallationId, "catalog");
26
+ const entries = await options.catalogHost.catalogForLibrary(selected, {
27
+ full: true,
28
+ });
29
+ const after = await member(id);
30
+ if (after?.membershipVersion !== before.membershipVersion)
31
+ throw new AppInstanceError("revoked");
32
+ return entries.apps;
33
+ },
34
+ };
35
+ }