@fungi.computer/oyster 0.1.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 (292) hide show
  1. package/README.md +376 -72
  2. package/dist/agent-avatars.d.ts +103 -0
  3. package/dist/agent-avatars.js +66 -0
  4. package/dist/agents.d.ts +15 -4
  5. package/dist/agents.js +43 -4
  6. package/dist/app-builds.d.ts +81 -8
  7. package/dist/app-builds.js +210 -60
  8. package/dist/app-storage.d.ts +3 -3
  9. package/dist/approval-capability.d.ts +5 -0
  10. package/dist/approval-capability.js +46 -0
  11. package/dist/approval-home.d.ts +51 -0
  12. package/dist/approval-home.js +226 -0
  13. package/dist/apps/catalog.d.ts +335 -0
  14. package/dist/apps/catalog.js +46 -0
  15. package/dist/apps/gate.d.ts +26 -12
  16. package/dist/apps/gate.js +48 -22
  17. package/dist/apps/index.d.ts +3 -1
  18. package/dist/apps/index.js +3 -1
  19. package/dist/apps/installation.d.ts +147 -66
  20. package/dist/apps/installation.js +193 -86
  21. package/dist/apps/permissions.d.ts +247 -0
  22. package/dist/apps/permissions.js +255 -0
  23. package/dist/boot-capability-route.d.ts +15 -0
  24. package/dist/boot-capability-route.js +196 -0
  25. package/dist/boot-capability.d.ts +103 -0
  26. package/dist/boot-capability.js +141 -0
  27. package/dist/boot-construction.d.ts +12 -0
  28. package/dist/boot-construction.js +175 -0
  29. package/dist/boot-gate.d.ts +267 -0
  30. package/dist/boot-gate.js +236 -0
  31. package/dist/boot-host-script.d.ts +2 -0
  32. package/dist/boot-host-script.js +1 -0
  33. package/dist/boot-mount.d.ts +21 -0
  34. package/dist/boot-mount.js +65 -0
  35. package/dist/browser/boot.d.ts +73 -0
  36. package/dist/browser/boot.js +502 -0
  37. package/dist/browser/browser.d.ts +104 -0
  38. package/dist/browser/browser.js +374 -0
  39. package/dist/browser/capability-source.d.ts +76 -0
  40. package/dist/browser/capability-source.js +338 -0
  41. package/dist/browser/cdp-client.d.ts +56 -0
  42. package/dist/browser/cdp-client.js +184 -0
  43. package/dist/browser/errors.d.ts +79 -0
  44. package/dist/browser/errors.js +32 -0
  45. package/dist/browser/index.d.ts +16 -0
  46. package/dist/browser/index.js +14 -0
  47. package/dist/browser/lease-registry.d.ts +61 -0
  48. package/dist/browser/lease-registry.js +101 -0
  49. package/dist/browser/operations.d.ts +225 -0
  50. package/dist/browser/operations.js +137 -0
  51. package/dist/browser/page.d.ts +25 -0
  52. package/dist/browser/page.js +172 -0
  53. package/dist/browser/quick-reads.d.ts +29 -0
  54. package/dist/browser/quick-reads.js +160 -0
  55. package/dist/browser/sessions.d.ts +52 -0
  56. package/dist/browser/sessions.js +118 -0
  57. package/dist/browser/url-policy.d.ts +18 -0
  58. package/dist/browser/url-policy.js +58 -0
  59. package/dist/build/app-imports.d.ts +12 -0
  60. package/dist/build/app-imports.js +105 -0
  61. package/dist/build/build-policy.d.ts +5 -3
  62. package/dist/build/build-policy.js +5 -3
  63. package/dist/build/candidate-builder.d.ts +1 -1
  64. package/dist/build/candidate-cli.mjs +8428 -337
  65. package/dist/build/local-build.d.ts +2 -0
  66. package/dist/build/local-build.js +28 -6
  67. package/dist/build/snapshot-package-store.d.ts +1 -1
  68. package/dist/build/snapshot-package-store.js +7 -2
  69. package/dist/build/workspace-build.d.ts +92 -5
  70. package/dist/build/workspace-build.js +93 -19
  71. package/dist/build-egress.js +4 -4
  72. package/dist/build.d.ts +1 -1
  73. package/dist/build.js +1 -1
  74. package/dist/catalog-frame.js +1 -1
  75. package/dist/cloudflare.d.ts +13 -3
  76. package/dist/cloudflare.js +106 -8
  77. package/dist/frame-binding-wire-script.js +1 -1
  78. package/dist/frame-binding-wire.d.ts +1 -1
  79. package/dist/frame-binding-wire.js +5 -3
  80. package/dist/git-auth.d.ts +0 -2
  81. package/dist/git-auth.js +0 -2
  82. package/dist/git-journal.js +4 -4
  83. package/dist/git.d.ts +0 -1
  84. package/dist/git.js +0 -2
  85. package/dist/host.d.ts +39 -13
  86. package/dist/host.js +268 -142
  87. package/dist/index.d.ts +26 -15
  88. package/dist/index.js +40 -16
  89. package/dist/install-release-cli.d.ts +2 -0
  90. package/dist/install-release-cli.js +46 -0
  91. package/dist/installation-client.d.ts +43 -0
  92. package/dist/installation-client.js +178 -0
  93. package/dist/installation-execution.d.ts +11 -0
  94. package/dist/installation-execution.js +184 -0
  95. package/dist/installation-operations.d.ts +1437 -0
  96. package/dist/installation-operations.js +664 -0
  97. package/dist/installation-store-generation.d.ts +3 -0
  98. package/dist/installation-store-generation.js +16 -0
  99. package/dist/instance-id.d.ts +10 -0
  100. package/dist/instance-id.js +10 -0
  101. package/dist/instance-refusal.d.ts +11 -0
  102. package/dist/instance-refusal.js +21 -0
  103. package/dist/internal/delivery.js +4 -2
  104. package/dist/internal/host-gate.js +7 -1
  105. package/dist/internal/registry-installations.js +4 -4
  106. package/dist/keys.d.ts +5 -1
  107. package/dist/keys.js +10 -2
  108. package/dist/machine-link/authority.d.ts +89 -0
  109. package/dist/machine-link/authority.js +37 -0
  110. package/dist/machine-link/client.d.ts +66 -0
  111. package/dist/machine-link/client.js +94 -0
  112. package/dist/machine-link/delivery.d.ts +18 -0
  113. package/dist/machine-link/delivery.js +55 -0
  114. package/dist/machine-link/enrollment.d.ts +111 -0
  115. package/dist/machine-link/enrollment.js +286 -0
  116. package/dist/machine-link/fences.d.ts +37 -0
  117. package/dist/machine-link/fences.js +166 -0
  118. package/dist/machine-link/home.d.ts +20 -0
  119. package/dist/machine-link/home.js +24 -0
  120. package/dist/machine-link/host.d.ts +43 -0
  121. package/dist/machine-link/host.js +189 -0
  122. package/dist/machine-link/index.d.ts +15 -0
  123. package/dist/machine-link/index.js +15 -0
  124. package/dist/machine-link/link.d.ts +48 -0
  125. package/dist/machine-link/link.js +223 -0
  126. package/dist/machine-link/presence.d.ts +10 -0
  127. package/dist/machine-link/presence.js +25 -0
  128. package/dist/machine-link/workers.d.ts +6 -0
  129. package/dist/machine-link/workers.js +6 -0
  130. package/dist/mcp-owner.d.ts +2 -2
  131. package/dist/mcp-owner.js +41 -33
  132. package/dist/mcp.d.ts +15 -3
  133. package/dist/mcp.js +146 -25
  134. package/dist/pack.d.ts +14 -3
  135. package/dist/pack.js +44 -19
  136. package/dist/page/app-access.d.ts +7 -0
  137. package/dist/page/app-access.js +75 -0
  138. package/dist/page/boot-host.js +57 -0
  139. package/dist/page/capabilities.d.ts +3 -3
  140. package/dist/page/capabilities.js +16 -12
  141. package/dist/page/computer-handles.d.ts +3 -3
  142. package/dist/page/computer-target.d.ts +3 -0
  143. package/dist/page/computer-target.js +9 -0
  144. package/dist/page/mount.d.ts +23 -7
  145. package/dist/page/mount.js +152 -16
  146. package/dist/page-assets.d.ts +22 -0
  147. package/dist/page-assets.js +28 -0
  148. package/dist/page-script.d.ts +3 -0
  149. package/dist/page-script.js +5 -4
  150. package/dist/release.d.ts +13 -6
  151. package/dist/release.js +10 -7
  152. package/dist/root-data.d.ts +16 -6
  153. package/dist/root-data.js +2 -2
  154. package/dist/source/index.d.ts +1 -1
  155. package/dist/source/team-source.d.ts +20 -8
  156. package/dist/source/team-source.js +39 -10
  157. package/dist/source/template-seed.d.ts +2 -2
  158. package/dist/standalone/agent-mail.d.ts +24 -0
  159. package/dist/standalone/agent-mail.js +62 -0
  160. package/dist/standalone/agent-store.d.ts +9 -0
  161. package/dist/standalone/agent-store.js +30 -0
  162. package/dist/standalone/agents.d.ts +78 -12
  163. package/dist/standalone/agents.js +470 -59
  164. package/dist/standalone/app-builds-binding.d.ts +20 -2
  165. package/dist/standalone/app-builds-binding.js +151 -27
  166. package/dist/standalone/app-set.d.ts +5 -2
  167. package/dist/standalone/app-set.js +174 -143
  168. package/dist/standalone/browser.d.ts +11 -0
  169. package/dist/standalone/browser.js +72 -0
  170. package/dist/standalone/builds.d.ts +13 -4
  171. package/dist/standalone/builds.js +28 -44
  172. package/dist/standalone/computer-egress.d.ts +2 -0
  173. package/dist/standalone/computer-egress.js +13 -4
  174. package/dist/standalone/computer-fleet.d.ts +43 -0
  175. package/dist/standalone/computer-fleet.js +200 -0
  176. package/dist/standalone/computer.d.ts +44 -9
  177. package/dist/standalone/computer.js +132 -25
  178. package/dist/standalone/install-apps-cli.js +43 -6
  179. package/dist/standalone/instance.d.ts +2 -2
  180. package/dist/standalone/instance.js +8 -11
  181. package/dist/standalone/machine-link.d.ts +354 -0
  182. package/dist/standalone/machine-link.js +368 -0
  183. package/dist/standalone/media.d.ts +2 -2
  184. package/dist/standalone/media.js +19 -16
  185. package/dist/standalone/preview.d.ts +2 -2
  186. package/dist/standalone/preview.js +39 -12
  187. package/dist/standalone/registry.d.ts +149 -9
  188. package/dist/standalone/registry.js +299 -8
  189. package/dist/standalone/worker.d.ts +19 -5
  190. package/dist/standalone/worker.js +90 -54
  191. package/dist/standalone/workflow-binding.d.ts +28 -32
  192. package/dist/standalone/workflow-binding.js +20 -20
  193. package/dist/standalone/workflow-control.d.ts +4 -0
  194. package/dist/standalone/workflow-control.js +40 -0
  195. package/dist/standalone/workflow-events.d.ts +2 -2
  196. package/dist/standalone/workflow-events.js +9 -10
  197. package/dist/standalone/workflow-git-capture.js +4 -3
  198. package/dist/standalone/workflow-host.d.ts +177 -152
  199. package/dist/standalone/workflow-host.js +137 -30
  200. package/dist/standalone/workflow-node-capture.js +11 -4
  201. package/dist/standalone/workflow-schedules.d.ts +5 -2
  202. package/dist/standalone/workflow-schedules.js +9 -10
  203. package/dist/standalone/workflow-trigger-host.d.ts +88 -6
  204. package/dist/standalone/workflow-trigger-host.js +2 -2
  205. package/dist/standalone/workflow-webhooks.d.ts +22 -0
  206. package/dist/standalone/workflow-webhooks.js +39 -0
  207. package/dist/standalone/workflows.d.ts +203 -50
  208. package/dist/standalone/workflows.js +198 -39
  209. package/dist/storage-frame.d.ts +6 -6
  210. package/dist/testing/capabilities.js +14 -3
  211. package/dist/testing/installation.js +26 -2
  212. package/dist/testing/worker.js +1 -1
  213. package/dist/testing/workers-runtime.d.ts +8 -2
  214. package/dist/testing/workers-runtime.js +20 -2
  215. package/dist/webhook-abuse.d.ts +25 -0
  216. package/dist/webhook-abuse.js +54 -0
  217. package/dist/webhook-custody.d.ts +144 -0
  218. package/dist/webhook-custody.js +222 -0
  219. package/dist/webhook-ingress.d.ts +36 -0
  220. package/dist/webhook-ingress.js +146 -0
  221. package/dist/webhooks.d.ts +111 -0
  222. package/dist/webhooks.js +189 -0
  223. package/dist/workers-git.d.ts +12 -17
  224. package/dist/workers-git.js +60 -55
  225. package/dist/workers.d.ts +224 -13
  226. package/dist/workers.js +351 -51
  227. package/dist/workflow-agent-authority.d.ts +8 -0
  228. package/dist/workflow-agent-authority.js +42 -0
  229. package/dist/workflows.d.ts +66 -0
  230. package/dist/workflows.js +97 -0
  231. package/docs/api/README.md +63 -37
  232. package/docs/api/agents.md +17 -0
  233. package/docs/api/app-storage.md +1 -1
  234. package/docs/api/apps.md +1394 -543
  235. package/docs/api/browser/boot.md +132 -0
  236. package/docs/api/browser.md +1440 -0
  237. package/docs/api/build-contract.md +38 -25
  238. package/docs/api/build.md +263 -84
  239. package/docs/api/cloudflare.md +93 -7
  240. package/docs/api/git.md +3 -13
  241. package/docs/api/host.md +944 -118
  242. package/docs/api/index.md +320 -18
  243. package/docs/api/keys.md +24 -1
  244. package/docs/api/mcp.md +59 -12
  245. package/docs/api/modules.md +33 -40
  246. package/docs/api/pack.md +57 -8
  247. package/docs/api/page/mount.md +92 -4
  248. package/docs/api/workers-git.md +103 -24
  249. package/docs/api/workers.md +1581 -244
  250. package/docs/site/agent-quickstart.mdx +28 -14
  251. package/examples/standalone/worker.ts +3 -1
  252. package/examples/standalone/wrangler.jsonc +2 -0
  253. package/package.json +73 -69
  254. package/dist/installation-consent.d.ts +0 -79
  255. package/dist/installation-consent.js +0 -82
  256. package/dist/shell/app-directory.d.ts +0 -542
  257. package/dist/shell/app-directory.js +0 -199
  258. package/dist/shell/browser-actions.d.ts +0 -7
  259. package/dist/shell/browser-actions.js +0 -56
  260. package/dist/shell/command-host.d.ts +0 -112
  261. package/dist/shell/command-host.js +0 -502
  262. package/dist/shell/commands.d.ts +0 -25
  263. package/dist/shell/commands.js +0 -78
  264. package/dist/shell/index.d.ts +0 -8
  265. package/dist/shell/index.js +0 -8
  266. package/dist/shell/keys.d.ts +0 -41
  267. package/dist/shell/keys.js +0 -134
  268. package/dist/shell/shell-module.d.ts +0 -55
  269. package/dist/shell/shell-module.js +0 -86
  270. package/dist/shell/window-manager.d.ts +0 -124
  271. package/dist/shell/window-manager.js +0 -711
  272. package/dist/shell/workbench-contract.d.ts +0 -68
  273. package/dist/shell/workbench-directory.d.ts +0 -332
  274. package/dist/shell/workbench-directory.js +0 -35
  275. package/dist/shell/workbench-host-types.d.ts +0 -96
  276. package/dist/shell/workbench-host-types.js +0 -1
  277. package/dist/shell/workbench-host.d.ts +0 -34
  278. package/dist/shell/workbench-host.js +0 -347
  279. package/dist/shell/workbench-model.d.ts +0 -192
  280. package/dist/shell/workbench-model.js +0 -53
  281. package/dist/shell/workbench.d.ts +0 -4
  282. package/dist/shell/workbench.js +0 -2
  283. package/docs/api/shell/app-directory.md +0 -599
  284. package/docs/api/shell/command-host.md +0 -224
  285. package/docs/api/shell/commands.md +0 -197
  286. package/docs/api/shell/keys.md +0 -183
  287. package/docs/api/shell/shell-module.md +0 -204
  288. package/docs/api/shell/window-manager.md +0 -267
  289. package/docs/api/shell/workbench-directory.md +0 -119
  290. package/docs/api/shell/workbench.md +0 -205
  291. package/docs/api/shell.md +0 -1915
  292. /package/dist/{shell/workbench-contract.js → page/boot-host.d.ts} +0 -0
