@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,98 @@
1
+ import { appBackendBindingOperation, } from "@fungi.computer/puffball/app";
2
+ import { sha256 } from "../release.js";
3
+ import canonicalJson from "fast-json-stable-stringify";
4
+ import { declaredOperationsSchema, } from "./installation.js";
5
+ /** Qualify backend operations so a same-named browser method grants no backend authority. */
6
+ export function bindingOperationKey(binding, operation) {
7
+ return `binding:${binding}:${operation}`;
8
+ }
9
+ /** Approval identity for one UI-side target method; upgrades intersect these exact pairs. */
10
+ export function appCallOperation(appId, method) {
11
+ return `route:apps.call:${appId}:${method}`;
12
+ }
13
+ /** Sole derivation owner. Inputs are the verified Release declaration and existing host adapters. */
14
+ export async function deriveDeclaredOperations(release, declaration, descriptors) {
15
+ if (descriptors.some((descriptor) => descriptor.id === "app.methods"))
16
+ throw new Error("app_methods_descriptor_reserved");
17
+ const capabilities = new Map(declaration.capabilities.map((capability) => [
18
+ capability,
19
+ new Set(),
20
+ ]));
21
+ for (const call of declaration.calls ?? [])
22
+ for (const method of call.methods)
23
+ capabilities.get("apps.call")?.add(appCallOperation(call.appId, method));
24
+ for (const [name, method] of Object.entries(declaration.api?.methods ?? {}))
25
+ for (const capability of method.requires)
26
+ capabilities.get(capability)?.add(`route:app.methods:${name}`);
27
+ const routes = descriptors
28
+ .filter((descriptor) => capabilities.has(descriptor.permission))
29
+ .map((descriptor) => {
30
+ const methods = Object.entries(descriptor.methods).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0);
31
+ for (const [name] of methods)
32
+ capabilities
33
+ .get(descriptor.permission)
34
+ ?.add(`route:${descriptor.id}:${name}`);
35
+ return { id: descriptor.id, permission: descriptor.permission, methods };
36
+ })
37
+ .sort((a, b) => a.id < b.id
38
+ ? -1
39
+ : a.id > b.id
40
+ ? 1
41
+ : a.permission < b.permission
42
+ ? -1
43
+ : a.permission > b.permission
44
+ ? 1
45
+ : 0);
46
+ const bindings = (declaration.backend?.bindings ?? [])
47
+ .flatMap((binding) => binding.operations.flatMap((name) => {
48
+ const operation = appBackendBindingOperation(binding.name, name);
49
+ if (!operation?.requires.every((capability) => capabilities.has(capability)))
50
+ return [];
51
+ for (const capability of operation.requires)
52
+ capabilities
53
+ .get(capability)
54
+ ?.add(bindingOperationKey(binding.name, name));
55
+ return [
56
+ {
57
+ binding: binding.name,
58
+ operation: name,
59
+ effect: operation.effect,
60
+ requires: [...operation.requires].sort(),
61
+ },
62
+ ];
63
+ }))
64
+ .sort((a, b) => bindingOperationKey(a.binding, a.operation) <
65
+ bindingOperationKey(b.binding, b.operation)
66
+ ? -1
67
+ : bindingOperationKey(a.binding, a.operation) >
68
+ bindingOperationKey(b.binding, b.operation)
69
+ ? 1
70
+ : 0);
71
+ const rows = [...capabilities]
72
+ .sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))
73
+ .map(([capability, operations]) => ({
74
+ capability,
75
+ operations: [...operations].sort(),
76
+ }));
77
+ const artifactDigest = release.artifact.artifactDigest;
78
+ const anonymousMethods = await Promise.all(Object.entries(declaration.api?.methods ?? {})
79
+ .filter(([, method]) => method.anonymous === true)
80
+ .sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))
81
+ .map(async ([name, method]) => ({
82
+ name,
83
+ digest: await sha256(new TextEncoder().encode(canonicalJson(method))),
84
+ })));
85
+ const digest = await sha256(new TextEncoder().encode(JSON.stringify({
86
+ artifactDigest,
87
+ capabilities: rows,
88
+ routes,
89
+ bindings,
90
+ anonymousMethods,
91
+ })));
92
+ return declaredOperationsSchema.parse({
93
+ artifactDigest,
94
+ digest,
95
+ capabilities: rows,
96
+ anonymousMethods,
97
+ });
98
+ }
@@ -0,0 +1,33 @@
1
+ import * as Schema from "effect/Schema";
2
+ import type { AppApi, AppDeclaration } from "@fungi.computer/puffball/app";
3
+ import type { Installation, DeclaredOperations } from "./installation.js";
4
+ /**
5
+ * One App-tool access level, with its own freshness pin. An Agent's comes
6
+ * from the owner's stored direct approval; a key's is computed fresh from
7
+ * its own Ear Tag grants (`keyAppAccess`), never stored as this shape --
8
+ * only read through it, for one call or one installation at a time.
9
+ */
10
+ export declare const appToolPolicySchema: Schema.Struct<{
11
+ readonly level: Schema.Literals<readonly ["read", "write"]>;
12
+ readonly revision: Schema.NonEmptyString;
13
+ readonly grantedBy: Schema.NonEmptyString;
14
+ }>;
15
+ /** The authenticated attaching member is recorded when this policy is approved. */
16
+ export type AppToolPolicy = typeof appToolPolicySchema.Type;
17
+ /** One installation's binding under the captured host approval. */
18
+ export declare const appToolAccessSchema: Schema.Struct<{
19
+ readonly bindingName: Schema.NonEmptyString;
20
+ readonly level: Schema.Literals<readonly ["read", "write"]>;
21
+ readonly revision: Schema.NonEmptyString;
22
+ readonly grantedBy: Schema.NonEmptyString;
23
+ }>;
24
+ /** One installation binding constrained by an authenticated tool-policy approval. */
25
+ export type AppToolAccess = typeof appToolAccessSchema.Type;
26
+ /** Pre-read entries: the catalog never resolves installations or calls back into a host. */
27
+ export type AppToolEntry = Readonly<{
28
+ installation: Installation;
29
+ declaration: AppDeclaration;
30
+ declaredOperations: DeclaredOperations;
31
+ api: AppApi | null;
32
+ access: AppToolAccess | null;
33
+ }>;
@@ -0,0 +1,17 @@
1
+ import * as Schema from "effect/Schema";
2
+ /**
3
+ * One App-tool access level, with its own freshness pin. An Agent's comes
4
+ * from the owner's stored direct approval; a key's is computed fresh from
5
+ * its own Ear Tag grants (`keyAppAccess`), never stored as this shape --
6
+ * only read through it, for one call or one installation at a time.
7
+ */
8
+ export const appToolPolicySchema = Schema.Struct({
9
+ level: Schema.Literals(["read", "write"]),
10
+ revision: Schema.NonEmptyString,
11
+ grantedBy: Schema.NonEmptyString,
12
+ });
13
+ /** One installation's binding under the captured host approval. */
14
+ export const appToolAccessSchema = Schema.Struct({
15
+ ...appToolPolicySchema.fields,
16
+ bindingName: Schema.NonEmptyString,
17
+ });
package/dist/auth.d.ts ADDED
@@ -0,0 +1,11 @@
1
+ import type { OysterAuth } from "./host.js";
2
+ import type { OysterKeyStore } from "./keys.js";
3
+ /** Cookie emitted by ownerKeyAuth, for composing credential-specific adapters. */
4
+ export declare const ownerKeyCookieName = "oyster_session";
5
+ /**
6
+ * Single-owner sign-in: a bearer token or a session cookie holding the
7
+ * owner key's digest. The key itself is never stored in the browser.
8
+ */
9
+ export declare function ownerKeyAuth(ownerKey: string, ownerId?: string): OysterAuth;
10
+ /** The owner key, or a named key admitted as its canonical Team-key actor. */
11
+ export declare function ownerOrKeyAuth(ownerKey: string, ownerId: string, keys: () => Pick<OysterKeyStore, "verify">): OysterAuth;
package/dist/auth.js ADDED
@@ -0,0 +1,93 @@
1
+ import * as Option from "effect/Option";
2
+ import * as Schema from "effect/Schema";
3
+ import { presentedGitKey } from "./git-auth.js";
4
+ import { sha256 } from "./release.js";
5
+ /** Cookie emitted by ownerKeyAuth, for composing credential-specific adapters. */
6
+ export const ownerKeyCookieName = "oyster_session";
7
+ const loginFormSchema = Schema.Struct({ key: Schema.String });
8
+ /** Compare two secrets through their fixed-length digests, in constant time. */
9
+ async function constantTimeEqual(a, b) {
10
+ const [left, right] = await Promise.all([
11
+ sha256(new TextEncoder().encode(a)),
12
+ sha256(new TextEncoder().encode(b)),
13
+ ]);
14
+ let difference = 0;
15
+ for (let index = 0; index < left.length; index += 1)
16
+ difference |= left.charCodeAt(index) ^ right.charCodeAt(index);
17
+ return difference === 0;
18
+ }
19
+ /**
20
+ * Single-owner sign-in: a bearer token or a session cookie holding the
21
+ * owner key's digest. The key itself is never stored in the browser.
22
+ */
23
+ export function ownerKeyAuth(ownerKey, ownerId = "owner") {
24
+ if (ownerKey.length < 24)
25
+ throw new Error("OYSTER_OWNER_KEY must be at least 24 characters");
26
+ const sessionValue = sha256(new TextEncoder().encode(`oyster:${ownerKey}`));
27
+ const owner = {
28
+ kind: "admitted",
29
+ principal: { actor: { kind: "member", memberId: ownerId }, role: "owner" },
30
+ };
31
+ return {
32
+ resolve: (actor) => Promise.resolve(actor.kind === "member" && actor.memberId === ownerId
33
+ ? owner.principal
34
+ : null),
35
+ async authenticate(request) {
36
+ const authorization = request.headers.get("Authorization");
37
+ if (authorization?.startsWith("Bearer ") === true)
38
+ return (await constantTimeEqual(authorization.slice(7), ownerKey))
39
+ ? owner
40
+ : { kind: "refused", status: 401 };
41
+ const cookie = (request.headers.get("Cookie") ?? "")
42
+ .split(";")
43
+ .map((part) => part.trim())
44
+ .find((part) => part.startsWith(`${ownerKeyCookieName}=`));
45
+ if (cookie !== undefined &&
46
+ (await constantTimeEqual(cookie.slice(ownerKeyCookieName.length + 1), await sessionValue)))
47
+ return owner;
48
+ return { kind: "refused", status: 401 };
49
+ },
50
+ async login(request) {
51
+ const form = Schema.decodeUnknownOption(loginFormSchema)(Object.fromEntries(await request.formData()));
52
+ if (Option.isNone(form) ||
53
+ !(await constantTimeEqual(form.value.key, ownerKey)))
54
+ return new Response("Wrong key", { status: 401 });
55
+ return new Response(null, {
56
+ status: 303,
57
+ headers: {
58
+ Location: "/",
59
+ "Set-Cookie": `${ownerKeyCookieName}=${await sessionValue}; Path=/; HttpOnly; Secure; SameSite=Strict; Max-Age=2592000`,
60
+ },
61
+ });
62
+ },
63
+ };
64
+ }
65
+ /** The owner key, or a named key admitted as its canonical Team-key actor. */
66
+ export function ownerOrKeyAuth(ownerKey, ownerId, keys) {
67
+ const owner = ownerKeyAuth(ownerKey, ownerId);
68
+ return {
69
+ resolve: (actor) => owner.resolve(actor),
70
+ async authenticate(request) {
71
+ const result = await owner.authenticate(request);
72
+ if (result.kind === "admitted")
73
+ return result;
74
+ const presented = presentedGitKey(request);
75
+ if (presented.kind !== "key")
76
+ return result;
77
+ const verified = await keys().verify(presented.value);
78
+ // A revoked or expired key, or one belonging to a different owner (a
79
+ // presented secret can never prove that on its own), authenticates as
80
+ // no one; the owner-key result above still stands.
81
+ return verified.kind === "ok" && verified.value.owner === ownerId
82
+ ? {
83
+ kind: "admitted",
84
+ principal: {
85
+ actor: { kind: "team-key", keyId: verified.value.keyId },
86
+ },
87
+ }
88
+ : result;
89
+ },
90
+ login: (request) => owner.login?.(request) ??
91
+ Promise.resolve(new Response("Sign-in is not available", { status: 404 })),
92
+ };
93
+ }
@@ -0,0 +1,3 @@
1
+ export declare function appFacetRequest(request: Request): Request;
2
+ /** Only content metadata crosses back from a guest; origin policy is host-owned. */
3
+ export declare function appFacetResponse(response: Response): Response;
@@ -0,0 +1,53 @@
1
+ /** Second boundary before an admitted request enters customer backend code.
2
+ * Hub normally builds this shape itself; callers of the App runtime must not
3
+ * be able to pass their own headers through the facet.
4
+ */
5
+ import { appBackendActorHeader, appBackendComputerHeader, } from "@fungi.computer/agar/backend";
6
+ export function appFacetRequest(request) {
7
+ const headers = new Headers();
8
+ for (const name of [
9
+ "Content-Type",
10
+ appBackendActorHeader,
11
+ appBackendComputerHeader,
12
+ ]) {
13
+ const value = request.headers.get(name);
14
+ if (value !== null)
15
+ headers.set(name, value);
16
+ }
17
+ const init = {
18
+ method: request.method,
19
+ headers,
20
+ body: request.body,
21
+ redirect: "manual",
22
+ };
23
+ if (request.body !== null)
24
+ init.duplex = "half";
25
+ return new Request(request.url, init);
26
+ }
27
+ /** Only content metadata crosses back from a guest; origin policy is host-owned. */
28
+ export function appFacetResponse(response) {
29
+ const headers = new Headers();
30
+ for (const name of [
31
+ "Content-Type",
32
+ "Content-Language",
33
+ "Content-Encoding",
34
+ "Content-Length",
35
+ "Content-Disposition",
36
+ "Content-Range",
37
+ "Accept-Ranges",
38
+ "ETag",
39
+ "Last-Modified",
40
+ ]) {
41
+ const value = response.headers.get(name);
42
+ if (value !== null)
43
+ headers.set(name, value);
44
+ }
45
+ headers.set("Cache-Control", "no-store");
46
+ headers.set("Content-Security-Policy", "sandbox; default-src 'none'");
47
+ headers.set("X-Content-Type-Options", "nosniff");
48
+ return new Response(response.body, {
49
+ status: response.status,
50
+ statusText: response.statusText,
51
+ headers,
52
+ });
53
+ }
@@ -0,0 +1,40 @@
1
+ import { type AssetBuildPolicy } from "@fungi.computer/puffball/assets";
2
+ import { type ArtifactRef, type Sha256Digest } from "@fungi.computer/puffball";
3
+ import type { BuildOutput, StoredArtifact } from "./effect.js";
4
+ import type { CandidateOutputManifest } from "./submission-contract.js";
5
+ /** Write-once object storage for one artifact root. */
6
+ export type ImmutableArtifactObjects = Readonly<{
7
+ /** Create the object, or answer `exists` when the reference is taken. */
8
+ create(artifactRef: ArtifactRef, bytes: Uint8Array): Promise<"created" | "exists">;
9
+ /** Read the object's bytes, or `null` when absent. */
10
+ read(artifactRef: ArtifactRef): Promise<Uint8Array | null>;
11
+ }>;
12
+ /** One object ready to store under its reference. */
13
+ export type StagedArtifactObject = Readonly<{
14
+ artifactRef: ArtifactRef;
15
+ bytes: Uint8Array;
16
+ digest: Sha256Digest;
17
+ }>;
18
+ /** A build's output manifest and each distinct object it names. */
19
+ export type StagedBuildOutput = Readonly<{
20
+ manifest: CandidateOutputManifest;
21
+ objects: readonly StagedArtifactObject[];
22
+ }>;
23
+ /**
24
+ * Stage a build's bytes: the output manifest that names each object by its
25
+ * digest and size, and each distinct object once.
26
+ */
27
+ export declare function stageBuildOutput(output: BuildOutput): StagedBuildOutput;
28
+ /**
29
+ * Write the asset manifest and App root an output manifest names. The
30
+ * objects it names are already stored, and checked, by the caller.
31
+ */
32
+ export declare function writeArtifactRoot(manifest: CandidateOutputManifest, objects: ImmutableArtifactObjects, options: Readonly<{
33
+ rootArtifactRef: ArtifactRef;
34
+ assetPolicy: AssetBuildPolicy | undefined;
35
+ }>): Promise<StoredArtifact>;
36
+ /** Persist one build's UI, backend and root; an existing object must hold the same bytes. */
37
+ export declare function writeImmutableArtifact(output: BuildOutput, objects: ImmutableArtifactObjects, options: Readonly<{
38
+ rootArtifactRef: ArtifactRef;
39
+ assetPolicy: AssetBuildPolicy | undefined;
40
+ }>): Promise<StoredArtifact>;
@@ -0,0 +1,181 @@
1
+ /**
2
+ * Oyster's one immutable artifact representation: digest-addressed bytes
3
+ * keyed by artifact reference, with media types carried by the App build
4
+ * and asset manifests (never by the store). The local packer writes it to a
5
+ * directory, and a host's build owner writes it to that host's artifact store,
6
+ * so a Release built on the host and one packed and uploaded share objects.
7
+ *
8
+ * It has two halves. Staging turns a build's bytes into content-addressed
9
+ * objects and the output manifest that names them by digest and size. The
10
+ * root half turns an output manifest into the small objects only the store's
11
+ * owner writes: the asset manifest and the App root. The local packer runs
12
+ * both; a host whose builder uploaded the objects itself runs only the root
13
+ * half, over the same manifest, so both write identical root bytes.
14
+ *
15
+ * Every object is derived and checked before the first is stored: an asset
16
+ * build that its policy refuses, or a root over its bound, stores nothing.
17
+ */
18
+ import { createHash } from "node:crypto";
19
+ import mime from "mime";
20
+ import * as Schema from "effect/Schema";
21
+ import { assetBuildJsonSchema, assetPathSchema, decodeAssetBuild, } from "@fungi.computer/puffball/assets";
22
+ import { appBuildJsonSchema, createAppBuild, } from "@fungi.computer/puffball/app";
23
+ import { parseArtifactRef, parseSha256Digest, } from "@fungi.computer/puffball";
24
+ function sha256(bytes) {
25
+ return parseSha256Digest(`sha256:${createHash("sha256").update(bytes).digest("hex")}`);
26
+ }
27
+ /** The content address of an object: `artifact:sha256-<hex>`. */
28
+ function contentArtifactRef(digest) {
29
+ return parseArtifactRef(`artifact:sha256-${digest.slice("sha256:".length)}`);
30
+ }
31
+ /**
32
+ * The root bound for an App whose recipe declares no asset policy: this
33
+ * writer's private fallback, not recipe policy. An asset recipe's own
34
+ * `maxManifestBytes` bounds its root as it bounds its asset manifest.
35
+ */
36
+ const fallbackRootBytes = 1024 * 1024;
37
+ /**
38
+ * Stage a build's bytes: the output manifest that names each object by its
39
+ * digest and size, and each distinct object once.
40
+ */
41
+ export function stageBuildOutput(output) {
42
+ const objects = [];
43
+ const stage = (bytes) => {
44
+ const digest = sha256(bytes);
45
+ if (!objects.some((object) => object.digest === digest))
46
+ objects.push({
47
+ artifactRef: contentArtifactRef(digest),
48
+ bytes: bytes.slice(),
49
+ digest,
50
+ });
51
+ return { digest, byteLength: bytes.byteLength };
52
+ };
53
+ const declared = output.declaration === undefined ? {} : { declaration: output.declaration };
54
+ const ui = (prepared) => prepared.content.kind === "html"
55
+ ? {
56
+ mediaType: "text/html; charset=utf-8",
57
+ content: { kind: "html", ...stage(prepared.content.bytes) },
58
+ }
59
+ : {
60
+ mediaType: "application/vnd.fungi.app-assets+json",
61
+ content: {
62
+ kind: "assets",
63
+ base: prepared.content.base,
64
+ assets: prepared.content.assets.map((asset) => ({
65
+ path: asset.path,
66
+ ...stage(asset.bytes),
67
+ })),
68
+ },
69
+ };
70
+ const manifest = output.kind === "ui"
71
+ ? { kind: "ui", ui: ui(output.ui), ...declared }
72
+ : output.kind === "backend"
73
+ ? { kind: "backend", backend: stage(output.backend), ...declared }
74
+ : {
75
+ kind: "app",
76
+ ui: ui(output.ui),
77
+ backend: stage(output.backend),
78
+ ...declared,
79
+ };
80
+ return { manifest, objects };
81
+ }
82
+ /**
83
+ * Derive the asset manifest (for an asset UI) and the App root an output
84
+ * manifest names, each checked against the policy, without storing either.
85
+ */
86
+ function artifactRootObjects(manifest, options) {
87
+ const objects = [];
88
+ const referenced = (object) => ({
89
+ artifactRef: contentArtifactRef(object.digest),
90
+ digest: object.digest,
91
+ version: object.digest,
92
+ byteLength: object.byteLength,
93
+ });
94
+ const stage = (bytes, ref) => {
95
+ const digest = sha256(bytes);
96
+ const artifactRef = ref ?? contentArtifactRef(digest);
97
+ objects.push({ artifactRef, bytes, digest });
98
+ return {
99
+ artifactRef,
100
+ digest,
101
+ version: digest,
102
+ byteLength: bytes.byteLength,
103
+ };
104
+ };
105
+ const ui = manifest.kind === "backend" ? null : manifest.ui;
106
+ const storedUi = (() => {
107
+ if (ui === null)
108
+ return null;
109
+ if (ui.content.kind === "html")
110
+ return { ...referenced(ui.content), mediaType: ui.mediaType };
111
+ if (!options.assetPolicy)
112
+ throw new Error("Asset writer requires explicit policy");
113
+ const byPath = new Map();
114
+ for (const asset of ui.content.assets) {
115
+ const path = Schema.decodeUnknownSync(assetPathSchema)(asset.path);
116
+ if (byPath.has(path))
117
+ throw new Error("Duplicate build path");
118
+ byPath.set(path, asset);
119
+ }
120
+ const assets = [];
121
+ for (const [path, asset] of [...byPath].sort(([left], [right]) => left < right ? -1 : left > right ? 1 : 0)) {
122
+ const mediaType = mime.getType(path);
123
+ const contentType = mediaType?.startsWith("text/")
124
+ ? `${mediaType}; charset=utf-8`
125
+ : mediaType;
126
+ if (!contentType)
127
+ throw new Error("Build asset has no maintained content type");
128
+ assets.push({ path, ...referenced(asset), contentType });
129
+ }
130
+ const assetManifest = new TextEncoder().encode(Schema.encodeUnknownSync(assetBuildJsonSchema)({
131
+ format: "fungi-assets-v1",
132
+ base: ui.content.base,
133
+ assets,
134
+ }));
135
+ decodeAssetBuild(assetManifest, options.assetPolicy);
136
+ return { ...stage(assetManifest), mediaType: ui.mediaType };
137
+ })();
138
+ const storedBackend = manifest.kind === "ui"
139
+ ? null
140
+ : {
141
+ ...referenced(manifest.backend),
142
+ mediaType: "application/vnd.fungi.app-backend+json",
143
+ };
144
+ const rootBytes = new TextEncoder().encode(Schema.encodeUnknownSync(appBuildJsonSchema)(createAppBuild(storedUi, storedBackend, manifest.declaration)));
145
+ if (rootBytes.byteLength >
146
+ (options.assetPolicy?.maxManifestBytes ?? fallbackRootBytes))
147
+ throw new Error("App root exceeds its manifest bound");
148
+ const root = stage(rootBytes, options.rootArtifactRef);
149
+ return {
150
+ objects,
151
+ root: Object.freeze({
152
+ artifactRef: root.artifactRef,
153
+ artifactDigest: root.digest,
154
+ }),
155
+ };
156
+ }
157
+ /** Store each object once; an existing object must hold the same bytes. */
158
+ async function storeObjects(staged, objects) {
159
+ for (const { artifactRef, bytes, digest } of staged)
160
+ if ((await objects.create(artifactRef, bytes)) === "exists") {
161
+ const existing = await objects.read(artifactRef);
162
+ if (existing === null || sha256(existing) !== digest)
163
+ throw new Error("Immutable artifact collision");
164
+ }
165
+ }
166
+ /**
167
+ * Write the asset manifest and App root an output manifest names. The
168
+ * objects it names are already stored, and checked, by the caller.
169
+ */
170
+ export async function writeArtifactRoot(manifest, objects, options) {
171
+ const derived = artifactRootObjects(manifest, options);
172
+ await storeObjects(derived.objects, objects);
173
+ return derived.root;
174
+ }
175
+ /** Persist one build's UI, backend and root; an existing object must hold the same bytes. */
176
+ export async function writeImmutableArtifact(output, objects, options) {
177
+ const staged = stageBuildOutput(output);
178
+ const derived = artifactRootObjects(staged.manifest, options);
179
+ await storeObjects([...staged.objects, ...derived.objects], objects);
180
+ return derived.root;
181
+ }
@@ -0,0 +1,128 @@
1
+ import * as Schema from "effect/Schema";
2
+ import type { Sha256Digest } from "@fungi.computer/puffball";
3
+ import type { PinnedRecipe } from "./effect.js";
4
+ /** The secret-free Team job; no provider token enters publication. */
5
+ declare const jobSchema: Schema.Struct<{
6
+ readonly jobId: Schema.NonEmptyString;
7
+ readonly computerId: Schema.NonEmptyString;
8
+ readonly credentialId: Schema.NonEmptyString;
9
+ readonly issuedAt: Schema.Number;
10
+ readonly repository: Schema.NonEmptyString;
11
+ readonly expiresAt: Schema.Number;
12
+ readonly remote: Schema.NonEmptyString;
13
+ readonly reportUrl: Schema.NonEmptyString;
14
+ readonly status: Schema.Literals<readonly ["running", "succeeded", "failed", "revoked"]>;
15
+ readonly artifact: Schema.NullOr<Schema.Struct<{
16
+ readonly digest: Schema.NonEmptyString;
17
+ readonly bytes: Schema.Number;
18
+ }>>;
19
+ readonly detail: Schema.NullOr<Schema.String>;
20
+ }>;
21
+ export type TeamBuildJob = Schema.Schema.Type<typeof jobSchema>;
22
+ export type BuildCredential = Pick<TeamBuildJob, "jobId" | "computerId" | "credentialId" | "repository" | "issuedAt" | "expiresAt" | "remote" | "reportUrl">;
23
+ declare const executionSchema: Schema.Struct<{
24
+ readonly output: Schema.optional<Schema.NullOr<Schema.Struct<{
25
+ readonly exitCode: Schema.NullOr<Schema.Number>;
26
+ readonly stderr: Schema.optional<Schema.String>;
27
+ }>>>;
28
+ }>;
29
+ export type TeamBuildComputer = Readonly<{
30
+ applyBuildDecision: (decision: {
31
+ kind: "build";
32
+ schemaVersion: 1;
33
+ teamId: string;
34
+ computerId: string;
35
+ jobId: string;
36
+ credentialId: string;
37
+ operations: readonly ["computer.execute"];
38
+ sequence: number;
39
+ issuedAt: number;
40
+ expiresAt: number;
41
+ status: "allowed" | "revoked";
42
+ basis: {
43
+ credentialExpiresAt: number;
44
+ };
45
+ }) => Promise<void>;
46
+ execute: (input: {
47
+ teamId: string;
48
+ computerId: string;
49
+ jobId: string;
50
+ credentialId: string;
51
+ command: string;
52
+ timeoutMs: number;
53
+ }) => Promise<Schema.Schema.Type<typeof executionSchema>>;
54
+ deliverTeamGitEvents: () => Promise<number>;
55
+ }>;
56
+ /** Publisher executes builds; Team retains job authority, Computer runs them. */
57
+ export declare function createTeamBuildPublisher(options: Readonly<{
58
+ team: Readonly<{
59
+ mintTeamBuildCredential: (input: {
60
+ jobId: string;
61
+ repository: string;
62
+ computerId: string;
63
+ ttlMs: number;
64
+ }) => Promise<BuildCredential>;
65
+ revokeTeamBuild: (jobId: string) => Promise<TeamBuildJob>;
66
+ /** One Team-owned door checks Computer ownership and settles its job. */
67
+ build: () => Promise<Readonly<{
68
+ prepare: (jobId: string) => Promise<TeamBuildJob>;
69
+ read: (jobId: string) => Promise<TeamBuildJob | null>;
70
+ complete: (jobId: string, status: "succeeded" | "failed", detail: string) => Promise<TeamBuildJob>;
71
+ deliverProjection: () => Promise<void>;
72
+ [Symbol.dispose]: () => void;
73
+ }>>;
74
+ }>;
75
+ computer: (computerId: string) => TeamBuildComputer;
76
+ now?: () => number;
77
+ teamId: string;
78
+ maxPermissionAgeMs?: number;
79
+ }>): Readonly<{
80
+ mint(input: Parameters<typeof options.team.mintTeamBuildCredential>[0]): Promise<BuildCredential>;
81
+ read(jobId: string): Promise<{
82
+ readonly jobId: string;
83
+ readonly computerId: string;
84
+ readonly credentialId: string;
85
+ readonly issuedAt: number;
86
+ readonly repository: string;
87
+ readonly expiresAt: number;
88
+ readonly remote: string;
89
+ readonly reportUrl: string;
90
+ readonly status: "revoked" | "running" | "failed" | "succeeded";
91
+ readonly artifact: {
92
+ readonly digest: string;
93
+ readonly bytes: number;
94
+ } | null;
95
+ readonly detail: string | null;
96
+ } | null>;
97
+ revoke(jobId: string): Promise<{
98
+ readonly jobId: string;
99
+ readonly computerId: string;
100
+ readonly credentialId: string;
101
+ readonly issuedAt: number;
102
+ readonly repository: string;
103
+ readonly expiresAt: number;
104
+ readonly remote: string;
105
+ readonly reportUrl: string;
106
+ readonly status: "revoked" | "running" | "failed" | "succeeded";
107
+ readonly artifact: {
108
+ readonly digest: string;
109
+ readonly bytes: number;
110
+ } | null;
111
+ readonly detail: string | null;
112
+ }>;
113
+ run(input: {
114
+ jobId: string;
115
+ command: string;
116
+ timeoutMs: number;
117
+ }): Promise<TeamBuildJob>;
118
+ }>;
119
+ /** Fixed captured builder command; neither source nor the customer supplies shell code. */
120
+ export declare function teamAppBuildCommand(configuration: Readonly<{
121
+ appId: string;
122
+ sourceRevision: string;
123
+ source: Readonly<{
124
+ recipe: PinnedRecipe;
125
+ buildRecipeDigest: Sha256Digest;
126
+ }>;
127
+ }>, uploadGrant: string): string;
128
+ export {};