@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,138 @@
1
+ import { checkedOrigin, immutable, createMemberId, membershipVersionSchema, } from "./registry-record.js";
2
+ import * as Schema from "effect/Schema";
3
+ import { tenantIdSchema } from "../tenant.js";
4
+ /** In-memory tenant and member owner; create updates both atomically. */
5
+ export function memoryRegistry(options = {}) {
6
+ const tenants = new Map();
7
+ const handles = new Map();
8
+ const domains = new Map();
9
+ const identities = new Map();
10
+ const memberships = new Map();
11
+ let revision = 0;
12
+ const nextRevision = () => {
13
+ revision = Schema.decodeUnknownSync(membershipVersionSchema)(revision + 1);
14
+ return revision;
15
+ };
16
+ const canonicalMember = (identity) => identities.get(identity.provider)?.get(identity.subject);
17
+ const retainMember = (identity, memberId) => {
18
+ let subjects = identities.get(identity.provider);
19
+ if (subjects === undefined) {
20
+ subjects = new Map();
21
+ identities.set(identity.provider, subjects);
22
+ }
23
+ const previous = subjects.get(identity.subject);
24
+ if (previous !== undefined && previous !== memberId)
25
+ throw new Error("Identity already has a canonical member mapping");
26
+ subjects.set(identity.subject, memberId);
27
+ };
28
+ const insert = (tenant) => {
29
+ if (tenants.has(tenant.id) ||
30
+ handles.has(tenant.handle) ||
31
+ tenant.domains.some((domain) => domains.has(domain)))
32
+ throw new Error("Tenant identity, handle or domain already exists");
33
+ const stored = immutable(tenant);
34
+ tenants.set(tenant.id, stored);
35
+ handles.set(tenant.handle, tenant.id);
36
+ for (const domain of tenant.domains)
37
+ domains.set(domain, tenant.id);
38
+ return stored;
39
+ };
40
+ for (const tenant of options.tenants ?? []) {
41
+ Schema.decodeUnknownSync(tenantIdSchema)(tenant.id);
42
+ Schema.decodeUnknownSync(tenantIdSchema)(tenant.handle);
43
+ insert({ ...tenant, origin: checkedOrigin(tenant.origin, tenant.domains) });
44
+ }
45
+ for (const membership of options.memberships ?? []) {
46
+ if (!tenants.has(membership.tenantId))
47
+ throw new Error("Membership tenant does not exist");
48
+ retainMember(membership.identity, membership.memberId);
49
+ let entries = memberships.get(membership.tenantId);
50
+ if (entries === undefined) {
51
+ entries = new Map();
52
+ memberships.set(membership.tenantId, entries);
53
+ }
54
+ entries.set(membership.memberId, {
55
+ memberId: membership.memberId,
56
+ role: membership.role,
57
+ membershipVersion: nextRevision(),
58
+ });
59
+ }
60
+ const get = (id) => tenants.get(id) ?? null;
61
+ return {
62
+ get: (id) => Promise.resolve(get(id)),
63
+ byHandle(handle) {
64
+ const id = handles.get(handle);
65
+ return Promise.resolve(id === undefined ? null : get(id));
66
+ },
67
+ byDomain(host) {
68
+ const id = domains.get(host);
69
+ return Promise.resolve(id === undefined ? null : get(id));
70
+ },
71
+ status: (id) => Promise.resolve(get(id)?.status ?? null),
72
+ memberById(id, memberId) {
73
+ const member = memberships.get(id)?.get(memberId);
74
+ return Promise.resolve(member === undefined ? null : { ...member });
75
+ },
76
+ membershipOf(id, identity) {
77
+ const memberId = canonicalMember(identity);
78
+ return Promise.resolve(memberId === undefined
79
+ ? null
80
+ : (memberships.get(id)?.get(memberId) ?? null));
81
+ },
82
+ create(input) {
83
+ return Promise.resolve().then(() => {
84
+ const handle = Schema.decodeUnknownSync(tenantIdSchema)(input.handle);
85
+ const origin = checkedOrigin(input.origin, input.domains);
86
+ const id = handle;
87
+ const memberId = canonicalMember(input.owner) ?? createMemberId();
88
+ const tenant = {
89
+ id,
90
+ handle,
91
+ origin,
92
+ domains: [...input.domains],
93
+ owners: [memberId],
94
+ status: "active",
95
+ };
96
+ const stored = insert(tenant);
97
+ retainMember(input.owner, memberId);
98
+ memberships.set(id, new Map([
99
+ [
100
+ memberId,
101
+ {
102
+ memberId,
103
+ role: "owner",
104
+ membershipVersion: nextRevision(),
105
+ },
106
+ ],
107
+ ]));
108
+ return stored;
109
+ });
110
+ },
111
+ setStatus(id, status) {
112
+ const tenant = get(id);
113
+ if (tenant === null)
114
+ return Promise.reject(new Error("Tenant does not exist"));
115
+ if (tenant.status !== status) {
116
+ const membershipVersion = nextRevision();
117
+ for (const [memberId, membership] of memberships.get(id) ?? [])
118
+ memberships
119
+ .get(id)
120
+ ?.set(memberId, { ...membership, membershipVersion });
121
+ }
122
+ tenants.set(id, immutable({ ...tenant, status }));
123
+ return Promise.resolve();
124
+ },
125
+ delete(id) {
126
+ const tenant = get(id);
127
+ if (tenant === null)
128
+ return Promise.resolve();
129
+ nextRevision();
130
+ tenants.delete(id);
131
+ handles.delete(tenant.handle);
132
+ memberships.delete(id);
133
+ for (const domain of tenant.domains)
134
+ domains.delete(domain);
135
+ return Promise.resolve();
136
+ },
137
+ };
138
+ }
@@ -0,0 +1,7 @@
1
+ import type { ReadonlyTenantRegistry, TenantResolverAdapter } from "./contracts.js";
2
+ /** Resolve a provisioned label immediately below a configured base hostname. */
3
+ export declare function subdomain(baseDomain: string): TenantResolverAdapter<"origin">;
4
+ /** Resolve a provisioned custom domain through the registry's domain index. */
5
+ export declare function hosts(registry: ReadonlyTenantRegistry): TenantResolverAdapter<"origin">;
6
+ /** Resolve provisioned path tenants for APIs; this resolver cannot host Oyster. */
7
+ export declare function path(pattern: string): TenantResolverAdapter<"path">;
@@ -0,0 +1,64 @@
1
+ import { match, parse } from "path-to-regexp";
2
+ import * as Schema from "effect/Schema";
3
+ import { tenantIdSchema } from "../tenant.js";
4
+ /** Resolve a provisioned label immediately below a configured base hostname. */
5
+ export function subdomain(baseDomain) {
6
+ const base = new URL(`https://${baseDomain}`);
7
+ if (base.host !== baseDomain.toLowerCase() ||
8
+ base.port !== "" ||
9
+ base.pathname !== "/" ||
10
+ base.search ||
11
+ base.hash ||
12
+ base.username ||
13
+ base.password ||
14
+ base.hostname.endsWith("."))
15
+ throw new TypeError("Configure a base hostname without credentials, path, port or trailing dot");
16
+ const suffix = `.${base.hostname}`;
17
+ return {
18
+ kind: "origin",
19
+ async resolve(url, registry) {
20
+ if (!url.hostname.endsWith(suffix))
21
+ return null;
22
+ const handle = url.hostname.slice(0, -suffix.length);
23
+ const parsed = Schema.decodeUnknownOption(tenantIdSchema)(handle);
24
+ if (parsed._tag === "None")
25
+ return null;
26
+ return (await registry.byHandle(handle))?.id ?? null;
27
+ },
28
+ };
29
+ }
30
+ /** Resolve a provisioned custom domain through the registry's domain index. */
31
+ export function hosts(registry) {
32
+ return {
33
+ kind: "origin",
34
+ resolve: async (url) => (await registry.byDomain?.(url.host))?.id ?? null,
35
+ };
36
+ }
37
+ /** Resolve provisioned path tenants for APIs; this resolver cannot host Oyster. */
38
+ export function path(pattern) {
39
+ const tokens = parse(pattern).tokens;
40
+ if (!tokens.some((token) => token.type === "param" && token.name === "tenant"))
41
+ throw new TypeError("A tenant path must contain the :tenant parameter");
42
+ const route = match(pattern, { end: false });
43
+ return {
44
+ kind: "path",
45
+ async resolve(url, registry) {
46
+ let result;
47
+ try {
48
+ result = route(url.pathname);
49
+ }
50
+ catch (cause) {
51
+ if (cause instanceof URIError)
52
+ return null;
53
+ throw cause;
54
+ }
55
+ if (result === false)
56
+ return null;
57
+ const handle = result.params.tenant;
58
+ const parsed = Schema.decodeUnknownOption(tenantIdSchema)(handle);
59
+ if (parsed._tag === "None")
60
+ return null;
61
+ return (await registry.byHandle(parsed.value))?.id ?? null;
62
+ },
63
+ };
64
+ }
@@ -0,0 +1,81 @@
1
+ import type { HostActor } from "@fungi.computer/puffball/app-method-runtime";
2
+ import * as Schema from "effect/Schema";
3
+ import type { OysterAuth } from "./host.js";
4
+ /**
5
+ * A host-minted tenant slug: 2–40 lowercase ASCII letters or digits, with
6
+ * single interior hyphens. The repository separator `--` is forbidden.
7
+ * Hosts resolve it before authentication; App-controlled inputs never select it.
8
+ */
9
+ export declare const tenantIdSchema: Schema.brand<Schema.String, "OysterTenantId">;
10
+ /** A validated host-selected tenant identity. */
11
+ export type TenantId = typeof tenantIdSchema.Type;
12
+ /** A canonical host actor with a tenant role only when the actor is a member. */
13
+ export type TenantPrincipal = Readonly<{
14
+ /** Stored member identity resolved by the tenant registry. */
15
+ actor: Extract<HostActor, {
16
+ kind: "member";
17
+ }>;
18
+ /** The member's admitted role; membership does not imply ownership. */
19
+ role: "owner" | "member";
20
+ /** The host attests this member is the tenant's only member. */
21
+ soleMember?: true;
22
+ }> | Readonly<{
23
+ /** An Agent or Team key retains its canonical actor identity. */
24
+ actor: Exclude<HostActor, {
25
+ kind: "member";
26
+ }>;
27
+ }>;
28
+ /** The single-tenant Oyster identity whose durable names remain unchanged. */
29
+ export declare const defaultTenant: TenantId;
30
+ /** Resolves a request before authentication; null means refuse, never fallback. */
31
+ export type TenantResolver = Readonly<{
32
+ /** Return the host-selected tenant and canonical origin, or refuse this URL. */
33
+ resolve(request: Request): Promise<Readonly<{
34
+ tenantId: TenantId;
35
+ origin: string;
36
+ }> | null>;
37
+ }>;
38
+ /** The only source of durable names for a standalone Oyster tenant. */
39
+ export type TenantScope = Readonly<{
40
+ /** Host-selected tenant identity; never taken from App input. */
41
+ tenantId: TenantId;
42
+ /** Canonical HTTP(S) origin for this tenant's public routes. */
43
+ origin: string;
44
+ /** Name of a tenant-owned Durable Object within its binding namespace. */
45
+ durableName(kind: "registry" | "keys" | "webhooks" | "git-push" | "agents" | "mcp"): string;
46
+ /** Name within the Computer binding; nondefault Computers await host support. */
47
+ computerName(computerId: string): string;
48
+ /** R2 prefix for mutable workspace files; immutable release bytes may be shared. */
49
+ workspacePrefix: string;
50
+ /**
51
+ * Provider prefix for an installation slug without `--`.
52
+ * The Git adapter must validate the final prefix + repo against the provider's
53
+ * 128-character limit; a prefix alone cannot establish that a repo name fits.
54
+ */
55
+ repositoryPrefix(installationId: string): string;
56
+ }>;
57
+ /**
58
+ * Name tenant-owned stores without changing any default-tenant name. The `t.`
59
+ * repository namespace cannot be a default installation slug, which forbids dots.
60
+ * Hub supplies its own Team-scoped ports and does not use this naming contract.
61
+ */
62
+ export declare function tenantScope(tenantId: TenantId, origin: string): TenantScope;
63
+ /**
64
+ * Admit defaultTenant only at the configured origin. Scheme, host and port must
65
+ * match after URL canonicalization; unknown origins never fall back to default.
66
+ */
67
+ export declare function singleTenant(origin: string): TenantResolver;
68
+ /** A host-selected scope paired with its existing authentication port. */
69
+ export type ResolvedTenant = Readonly<{
70
+ /** Canonical origin and durable names selected before authentication. */
71
+ scope: TenantScope;
72
+ /** Authentication bound to this tenant; resolution does not invoke it. */
73
+ auth: OysterAuth;
74
+ }>;
75
+ /** Resolve an origin-scoped host; null refuses the request without a fallback. */
76
+ export type OysterTenancy = Readonly<{
77
+ /** Select one tenant before authentication; unknown origins return null. */
78
+ resolve(request: Request): Promise<ResolvedTenant | null>;
79
+ }>;
80
+ /** Preserve the default tenant's names and auth only at the configured origin. */
81
+ export declare function implicitTenancy(origin: string, auth: OysterAuth): OysterTenancy;
package/dist/tenant.js ADDED
@@ -0,0 +1,71 @@
1
+ import * as Schema from "effect/Schema";
2
+ /**
3
+ * A host-minted tenant slug: 2–40 lowercase ASCII letters or digits, with
4
+ * single interior hyphens. The repository separator `--` is forbidden.
5
+ * Hosts resolve it before authentication; App-controlled inputs never select it.
6
+ */
7
+ export const tenantIdSchema = Schema.String.check(Schema.isPattern(/^[a-z0-9](?:[a-z0-9]|-(?!-)){0,38}[a-z0-9]$/u)).pipe(Schema.brand("OysterTenantId"));
8
+ /** The single-tenant Oyster identity whose durable names remain unchanged. */
9
+ export const defaultTenant = Schema.decodeUnknownSync(tenantIdSchema)("default");
10
+ function configuredOrigin(origin) {
11
+ const url = new URL(origin);
12
+ if (!["https:", "http:"].includes(url.protocol) ||
13
+ url.username !== "" ||
14
+ url.password !== "" ||
15
+ url.pathname !== "/" ||
16
+ url.search !== "" ||
17
+ url.hash !== "") {
18
+ throw new TypeError("Configure an HTTP(S) origin without credentials or a path");
19
+ }
20
+ return url;
21
+ }
22
+ const installationIdSchema = Schema.String.check(Schema.isPattern(/^[a-z][a-z0-9-]{0,62}$/u), Schema.isPattern(/^(?!.*--)/u));
23
+ /**
24
+ * Name tenant-owned stores without changing any default-tenant name. The `t.`
25
+ * repository namespace cannot be a default installation slug, which forbids dots.
26
+ * Hub supplies its own Team-scoped ports and does not use this naming contract.
27
+ */
28
+ export function tenantScope(tenantId, origin) {
29
+ const canonicalOrigin = configuredOrigin(origin).origin;
30
+ const isDefault = tenantId === defaultTenant;
31
+ const durablePrefix = isDefault ? "" : `t.${tenantId}.`;
32
+ return {
33
+ tenantId,
34
+ origin: canonicalOrigin,
35
+ durableName: (kind) => `${durablePrefix}${kind}`,
36
+ computerName: (computerId) => `${durablePrefix}${computerId}`,
37
+ workspacePrefix: isDefault
38
+ ? "workspace/"
39
+ : `tenants/${tenantId}/workspace/`,
40
+ repositoryPrefix(installationId) {
41
+ const installation = Schema.decodeUnknownSync(installationIdSchema)(installationId);
42
+ return isDefault
43
+ ? `${installation}--`
44
+ : `t.${tenantId}--${installation}--`;
45
+ },
46
+ };
47
+ }
48
+ /**
49
+ * Admit defaultTenant only at the configured origin. Scheme, host and port must
50
+ * match after URL canonicalization; unknown origins never fall back to default.
51
+ */
52
+ export function singleTenant(origin) {
53
+ const canonicalOrigin = configuredOrigin(origin).origin;
54
+ return {
55
+ resolve: (request) => Promise.resolve(new URL(request.url).origin === canonicalOrigin
56
+ ? { tenantId: defaultTenant, origin: canonicalOrigin }
57
+ : null),
58
+ };
59
+ }
60
+ /** Preserve the default tenant's names and auth only at the configured origin. */
61
+ export function implicitTenancy(origin, auth) {
62
+ const resolver = singleTenant(origin);
63
+ const scope = tenantScope(defaultTenant, origin);
64
+ return {
65
+ async resolve(request) {
66
+ return (await resolver.resolve(request)) === null
67
+ ? null
68
+ : { scope, auth };
69
+ },
70
+ };
71
+ }
@@ -0,0 +1,29 @@
1
+ import { Hono } from "hono";
2
+ import { type AppCapabilityId } from "@fungi.computer/puffball/app";
3
+ import { type OysterAgents } from "../agents.js";
4
+ import { type FileSource } from "../files.js";
5
+ import { type OysterAuthResult, type OysterCapability } from "../host.js";
6
+ /** How one capability fixture host admits its single installation. */
7
+ export type CapabilityFixtureOptions = Readonly<{
8
+ /** `false` publishes a Release that declares no capability. */
9
+ declared?: boolean;
10
+ /** Capabilities the Release declares; defaults to `computer.list`. */
11
+ capabilities?: readonly AppCapabilityId[];
12
+ /** Adapters the host grants; defaults to a `computer.list` probe. */
13
+ adapters?: readonly OysterCapability[];
14
+ /** Grant the file adapters over this source instead of the probe. */
15
+ fileSource?: FileSource;
16
+ /** `false` clears the owner's approval after install. */
17
+ admitted?: boolean;
18
+ /** Serve a public installation to an anonymous visitor. */
19
+ visitor?: boolean;
20
+ authenticate?: (request: Request) => Promise<OysterAuthResult>;
21
+ agents?: OysterAgents;
22
+ }>;
23
+ /** One Oyster host with a single installation whose capability grants the options shape. */
24
+ export declare function capabilityFixture(options?: CapabilityFixtureOptions): Promise<{
25
+ app: Hono<import("../host.js").OysterEnv, import("hono/types").BlankSchema, "/">;
26
+ page: () => Response | Promise<Response>;
27
+ request: () => Response | Promise<Response>;
28
+ removeDeclaration(): Promise<void>;
29
+ }>;
@@ -0,0 +1,130 @@
1
+ import { Hono } from "hono";
2
+ import * as Schema from "effect/Schema";
3
+ import { releaseSchema } from "@fungi.computer/puffball/effect";
4
+ import { parseArtifactRef, parseSha256Digest } from "@fungi.computer/puffball";
5
+ import { createAppBuild, emptyAppDeclaration, } from "@fungi.computer/puffball/app";
6
+ import { computerList } from "@fungi.computer/agar/computer";
7
+ import { agentApprovalRoutes } from "../agents.js";
8
+ import { fileCapabilities } from "../files.js";
9
+ import { createOyster, } from "../host.js";
10
+ import { instanceIds } from "../index.js";
11
+ import { sha256, verifyRelease } from "../release.js";
12
+ import { approvedInstallation } from "./installation.js";
13
+ /** One Oyster host with a single installation whose capability grants the options shape. */
14
+ export async function capabilityFixture(options = {}) {
15
+ const artifacts = new Map();
16
+ async function put(bytes) {
17
+ const digest = parseSha256Digest(await sha256(bytes));
18
+ const artifactRef = parseArtifactRef(`artifact:sha256-${digest.slice(7)}`);
19
+ artifacts.set(artifactRef, bytes);
20
+ return { artifactRef, digest };
21
+ }
22
+ const encode = (text) => new TextEncoder().encode(text);
23
+ const uiBytes = encode("<!doctype html><title>Capability fixture</title>");
24
+ const ui = {
25
+ ...(await put(uiBytes)),
26
+ version: "fixture",
27
+ byteLength: uiBytes.length,
28
+ mediaType: "text/html; charset=utf-8",
29
+ };
30
+ async function release(releaseId, capabilities) {
31
+ const root = await put(encode(JSON.stringify(createAppBuild(ui, null, { capabilities, handlers: [], roles: [] }))));
32
+ return Schema.decodeUnknownSync(releaseSchema)({
33
+ releaseId,
34
+ appId: "app_capability",
35
+ artifact: {
36
+ artifactRef: root.artifactRef,
37
+ artifactDigest: root.digest,
38
+ provenance: {
39
+ sourceRevision: `git:${"a".repeat(40)}`,
40
+ sourceTreeDigest: root.digest,
41
+ buildRecipeDigest: root.digest,
42
+ },
43
+ },
44
+ });
45
+ }
46
+ const routes = new Hono().post("/list", (context) => context.json({ entries: [] }));
47
+ const capabilities = options.adapters ??
48
+ (options.fileSource
49
+ ? fileCapabilities(options.fileSource)
50
+ : [
51
+ {
52
+ id: computerList.id,
53
+ permission: "computer.list",
54
+ version: computerList.version,
55
+ base: "/probe/",
56
+ routes,
57
+ methods: { list: { kind: "json", method: "POST", path: "list" } },
58
+ },
59
+ ]);
60
+ const declaredRelease = await release("rel_declared", options.declared === false
61
+ ? []
62
+ : (options.capabilities ?? ["computer.list"]));
63
+ const declaration = (await verifyRelease({
64
+ get: (ref) => Promise.resolve(artifacts.get(ref) ?? null),
65
+ put: () => Promise.reject(new Error("Fixture artifacts are read-only")),
66
+ }, declaredRelease)).declaration;
67
+ let installation = await approvedInstallation({
68
+ installationId: "capability-fixture",
69
+ instanceId: instanceIds.instance.generate(),
70
+ release: declaredRelease,
71
+ declaration: declaration ?? emptyAppDeclaration,
72
+ descriptors: capabilities,
73
+ });
74
+ if (options.admitted === false)
75
+ installation = { ...installation, approvedGrants: [], approval: null };
76
+ // An anonymous reader reaches only a public App, as the host's visitor.
77
+ if (options.visitor)
78
+ installation = { ...installation, visibility: "public" };
79
+ const readOnly = () => Promise.reject(new Error("Fixture installations are read-only"));
80
+ const base = {
81
+ auth: {
82
+ resolve: () => Promise.reject(new Error("Capability fixture resolves no actor")),
83
+ authenticate: options.authenticate ??
84
+ ((request) => Promise.resolve(options.visitor || request.headers.get("x-who") === "visitor"
85
+ ? { kind: "refused", status: 401 }
86
+ : {
87
+ kind: "admitted",
88
+ principal: {
89
+ actor: { kind: "member", memberId: "owner" },
90
+ role: "owner",
91
+ },
92
+ })),
93
+ },
94
+ artifacts: {
95
+ get: (ref) => Promise.resolve(artifacts.get(ref) ?? null),
96
+ put: () => Promise.reject(new Error("Fixture artifacts are read-only")),
97
+ },
98
+ installations: {
99
+ list: () => Promise.resolve([installation]),
100
+ get: (id) => Promise.resolve(id === installation.installationId ? installation : null),
101
+ put: readOnly,
102
+ compareAndPut: readOnly,
103
+ },
104
+ instances: {
105
+ get: () => {
106
+ throw new Error("Capability routes must not execute an App backend");
107
+ },
108
+ },
109
+ capabilities,
110
+ };
111
+ const host = options.agents === undefined
112
+ ? base
113
+ : { ...base, ownerRoutes: agentApprovalRoutes(options.agents) };
114
+ const app = createOyster(host);
115
+ return {
116
+ app,
117
+ page: () => app.request("https://oyster.test/apps/capability-fixture/"),
118
+ request: () => app.request("https://oyster.test/probe/list", {
119
+ method: "POST",
120
+ headers: { "X-Oyster-Installation": installation.installationId },
121
+ }),
122
+ async removeDeclaration() {
123
+ installation = {
124
+ ...installation,
125
+ release: await release("rel_undeclared", []),
126
+ authorityVersion: installation.authorityVersion + 1,
127
+ };
128
+ },
129
+ };
130
+ }
@@ -0,0 +1,3 @@
1
+ /** Digest-verifying local catalog shared by native App host proofs. */
2
+ export declare function createFixtureCatalog(encoded: string): import("@fungi.computer/puffball/effect").AppReleaseCore;
3
+ export declare function seedFixtureCatalog(encoded: string, catalog: ReturnType<typeof createFixtureCatalog>): Promise<void>;
@@ -0,0 +1,53 @@
1
+ import * as Effect from "effect/Effect";
2
+ import * as Schema from "effect/Schema";
3
+ import { createAppReleaseCore, createDeterministicMemoryAppReleaseStore, adaptPromiseAppReleaseStore, releaseSchema, adaptPromiseArtifactReader, } from "@fungi.computer/puffball/effect";
4
+ import { parseSha256Digest } from "@fungi.computer/puffball";
5
+ import { appMediaType } from "@fungi.computer/puffball/app";
6
+ import { backendMediaType } from "@fungi.computer/puffball/backend";
7
+ // The Publisher's artifact store: every Release root and child object by ref.
8
+ const fixtureSchema = Schema.Struct({
9
+ releases: Schema.Array(releaseSchema),
10
+ objects: Schema.Record(Schema.String, Schema.Struct({
11
+ mediaType: Schema.Literals([
12
+ appMediaType,
13
+ backendMediaType,
14
+ "text/html; charset=utf-8",
15
+ ]),
16
+ body: Schema.String,
17
+ })),
18
+ });
19
+ const hex = (bytes) => Array.from(new Uint8Array(bytes), (b) => b.toString(16).padStart(2, "0")).join("");
20
+ /** Digest-verifying local catalog shared by native App host proofs. */
21
+ export function createFixtureCatalog(encoded) {
22
+ const fixture = Schema.decodeUnknownSync(Schema.fromJsonString(fixtureSchema))(encoded);
23
+ return createAppReleaseCore({
24
+ store: adaptPromiseAppReleaseStore(createDeterministicMemoryAppReleaseStore()),
25
+ artifactReader: adaptPromiseArtifactReader({
26
+ read: async ({ artifactRef, expectedDigest }) => {
27
+ const object = fixture.objects[artifactRef];
28
+ if (!object)
29
+ return { kind: "missing" };
30
+ const bytes = new TextEncoder().encode(object.body);
31
+ const digest = parseSha256Digest(`sha256:${hex(await crypto.subtle.digest("SHA-256", bytes))}`);
32
+ if (digest !== expectedDigest)
33
+ return { kind: "digest-mismatch", actualDigest: digest };
34
+ return {
35
+ kind: "verified",
36
+ digest,
37
+ mediaType: object.mediaType,
38
+ bytes: { byteLength: bytes.length, copy: () => bytes.slice() },
39
+ byteLength: bytes.length,
40
+ };
41
+ },
42
+ }),
43
+ artifactPolicy: { maxBytes: 1024 * 1024 },
44
+ });
45
+ }
46
+ export async function seedFixtureCatalog(encoded, catalog) {
47
+ const fixture = Schema.decodeUnknownSync(Schema.fromJsonString(fixtureSchema))(encoded);
48
+ for (const release of fixture.releases) {
49
+ await Effect.runPromise(catalog.createApp({ appId: release.appId }));
50
+ await Effect.runPromise(catalog.createRelease(release));
51
+ await Effect.runPromise(catalog.verifyReleaseArtifact(release.releaseId));
52
+ }
53
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Test kit for Oyster hosts and Apps: fixture installations, a capability
3
+ * host, a digest-verifying release catalog and the frame binding wire.
4
+ * The `cloudflare:workers` stand-in lives at `./testing/workers-runtime`.
5
+ */
6
+ export { approvedInstallation } from "./installation.js";
7
+ export { capabilityFixture, type CapabilityFixtureOptions, } from "./capabilities.js";
8
+ export { createFixtureCatalog, seedFixtureCatalog } from "./catalog.js";
9
+ export { createFrameBindingWire, type FrameBindingReply, type FrameBindingWire, } from "../frame-binding-wire.js";
10
+ export { startNativeWorker, type NativeWorker, type NativeWorkerOptions, } from "./native-worker.js";
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Test kit for Oyster hosts and Apps: fixture installations, a capability
3
+ * host, a digest-verifying release catalog and the frame binding wire.
4
+ * The `cloudflare:workers` stand-in lives at `./testing/workers-runtime`.
5
+ */
6
+ export { approvedInstallation } from "./installation.js";
7
+ export { capabilityFixture, } from "./capabilities.js";
8
+ export { createFixtureCatalog, seedFixtureCatalog } from "./catalog.js";
9
+ export { createFrameBindingWire, } from "../frame-binding-wire.js";
10
+ export { startNativeWorker, } from "./native-worker.js";
@@ -0,0 +1,11 @@
1
+ import type { AppDeclaration } from "@fungi.computer/puffball/app";
2
+ import { type CapabilityOperationDescriptor, type Installation } from "../apps/index.js";
3
+ /** Build fixture state through the same approval owner used by installation routes. */
4
+ export declare function approvedInstallation(input: Readonly<{
5
+ release: Installation["release"];
6
+ declaration: AppDeclaration;
7
+ installationId: string;
8
+ instanceId: Installation["instanceId"];
9
+ descriptors?: readonly CapabilityOperationDescriptor[];
10
+ visibility?: Installation["visibility"];
11
+ }>): Promise<Installation>;
@@ -0,0 +1,25 @@
1
+ import * as Result from "effect/Result";
2
+ import { applyInstallationCommand, deriveDeclaredOperations, } from "../apps/index.js";
3
+ /** Build fixture state through the same approval owner used by installation routes. */
4
+ export async function approvedInstallation(input) {
5
+ const result = applyInstallationCommand(null, {
6
+ kind: "install",
7
+ release: input.release,
8
+ releasePin: "pinned",
9
+ grants: "declared",
10
+ }, {
11
+ by: { kind: "member", memberId: "fixture-owner" },
12
+ role: "owner",
13
+ expectedVersion: null,
14
+ declaration: input.declaration,
15
+ declaredOperations: await deriveDeclaredOperations(input.release, input.declaration, input.descriptors ?? []),
16
+ now: 1000,
17
+ identity: {
18
+ installationId: input.installationId,
19
+ instanceId: input.instanceId,
20
+ },
21
+ });
22
+ if (Result.isFailure(result))
23
+ throw new Error(result.failure.code);
24
+ return { ...result.success.next, visibility: input.visibility ?? "private" };
25
+ }
@@ -0,0 +1,21 @@
1
+ /** One `wrangler dev --local` launch: its config, vars and optional fixed port. */
2
+ export type NativeWorkerOptions = Readonly<{
3
+ /** The Worker's wrangler config. */
4
+ configPath: string;
5
+ /** Plain-text vars passed with `--var`. */
6
+ vars: Readonly<Record<string, string>>;
7
+ /** A fixed port; 0 (the default) reserves a free one. */
8
+ port?: number;
9
+ /** Append the Worker's output to this file, which must have mode 0600. */
10
+ logFile?: string;
11
+ }>;
12
+ /** A ready local Worker; `stop` ends it and removes its persisted state. */
13
+ export type NativeWorker = Readonly<{
14
+ port: number;
15
+ stop: () => Promise<void>;
16
+ }>;
17
+ /**
18
+ * Start `wrangler dev --local` on 127.0.0.1 with fresh persisted state and
19
+ * resolve once its root answers, or reject after 60 seconds.
20
+ */
21
+ export declare function startNativeWorker({ configPath, vars, port: requestedPort, logFile, }: NativeWorkerOptions): Promise<NativeWorker>;