@@ -1,6 +1,6 @@
1
1
  import { Hono } from "hono";
2
+ import { attachedBackendId, hostedBackingSchema, } from "@fungi.computer/computer/backing";
2
3
  import { appMethodRefusalStatus, appMethodRefusedReplySchema, } from "@fungi.computer/agar/backend";
3
- import { workflowGitPassResultSchema, } from "@fungi.computer/agar/computer-contracts";
4
4
  import * as Schema from "effect/Schema";
5
5
  import { computerPortsSchema, } from "@fungi.computer/agar/computer";
6
6
  import { verifyRelease } from "../host.js";
@@ -9,16 +9,19 @@ import { createStandalonePreview, previewOrigins, previewSelectorSchema, Preview
9
9
  import { standaloneTenancy } from "./tenancy.js";
10
10
  import { Base64 } from "effect/encoding";
11
11
  import * as z from "zod";
12
- import { ComputerPortConflictError, computerByteReadReplySchema, computerProcessSchema, installationProcessScope, processStartInputSchema, processStartToolInputSchema, computerCapabilities, computerToolResultSchema, readOutputSchema, } from "@fungi.computer/computer/operations";
13
- import { ComputerDO as NativeComputerDO, ComputerFileConflict, ComputerOperationConflictError, computerExecutionRequestSchema, computerValueSchema, computerPortInputSchema, listInputSchema, listOutputSchema, readBytesInputSchema, writeInputSchema, replaceInputSchema, writeOutputSchema, mkdirOutputSchema, computerPortsOutputSchema, computerPortOutputSchema, computerPortRemovalOutputSchema, processListOutputSchema, processKillOutputSchema, computerOperationResultSchema, mintComputerRequestId, processIdInputSchema, } from "@fungi.computer/computer";
14
- import { checkComputerGrant, computerActionSchema, computerGrantSchema, createFleetBacking, FleetGrantRefused, } from "@fungi.computer/computer/fleet";
12
+ import { ComputerPortConflictError, computerByteReadReplySchema, computerProcessSchema, installationProcessScope, processStartInputSchema, processStartToolInputSchema, computerCapabilities, computerToolResultSchema, readOutputSchema, } from "@fungi.computer/computer-contract";
13
+ import { ComputerDO as NativeComputerDO, ComputerOperationConflictError, } from "@fungi.computer/computer";
14
+ import { ComputerFileConflict, computerExecutionRequestSchema, computerValueSchema, computerPortInputSchema, listInputSchema, listOutputSchema, readBytesInputSchema, writeInputSchema, replaceInputSchema, writeOutputSchema, mkdirOutputSchema, computerPortsOutputSchema, computerPortOutputSchema, computerPortRemovalOutputSchema, processListOutputSchema, processKillOutputSchema, computerOperationResultSchema, mintComputerRequestId, processIdInputSchema, } from "@fungi.computer/computer-contract";
15
+ import { workflowGitExecutionEnvironment, workflowGitPassGrantSchema, } from "@fungi.computer/braid/runtime";
16
+ import { checkComputerGrant, computerActionSchema, computerGrantSchema, createFleetBacking, FleetGrantRefused, FleetLifecycle, } from "@fungi.computer/computer/fleet";
15
17
  import { AppInstanceError, selectionOf } from "../index.js";
16
18
  import {} from "../cloudflare.js";
17
19
  import { computerSlots } from "../computer.js";
18
20
  import { oysterPrincipalId, oysterPrincipalIsOwner, } from "../host.js";
19
21
  import { keyAllowsComputerUse } from "../keys.js";
20
- import { oysterAppBuildTimeoutMs } from "../app-builds.js";
21
- const missingFile = z.object({ code: z.literal("ENOENT") });
22
+ import { oysterAppBuildTimeoutMs, } from "../app-builds.js";
23
+ import { workflowGitStepGrant, } from "./workflow-binding.js";
24
+ const missingFile = z.lazy(() => z.object({ code: z.literal("ENOENT") }));
22
25
  const workspaceId = computerGrantSchema.shape.computerId;
23
26
  const command = computerExecutionRequestSchema({ maximumTimeMs: 45_000 }).omit({
24
27
  requestId: true,
@@ -39,10 +42,10 @@ const processStart = processStartInputSchema.omit({
39
42
  const processStartHttpBody = processStartInputSchema.omit({
40
43
  requestScope: true,
41
44
  });
42
- const processStartReply = z.union([
45
+ const processStartReply = z.lazy(() => z.union([
43
46
  computerProcessSchema,
44
47
  z.strictObject({ error: z.literal("conflict") }),
45
- ]);
48
+ ]));
46
49
  const bindingWrite = writeInputSchema.extend({
47
50
  expectedContent: replaceInputSchema.shape.expectedContent.optional(),
48
51
  });
@@ -109,11 +112,14 @@ const previewRefusal = appMethodRefusedReplySchema.parse({
109
112
  code: "forbidden",
110
113
  message: "Port previews belong to the current owner of an enabled installation.",
111
114
  });
115
+ /** The non-secret context a native command carries for its admitted Git step. */
116
+ const workflowGitExecutionContextSchema = z.lazy(() => z.strictObject({ workflowGit: workflowGitPassGrantSchema }));
112
117
  /** Durable workspace and existing backings, with fleet checks at native acceptance. */
113
118
  export class ComputerDO extends NativeComputerDO {
114
119
  previewBindings;
115
120
  #backends;
116
121
  #preview;
122
+ #lifecycle;
117
123
  constructor(state, previewBindings) {
118
124
  // The Computer's HTTP(S) egress goes only through the gateway the
119
125
  // standalone Worker exports beside this object.
@@ -123,6 +129,50 @@ export class ComputerDO extends NativeComputerDO {
123
129
  });
124
130
  this.previewBindings = previewBindings;
125
131
  }
132
+ #fleetLifecycle() {
133
+ if (this.#lifecycle !== undefined)
134
+ return this.#lifecycle;
135
+ this.#lifecycle = new FleetLifecycle({
136
+ storage: this.state.storage,
137
+ alarm: this.state,
138
+ computerId: this.#id(),
139
+ runtimeWatchMs: undefined,
140
+ backend: {
141
+ state: this.state,
142
+ onBoxVmHost: (hostId) => this.onBoxVmHost(hostId),
143
+ hostedContainerHost: (backing) => this.hostedContainerHost(backing),
144
+ readyWorkspace: () => this.readyWorkspace(),
145
+ workspace: () => this.workspace(),
146
+ syncRecycledWorkspace: async (backing) => {
147
+ const workspace = await this.readyWorkspace();
148
+ const id = attachedBackendId(backing);
149
+ await workspace.push(id);
150
+ await workspace.pull(id);
151
+ },
152
+ attachedBackends: () => this.attachedBackends(),
153
+ describeBackends: () => this.describeBackends(),
154
+ requireHostedRoute: () => this.requireHostedRoute(),
155
+ containerRunning: (backing) => this.containerRunning(backing),
156
+ // Standalone boots use the native host directly, not Hub's admission queue.
157
+ vmQueuePosition: () => Promise.resolve(null),
158
+ },
159
+ });
160
+ return this.#lifecycle;
161
+ }
162
+ /** Install the fleet's creation projection before exposing its new identity. */
163
+ initializeFleetBackings(backings) {
164
+ const owner = createFleetBacking(this.state.storage);
165
+ owner.ensure();
166
+ this.#backends = owner.computerCreated(this.#id(), z.array(hostedBackingSchema).parse(backings)).backends;
167
+ }
168
+ /** An admitted fleet control starts exactly this Computer's hosted backend. */
169
+ async startHostedComputer(authority) {
170
+ this.#accept(authority, "start");
171
+ return this.#fleetLifecycle().startHostedComputer(async (_backing, run) => {
172
+ this.#accept(authority, "start");
173
+ await run();
174
+ });
175
+ }
126
176
  #previewGrant(ownerId) {
127
177
  return {
128
178
  computerId: this.#id(),
@@ -173,6 +223,12 @@ export class ComputerDO extends NativeComputerDO {
173
223
  });
174
224
  return this.#preview;
175
225
  }
