@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,554 @@
1
+ import { createCodemodeSandbox } from "@fungi.computer/mycelium/workers";
2
+ import { DurableObject } from "cloudflare:workers";
3
+ import * as S from "effect/Schema";
4
+ import { GraphWorkflow, } from "@fungi.computer/braid/cloudflare";
5
+ import {} from "@fungi.computer/braid";
6
+ import { standaloneTenancy } from "./tenancy.js";
7
+ import { createStandaloneWorkflowGitAccess, createStandaloneWorkflowRootAuthority, } from "./workflow-root-authority.js";
8
+ import { createStandaloneWorkflowHost } from "./workflow-host.js";
9
+ import { createStandaloneWorkflowSchedules, } from "./workflow-schedules.js";
10
+ import { createStandaloneWorkflowEvents, } from "./workflow-events.js";
11
+ import { createStandaloneWorkflowTriggerHost, standaloneTriggerErrors, } from "./workflow-trigger-host.js";
12
+ import { createWorkflowPublishedDefinitionPort, readPublishedWorkflow, } from "@fungi.computer/workflows-wire";
13
+ import { defaultTenant, tenantScope } from "../tenant.js";
14
+ import { AppInstanceError } from "../index.js";
15
+ import { workflowGitPassPrefix } from "../git.js";
16
+ import { createWorkflowGitPasses, createWorkflowGitPassStore, workflowGitPassWriter, workflowRunReport, wireRunInitiator, } from "@fungi.computer/braid/runtime";
17
+ import { canonicalJson } from "@fungi.computer/braid";
18
+ import { runKey, reviewKey, workflowGitPassRequestSchema, } from "./workflow-binding.js";
19
+ import { agentDirectoryForScope } from "./agent-directory.js";
20
+ /** SQLite and the native Workflow binding belong to this standalone host; its root supplies `hostFor`. */
21
+ export class StandaloneWorkflowRunsBase extends DurableObject {
22
+ composed;
23
+ /** Schedule time and its native alarm instant; production is the wall clock. */
24
+ scheduleClock = {
25
+ now: () => Date.now(),
26
+ alarmAt: (due) => due,
27
+ };
28
+ owner;
29
+ constructor(ctx, env) {
30
+ super(ctx, env);
31
+ // This object is named by its Workflows library installation; the alarm
32
+ // and App events have no library frame, so the first admitted trigger
33
+ // control records that name.
34
+ ctx.storage.sql
35
+ .exec(`CREATE TABLE IF NOT EXISTS standalone_workflow_library (
36
+ singleton INTEGER PRIMARY KEY CHECK (singleton = 1), installation_id TEXT NOT NULL
37
+ ) STRICT`);
38
+ this.composed = ctx.blockConcurrencyWhile(async () => {
39
+ const shared = await standaloneTenancy(env);
40
+ const host = this.hostFor(tenantScope(defaultTenant, env.OYSTER_ORIGIN));
41
+ const agentToolPolicy = async (agentId) => (await host.appToolPolicy?.({ kind: "agent", agentId })) ?? null;
42
+ // A key's App access is its own grants (Ear Tag), read through
43
+ // `host.keys.live`, never through `appToolPolicy`.
44
+ const keyLive = async (keyId) => (await host.keys?.live(keyId)) ?? null;
45
+ // The one owner reaches every Agent; the report needs one that exists.
46
+ const admitDelivery = (_memberId, delivery) => agentDirectoryForScope(env, tenantScope(defaultTenant, env.OYSTER_ORIGIN)).has(delivery.agentId);
47
+ const owner = createStandaloneWorkflowHost(ctx.storage, host, {
48
+ authority(libraryInstallationId) {
49
+ return createStandaloneWorkflowRootAuthority({
50
+ shared,
51
+ host,
52
+ libraryInstallationId,
53
+ catalogHost: owner,
54
+ agentToolPolicy,
55
+ keyLive,
56
+ });
57
+ },
58
+ admitDelivery,
59
+ });
60
+ const triggersFor = (libraryId) => {
61
+ const authority = createStandaloneWorkflowRootAuthority({
62
+ shared,
63
+ host,
64
+ libraryInstallationId: libraryId,
65
+ catalogHost: owner,
66
+ agentToolPolicy,
67
+ keyLive,
68
+ });
69
+ const admitTrigger = async (memberId, installationId, computerId) => {
70
+ await authority.member(memberId);
71
+ if (installationId !== libraryId ||
72
+ !(await authority.memberComputer(memberId, computerId)))
73
+ throw standaloneTriggerErrors.denied();
74
+ return owner.triggerAuthority(installationId);
75
+ };
76
+ const triggers = createStandaloneWorkflowTriggerHost({
77
+ authority,
78
+ admitDelivery,
79
+ runs: {
80
+ sql: ctx.storage.sql,
81
+ transactionSync: (body) => ctx.storage.transactionSync(body),
82
+ teamId: () => libraryId,
83
+ },
84
+ triggers: {
85
+ published: createWorkflowPublishedDefinitionPort((git) => ({
86
+ published: (workflowId, at) => readPublishedWorkflow(git, workflowId, at),
87
+ })),
88
+ admit: async (memberId, installationId, computerId) => (await admitTrigger(memberId, installationId, computerId)).pin,
89
+ async source(memberId, installationId, computerId) {
90
+ const { installation } = await admitTrigger(memberId, installationId, computerId);
91
+ return (await host.git.git(installation)).binding({
92
+ kind: "member",
93
+ memberId,
94
+ });
95
+ },
96
+ // Standalone has no Connector-attachment store (Hub's own
97
+ // mechanism for Agent arming); an Agent's owner-approved tool
98
+ // policy governs dispatch, not arming here. See W-1/B-4(b).
99
+ readAgentAccess: () => Promise.resolve(null),
100
+ // The member's current Workflows Git policy; mints no pass itself.
101
+ admitGitAccess: async (memberId, repository, scope) => {
102
+ if (!(await gitAccess(libraryId)(memberId, repository, scope)))
103
+ throw standaloneTriggerErrors.denied();
104
+ },
105
+ launch: (run) => this.launch(libraryId, run),
106
+ },
107
+ recordCaller: (runId, memberId, computerId) => {
108
+ owner.recordTriggerCaller(runId, memberId, computerId);
109
+ },
110
+ });
111
+ const controls = {
112
+ sql: ctx.storage.sql,
113
+ triggers: triggers.triggers,
114
+ teamId: () => libraryId,
115
+ commit: (body) => Promise.resolve(ctx.storage.transactionSync(body)),
116
+ async authorize(memberId, installationId) {
117
+ await authority.member(memberId);
118
+ if (installationId !== libraryId)
119
+ throw standaloneTriggerErrors.denied();
120
+ },
121
+ admitInvocation: async (facts, operation) => {
122
+ const admitted = await owner.admitInvocation(facts, operation);
123
+ if (admitted.libraryId !== libraryId)
124
+ throw new AppInstanceError("revoked");
125
+ return admitted;
126
+ },
127
+ };
128
+ return {
129
+ schedules: createStandaloneWorkflowSchedules({
130
+ ...controls,
131
+ now: () => this.scheduleClock.now(),
132
+ wake: (due) => due === null
133
+ ? ctx.storage.deleteAlarm()
134
+ : ctx.storage.setAlarm(this.scheduleClock.alarmAt(due)),
135
+ reportFailure: ({ source, cause }) => {
136
+ console.error("Standalone Workflow schedule failed", source, cause);
137
+ },
138
+ }),
139
+ events: createStandaloneWorkflowEvents(controls),
140
+ };
141
+ };
142
+ let library = null;
143
+ const libraryTriggers = (libraryId) => {
144
+ this.nameLibrary(libraryId);
145
+ return (library ??= triggersFor(libraryId));
146
+ };
147
+ const computerId = host.computerId;
148
+ // Passes live with the run owner that records the Git steps they serve.
149
+ const passStore = createWorkflowGitPassStore({
150
+ sql: ctx.storage.sql,
151
+ transactionSync: (body) => ctx.storage.transactionSync(body),
152
+ ownerId: () => this.libraryId(),
153
+ computerId: () => computerId,
154
+ mintPassId: () => Array.from(crypto.getRandomValues(new Uint8Array(32)), (byte) => byte.toString(16).padStart(2, "0")).join(""),
155
+ remote: (_passId, repository) => {
156
+ const libraryId = this.libraryId();
157
+ if (repository.kind !== "installation" || libraryId === null)
158
+ throw new AppInstanceError("revoked");
159
+ // The tenant's ordinary smart-HTTP door; the pass never enters the
160
+ // URL and travels only as the bearer header.
161
+ return new URL(`/git/${repository.installationId}/${repository.repository}.git`, env.OYSTER_ORIGIN).href;
162
+ },
163
+ denied: () => new AppInstanceError("revoked"),
164
+ });
165
+ const gitAccess = (libraryId) => createStandaloneWorkflowGitAccess({
166
+ shared,
167
+ host,
168
+ libraryInstallationId: libraryId,
169
+ catalogHost: owner,
170
+ });
171
+ const gitPasses = (libraryId) => {
172
+ const access = gitAccess(libraryId);
173
+ return createWorkflowGitPasses({
174
+ denied: () => new AppInstanceError("revoked"),
175
+ admitNode: (context, target) => {
176
+ const node = owner.gitNode(context.idempotencyKey);
177
+ return node?.teamId === libraryId &&
178
+ node.installationId === target.installationId &&
179
+ node.method === target.method
180
+ ? node
181
+ : null;
182
+ },
183
+ async admitMemberGitAccess(memberId, repository, scope) {
184
+ if (!(await access(memberId, repository, scope)))
185
+ throw new AppInstanceError("revoked");
186
+ },
187
+ open: (_computer, grant) => {
188
+ const pass = passStore.openWorkflowPass(grant);
189
+ return Promise.resolve({
190
+ ...pass,
191
+ bearer: `${workflowGitPassPrefix}${libraryId}.${pass.passId}`,
192
+ });
193
+ },
194
+ revoke: (_computer, input) => {
195
+ passStore.revokeWorkflowRunGitPasses(input);
196
+ return Promise.resolve();
197
+ },
198
+ // Revocation runs in this object when the run ends; nothing is queued.
199
+ pending: () => [],
200
+ acknowledge: () => undefined,
201
+ });
202
+ };
203
+ return { owner, libraryTriggers, passStore, gitAccess, gitPasses };
204
+ });
205
+ this.owner = this.composed.then(({ owner }) => owner);
206
+ }
207
+ /** Record the library this object serves; another name is a revocation. */
208
+ nameLibrary(libraryId) {
209
+ const named = this.libraryId();
210
+ if (named !== null && named !== libraryId)
211
+ throw new AppInstanceError("revoked");
212
+ if (named === null)
213
+ this.ctx.storage.sql.exec("INSERT INTO standalone_workflow_library(singleton,installation_id) VALUES(1,?)", libraryId);
214
+ }
215
+ /**
216
+ * Mint (or renew) the Git pass for the Git step a node frame is executing.
217
+ * Null when this library dispatched no such step; a step that does not match
218
+ * the frame's attested Workflow, member and Computer is a revocation.
219
+ */
220
+ async openWorkflowGitPass(raw) {
221
+ const request = workflowGitPassRequestSchema.parse(raw);
222
+ const { owner, gitPasses } = await this.composed;
223
+ const libraryId = this.libraryId();
224
+ const node = owner.gitNode(request.idempotencyKey);
225
+ if (libraryId === null || node === null)
226
+ return null;
227
+ if (node.teamId !== libraryId ||
228
+ node.installationId !== request.installationId ||
229
+ node.memberId !== request.memberId ||
230
+ node.computerId !== request.computerId ||
231
+ node.context.workflowId !== request.workflow.workflowId ||
232
+ node.context.runId !== request.workflow.runId ||
233
+ node.context.nodeId !== request.workflow.nodeId ||
234
+ canonicalJson(node.context.armedBy) !==
235
+ canonicalJson(request.workflow.armedBy))
236
+ throw new AppInstanceError("revoked");
237
+ return gitPasses(libraryId).openWorkflowPass(node.context, {
238
+ installationId: node.installationId,
239
+ method: node.method,
240
+ });
241
+ }
242
+ /** The tenant Git door's per-request admission of a presented pass. */
243
+ async admitWorkflowGitPass(passId, repository, scope) {
244
+ const { owner, passStore, gitAccess } = await this.composed;
245
+ const libraryId = this.libraryId();
246
+ const grant = passStore.admit({ passId, repository, scope });
247
+ if (libraryId === null ||
248
+ grant === null ||
249
+ owner.store.outcome(grant.runId) !== "active" ||
250
+ !(await gitAccess(libraryId)(grant.memberId, repository, scope)))
251
+ return null;
252
+ // The live root admission above holds only for the canonical owner.
253
+ return { ...workflowGitPassWriter(grant), role: "owner" };
254
+ }
255
+ /** A finished run's passes and recorded Git steps end with it. */
256
+ async endRunGit(runId) {
257
+ const { owner, passStore } = await this.composed;
258
+ const run = owner.store.read(runId);
259
+ if (run !== null)
260
+ passStore.revokeWorkflowRunGitPasses({ teamId: run.teamId, runId });
261
+ owner.forgetGitNodes(runId);
262
+ }
263
+ libraryId() {
264
+ const row = this.ctx.storage.sql
265
+ .exec("SELECT installation_id FROM standalone_workflow_library")
266
+ .toArray()[0];
267
+ return row === undefined
268
+ ? null
269
+ : S.decodeUnknownSync(S.Struct({ installation_id: S.String }))(row)
270
+ .installation_id;
271
+ }
272
+ /** Trigger controls open the library named by the admitted frame. */
273
+ async library(facts, operation) {
274
+ const { owner, libraryTriggers } = await this.composed;
275
+ const { libraryId } = await owner.admitInvocation(facts, operation);
276
+ return libraryTriggers(libraryId);
277
+ }
278
+ /** Idempotently ensure a recorded run has its engine instance. */
279
+ async launch(installationId, run) {
280
+ try {
281
+ await this.env.GRAPH_WORKFLOWS.create({
282
+ id: run.runId,
283
+ params: {
284
+ kind: "run",
285
+ teamId: installationId,
286
+ runId: run.runId,
287
+ planDigest: run.planDigest,
288
+ },
289
+ });
290
+ }
291
+ catch (cause) {
292
+ // The durable run survives a lost create reply. Confirm its owned engine
293
+ // instance exists; unrelated create failures remain failures.
294
+ try {
295
+ await (await this.env.GRAPH_WORKFLOWS.get(run.runId)).status();
296
+ }
297
+ catch {
298
+ throw cause;
299
+ }
300
+ }
301
+ }
302
+ async armSchedule(facts, input) {
303
+ return (await this.library(facts, "armSchedule")).schedules.arm(facts, input);
304
+ }
305
+ async disarmSchedule(facts, input) {
306
+ return (await this.library(facts, "disarmSchedule")).schedules.disarm(facts, input);
307
+ }
308
+ async schedules(facts) {
309
+ return (await this.library(facts, "schedules")).schedules.list(facts);
310
+ }
311
+ async armEventTrigger(facts, input) {
312
+ return (await this.library(facts, "armEventTrigger")).events.arm(facts, input);
313
+ }
314
+ async disarmEventTrigger(facts, input) {
315
+ return (await this.library(facts, "disarmEventTrigger")).events.disarm(facts, input);
316
+ }
317
+ async eventTriggers(facts) {
318
+ return (await this.library(facts, "eventTriggers")).events.list(facts);
319
+ }
320
+ /** Up to 50 runs of this library, newest first; this host has no per-member rule. */
321
+ async runs(facts, input) {
322
+ const owner = await this.owner;
323
+ const { libraryId } = await owner.admitInvocation(facts, "runs");
324
+ const filter = input.workflowId === undefined
325
+ ? { installationId: libraryId }
326
+ : { installationId: libraryId, workflowId: input.workflowId };
327
+ return {
328
+ runs: owner.store.list(filter).map((run) => {
329
+ const row = {
330
+ runId: run.runId,
331
+ workflowId: run.publication?.workflowId ?? run.trigger?.workflowId,
332
+ commit: run.publication?.commit ?? run.trigger?.commit,
333
+ source: run.source,
334
+ initiatedBy: wireRunInitiator(run.initiatedBy),
335
+ startedAt: run.startedAt,
336
+ };
337
+ return run.trigger === undefined
338
+ ? row
339
+ : { ...row, triggerId: run.trigger.triggerId };
340
+ }),
341
+ };
342
+ }
343
+ /** Native alarm delivery: fire every due schedule of this library. */
344
+ async alarm() {
345
+ const { libraryTriggers } = await this.composed;
346
+ const libraryId = this.libraryId();
347
+ if (libraryId === null)
348
+ return;
349
+ await libraryTriggers(libraryId).schedules.alarm();
350
+ }
351
+ /**
352
+ * The emitting App's Instance delivers each accepted event here. The
353
+ * emitter's live Release must declare the event and its payload schema
354
+ * must accept it before any armed trigger of this library fires.
355
+ */
356
+ async deliverEvent(fact) {
357
+ const { owner, libraryTriggers } = await this.composed;
358
+ const libraryId = this.libraryId();
359
+ if (libraryId === null)
360
+ return "accepted";
361
+ const admitted = await owner.admitEventFact(fact);
362
+ return libraryTriggers(libraryId).events.deliver(admitted);
363
+ }
364
+ async catalog(facts, input) {
365
+ const owner = await this.owner;
366
+ return owner.catalog(facts, input);
367
+ }
368
+ async start(facts, input) {
369
+ const owner = await this.owner;
370
+ const result = await owner.prepare(facts, input);
371
+ const installationId = facts.selection.owner.id;
372
+ if (!("kind" in result) ||
373
+ (result.kind !== "started" && result.kind !== "replay"))
374
+ return result;
375
+ if (owner.store.outcome(result.runId) !== "active")
376
+ return result;
377
+ this.nameLibrary(installationId);
378
+ await this.launch(installationId, result);
379
+ return result;
380
+ }
381
+ async loadRun(installationId, runId) {
382
+ const owner = await this.owner;
383
+ return owner.loadRun(installationId, runId);
384
+ }
385
+ async beforeNode(event) {
386
+ const owner = await this.owner;
387
+ return owner.beforeNode(event);
388
+ }
389
+ async dispatch(call) {
390
+ const owner = await this.owner;
391
+ return owner.dispatch(call);
392
+ }
393
+ async recordEvent(event) {
394
+ const owner = await this.owner;
395
+ owner.store.recordEvent(event);
396
+ }
397
+ async openReview(review) {
398
+ const owner = await this.owner;
399
+ return owner.store.openReview(review);
400
+ }
401
+ async answerReview(facts, raw) {
402
+ const owner = await this.owner;
403
+ const input = S.decodeUnknownSync(reviewKey)(raw, {
404
+ onExcessProperty: "error",
405
+ });
406
+ const { libraryId, context } = await (await this.owner).admitInvocation(facts, "answerReview");
407
+ const run = owner.store.read(input.runId);
408
+ if (run?.teamId !== libraryId || context.actor.kind !== "member")
409
+ return { result: "denied" };
410
+ const result = owner.store.answerReview(input, input.answer, {
411
+ kind: "member",
412
+ actorId: context.actor.memberId,
413
+ isOwner: context.role === "owner",
414
+ });
415
+ if (result === "denied" || result === "invalid")
416
+ return { result };
417
+ for (const delivery of owner.store.pendingReviewDeliveries(input.runId)) {
418
+ const instance = await this.env.GRAPH_WORKFLOWS.get(delivery.instanceId);
419
+ await owner.admitInvocation(facts, "answerReview");
420
+ if (owner.store.outcome(input.runId) !== "active")
421
+ break;
422
+ await instance.sendEvent({
423
+ type: delivery.eventType,
424
+ payload: delivery.answer,
425
+ });
426
+ owner.store.markReviewDelivered(delivery);
427
+ }
428
+ return { result };
429
+ }
430
+ /** Record the engine's ending, then report it to the run's declared delivery. */
431
+ async terminal(runId, ending) {
432
+ const owner = await this.owner;
433
+ owner.store.markTerminal(runId, "terminal");
434
+ await this.endRunGit(runId);
435
+ await this.report(runId, ending);
436
+ }
437
+ /**
438
+ * One idempotent follow-up into the run's declared delivery Session, however
439
+ * the run started, through the same Session send that the AGENTS binding
440
+ * uses. Delivery never changes the run's recorded outcome.
441
+ */
442
+ async report(runId, ending) {
443
+ const owner = await this.owner;
444
+ const run = owner.store.read(runId);
445
+ const delivery = run?.delivery;
446
+ if (run === null || delivery === undefined)
447
+ return;
448
+ try {
449
+ await this.env.AGENTS.getByName(delivery.agentId).sendFromApp(run.teamId, delivery.sessionId, {
450
+ requestId: `workflow-run-report:${runId}`,
451
+ text: workflowRunReport(run.plan.definition.title, runId, ending),
452
+ delivery: "followUp",
453
+ });
454
+ }
455
+ catch (cause) {
456
+ console.error("Workflow run report was not delivered", runId, cause);
457
+ }
458
+ }
459
+ async status(facts, raw) {
460
+ const owner = await this.owner;
461
+ const { runId } = S.decodeUnknownSync(runKey)(raw, {
462
+ onExcessProperty: "error",
463
+ });
464
+ const { libraryId: installationId } = await (await this.owner).admitInvocation(facts, "graphStatus");
465
+ const run = owner.store.read(runId);
466
+ if (run?.teamId !== installationId)
467
+ return { error: "not_found" };
468
+ const outcome = owner.store.outcome(runId);
469
+ let status;
470
+ try {
471
+ status = await (await this.env.GRAPH_WORKFLOWS.get(runId)).status();
472
+ }
473
+ catch (cause) {
474
+ // The recorded run is visible before its engine instance is: while
475
+ // the run is still active, a failed instance lookup is that honest
476
+ // wait, not a fault. A settled run's lookup failure stays a failure.
477
+ if (outcome !== "active")
478
+ throw cause;
479
+ status = { status: "queued" };
480
+ }
481
+ return {
482
+ runId,
483
+ planDigest: run.plan.planDigest,
484
+ title: run.plan.definition.title,
485
+ outcome,
486
+ events: owner.store.events(runId),
487
+ reviews: owner.store.reviews(runId),
488
+ status: outcome === "cancelled" ? "terminated" : status.status,
489
+ output: outcome === "cancelled" ? null : (status.output ?? null),
490
+ error: status.error ?? null,
491
+ };
492
+ }
493
+ async cancel(facts, raw) {
494
+ const owner = await this.owner;
495
+ const { runId } = S.decodeUnknownSync(runKey)(raw, {
496
+ onExcessProperty: "error",
497
+ });
498
+ const { libraryId: installationId } = await (await this.owner).admitInvocation(facts, "cancel");
499
+ const run = owner.store.read(runId);
500
+ if (run?.teamId !== installationId)
501
+ return { kind: "refused", reason: "denied" };
502
+ if (owner.store.outcome(runId) !== "active")
503
+ return { kind: "terminal" };
504
+ const instance = await this.env.GRAPH_WORKFLOWS.get(runId);
505
+ try {
506
+ await instance.terminate();
507
+ }
508
+ catch (cause) {
509
+ const status = await instance.status();
510
+ if (status.status !== "terminated" &&
511
+ status.status !== "complete" &&
512
+ status.status !== "errored")
513
+ throw cause;
514
+ owner.store.markTerminal(runId, status.status === "terminated" ? "cancelled" : "terminal");
515
+ await this.endRunGit(runId);
516
+ if (status.status === "terminated")
517
+ await this.report(runId, { kind: "cancelled" });
518
+ return {
519
+ kind: status.status === "terminated" ? "cancelled" : "terminal",
520
+ };
521
+ }
522
+ owner.store.markTerminal(runId, "cancelled");
523
+ await this.endRunGit(runId);
524
+ await this.report(runId, { kind: "cancelled" });
525
+ return { kind: "cancelled" };
526
+ }
527
+ }
528
+ /** The SAME extracted interpreter as Hub, with this host's owned ports. */
529
+ export class StandaloneGraphWorkflow extends GraphWorkflow {
530
+ codeExecution() {
531
+ const localCpuUnenforced = "WORKFLOW_CODE_LOCAL_DEV" in this.env &&
532
+ this.env.WORKFLOW_CODE_LOCAL_DEV === "true";
533
+ return {
534
+ sandbox: createCodemodeSandbox(this.env.LOADER, "workflow-code", {
535
+ localDevUnenforcedLimits: localCpuUnenforced ? ["cpu"] : [],
536
+ }),
537
+ // Workflow data transforms have a host-owned 30-second execution budget.
538
+ timeoutMs: 30_000,
539
+ };
540
+ }
541
+ dispatcher(params) {
542
+ const runs = this.env.WORKFLOW_RUNS.getByName(params.teamId);
543
+ return {
544
+ loadRun: (installationId, runId) => runs.loadRun(installationId, runId),
545
+ beforeNode: (event) => runs.beforeNode(event),
546
+ dispatch: (call) => runs.dispatch(call),
547
+ recordEvent: (event) => runs.recordEvent(event),
548
+ openReview: (review) => runs.openReview(review),
549
+ };
550
+ }
551
+ terminalTriggerRun(params, ending) {
552
+ return this.env.WORKFLOW_RUNS.getByName(params.teamId).terminal(params.runId, ending);
553
+ }
554
+ }
@@ -0,0 +1,64 @@
1
+ import * as Schema from "effect/Schema";
2
+ /** The storage codecs and operation checks used by both sides of the native frame. */
3
+ export declare const storageFrameMethods: {
4
+ APP_STORAGE: {
5
+ read: {
6
+ decodeArgs: (input: unknown, options?: import("effect/SchemaAST").ParseOptions) => readonly [string];
7
+ decodeResult: (input: unknown, options?: import("effect/SchemaAST").ParseOptions) => {
8
+ readonly kind: "accepted";
9
+ readonly value: {
10
+ readonly value: Schema.Json;
11
+ readonly source: "team" | "member" | "none";
12
+ readonly team: {
13
+ readonly value: Schema.Json;
14
+ readonly revision: number;
15
+ } | null;
16
+ readonly member: {
17
+ readonly value: Schema.Json;
18
+ readonly revision: number;
19
+ } | null;
20
+ readonly canWriteTeam: boolean;
21
+ };
22
+ } | {
23
+ readonly kind: "rejected";
24
+ readonly code: "unavailable" | "invalid" | "undeclared_key" | "scope_not_declared" | "owner_required" | "too_large" | "quota_exceeded" | "conflict";
25
+ };
26
+ requiredOperations: () => string[];
27
+ };
28
+ write: {
29
+ decodeArgs: (input: unknown, options?: import("effect/SchemaAST").ParseOptions) => readonly [{
30
+ readonly value: Schema.Json;
31
+ readonly key: string;
32
+ readonly scope: "team" | "member";
33
+ readonly basedOn: number | null;
34
+ }];
35
+ decodeResult: (input: unknown, options?: import("effect/SchemaAST").ParseOptions) => {
36
+ readonly kind: "accepted";
37
+ readonly value: {
38
+ readonly revision: number;
39
+ };
40
+ } | {
41
+ readonly kind: "rejected";
42
+ readonly code: "unavailable" | "invalid" | "undeclared_key" | "scope_not_declared" | "owner_required" | "too_large" | "quota_exceeded" | "conflict";
43
+ };
44
+ requiredOperations: () => string[];
45
+ };
46
+ reset: {
47
+ decodeArgs: (input: unknown, options?: import("effect/SchemaAST").ParseOptions) => readonly [{
48
+ readonly key: string;
49
+ readonly scope: "team" | "member";
50
+ readonly basedOn: number | null;
51
+ }];
52
+ decodeResult: (input: unknown, options?: import("effect/SchemaAST").ParseOptions) => {
53
+ readonly kind: "accepted";
54
+ readonly value: {
55
+ readonly revision: number;
56
+ };
57
+ } | {
58
+ readonly kind: "rejected";
59
+ readonly code: "unavailable" | "invalid" | "undeclared_key" | "scope_not_declared" | "owner_required" | "too_large" | "quota_exceeded" | "conflict";
60
+ };
61
+ requiredOperations: () => string[];
62
+ };
63
+ };
64
+ };
@@ -0,0 +1,28 @@
1
+ import * as Schema from "effect/Schema";
2
+ import { appStorageKeyNameSchema, appStorageWriteSchema, appStorageResetSchema, appStorageReadResultSchema, appStorageMutationResultSchema, } from "@fungi.computer/agar/storage";
3
+ /** The storage codecs and operation checks used by both sides of the native frame. */
4
+ export const storageFrameMethods = {
5
+ APP_STORAGE: {
6
+ read: {
7
+ decodeArgs: Schema.decodeUnknownSync(Schema.Tuple([appStorageKeyNameSchema]), { onExcessProperty: "error" }),
8
+ decodeResult: Schema.decodeUnknownSync(appStorageReadResultSchema, {
9
+ onExcessProperty: "error",
10
+ }),
11
+ requiredOperations: () => ["read"],
12
+ },
13
+ write: {
14
+ decodeArgs: Schema.decodeUnknownSync(Schema.Tuple([appStorageWriteSchema]), { onExcessProperty: "error" }),
15
+ decodeResult: Schema.decodeUnknownSync(appStorageMutationResultSchema, {
16
+ onExcessProperty: "error",
17
+ }),
18
+ requiredOperations: () => ["write"],
19
+ },
20
+ reset: {
21
+ decodeArgs: Schema.decodeUnknownSync(Schema.Tuple([appStorageResetSchema]), { onExcessProperty: "error" }),
22
+ decodeResult: Schema.decodeUnknownSync(appStorageMutationResultSchema, {
23
+ onExcessProperty: "error",
24
+ }),
25
+ requiredOperations: () => ["reset"],
26
+ },
27
+ },
28
+ };