@fungi.computer/oyster 0.2.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.
- package/README.md +376 -72
- package/dist/agents.d.ts +3 -2
- package/dist/agents.js +4 -2
- package/dist/app-builds.d.ts +81 -8
- package/dist/app-builds.js +210 -60
- package/dist/app-storage.d.ts +3 -3
- package/dist/approval-capability.d.ts +5 -0
- package/dist/approval-capability.js +46 -0
- package/dist/approval-home.d.ts +51 -0
- package/dist/approval-home.js +226 -0
- package/dist/apps/catalog.d.ts +335 -0
- package/dist/apps/catalog.js +46 -0
- package/dist/apps/gate.d.ts +20 -6
- package/dist/apps/gate.js +42 -16
- package/dist/apps/index.d.ts +3 -1
- package/dist/apps/index.js +3 -1
- package/dist/apps/installation.d.ts +132 -51
- package/dist/apps/installation.js +176 -70
- package/dist/apps/permissions.d.ts +247 -0
- package/dist/apps/permissions.js +255 -0
- package/dist/boot-capability-route.d.ts +15 -0
- package/dist/boot-capability-route.js +196 -0
- package/dist/boot-capability.d.ts +103 -0
- package/dist/boot-capability.js +141 -0
- package/dist/boot-construction.d.ts +12 -0
- package/dist/boot-construction.js +175 -0
- package/dist/boot-gate.d.ts +267 -0
- package/dist/boot-gate.js +236 -0
- package/dist/boot-host-script.d.ts +2 -0
- package/dist/boot-host-script.js +1 -0
- package/dist/boot-mount.d.ts +21 -0
- package/dist/boot-mount.js +65 -0
- package/dist/browser/boot.d.ts +73 -0
- package/dist/browser/boot.js +502 -0
- package/dist/browser/browser.d.ts +104 -0
- package/dist/browser/browser.js +374 -0
- package/dist/browser/capability-source.d.ts +76 -0
- package/dist/browser/capability-source.js +338 -0
- package/dist/browser/cdp-client.d.ts +56 -0
- package/dist/browser/cdp-client.js +184 -0
- package/dist/browser/errors.d.ts +79 -0
- package/dist/browser/errors.js +32 -0
- package/dist/browser/index.d.ts +16 -0
- package/dist/browser/index.js +14 -0
- package/dist/browser/lease-registry.d.ts +61 -0
- package/dist/browser/lease-registry.js +101 -0
- package/dist/browser/operations.d.ts +225 -0
- package/dist/browser/operations.js +137 -0
- package/dist/browser/page.d.ts +25 -0
- package/dist/browser/page.js +172 -0
- package/dist/browser/quick-reads.d.ts +29 -0
- package/dist/browser/quick-reads.js +160 -0
- package/dist/browser/sessions.d.ts +52 -0
- package/dist/browser/sessions.js +118 -0
- package/dist/browser/url-policy.d.ts +18 -0
- package/dist/browser/url-policy.js +58 -0
- package/dist/build/app-imports.d.ts +12 -0
- package/dist/build/app-imports.js +105 -0
- package/dist/build/build-policy.d.ts +5 -3
- package/dist/build/build-policy.js +5 -3
- package/dist/build/candidate-builder.d.ts +1 -1
- package/dist/build/candidate-cli.mjs +8375 -334
- package/dist/build/local-build.d.ts +2 -0
- package/dist/build/local-build.js +28 -6
- package/dist/build/snapshot-package-store.d.ts +1 -1
- package/dist/build/snapshot-package-store.js +7 -2
- package/dist/build/workspace-build.d.ts +92 -5
- package/dist/build/workspace-build.js +93 -19
- package/dist/build.d.ts +1 -1
- package/dist/build.js +1 -1
- package/dist/catalog-frame.js +1 -1
- package/dist/cloudflare.d.ts +13 -3
- package/dist/cloudflare.js +102 -4
- package/dist/frame-binding-wire-script.js +1 -1
- package/dist/frame-binding-wire.js +4 -2
- package/dist/git-auth.d.ts +0 -2
- package/dist/git-auth.js +0 -2
- package/dist/git.d.ts +0 -1
- package/dist/git.js +0 -2
- package/dist/host.d.ts +39 -13
- package/dist/host.js +268 -142
- package/dist/index.d.ts +25 -15
- package/dist/index.js +39 -16
- package/dist/install-release-cli.d.ts +2 -0
- package/dist/install-release-cli.js +46 -0
- package/dist/installation-client.d.ts +43 -0
- package/dist/installation-client.js +178 -0
- package/dist/installation-execution.d.ts +11 -0
- package/dist/installation-execution.js +184 -0
- package/dist/installation-operations.d.ts +1437 -0
- package/dist/installation-operations.js +664 -0
- package/dist/installation-store-generation.d.ts +3 -0
- package/dist/installation-store-generation.js +16 -0
- package/dist/instance-id.d.ts +10 -0
- package/dist/instance-id.js +10 -0
- package/dist/instance-refusal.d.ts +11 -0
- package/dist/instance-refusal.js +21 -0
- package/dist/internal/delivery.js +4 -2
- package/dist/internal/host-gate.js +7 -1
- package/dist/internal/registry-installations.js +4 -4
- package/dist/keys.d.ts +5 -1
- package/dist/keys.js +10 -2
- package/dist/machine-link/authority.d.ts +89 -0
- package/dist/machine-link/authority.js +37 -0
- package/dist/machine-link/client.d.ts +66 -0
- package/dist/machine-link/client.js +94 -0
- package/dist/machine-link/delivery.d.ts +18 -0
- package/dist/machine-link/delivery.js +55 -0
- package/dist/machine-link/enrollment.d.ts +111 -0
- package/dist/machine-link/enrollment.js +286 -0
- package/dist/machine-link/fences.d.ts +37 -0
- package/dist/machine-link/fences.js +166 -0
- package/dist/machine-link/home.d.ts +20 -0
- package/dist/machine-link/home.js +24 -0
- package/dist/machine-link/host.d.ts +43 -0
- package/dist/machine-link/host.js +189 -0
- package/dist/machine-link/index.d.ts +15 -0
- package/dist/machine-link/index.js +15 -0
- package/dist/machine-link/link.d.ts +48 -0
- package/dist/machine-link/link.js +223 -0
- package/dist/machine-link/presence.d.ts +10 -0
- package/dist/machine-link/presence.js +25 -0
- package/dist/machine-link/workers.d.ts +6 -0
- package/dist/machine-link/workers.js +6 -0
- package/dist/mcp-owner.d.ts +2 -2
- package/dist/mcp-owner.js +41 -33
- package/dist/mcp.d.ts +15 -3
- package/dist/mcp.js +146 -25
- package/dist/pack.d.ts +14 -3
- package/dist/pack.js +44 -19
- package/dist/page/app-access.d.ts +7 -0
- package/dist/page/app-access.js +75 -0
- package/dist/page/boot-host.js +57 -0
- package/dist/page/capabilities.d.ts +3 -3
- package/dist/page/capabilities.js +16 -12
- package/dist/page/computer-handles.d.ts +3 -3
- package/dist/page/computer-target.d.ts +3 -0
- package/dist/page/computer-target.js +9 -0
- package/dist/page/mount.d.ts +23 -3
- package/dist/page/mount.js +152 -16
- package/dist/page-assets.d.ts +22 -0
- package/dist/page-assets.js +28 -0
- package/dist/page-script.d.ts +3 -0
- package/dist/page-script.js +5 -4
- package/dist/release.d.ts +13 -6
- package/dist/release.js +10 -7
- package/dist/root-data.d.ts +16 -6
- package/dist/root-data.js +2 -2
- package/dist/source/index.d.ts +1 -1
- package/dist/source/team-source.d.ts +20 -8
- package/dist/source/team-source.js +39 -10
- package/dist/source/template-seed.d.ts +2 -2
- package/dist/standalone/agent-mail.d.ts +24 -0
- package/dist/standalone/agent-mail.js +62 -0
- package/dist/standalone/agent-store.d.ts +9 -0
- package/dist/standalone/agent-store.js +30 -0
- package/dist/standalone/agents.d.ts +68 -9
- package/dist/standalone/agents.js +428 -56
- package/dist/standalone/app-builds-binding.d.ts +20 -2
- package/dist/standalone/app-builds-binding.js +147 -23
- package/dist/standalone/app-set.d.ts +5 -2
- package/dist/standalone/app-set.js +174 -143
- package/dist/standalone/browser.d.ts +11 -0
- package/dist/standalone/browser.js +72 -0
- package/dist/standalone/builds.d.ts +13 -4
- package/dist/standalone/builds.js +28 -44
- package/dist/standalone/computer-egress.d.ts +2 -0
- package/dist/standalone/computer-egress.js +13 -4
- package/dist/standalone/computer-fleet.d.ts +43 -0
- package/dist/standalone/computer-fleet.js +200 -0
- package/dist/standalone/computer.d.ts +44 -9
- package/dist/standalone/computer.js +129 -22
- package/dist/standalone/install-apps-cli.js +43 -6
- package/dist/standalone/instance.d.ts +2 -2
- package/dist/standalone/instance.js +8 -11
- package/dist/standalone/machine-link.d.ts +354 -0
- package/dist/standalone/machine-link.js +368 -0
- package/dist/standalone/media.d.ts +2 -2
- package/dist/standalone/media.js +19 -16
- package/dist/standalone/preview.d.ts +2 -2
- package/dist/standalone/preview.js +37 -10
- package/dist/standalone/registry.d.ts +149 -9
- package/dist/standalone/registry.js +299 -8
- package/dist/standalone/worker.d.ts +19 -5
- package/dist/standalone/worker.js +90 -54
- package/dist/standalone/workflow-binding.d.ts +18 -22
- package/dist/standalone/workflow-binding.js +18 -18
- package/dist/standalone/workflow-control.d.ts +4 -0
- package/dist/standalone/workflow-control.js +40 -0
- package/dist/standalone/workflow-events.d.ts +2 -2
- package/dist/standalone/workflow-events.js +9 -10
- package/dist/standalone/workflow-git-capture.js +4 -3
- package/dist/standalone/workflow-host.d.ts +162 -161
- package/dist/standalone/workflow-host.js +101 -21
- package/dist/standalone/workflow-node-capture.js +11 -4
- package/dist/standalone/workflow-schedules.d.ts +5 -2
- package/dist/standalone/workflow-schedules.js +9 -10
- package/dist/standalone/workflow-trigger-host.d.ts +65 -5
- package/dist/standalone/workflow-trigger-host.js +2 -2
- package/dist/standalone/workflow-webhooks.d.ts +22 -0
- package/dist/standalone/workflow-webhooks.js +39 -0
- package/dist/standalone/workflows.d.ts +140 -43
- package/dist/standalone/workflows.js +198 -39
- package/dist/storage-frame.d.ts +6 -6
- package/dist/testing/capabilities.js +14 -3
- package/dist/testing/installation.js +26 -2
- package/dist/testing/worker.js +1 -1
- package/dist/testing/workers-runtime.d.ts +8 -2
- package/dist/testing/workers-runtime.js +20 -2
- package/dist/webhook-abuse.d.ts +25 -0
- package/dist/webhook-abuse.js +54 -0
- package/dist/webhook-custody.d.ts +144 -0
- package/dist/webhook-custody.js +222 -0
- package/dist/webhook-ingress.d.ts +36 -0
- package/dist/webhook-ingress.js +146 -0
- package/dist/webhooks.d.ts +111 -0
- package/dist/webhooks.js +189 -0
- package/dist/workers-git.d.ts +12 -17
- package/dist/workers-git.js +60 -52
- package/dist/workers.d.ts +224 -13
- package/dist/workers.js +351 -51
- package/dist/workflow-agent-authority.d.ts +8 -0
- package/dist/workflow-agent-authority.js +42 -0
- package/dist/workflows.d.ts +66 -0
- package/dist/workflows.js +97 -0
- package/docs/api/README.md +63 -37
- package/docs/api/agents.md +17 -0
- package/docs/api/app-storage.md +1 -1
- package/docs/api/apps.md +1394 -543
- package/docs/api/browser/boot.md +132 -0
- package/docs/api/browser.md +1440 -0
- package/docs/api/build-contract.md +38 -25
- package/docs/api/build.md +263 -84
- package/docs/api/cloudflare.md +93 -7
- package/docs/api/git.md +3 -13
- package/docs/api/host.md +944 -118
- package/docs/api/index.md +320 -18
- package/docs/api/keys.md +24 -1
- package/docs/api/mcp.md +59 -12
- package/docs/api/modules.md +33 -40
- package/docs/api/pack.md +57 -8
- package/docs/api/page/mount.md +92 -4
- package/docs/api/workers-git.md +103 -24
- package/docs/api/workers.md +1581 -244
- package/docs/site/agent-quickstart.mdx +28 -14
- package/examples/standalone/worker.ts +3 -1
- package/examples/standalone/wrangler.jsonc +2 -0
- package/package.json +73 -71
- package/dist/installation-consent.d.ts +0 -81
- package/dist/installation-consent.js +0 -84
- package/dist/shell/app-directory.d.ts +0 -542
- package/dist/shell/app-directory.js +0 -196
- package/dist/shell/browser-actions.d.ts +0 -7
- package/dist/shell/browser-actions.js +0 -56
- package/dist/shell/command-host.d.ts +0 -112
- package/dist/shell/command-host.js +0 -502
- package/dist/shell/commands.d.ts +0 -25
- package/dist/shell/commands.js +0 -78
- package/dist/shell/index.d.ts +0 -8
- package/dist/shell/index.js +0 -8
- package/dist/shell/keys.d.ts +0 -41
- package/dist/shell/keys.js +0 -134
- package/dist/shell/shell-module.d.ts +0 -55
- package/dist/shell/shell-module.js +0 -86
- package/dist/shell/window-manager.d.ts +0 -128
- package/dist/shell/window-manager.js +0 -730
- package/dist/shell/workbench-contract.d.ts +0 -68
- package/dist/shell/workbench-directory.d.ts +0 -332
- package/dist/shell/workbench-directory.js +0 -35
- package/dist/shell/workbench-host-types.d.ts +0 -96
- package/dist/shell/workbench-host-types.js +0 -1
- package/dist/shell/workbench-host.d.ts +0 -34
- package/dist/shell/workbench-host.js +0 -352
- package/dist/shell/workbench-model.d.ts +0 -194
- package/dist/shell/workbench-model.js +0 -53
- package/dist/shell/workbench.d.ts +0 -4
- package/dist/shell/workbench.js +0 -2
- package/docs/api/shell/app-directory.md +0 -599
- package/docs/api/shell/command-host.md +0 -224
- package/docs/api/shell/commands.md +0 -197
- package/docs/api/shell/keys.md +0 -183
- package/docs/api/shell/shell-module.md +0 -204
- package/docs/api/shell/window-manager.md +0 -267
- package/docs/api/shell/workbench-directory.md +0 -119
- package/docs/api/shell/workbench.md +0 -205
- package/docs/api/shell.md +0 -1915
- /package/dist/{shell/workbench-contract.js → page/boot-host.d.ts} +0 -0
|
@@ -7,7 +7,7 @@ import { verifyRelease, } from "../host.js";
|
|
|
7
7
|
import { APP_INVOCATION_CEILING_MS, invocationAuthorityOf, invocationFacts, invocationFactsSchema, } from "../invocation.js";
|
|
8
8
|
import { AppInstanceError } from "../index.js";
|
|
9
9
|
import { appCapabilityIds } from "@fungi.computer/puffball";
|
|
10
|
-
import { createGraphWorkflowRuns, compileForMember, graphStartRequestSchema, preparationFromRun, planNodeCall, workflowRunContextSchema, } from "@fungi.computer/braid/runtime";
|
|
10
|
+
import { createGraphWorkflowRuns, createTriggerRunReservations, compileForMember, graphStartRequestSchema, preparationFromRun, planNodeCall, workflowRunContextSchema, } from "@fungi.computer/braid/runtime";
|
|
11
11
|
import { appMethodCallContextSchema } from "@fungi.computer/puffball/app";
|
|
12
12
|
import { appEventFactSchema, } from "@fungi.computer/puffball/app-event-facts";
|
|
13
13
|
import { keyAppAccess } from "../keys.js";
|
|
@@ -97,7 +97,19 @@ const summarizedEntry = (entry) => {
|
|
|
97
97
|
};
|
|
98
98
|
/** The standalone host's installation admission, above the portable engine. */
|
|
99
99
|
export function createStandaloneWorkflowHost(owner, host, options) {
|
|
100
|
-
const
|
|
100
|
+
const graph = createGraphWorkflowRuns(owner);
|
|
101
|
+
const reservations = createTriggerRunReservations(owner);
|
|
102
|
+
const store = {
|
|
103
|
+
...graph,
|
|
104
|
+
markTerminal(...args) {
|
|
105
|
+
// The standalone run owner ends graph/review state and the SAME Braid
|
|
106
|
+
// reservation atomically, without creating a Hub Computer-cleanup queue.
|
|
107
|
+
owner.transactionSync(() => {
|
|
108
|
+
graph.markTerminal(...args);
|
|
109
|
+
reservations.terminalTriggerRun(args[0]);
|
|
110
|
+
});
|
|
111
|
+
},
|
|
112
|
+
};
|
|
101
113
|
// Only the host's attested invocation context is associated here. Lifecycle,
|
|
102
114
|
// events, reviews and replay are owned by the shared runtime store.
|
|
103
115
|
owner.sql.exec(`CREATE TABLE IF NOT EXISTS standalone_workflow_callers (
|
|
@@ -169,14 +181,22 @@ export function createStandaloneWorkflowHost(owner, host, options) {
|
|
|
169
181
|
if (declaration === null)
|
|
170
182
|
return false;
|
|
171
183
|
const snapshot = await deriveDeclaredOperations(selected.installation.release, declaration, host.capabilities ?? []);
|
|
172
|
-
return (grantsFor(
|
|
184
|
+
return (grantsFor({
|
|
185
|
+
status: selected.installation.status,
|
|
186
|
+
authority: selected.installation.authority,
|
|
187
|
+
artifactDigest: selected.installation.release.artifact.artifactDigest,
|
|
188
|
+
}, declaration, snapshot, "workflow.publish") !== null);
|
|
173
189
|
};
|
|
174
190
|
const runGranted = async (selected) => {
|
|
175
191
|
const declaration = selected.verified.declaration;
|
|
176
192
|
if (declaration === null)
|
|
177
193
|
return false;
|
|
178
194
|
const snapshot = await deriveDeclaredOperations(selected.installation.release, declaration, host.capabilities ?? []);
|
|
179
|
-
if (grantsFor(
|
|
195
|
+
if (grantsFor({
|
|
196
|
+
status: selected.installation.status,
|
|
197
|
+
authority: selected.installation.authority,
|
|
198
|
+
artifactDigest: selected.installation.release.artifact.artifactDigest,
|
|
199
|
+
}, declaration, snapshot, "workflow.run") === null)
|
|
180
200
|
return false;
|
|
181
201
|
return approvedBindingGrants(selected.installation, declaration).some((binding) => binding.binding === "WORKFLOWS" &&
|
|
182
202
|
binding.operations.includes("runGraph"));
|
|
@@ -192,12 +212,10 @@ export function createStandaloneWorkflowHost(owner, host, options) {
|
|
|
192
212
|
checkLibraryOperation(selected, operation);
|
|
193
213
|
return selected;
|
|
194
214
|
};
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
if (facts.subject.kind !== "call" ||
|
|
200
|
-
facts.selection.owner.kind !== "installation")
|
|
215
|
+
// All frame kinds re-open the same live installation pin and grants.
|
|
216
|
+
// A lifecycle admission does not turn the frame into a member call.
|
|
217
|
+
const admitFrame = async (facts, operation) => {
|
|
218
|
+
if (facts.selection.owner.kind !== "installation")
|
|
201
219
|
throw new AppInstanceError("revoked");
|
|
202
220
|
if (facts.deadlineAt <= Date.now())
|
|
203
221
|
throw new AppInstanceError("expired");
|
|
@@ -217,13 +235,54 @@ export function createStandaloneWorkflowHost(owner, host, options) {
|
|
|
217
235
|
return {
|
|
218
236
|
...selected,
|
|
219
237
|
libraryId,
|
|
220
|
-
context: facts.subject.context,
|
|
221
238
|
installationAuthority: {
|
|
222
239
|
selection: current.selection,
|
|
223
240
|
artifactDigest: current.artifactDigest,
|
|
224
241
|
},
|
|
225
242
|
};
|
|
226
243
|
};
|
|
244
|
+
const admitInvocation = async (raw, operation) => {
|
|
245
|
+
const facts = S.decodeUnknownSync(invocationFactsSchema)(raw, {
|
|
246
|
+
onExcessProperty: "error",
|
|
247
|
+
});
|
|
248
|
+
if (facts.subject.kind !== "call")
|
|
249
|
+
throw new AppInstanceError("revoked");
|
|
250
|
+
return {
|
|
251
|
+
...(await admitFrame(facts, operation)),
|
|
252
|
+
context: facts.subject.context,
|
|
253
|
+
};
|
|
254
|
+
};
|
|
255
|
+
const admitLifecycle = async (raw, operation) => {
|
|
256
|
+
const facts = S.decodeUnknownSync(invocationFactsSchema)(raw, {
|
|
257
|
+
onExcessProperty: "error",
|
|
258
|
+
});
|
|
259
|
+
if (facts.subject.kind !== "lifecycle" ||
|
|
260
|
+
facts.subject.name !== "onGitPush")
|
|
261
|
+
throw new AppInstanceError("revoked");
|
|
262
|
+
return admitFrame(facts, operation);
|
|
263
|
+
};
|
|
264
|
+
const admitStart = async (raw) => {
|
|
265
|
+
const facts = S.decodeUnknownSync(invocationFactsSchema)(raw, {
|
|
266
|
+
onExcessProperty: "error",
|
|
267
|
+
});
|
|
268
|
+
if (facts.subject.kind === "call")
|
|
269
|
+
return admitInvocation(facts, "runGraph");
|
|
270
|
+
const selected = await admitLifecycle(facts, "runGraph");
|
|
271
|
+
const member = await options.lifecycleMember();
|
|
272
|
+
if (member === null)
|
|
273
|
+
throw new AppInstanceError("revoked");
|
|
274
|
+
const current = await options
|
|
275
|
+
.authority(selected.libraryId)
|
|
276
|
+
.member(member.memberId);
|
|
277
|
+
if (current.membershipVersion !== member.membershipVersion)
|
|
278
|
+
throw new AppInstanceError("revoked");
|
|
279
|
+
const context = {
|
|
280
|
+
actor: { kind: "member", memberId: member.memberId },
|
|
281
|
+
role: "owner",
|
|
282
|
+
computerId: host.computerId ?? null,
|
|
283
|
+
};
|
|
284
|
+
return { ...selected, context };
|
|
285
|
+
};
|
|
227
286
|
/** A trigger's live library pin; any missing grant is a typed revocation. */
|
|
228
287
|
const triggerAuthority = async (installationId) => {
|
|
229
288
|
const installation = await host.installations.get(installationId);
|
|
@@ -325,7 +384,8 @@ export function createStandaloneWorkflowHost(owner, host, options) {
|
|
|
325
384
|
workflowRunGranted = await runGranted(selected);
|
|
326
385
|
const after = await host.installations.get(current.installationId);
|
|
327
386
|
if (after?.status !== "enabled" ||
|
|
328
|
-
after.authorityVersion !==
|
|
387
|
+
after.authority.authorityVersion !==
|
|
388
|
+
current.authority.authorityVersion ||
|
|
329
389
|
releaseKey(after) !== releaseKey(current))
|
|
330
390
|
throw new AppInstanceError("revoked");
|
|
331
391
|
}
|
|
@@ -357,7 +417,9 @@ export function createStandaloneWorkflowHost(owner, host, options) {
|
|
|
357
417
|
return { apps };
|
|
358
418
|
}
|
|
359
419
|
const catalog = async (facts, input) => {
|
|
360
|
-
const library =
|
|
420
|
+
const library = facts.subject.kind === "call"
|
|
421
|
+
? await admitInvocation(facts, "catalog")
|
|
422
|
+
: await admitLifecycle(facts, "catalog");
|
|
361
423
|
if (input?.installationId === undefined)
|
|
362
424
|
return catalogForLibrary(library);
|
|
363
425
|
const { apps } = await catalogForLibrary(library, {
|
|
@@ -399,12 +461,15 @@ export function createStandaloneWorkflowHost(owner, host, options) {
|
|
|
399
461
|
// Host readers use this only after admitting the live member and library.
|
|
400
462
|
catalogForLibrary,
|
|
401
463
|
async prepare(facts, raw) {
|
|
402
|
-
const selected = await
|
|
464
|
+
const selected = await admitStart(facts);
|
|
403
465
|
const { libraryId, context } = selected;
|
|
404
466
|
const request = S.decodeUnknownSync(graphStartRequestSchema)(raw, {
|
|
405
467
|
onExcessProperty: "error",
|
|
406
468
|
});
|
|
407
469
|
const publication = request.published;
|
|
470
|
+
// Only immutable, published runs receive Workflow node attestations.
|
|
471
|
+
if (facts.subject.kind === "lifecycle" && publication === undefined)
|
|
472
|
+
return { error: "not_granted" };
|
|
408
473
|
if (publication !== undefined) {
|
|
409
474
|
if (!(await publisherGranted(selected)))
|
|
410
475
|
return { error: "not_granted" };
|
|
@@ -459,8 +524,11 @@ export function createStandaloneWorkflowHost(owner, host, options) {
|
|
|
459
524
|
const admitted = admitRunInput(compiled.plan.definition, request.input);
|
|
460
525
|
if (admitted.kind === "invalid")
|
|
461
526
|
return { error: "invalid_input" };
|
|
462
|
-
|
|
463
|
-
|
|
527
|
+
// The host's accepted event owns lifecycle replay, not a guest request id.
|
|
528
|
+
const requestId = facts.subject.kind === "lifecycle" && facts.subject.name === "onGitPush"
|
|
529
|
+
? ["onGitPush", facts.subject.eventId]
|
|
530
|
+
: request.requestId;
|
|
531
|
+
const runId = "run_" + (await contentDigest([libraryId, requestId])).slice(7, 47);
|
|
464
532
|
// An Agent runs with the access of the person who approved its App
|
|
465
533
|
// tools.
|
|
466
534
|
const subject = facts.subject.kind === "call" ? facts.subject : null;
|
|
@@ -516,7 +584,7 @@ export function createStandaloneWorkflowHost(owner, host, options) {
|
|
|
516
584
|
}
|
|
517
585
|
: {
|
|
518
586
|
initiatedBy: { kind: "member", memberId: actorId },
|
|
519
|
-
source: "manual",
|
|
587
|
+
source: facts.subject.kind === "lifecycle" ? "event" : "manual",
|
|
520
588
|
libraryInstallationId: libraryId,
|
|
521
589
|
computerId: context.computerId,
|
|
522
590
|
plan: compiled.plan,
|
|
@@ -532,7 +600,7 @@ export function createStandaloneWorkflowHost(owner, host, options) {
|
|
|
532
600
|
const started = {
|
|
533
601
|
teamId: libraryId,
|
|
534
602
|
runId,
|
|
535
|
-
requestId: await contentDigest([libraryId,
|
|
603
|
+
requestId: await contentDigest([libraryId, requestId]),
|
|
536
604
|
actorId,
|
|
537
605
|
computerId: context.computerId,
|
|
538
606
|
plan: compiled.plan,
|
|
@@ -542,7 +610,10 @@ export function createStandaloneWorkflowHost(owner, host, options) {
|
|
|
542
610
|
initiatedBy: preparation.initiatedBy,
|
|
543
611
|
};
|
|
544
612
|
const record = delivery === undefined ? started : { ...started, delivery };
|
|
545
|
-
const current = await
|
|
613
|
+
const current = await admitStart(facts);
|
|
614
|
+
if (canonicalJson(S.decodeUnknownSync(S.Json)(current.context)) !==
|
|
615
|
+
canonicalJson(S.decodeUnknownSync(S.Json)(context)))
|
|
616
|
+
return { error: "not_granted" };
|
|
546
617
|
if (publication !== undefined && !(await publisherGranted(current)))
|
|
547
618
|
return { error: "not_granted" };
|
|
548
619
|
const callContext = {
|
|
@@ -699,6 +770,7 @@ export function createStandaloneWorkflowHost(owner, host, options) {
|
|
|
699
770
|
// retryable fault; same for an instance the owner state marks
|
|
700
771
|
// inactive.
|
|
701
772
|
case "revoked":
|
|
773
|
+
case "disabled":
|
|
702
774
|
case "instance_owner_mismatch":
|
|
703
775
|
case "instance_authority_version_mismatch":
|
|
704
776
|
case "instance_release_mismatch":
|
|
@@ -714,6 +786,14 @@ export function createStandaloneWorkflowHost(owner, host, options) {
|
|
|
714
786
|
? "App execution failed"
|
|
715
787
|
: `App execution failed (${result.causeClass})`);
|
|
716
788
|
case "expired":
|
|
789
|
+
case "backend_load_failed":
|
|
790
|
+
case "backend_construction_failed":
|
|
791
|
+
case "ui_mount_failed":
|
|
792
|
+
case "boot_runner_unavailable":
|
|
793
|
+
case "boot_browser_binding_missing":
|
|
794
|
+
case "boot_clone_unavailable":
|
|
795
|
+
case "boot_timeout":
|
|
796
|
+
case "boot_cleanup_failed":
|
|
717
797
|
case "binding_unavailable":
|
|
718
798
|
case "reentrant":
|
|
719
799
|
case "conflicting_owner_state":
|
|
@@ -731,9 +811,9 @@ export function createStandaloneWorkflowHost(owner, host, options) {
|
|
|
731
811
|
}
|
|
732
812
|
}
|
|
733
813
|
// The engine's closed refusal vocabulary names host decisions; an App's
|
|
734
|
-
// own domain refusal fails the node with its code.
|
|
814
|
+
// own domain refusal fails the node with its code and caller-facing reason.
|
|
735
815
|
if (result.value.kind === "refused")
|
|
736
|
-
throw new Error(`App method refused (${result.value.code})`);
|
|
816
|
+
throw new Error(`App method refused (${result.value.code}): ${result.value.message}`);
|
|
737
817
|
const value = result.value.value;
|
|
738
818
|
const pending = pinned.pending === true ? pendingSchema.safeParse(value) : null;
|
|
739
819
|
if (pending?.success)
|
|
@@ -31,7 +31,13 @@ export function createStandaloneWorkflowNodeCapture(options) {
|
|
|
31
31
|
return null;
|
|
32
32
|
const facts = {
|
|
33
33
|
principal: { actor: caller.actor, role: caller.role ?? null },
|
|
34
|
-
installation
|
|
34
|
+
installation: {
|
|
35
|
+
status: installation.status,
|
|
36
|
+
visibility: installation.visibility,
|
|
37
|
+
instanceId: installation.instanceId,
|
|
38
|
+
authority: installation.authority,
|
|
39
|
+
artifactDigest: installation.release.artifact.artifactDigest,
|
|
40
|
+
},
|
|
35
41
|
declaration: verified.declaration,
|
|
36
42
|
declaredOperations: await deriveDeclaredOperations(installation.release, verified.declaration, host.capabilities ?? []),
|
|
37
43
|
api: verified.api,
|
|
@@ -57,9 +63,10 @@ export function createStandaloneWorkflowNodeCapture(options) {
|
|
|
57
63
|
const after = await readers.member(actor.memberId);
|
|
58
64
|
if (latest?.status !== "enabled" ||
|
|
59
65
|
latest.instanceId !== installation.instanceId ||
|
|
60
|
-
latest.authorityVersion !==
|
|
61
|
-
|
|
62
|
-
|
|
66
|
+
latest.authority.authorityVersion !==
|
|
67
|
+
installation.authority.authorityVersion ||
|
|
68
|
+
latest.authority.declaredOperationsDigest !==
|
|
69
|
+
installation.authority.declaredOperationsDigest ||
|
|
63
70
|
latest.release.releaseId !== installation.release.releaseId ||
|
|
64
71
|
latest.release.artifact.artifactDigest !==
|
|
65
72
|
installation.release.artifact.artifactDigest ||
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { createWorkflowSchedules, type WorkflowInstallationAuthority } from "@fungi.computer/braid/runtime";
|
|
2
2
|
import type { AppMethodCallContext } from "@fungi.computer/puffball/app-method-runtime";
|
|
3
|
+
import type { workflowAgentAuthority } from "../workflow-agent-authority.js";
|
|
3
4
|
import { type TriggerArmTarget, type ScheduleDisarmInput } from "./workflow-binding.js";
|
|
4
5
|
/** The host supplies this result only after reopening the protected invocation. */
|
|
5
6
|
export type TriggerAdmission = Readonly<{
|
|
7
|
+
authority: Awaited<ReturnType<typeof workflowAgentAuthority>>;
|
|
6
8
|
libraryId: string;
|
|
7
9
|
context: AppMethodCallContext;
|
|
8
10
|
/** The admitted frame's selection and verified artifact, pinned on arm. */
|
|
@@ -26,7 +28,7 @@ export declare function createStandaloneWorkflowSchedules<Frame>(options: Parame
|
|
|
26
28
|
} | {
|
|
27
29
|
kind: "refused";
|
|
28
30
|
reason: string;
|
|
29
|
-
|
|
31
|
+
error?: undefined;
|
|
30
32
|
}>;
|
|
31
33
|
disarm(frame: Frame, raw: ScheduleDisarmInput): Promise<{
|
|
32
34
|
kind: "not_found";
|
|
@@ -41,12 +43,13 @@ export declare function createStandaloneWorkflowSchedules<Frame>(options: Parame
|
|
|
41
43
|
} | {
|
|
42
44
|
schedules: {
|
|
43
45
|
scheduleId: string;
|
|
46
|
+
installationId: string;
|
|
44
47
|
workflowId: string;
|
|
45
48
|
triggerId: string;
|
|
46
49
|
nextDueAt: number;
|
|
47
50
|
state: "active" | "finished" | "paused" | "cancelled";
|
|
48
51
|
lastError: string | null;
|
|
49
52
|
}[];
|
|
50
|
-
|
|
53
|
+
error?: undefined;
|
|
51
54
|
}>;
|
|
52
55
|
};
|
|
@@ -10,30 +10,29 @@ export function createStandaloneWorkflowSchedules(options) {
|
|
|
10
10
|
alarm: () => schedules.fireDue(),
|
|
11
11
|
async arm(frame, raw) {
|
|
12
12
|
const target = triggerArmTargetSchema.parse(raw);
|
|
13
|
-
const { libraryId, context, installationAuthority } = await options.admitInvocation(frame, "armSchedule");
|
|
14
|
-
if (
|
|
13
|
+
const { libraryId, context, installationAuthority, authority } = await options.admitInvocation(frame, "armSchedule");
|
|
14
|
+
if (authority === null)
|
|
15
15
|
return { error: "not_granted" };
|
|
16
16
|
if (context.computerId === null)
|
|
17
17
|
return {
|
|
18
18
|
kind: "refused",
|
|
19
19
|
reason: "Select a Computer before arming a schedule",
|
|
20
20
|
};
|
|
21
|
-
|
|
22
|
-
return schedules.arm({ armedBy: context.actor.memberId, armedVia: null }, { ...target, installationId: libraryId }, context.computerId, installationAuthority);
|
|
21
|
+
return schedules.arm(authority.armer, { ...target, installationId: libraryId }, context.computerId, installationAuthority);
|
|
23
22
|
},
|
|
24
23
|
async disarm(frame, raw) {
|
|
25
24
|
const { scheduleId } = scheduleDisarmInputSchema.parse(raw);
|
|
26
|
-
const { libraryId,
|
|
27
|
-
if (
|
|
25
|
+
const { libraryId, authority } = await options.admitInvocation(frame, "disarmSchedule");
|
|
26
|
+
if (authority === null)
|
|
28
27
|
return { error: "not_granted" };
|
|
29
|
-
return schedules.disarm(
|
|
28
|
+
return schedules.disarm(authority.principal, libraryId, scheduleId);
|
|
30
29
|
},
|
|
31
30
|
async list(frame) {
|
|
32
|
-
const { libraryId,
|
|
33
|
-
if (
|
|
31
|
+
const { libraryId, authority } = await options.admitInvocation(frame, "schedules");
|
|
32
|
+
if (authority === null)
|
|
34
33
|
return { error: "not_granted" };
|
|
35
34
|
return {
|
|
36
|
-
schedules: await schedules.list(
|
|
35
|
+
schedules: await schedules.list(authority.principal, libraryId),
|
|
37
36
|
};
|
|
38
37
|
},
|
|
39
38
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createTeamWorkflowRuns, createTeamWorkflowTriggers, type WorkflowErrorPort, type WorkflowRunAuthority } from "@fungi.computer/braid/runtime";
|
|
1
|
+
import { createTeamWorkflowRuns, createTeamWorkflowTriggers, type WorkflowErrorPort, type WorkflowRunAuthority, type WorkflowTriggerAdmission } from "@fungi.computer/braid/runtime";
|
|
2
2
|
/** Standalone denial is the Instance's revoked refusal; other failures retry. */
|
|
3
3
|
export declare const standaloneTriggerErrors: WorkflowErrorPort;
|
|
4
4
|
/**
|
|
@@ -8,7 +8,7 @@ export declare const standaloneTriggerErrors: WorkflowErrorPort;
|
|
|
8
8
|
*/
|
|
9
9
|
export declare function createStandaloneWorkflowTriggerHost(options: {
|
|
10
10
|
authority: WorkflowRunAuthority;
|
|
11
|
-
runs: Pick<Parameters<typeof createTeamWorkflowRuns>[0], "sql" | "transactionSync" | "teamId">;
|
|
11
|
+
runs: Pick<Parameters<typeof createTeamWorkflowRuns>[0], "sql" | "transactionSync" | "teamId" | "now">;
|
|
12
12
|
triggers: Omit<Parameters<typeof createTeamWorkflowTriggers>[0], "runs" | "errors">;
|
|
13
13
|
/** Whether this member may have run endings reported to the Agent Session. */
|
|
14
14
|
admitDelivery: Parameters<typeof createTeamWorkflowRuns>[0]["admitDelivery"];
|
|
@@ -344,7 +344,7 @@ export declare function createStandaloneWorkflowTriggerHost(options: {
|
|
|
344
344
|
readonly operations: readonly string[];
|
|
345
345
|
}[];
|
|
346
346
|
readonly nodeId: string;
|
|
347
|
-
readonly capabilities: readonly ("agent.list" | "agent.manage" | "agent.rename" | "agent.avatar" | "agent.toolPolicy" | "agent.read" | "agent.send" | "computer.list" | "computer.readFile" | "computer.writeFile" | "computer.execute" | "llm.generation" | "media.generation" | "games:servers" | "git.repository" | "apps.open" | "apps.call" | "app.storage" | "app.events.emit" | "team.workbenchHost" | "marketplace.browse" | "marketplace.purchase" | "workflow.run" | "workflow.publish" | "workflow.arm" | "world" | "channels.read" | "channels.personal.manage")[];
|
|
347
|
+
readonly capabilities: readonly ("approvals" | "agent.list" | "agent.manage" | "agent.rename" | "agent.avatar" | "agent.toolPolicy" | "agent.read" | "agent.send" | "computer.list" | "computer.readFile" | "computer.writeFile" | "computer.execute" | "llm.generation" | "media.generation" | "games:servers" | "git.repository" | "apps.open" | "apps.call" | "app.storage" | "app.events.emit" | "team.workbenchHost" | "marketplace.browse" | "marketplace.purchase" | "workflow.run" | "workflow.publish" | "workflow.arm" | "world" | "channels.read" | "channels.personal.manage")[];
|
|
348
348
|
readonly graphId?: string;
|
|
349
349
|
}[];
|
|
350
350
|
readonly nodeGitAccess: readonly {
|
|
@@ -1242,7 +1242,7 @@ export declare function createStandaloneWorkflowTriggerHost(options: {
|
|
|
1242
1242
|
}>;
|
|
1243
1243
|
startCachedGraph(binding: import("@fungi.computer/braid/runtime").ArmedWorkflowTrigger, started: Pick<import("@fungi.computer/braid/runtime").GraphStartRequest, "requestId" | "input"> & {
|
|
1244
1244
|
triggerRepository?: string;
|
|
1245
|
-
}): Promise<{
|
|
1245
|
+
}, admission?: WorkflowTriggerAdmission): Promise<{
|
|
1246
1246
|
kind: "conflict" | "started" | "replay";
|
|
1247
1247
|
runId: string;
|
|
1248
1248
|
planDigest: string;
|
|
@@ -1264,6 +1264,66 @@ export declare function createStandaloneWorkflowTriggerHost(options: {
|
|
|
1264
1264
|
revokeInstallationInMutation(rawInstallationId: string): void;
|
|
1265
1265
|
revokeReleaseInMutation(appIds: readonly string[]): void;
|
|
1266
1266
|
onLifecycle(status: "active" | "suspended"): void;
|
|
1267
|
+
reserveWebhookDelivery(raw: import("zod").input<import("zod").ZodObject<{
|
|
1268
|
+
scope: import("zod").ZodLazy<import("zod").ZodObject<{
|
|
1269
|
+
installationId: import("zod").ZodString;
|
|
1270
|
+
workflowId: import("zod").ZodString;
|
|
1271
|
+
commit: import("zod").ZodString;
|
|
1272
|
+
triggerId: import("zod").ZodString;
|
|
1273
|
+
}, import("zod/v4/core").$strict>>;
|
|
1274
|
+
delivery: import("zod").ZodObject<{
|
|
1275
|
+
host: import("zod").ZodString;
|
|
1276
|
+
tenant: import("zod").ZodString;
|
|
1277
|
+
bindingId: import("zod").ZodString;
|
|
1278
|
+
eventId: import("zod").ZodString;
|
|
1279
|
+
}, import("zod/v4/core").$strict>;
|
|
1280
|
+
cargo: import("zod").ZodJSONSchema;
|
|
1281
|
+
limits: import("zod").ZodObject<{
|
|
1282
|
+
hostConcurrency: import("zod").ZodNumber;
|
|
1283
|
+
bindingConcurrency: import("zod").ZodNumber;
|
|
1284
|
+
}, import("zod/v4/core").$strict>;
|
|
1285
|
+
}, import("zod/v4/core").$strict>>): {
|
|
1286
|
+
kind: "reserved" | "replay";
|
|
1287
|
+
reservationId: string;
|
|
1288
|
+
runId: string | null;
|
|
1289
|
+
} | {
|
|
1290
|
+
kind: "refused";
|
|
1291
|
+
reason: "limit";
|
|
1292
|
+
};
|
|
1293
|
+
pendingWebhookDeliveries(rawOwner: import("zod").input<import("zod").ZodObject<{
|
|
1294
|
+
host: import("zod").ZodString;
|
|
1295
|
+
tenant: import("zod").ZodString;
|
|
1296
|
+
}, import("zod/v4/core").$strict>>): ({
|
|
1297
|
+
kind: "launch";
|
|
1298
|
+
reservationId: string;
|
|
1299
|
+
runId: string;
|
|
1300
|
+
} | {
|
|
1301
|
+
scope: {
|
|
1302
|
+
installationId: string;
|
|
1303
|
+
workflowId: string;
|
|
1304
|
+
commit: string;
|
|
1305
|
+
triggerId: string;
|
|
1306
|
+
};
|
|
1307
|
+
delivery: {
|
|
1308
|
+
host: string;
|
|
1309
|
+
tenant: string;
|
|
1310
|
+
bindingId: string;
|
|
1311
|
+
eventId: string;
|
|
1312
|
+
};
|
|
1313
|
+
cargo: import("zod").JSONType;
|
|
1314
|
+
limits: {
|
|
1315
|
+
hostConcurrency: number;
|
|
1316
|
+
bindingConcurrency: number;
|
|
1317
|
+
};
|
|
1318
|
+
kind: "start";
|
|
1319
|
+
reservationId: string;
|
|
1320
|
+
runId?: never;
|
|
1321
|
+
})[];
|
|
1322
|
+
nextWebhookWake(rawOwner: import("zod").input<import("zod").ZodObject<{
|
|
1323
|
+
host: import("zod").ZodString;
|
|
1324
|
+
tenant: import("zod").ZodString;
|
|
1325
|
+
}, import("zod/v4/core").$strict>>): number | null;
|
|
1326
|
+
completeWebhookDelivery(rawReservationId: string): void;
|
|
1267
1327
|
reserveTriggerRun(raw: {
|
|
1268
1328
|
scope: import("@fungi.computer/braid/runtime").TriggerRunScope;
|
|
1269
1329
|
requestId: string;
|
|
@@ -1702,7 +1762,7 @@ export declare function createStandaloneWorkflowTriggerHost(options: {
|
|
|
1702
1762
|
refusal: (binding: import("@fungi.computer/braid/runtime").ArmedWorkflowTrigger) => string | null;
|
|
1703
1763
|
invalidateInstallation: (installationId: string) => void;
|
|
1704
1764
|
revalidatePublished(installationId: string, git: import("@fungi.computer/agar/git").GitBinding): Promise<void>;
|
|
1705
|
-
fire(raw: import("@fungi.computer/braid/runtime").ArmedWorkflowTrigger, scope: string, event: import("@fungi.computer/braid").TriggerEvent): Promise<Readonly<{
|
|
1765
|
+
fire(raw: import("@fungi.computer/braid/runtime").ArmedWorkflowTrigger, scope: string, event: import("@fungi.computer/braid").TriggerEvent, admission?: WorkflowTriggerAdmission): Promise<Readonly<{
|
|
1706
1766
|
kind: "ignored";
|
|
1707
1767
|
}> | Readonly<{
|
|
1708
1768
|
kind: "started" | "replay" | "conflict";
|
|
@@ -37,8 +37,8 @@ export function createStandaloneWorkflowTriggerHost(options) {
|
|
|
37
37
|
errors: standaloneTriggerErrors,
|
|
38
38
|
runs: {
|
|
39
39
|
...runs,
|
|
40
|
-
async startCachedGraph(binding, request) {
|
|
41
|
-
const started = await runs.startCachedGraph(binding, request);
|
|
40
|
+
async startCachedGraph(binding, request, admission) {
|
|
41
|
+
const started = await runs.startCachedGraph(binding, request, admission);
|
|
42
42
|
// A replay re-records the same caller after a lost reply.
|
|
43
43
|
if (started.kind === "started" || started.kind === "replay")
|
|
44
44
|
options.recordCaller(started.runId, binding.armedBy, binding.computerId);
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Hono } from "hono";
|
|
2
|
+
import type { OysterEnv, OysterHost } from "../host.js";
|
|
3
|
+
import type { StandaloneWorkflowEnv } from "./workflows.js";
|
|
4
|
+
/** The root resolves the origin before this unauthenticated signed-delivery path. */
|
|
5
|
+
export declare function standaloneWorkflowWebhookRoutes(env: Pick<StandaloneWorkflowEnv, "WORKFLOW_RUNS">, hostFor: (request: Request) => Promise<Pick<OysterHost, "installations"> | null>): import("hono/hono-base").HonoBase<import("hono/types").BlankEnv, {
|
|
6
|
+
"/api/installations/:installationId/workflows/webhooks/:bindingId": {
|
|
7
|
+
$all: {
|
|
8
|
+
input: {
|
|
9
|
+
param: {
|
|
10
|
+
installationId: string;
|
|
11
|
+
} & {
|
|
12
|
+
bindingId: string;
|
|
13
|
+
};
|
|
14
|
+
};
|
|
15
|
+
output: {};
|
|
16
|
+
outputFormat: string;
|
|
17
|
+
status: import("hono/utils/http-status").StatusCode;
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
}, "/", "/api/installations/:installationId/workflows/webhooks/:bindingId">;
|
|
21
|
+
/** Mounted only under the root's authenticated owner routes; the DO re-admits. */
|
|
22
|
+
export declare function standaloneWorkflowWebhookControls(env: Pick<StandaloneWorkflowEnv, "WORKFLOW_RUNS">, hostFor: () => Promise<Pick<OysterHost, "installations">>): Hono<OysterEnv>;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { Hono } from "hono";
|
|
2
|
+
import { bodyLimit } from "hono/body-limit";
|
|
3
|
+
import * as Schema from "effect/Schema";
|
|
4
|
+
import { workflowWebhookCommandSchema } from "../webhooks.js";
|
|
5
|
+
const refused = () => Response.json({ error: "refused" }, { status: 403, headers: { "cache-control": "no-store" } });
|
|
6
|
+
/** Address an existing library, never use an opaque binding ID as a DO name. */
|
|
7
|
+
async function libraryExists(host, installationId) {
|
|
8
|
+
const installation = await host.installations.get(installationId);
|
|
9
|
+
return (installation?.status === "enabled" &&
|
|
10
|
+
installation.authority.approvedGrants.some((grant) => grant.capability === "workflow.run"));
|
|
11
|
+
}
|
|
12
|
+
/** The root resolves the origin before this unauthenticated signed-delivery path. */
|
|
13
|
+
export function standaloneWorkflowWebhookRoutes(env, hostFor) {
|
|
14
|
+
return new Hono().all("/api/installations/:installationId/workflows/webhooks/:bindingId", async (context) => {
|
|
15
|
+
const host = await hostFor(context.req.raw);
|
|
16
|
+
const installationId = context.req.param("installationId");
|
|
17
|
+
if (host === null || !(await libraryExists(host, installationId)))
|
|
18
|
+
return refused();
|
|
19
|
+
const address = new URL(context.req.url);
|
|
20
|
+
address.pathname = `/webhooks/${encodeURIComponent(context.req.param("bindingId"))}`;
|
|
21
|
+
address.search = "";
|
|
22
|
+
return env.WORKFLOW_RUNS.getByName(installationId).fetch(new Request(address, context.req.raw));
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
/** Mounted only under the root's authenticated owner routes; the DO re-admits. */
|
|
26
|
+
export function standaloneWorkflowWebhookControls(env, hostFor) {
|
|
27
|
+
return new Hono().post("/api/installations/:installationId/workflows/webhooks", bodyLimit({ maxSize: 64 * 1024, onError: refused }), async (context) => {
|
|
28
|
+
const installationId = context.req.param("installationId");
|
|
29
|
+
if (!(await libraryExists(await hostFor(), installationId)))
|
|
30
|
+
return refused();
|
|
31
|
+
const body = Schema.decodeUnknownResult(Schema.fromJsonString(Schema.Json))(await context.req.text());
|
|
32
|
+
if (body._tag === "Failure")
|
|
33
|
+
return refused();
|
|
34
|
+
const command = workflowWebhookCommandSchema.safeParse(body.success);
|
|
35
|
+
if (!command.success)
|
|
36
|
+
return refused();
|
|
37
|
+
return env.WORKFLOW_RUNS.getByName(installationId).webhookControl(context.get("principal"), installationId, command.data);
|
|
38
|
+
});
|
|
39
|
+
}
|