226
+ /** A read-only preflight over the maintained hosted-route selector. */
227
+ buildBackend() {
228
+ return this.hostedRoute() === undefined
229
+ ? { kind: "no_backend" }
230
+ : { kind: "hosted" };
231
+ }
176
232
  /**
177
233
  * Run one App build job's fixed command, under the build budget. The
178
234
  * registry that minted the job re-admits it for this Computer first.
@@ -186,7 +242,7 @@ export class ComputerDO extends NativeComputerDO {
186
242
  cwd: "/workspace",
187
243
  timeoutMs: input.timeoutMs,
188
244
  requestId: mintComputerRequestId(),
189
- }, () => undefined, "bash", { maximumTimeMs: oysterAppBuildTimeoutMs });
245
+ }, () => undefined, "hosted", { maximumTimeMs: oysterAppBuildTimeoutMs });
190
246
  const output = z
191
247
  .object({ exitCode: z.number().nullable(), stderr: z.string() })
192
248
  .safeParse(result.output);
@@ -231,6 +287,36 @@ export class ComputerDO extends NativeComputerDO {
231
287
  #id() {
232
288
  return workspaceId.parse(this.state.id.name);
233
289
  }
290
+ /**
291
+ * The host side of native execution (Computer ABI 0edb0e4a): the grant's
292
+ * library run owner issues one credential for the invocation and the
293
+ * Computer hands it only to that command's Git environment.
294
+ */
295
+ executionHooks() {
296
+ const issuer = (context) => {
297
+ const grant = workflowGitExecutionContextSchema.parse(context).workflowGit;
298
+ return {
299
+ grant,
300
+ runs: this.previewBindings.WORKFLOW_RUNS.getByName(grant.teamId),
301
+ };
302
+ };
303
+ return {
304
+ async open(invocation, context) {
305
+ const { grant, runs } = issuer(context);
306
+ return {
307
+ env: workflowGitExecutionEnvironment(await runs.issueWorkflowGit(invocation.id, grant)),
308
+ };
309
+ },
310
+ async renew(invocation, context) {
311
+ const { grant, runs } = issuer(context);
312
+ await runs.renewWorkflowGit(invocation.id, grant);
313
+ },
314
+ async close(invocation, context) {
315
+ const { grant, runs } = issuer(context);
316
+ await runs.closeWorkflowGit(invocation.id, grant);
317
+ },
318
+ };
319
+ }
234
320
  attachedBackends() {
235
321
  if (this.#backends !== undefined)
236
322
  return this.#backends;
@@ -268,7 +354,12 @@ export class ComputerDO extends NativeComputerDO {
268
354
  return output.parse(await this.callAfterAcceptance(tool, input, accept));
269
355
  }
270
356
  /** JSON operations keep the Computer's maintained parsers and execution engine. */
271
- async value(authority, action, input) {
357
+ async value(authority, action, input, executionContext) {
358
+ // Only an exact public grant rides along, and only on a native command.
359
+ if (executionContext !== undefined &&
360
+ ((action !== "exec" && action !== "processStart") ||
361
+ !workflowGitExecutionContextSchema.safeParse(executionContext).success))
362
+ throw new AppInstanceError("binding_unavailable");
272
363
  const accept = () => {
273
364
  this.#accept(authority, action);
274
365
  };
@@ -283,7 +374,7 @@ export class ComputerDO extends NativeComputerDO {
283
374
  return this.listProcessesAfterAcceptance(accept);
284
375
  case "processStart":
285
376
  try {
286
- return await this.startProcessAfterAcceptance(input, accept);
377
+ return await this.startProcessAfterAcceptance(input, accept, executionContext);
287
378
  }
288
379
  catch (cause) {
289
380
  if (cause instanceof ComputerOperationConflictError ||
@@ -294,7 +385,7 @@ export class ComputerDO extends NativeComputerDO {
294
385
  case "processKill":
295
386
  return this.killProcessAfterAcceptance(processIdInputSchema.parse(input), accept);
296
387
  case "exec":
297
- return this.executeOperationAfterAcceptance({ ...command.parse(input), requestId: mintComputerRequestId() }, accept, "bash", { maximumTimeMs: 45_000 });
388
+ return this.executeOperationAfterAcceptance({ ...command.parse(input), requestId: mintComputerRequestId() }, accept, "bash", { maximumTimeMs: 45_000 }, undefined, executionContext);
298
389
  case "files.list":
299
390
  return listOutputSchema.parse(await this.callAfterAcceptance("list", input, accept));
300
391
  case "files.readBytes": {
@@ -406,8 +497,8 @@ export class ComputerDO extends NativeComputerDO {
406
497
  * omit it, since a team-key principal then simply never reaches the
407
498
  * Computer, rather than reading `env.KEYS` directly and skipping the check.
408
499
  */
409
- export function standaloneWorkspace(env, keysLive) {
410
- const computerId = workspaceId.parse(env.OYSTER_COMPUTER_ID);
500
+ export function standaloneWorkspace(env, keysLive, selectedComputerId = env.OYSTER_COMPUTER_ID) {
501
+ const computerId = workspaceId.parse(selectedComputerId);
411
502
  const computer = () => env.COMPUTER.getByName(computerId);
412
503
  const authority = () => grant(computerId, "oyster-workspace");
413
504
  return {
@@ -524,11 +615,6 @@ export function standaloneComputerCapabilities(env, workspace, keysLive) {
524
615
  /** Closed COMPUTER binding codecs, joined into the standalone Instance. */
525
616
  export const computerFrameMethods = {
526
617
  COMPUTER: {
527
- workflowGitPass: {
528
- decodeArgs: (args) => z.tuple([empty]).parse(args),
529
- decodeResult: (value) => workflowGitPassResultSchema.parse(value),
530
- requiredOperations: () => ["workflowGitPass"],
531
- },
532
618
  list: {
533
619
  decodeArgs: (args) => z.tuple([listInputSchema]).parse(args),
534
620
  decodeResult: (value) => listOutputSchema.parse(value),
@@ -592,13 +678,13 @@ export const computerFrameMethods = {
592
678
  /** Owner-only preview tickets carry the verified frame's Installation selection. */
593
679
  async function computerPreviewInvocation(env, frame, args) {
594
680
  const subject = frame.facts.subject;
595
- const computerId = workspaceId.parse(env.OYSTER_COMPUTER_ID);
596
681
  const owner = frame.facts.selection.owner;
597
682
  if (subject.kind !== "call" ||
598
683
  subject.context.actor.kind !== "member" ||
599
684
  subject.context.role !== "owner" ||
600
685
  owner.kind !== "installation")
601
686
  throw new AppInstanceError("revoked");
687
+ const computerId = workspaceId.parse(subject.context.computerId);
602
688
  // The admitted owner's member ID; the Computer owner proves it is still
603
689
  // the live canonical owner before issuing any ticket.
604
690
  const ownerId = subject.context.actor.memberId;
@@ -623,12 +709,27 @@ async function computerPreviewInvocation(env, frame, args) {
623
709
  throw new AppInstanceError("revoked");
624
710
  return preview;
625
711
  }
626
- /** Dispatch one admitted COMPUTER operation under its captured frame. */
627
- export function computerInvocation(env, frame, method, args) {
712
+ /** Re-admit the Computer captured by this invocation, never the current UI selection. */
713
+ async function frameComputerId(env, scope, frame) {
714
+ frame.signal.throwIfAborted();
715
+ const subject = frame.facts.subject;
716
+ if (subject.kind !== "call" || subject.context.computerId === null)
717
+ throw new AppInstanceError("revoked");
718
+ const computerId = workspaceId.parse(subject.context.computerId);
719
+ if (!(await env.REGISTRY.getByName(scope.durableName("registry")).ownsComputer(computerId)))
720
+ throw new AppInstanceError("revoked");
628
721
  frame.signal.throwIfAborted();
722
+ return computerId;
723
+ }
724
+ /** Media and Computer bindings capture the same inventory-admitted workspace. */
725
+ export async function standaloneFrameWorkspace(env, scope, frame) {
726
+ return standaloneWorkspace(env, undefined, await frameComputerId(env, scope, frame));
727
+ }
728
+ /** Dispatch one admitted COMPUTER operation under its captured frame. */
729
+ export async function computerInvocation(env, scope, frame, method, args) {
730
+ const computerId = await frameComputerId(env, scope, frame);
629
731
  const subject = frame.facts.subject;
630
- const computerId = workspaceId.parse(env.OYSTER_COMPUTER_ID);
631
- if (subject.kind !== "call" || subject.context.computerId !== computerId)
732
+ if (subject.kind !== "call")
632
733
  throw new AppInstanceError("revoked");
633
734
  const actor = subject.context.actor;
634
735
  if (method === "preview")
@@ -703,6 +804,10 @@ export function computerInvocation(env, frame, method, args) {
703
804
  default:
704
805
  throw new AppInstanceError("revoked");
705
806
  }
807
+ // A Workflow Git step's native commands carry only its public grant.
808
+ const grant = action === "exec" || action === "processStart"
809
+ ? await workflowGitStepGrant(env, env.REGISTRY.getByName(scope.durableName("registry")), frame)
810
+ : null;
706
811
  const native = env.COMPUTER.getByName(computerId);
707
812
  frame.signal.throwIfAborted();
708
813
  return native.value({
@@ -710,5 +815,7 @@ export function computerInvocation(env, frame, method, args) {
710
815
  actions: new Set([action]),
711
816
  expiresAt: Math.min(frame.facts.deadlineAt, Date.now() + 60_000),
712
817
  provenance: { actor: `${actor.kind}:${actorId}` },
713
- }, action, input);
818
+ }, action, input, grant === null
819
+ ? undefined
820
+ : computerValueSchema.parse(JSON.parse(JSON.stringify({ workflowGit: grant }))));
714
821
  }
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  // `oyster install-apps`: seed, build on the Oyster's own Computer, and install
3
- // the starter Apps (Tourtois, Bonsai, Shed, Inkcap, Trellis, Prints and
4
- // Grove). Each App comes from its own public repository by default, or from a
5
- // Botanical checkout with `--from <directory>`.
3
+ // the seven starter Apps (Tourtois, Bonsai, Shed, Inkcap, Trellis, Prints,
4
+ // and Grove). Each comes from its own public repository by default,
5
+ // or from a Botanical checkout with `--from <directory>`.
6
6
  //
7
7
  // OYSTER_URL=https://… OYSTER_OWNER_KEY=… oyster install-apps
8
8
  // [--repositories <base URL>] [--ref <branch or tag>]
@@ -13,10 +13,39 @@ import { tmpdir } from "node:os";
13
13
  import { join, resolve } from "node:path";
14
14
  import process from "node:process";
15
15
  import { parseArgs } from "node:util";
16
+ import { installReleaseCommand, installReleaseUsage, } from "../install-release-cli.js";
16
17
  import { defaultStarterBuildConcurrency, installStandaloneAppSet, } from "./app-set.js";
17
18
  /** The starter Apps' public source repositories. */
18
19
  const defaultRepositories = "https://github.com/fungi-computer";
19
20
  const usage = "Usage: oyster install-apps [--repositories <base URL>] [--ref <ref>] [--from <checkout>] [--concurrency 1-3] [--timings <file>]";
21
+ const urlSource = "OYSTER_URL is the address of your deployed Oyster Worker, such as https://oyster.<your-subdomain>.workers.dev (http://127.0.0.1:8787 for a local one). You set it when you deploy.";
22
+ const keySource = "OYSTER_OWNER_KEY is the owner key you chose for that Oyster: export OYSTER_OWNER_KEY=$(openssl rand -hex 24), then store it with `wrangler secret put OYSTER_OWNER_KEY`.";
23
+ const helpText = `${usage}
24
+
25
+ Builds the seven starter Apps on your Oyster's own Computer and installs them.
26
+ Starter builds need a deployed Oyster host; this command does not create one.
27
+ Deploy first (see the Oyster README), then set:
28
+
29
+ ${urlSource}
30
+ ${keySource}
31
+
32
+ Options:
33
+ --repositories <base URL> fetch each App from <base URL> instead of the public repositories
34
+ --ref <ref> branch or tag to build (default main)
35
+ --from <checkout> install from a Botanical checkout directory
36
+ --concurrency 1-3 Apps to build at once
37
+ --timings <file> write the build timings as JSON
38
+ -h, --help show this help
39
+ `;
40
+ function missingVariableMessage(url, key) {
41
+ const lines = [];
42
+ if (!url)
43
+ lines.push(`OYSTER_URL is not set. ${urlSource}`);
44
+ if (!key)
45
+ lines.push(`OYSTER_OWNER_KEY is not set. ${keySource}`);
46
+ lines.push("Run oyster install-apps --help for details.");
47
+ return lines.join("\n");
48
+ }
20
49
  function seconds(milliseconds) {
21
50
  return `${(milliseconds / 1000).toFixed(1)}s`;
22
51
  }
@@ -56,13 +85,18 @@ async function installApps(args) {
56
85
  from: { type: "string" },
57
86
  concurrency: { type: "string" },
58
87
  timings: { type: "string" },
88
+ help: { type: "boolean", short: "h" },
59
89
  },
60
90
  strict: true,
61
91
  });
92
+ if (values.help === true) {
93
+ process.stdout.write(helpText);
94
+ return;
95
+ }
62
96
  const url = process.env.OYSTER_URL;
63
97
  const key = process.env.OYSTER_OWNER_KEY;
64
98
  if (!url || !key)
65
- throw new Error("Set OYSTER_URL and OYSTER_OWNER_KEY");
99
+ throw new Error(missingVariableMessage(url, key));
66
100
  if (values.from !== undefined && values.repositories !== undefined)
67
101
  throw new Error("Choose either --from or --repositories");
68
102
  const concurrency = values.concurrency === undefined
@@ -115,12 +149,15 @@ async function installApps(args) {
115
149
  }
116
150
  }
117
151
  const [command, ...rest] = process.argv.slice(2);
118
- if (command !== "install-apps") {
152
+ if (command === "--help" || command === "-h") {
153
+ process.stdout.write(usage + "\n" + installReleaseUsage + "\n");
154
+ }
155
+ else if (command !== "install-apps" && command !== "install") {
119
156
  process.stderr.write(`${usage}\n`);
120
157
  process.exitCode = 2;
121
158
  }
122
159
  else
123
- await installApps(rest).catch((cause) => {
160
+ await (command === "install" ? installReleaseCommand(rest) : installApps(rest)).catch((cause) => {
124
161
  process.stderr.write(`${cause instanceof Error ? cause.message : String(cause)}\n`);
125
162
  process.exitCode = 1;
126
163
  });
@@ -4,8 +4,8 @@ import { type StandaloneAgentsEnv } from "./agents.js";
4
4
  import { type StandaloneComputerEnv } from "./computer.js";
5
5
  import { type OysterMediaEnv } from "./media.js";
6
6
  import { type AppSourceBindingEnv } from "./app-source.js";
7
- import { type WorkflowBindingEnv, type WorkflowEventEnv, type WorkflowGitPassEnv } from "./workflow-binding.js";
8
- export type StandaloneInstanceEnv = OysterGitWorkerEnv & WorkflowBindingEnv & WorkflowEventEnv & WorkflowGitPassEnv & StandaloneComputerEnv & StandaloneAgentsEnv & OysterMediaEnv & AppSourceBindingEnv;
7
+ import { type WorkflowBindingEnv, type WorkflowEventEnv } from "./workflow-binding.js";
8
+ export type StandaloneInstanceEnv = OysterGitWorkerEnv & WorkflowBindingEnv & WorkflowEventEnv & StandaloneComputerEnv & StandaloneAgentsEnv & OysterMediaEnv & AppSourceBindingEnv;
9
9
  /** Git and Events plus the declared Computer, Media, Workflow, Agent and App
10
10
  * source bindings. */
11
11
  export declare const standaloneBindings: OysterBindingTable<StandaloneInstanceEnv>;
@@ -2,10 +2,10 @@ import { AppInstanceError } from "../index.js";
2
2
  import { oysterBindingEntrypoint, } from "../workers.js";
3
3
  import { OysterGitInstance, OysterWebhooks, eventsBindingFor, gitBindings, } from "../workers-git.js";
4
4
  import { agentFrameMethods, dispatchAgentBinding, } from "./agents.js";
5
- import { computerFrameMethods, computerInvocation, standaloneWorkspace, } from "./computer.js";
5
+ import { computerFrameMethods, computerInvocation, standaloneFrameWorkspace, } from "./computer.js";
6
6
  import { dispatchMediaBinding, mediaFrameMethods, } from "./media.js";
7
7
  import { appSourceFrameMethods, dispatchAppSourceBinding, } from "./app-source.js";
8
- import { workflowFrameMethods, dispatchWorkflowBinding, deliverWorkflowEvent, openWorkflowGitPass, workflowEventRecipients, } from "./workflow-binding.js";
8
+ import { workflowFrameMethods, dispatchWorkflowBinding, deliverWorkflowEvent, workflowEventRecipients, } from "./workflow-binding.js";
9
9
  import { appsFrameMethods, dispatchAppsBuildsBinding, } from "./app-builds-binding.js";
10
10
  /** Git and Events plus the declared Computer, Media, Workflow, Agent and App
11
11
  * source bindings. */
@@ -21,19 +21,16 @@ export const standaloneBindings = {
21
21
  },
22
22
  COMPUTER: {
23
23
  methods: computerFrameMethods.COMPUTER,
24
- async dispatch(context, frame, _props, method, args) {
25
- if (method !== "workflowGitPass")
26
- return computerInvocation(context.env, frame, method, args);
27
- // A published Workflow Git step's pass, minted by the library that runs it.
28
- const host = await context.backgroundHost();
29
- if (host === null)
30
- throw new AppInstanceError("revoked");
31
- return openWorkflowGitPass(context.env, host.installations, frame, args);
24
+ dispatch(context, frame, _props, method, args) {
25
+ return computerInvocation(context.env, context.scope(), frame, method, args);
32
26
  },
33
27
  },
34
28
  MEDIA: {
35
29
  methods: mediaFrameMethods.MEDIA,
36
- dispatch: (context, frame, props, method, args) => dispatchMediaBinding(context.env, frame, props, method, args, standaloneWorkspace(context.env)),
30
+ async dispatch(context, frame, props, method, args) {
31
+ const workspace = await standaloneFrameWorkspace(context.env, context.scope(), frame);
32
+ return dispatchMediaBinding(context.env, frame, props, method, args, workspace);
33
+ },
37
34
  },
38
35
  WORKFLOWS: {
39
36
  methods: workflowFrameMethods.WORKFLOWS,