@fungi.computer/oyster 0.0.1 → 0.2.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 +228 -176
- package/dist/agent-avatars.d.ts +103 -0
- package/dist/agent-avatars.js +66 -0
- package/dist/agents.d.ts +12 -2
- package/dist/agents.js +39 -2
- package/dist/app-builds.d.ts +76 -17
- package/dist/app-builds.js +207 -63
- package/dist/app-sources.d.ts +7 -22
- package/dist/app-sources.js +49 -14
- package/dist/apps/gate.d.ts +7 -7
- package/dist/apps/gate.js +6 -6
- package/dist/apps/installation.d.ts +35 -35
- package/dist/apps/installation.js +20 -19
- package/dist/build/artifact-writer.d.ts +40 -0
- package/dist/build/artifact-writer.js +181 -0
- package/dist/build/build-jobs.js +7 -1
- package/dist/build/build-policy.d.ts +10 -0
- package/dist/build/build-policy.js +10 -0
- package/dist/build/build-step.d.ts +12 -1
- package/dist/build/build-step.js +9 -1
- package/dist/build/candidate-builder.d.ts +68 -0
- package/dist/build/candidate-builder.js +91 -0
- package/dist/build/candidate-cli.d.ts +1 -0
- package/dist/build/candidate-cli.js +119 -0
- package/dist/build/candidate-cli.mjs +28220 -0
- package/dist/build/content-objects.d.ts +37 -0
- package/dist/build/content-objects.js +53 -0
- package/dist/build/effect.d.ts +18 -0
- package/dist/build/effect.js +52 -12
- package/dist/build/git-environment.d.ts +3 -0
- package/dist/build/git-environment.js +21 -9
- package/dist/build/local-build.d.ts +13 -3
- package/dist/build/local-build.js +149 -99
- package/dist/build/snapshot-package-store.d.ts +10 -0
- package/dist/build/snapshot-package-store.js +15 -0
- package/dist/build/snapshot-program-scripts.js +1 -1
- package/dist/build/snapshot-programs/snapshot-path.d.ts +2 -0
- package/dist/build/snapshot-programs/snapshot-path.js +6 -0
- package/dist/build/submission-contract.d.ts +461 -17
- package/dist/build/submission-contract.js +136 -26
- package/dist/build/workspace-build.d.ts +7 -2
- package/dist/build/workspace-build.js +55 -11
- package/dist/build-contract.d.ts +3 -0
- package/dist/build-contract.js +3 -0
- package/dist/build-egress.d.ts +62 -0
- package/dist/build-egress.js +148 -0
- package/dist/build.d.ts +1 -0
- package/dist/build.js +1 -0
- package/dist/cloudflare.js +4 -4
- package/dist/frame-binding-wire-script.js +1 -1
- package/dist/frame-binding-wire.d.ts +1 -1
- package/dist/frame-binding-wire.js +1 -1
- package/dist/git-journal.js +4 -4
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/installation-consent.d.ts +3 -1
- package/dist/installation-consent.js +2 -0
- package/dist/mcp-owner.js +20 -8
- package/dist/pack.d.ts +6 -3
- package/dist/pack.js +28 -11
- package/dist/page/gill-observation.d.ts +4 -0
- package/dist/page/gill-observation.js +31 -0
- package/dist/page/mount.d.ts +4 -8
- package/dist/page/window-launch.d.ts +18 -0
- package/dist/page/window-launch.js +11 -0
- package/dist/page-script.js +4 -4
- package/dist/release.d.ts +11 -0
- package/dist/release.js +4 -1
- package/dist/shell/app-directory.d.ts +50 -45
- package/dist/shell/app-directory.js +19 -18
- package/dist/shell/shell-module.d.ts +26 -2
- package/dist/shell/shell-module.js +51 -2
- package/dist/shell/window-manager.d.ts +5 -1
- package/dist/shell/window-manager.js +21 -2
- package/dist/shell/workbench-directory.d.ts +332 -0
- package/dist/shell/workbench-directory.js +35 -0
- package/dist/shell/workbench-host-types.d.ts +1 -1
- package/dist/shell/workbench-host.js +5 -0
- package/dist/shell/workbench-model.d.ts +3 -1
- package/dist/source/index.d.ts +6 -7
- package/dist/source/index.js +6 -7
- package/dist/source/template-push.d.ts +33 -0
- package/dist/source/template-push.js +223 -0
- package/dist/source/template-repository.d.ts +2 -0
- package/dist/source/template-repository.js +13 -0
- package/dist/source/{template-write.d.ts → template-seed.d.ts} +19 -20
- package/dist/source/template-seed.js +80 -0
- package/dist/standalone/agent-directory.d.ts +4 -0
- package/dist/standalone/agent-directory.js +4 -0
- package/dist/standalone/agents.d.ts +227 -0
- package/dist/standalone/agents.js +834 -0
- package/dist/standalone/app-builds-binding.d.ts +36 -0
- package/dist/standalone/app-builds-binding.js +119 -0
- package/dist/standalone/app-set.d.ts +91 -0
- package/dist/standalone/app-set.js +395 -0
- package/dist/standalone/app-source.d.ts +41 -0
- package/dist/standalone/app-source.js +59 -0
- package/dist/standalone/builds.d.ts +18 -0
- package/dist/standalone/builds.js +101 -0
- package/dist/standalone/computer-egress.d.ts +37 -0
- package/dist/standalone/computer-egress.js +111 -0
- package/dist/standalone/computer.d.ts +122 -0
- package/dist/standalone/computer.js +714 -0
- package/dist/standalone/install-apps-cli.d.ts +2 -0
- package/dist/standalone/install-apps-cli.js +126 -0
- package/dist/standalone/instance.d.ts +35 -0
- package/dist/standalone/instance.js +74 -0
- package/dist/standalone/media.d.ts +76 -0
- package/dist/standalone/media.js +227 -0
- package/dist/standalone/preview.d.ts +51 -0
- package/dist/standalone/preview.js +167 -0
- package/dist/standalone/registry.d.ts +102 -0
- package/dist/standalone/registry.js +193 -0
- package/dist/standalone/tenancy.d.ts +42 -0
- package/dist/standalone/tenancy.js +241 -0
- package/dist/standalone/worker.d.ts +55 -0
- package/dist/standalone/worker.js +147 -0
- package/dist/standalone/workflow-authority-readers.d.ts +16 -0
- package/dist/standalone/workflow-authority-readers.js +35 -0
- package/dist/standalone/workflow-binding.d.ts +216 -0
- package/dist/standalone/workflow-binding.js +217 -0
- package/dist/standalone/workflow-events.d.ts +46 -0
- package/dist/standalone/workflow-events.js +39 -0
- package/dist/standalone/workflow-git-capture.d.ts +7 -0
- package/dist/standalone/workflow-git-capture.js +62 -0
- package/dist/standalone/workflow-host.d.ts +955 -0
- package/dist/standalone/workflow-host.js +778 -0
- package/dist/standalone/workflow-node-capture.d.ts +13 -0
- package/dist/standalone/workflow-node-capture.js +72 -0
- package/dist/standalone/workflow-root-authority.d.ts +27 -0
- package/dist/standalone/workflow-root-authority.js +87 -0
- package/dist/standalone/workflow-schedules.d.ts +52 -0
- package/dist/standalone/workflow-schedules.js +40 -0
- package/dist/standalone/workflow-trigger-host.d.ts +1716 -0
- package/dist/standalone/workflow-trigger-host.js +52 -0
- package/dist/standalone/workflows.d.ts +713 -0
- package/dist/standalone/workflows.js +554 -0
- package/dist/tenancy/auth0.d.ts +64 -0
- package/dist/tenancy/auth0.js +198 -0
- package/dist/tenancy/cloudflare/rate-limit.d.ts +8 -0
- package/dist/tenancy/cloudflare/rate-limit.js +19 -0
- package/dist/tenancy/cloudflare/registry.d.ts +50 -0
- package/dist/tenancy/cloudflare/registry.js +301 -0
- package/dist/tenancy/cloudflare.d.ts +2 -0
- package/dist/tenancy/cloudflare.js +2 -0
- package/dist/tenancy/cold-directory.d.ts +13 -0
- package/dist/tenancy/cold-directory.js +43 -0
- package/dist/tenancy/contracts.d.ts +195 -0
- package/dist/tenancy/contracts.js +1 -0
- package/dist/tenancy/index.d.ts +19 -0
- package/dist/tenancy/index.js +156 -0
- package/dist/tenancy/owner-key.d.ts +26 -0
- package/dist/tenancy/owner-key.js +63 -0
- package/dist/tenancy/oyster.d.ts +4 -0
- package/dist/tenancy/oyster.js +25 -0
- package/dist/tenancy/registry-record.d.ts +7 -0
- package/dist/tenancy/registry-record.js +44 -0
- package/dist/tenancy/registry.d.ts +27 -0
- package/dist/tenancy/registry.js +138 -0
- package/dist/tenancy/resolve.d.ts +7 -0
- package/dist/tenancy/resolve.js +64 -0
- package/dist/workers-git.js +1 -4
- package/dist/workers.d.ts +2 -2
- package/dist/workers.js +3 -1
- package/docs/api/build-contract.md +829 -32
- package/docs/api/build.md +253 -17
- package/docs/api/modules.md +40 -32
- package/docs/api/pack.md +6 -3
- package/docs/api/shell/app-directory.md +267 -0
- package/docs/api/shell/shell-module.md +64 -3
- package/docs/api/shell/workbench-directory.md +119 -0
- package/docs/api/shell/workbench.md +19 -0
- package/docs/api/shell.md +127 -0
- package/docs/api/tenancy/auth0.md +110 -0
- package/docs/api/tenancy/cloudflare.md +221 -0
- package/docs/api/tenancy/cold-directory.md +46 -0
- package/docs/api/tenancy/owner-key.md +63 -0
- package/docs/api/tenancy/registry.md +77 -0
- package/docs/api/tenancy/resolve.md +69 -0
- package/docs/api/tenancy.md +430 -0
- package/docs/api/workers-git.md +140 -21
- package/docs/api/workers.md +15 -2
- package/docs/site/agent-quickstart.mdx +52 -55
- package/examples/standalone/worker.ts +7 -14
- package/examples/standalone/wrangler.jsonc +67 -6
- package/package.json +78 -25
- package/dist/build/r2-artifact-writer.d.ts +0 -31
- package/dist/build/r2-artifact-writer.js +0 -179
- package/dist/source/template-write.js +0 -344
- package/docs/media/architecture.mmd +0 -32
- package/docs/media/architecture.png +0 -0
- package/docs/media/bonsai-network.png +0 -0
- package/docs/media/bonsai-repository.png +0 -0
- package/examples/git/worker.ts +0 -39
- package/examples/git/wrangler.jsonc +0 -32
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type WorkflowCeilingCapturePorts } from "@fungi.computer/braid/runtime";
|
|
2
|
+
import { type OysterHost } from "../host.js";
|
|
3
|
+
import type { StandaloneWorkflowAuthorityReaders } from "./workflow-authority-readers.js";
|
|
4
|
+
/** Live host readers and approvals; a compile catalog is deliberately not a grant port. */
|
|
5
|
+
export type StandaloneWorkflowNodeCaptureOptions = Readonly<{
|
|
6
|
+
host: Pick<OysterHost, "artifacts" | "computerId" | "capabilities"> & Readonly<{
|
|
7
|
+
auth: Pick<OysterHost["auth"], "resolve">;
|
|
8
|
+
installations: Pick<OysterHost["installations"], "get">;
|
|
9
|
+
}>;
|
|
10
|
+
readers: Pick<StandaloneWorkflowAuthorityReaders, "member" | "memberComputer">;
|
|
11
|
+
}>;
|
|
12
|
+
/** Capture one compiled node through the same metadata admission as ordinary App calls. */
|
|
13
|
+
export declare function createStandaloneWorkflowNodeCapture(options: StandaloneWorkflowNodeCaptureOptions): WorkflowCeilingCapturePorts["node"];
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import * as Schema from "effect/Schema";
|
|
2
|
+
import { appBackendBindingsSchema } from "@fungi.computer/puffball/app";
|
|
3
|
+
import { catalogMethods } from "@fungi.computer/braid";
|
|
4
|
+
import { workflowMethodRequiresComputer, } from "@fungi.computer/braid/runtime";
|
|
5
|
+
import { admitMethodMetadata, appInvocationCeiling, approvedBindingGrants, deriveDeclaredOperations, scopedBindings, } from "../apps/index.js";
|
|
6
|
+
import { oysterPrincipalIsOwner, verifyRelease, } from "../host.js";
|
|
7
|
+
import { invocationCallerContext } from "../invocation.js";
|
|
8
|
+
/** Capture one compiled node through the same metadata admission as ordinary App calls. */
|
|
9
|
+
export function createStandaloneWorkflowNodeCapture(options) {
|
|
10
|
+
const { host, readers } = options;
|
|
11
|
+
return async (actor, member, target) => {
|
|
12
|
+
const before = await readers.member(actor.memberId);
|
|
13
|
+
if (before?.membershipVersion !== member.membershipVersion ||
|
|
14
|
+
(target.computerId !== null &&
|
|
15
|
+
(target.computerId !== host.computerId ||
|
|
16
|
+
!(await readers.memberComputer(actor.memberId, target.computerId)))))
|
|
17
|
+
return null;
|
|
18
|
+
const principal = await host.auth.resolve(actor);
|
|
19
|
+
if (principal === null || !oysterPrincipalIsOwner(principal))
|
|
20
|
+
return null;
|
|
21
|
+
const caller = invocationCallerContext(principal, target.computerId);
|
|
22
|
+
if (caller.actor.kind !== "member" ||
|
|
23
|
+
caller.actor.memberId !== actor.memberId)
|
|
24
|
+
return null;
|
|
25
|
+
const installation = await host.installations.get(target.installationId);
|
|
26
|
+
if (installation?.status !== "enabled" ||
|
|
27
|
+
installation.appId !== target.appId)
|
|
28
|
+
return null;
|
|
29
|
+
const verified = await verifyRelease(host.artifacts, installation.release, undefined, false);
|
|
30
|
+
if (verified.declaration === null || verified.api === null)
|
|
31
|
+
return null;
|
|
32
|
+
const facts = {
|
|
33
|
+
principal: { actor: caller.actor, role: caller.role ?? null },
|
|
34
|
+
installation,
|
|
35
|
+
declaration: verified.declaration,
|
|
36
|
+
declaredOperations: await deriveDeclaredOperations(installation.release, verified.declaration, host.capabilities ?? []),
|
|
37
|
+
api: verified.api,
|
|
38
|
+
access: null,
|
|
39
|
+
};
|
|
40
|
+
const decision = admitMethodMetadata(facts, target.method);
|
|
41
|
+
if (decision.kind !== "admitted")
|
|
42
|
+
return null;
|
|
43
|
+
const { method } = decision;
|
|
44
|
+
if (method.effect !== "read" && method.effect !== "write")
|
|
45
|
+
return null;
|
|
46
|
+
if (target.computerId === null &&
|
|
47
|
+
workflowMethodRequiresComputer(method.requires))
|
|
48
|
+
return null;
|
|
49
|
+
const pinned = await catalogMethods({ [target.method]: method });
|
|
50
|
+
if (pinned.methods[target.method]?.digest !== target.methodDigest)
|
|
51
|
+
return null;
|
|
52
|
+
const bindings = Schema.decodeUnknownSync(appBackendBindingsSchema)(scopedBindings(approvedBindingGrants(installation, verified.declaration), method, appInvocationCeiling(facts)).map((binding) => ({
|
|
53
|
+
name: binding.binding,
|
|
54
|
+
operations: binding.operations,
|
|
55
|
+
})));
|
|
56
|
+
const latest = await host.installations.get(target.installationId);
|
|
57
|
+
const after = await readers.member(actor.memberId);
|
|
58
|
+
if (latest?.status !== "enabled" ||
|
|
59
|
+
latest.instanceId !== installation.instanceId ||
|
|
60
|
+
latest.authorityVersion !== installation.authorityVersion ||
|
|
61
|
+
latest.declaredOperationsDigest !==
|
|
62
|
+
installation.declaredOperationsDigest ||
|
|
63
|
+
latest.release.releaseId !== installation.release.releaseId ||
|
|
64
|
+
latest.release.artifact.artifactDigest !==
|
|
65
|
+
installation.release.artifact.artifactDigest ||
|
|
66
|
+
after?.membershipVersion !== member.membershipVersion ||
|
|
67
|
+
(target.computerId !== null &&
|
|
68
|
+
!(await readers.memberComputer(actor.memberId, target.computerId))))
|
|
69
|
+
return null;
|
|
70
|
+
return { level: method.effect, capabilities: method.requires, bindings };
|
|
71
|
+
};
|
|
72
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { type WorkflowRunAuthority } from "@fungi.computer/braid/runtime";
|
|
2
|
+
import type { RepositoryRef } from "@fungi.computer/agar/git";
|
|
3
|
+
import type { AppToolPolicy } from "../apps/index.js";
|
|
4
|
+
import { type LiveOysterKey } from "../keys.js";
|
|
5
|
+
import type { StandaloneTenancy } from "./tenancy.js";
|
|
6
|
+
import { type StandaloneWorkflowNodeCaptureOptions } from "./workflow-node-capture.js";
|
|
7
|
+
import type { StandaloneWorkflowAuthorityReaderOptions } from "./workflow-authority-readers.js";
|
|
8
|
+
/** The tenant, host and library a standalone Workflow root admits through. */
|
|
9
|
+
export type StandaloneWorkflowRootOptions = Readonly<{
|
|
10
|
+
shared: Pick<StandaloneTenancy, "tenantId" | "tenancy" | "owner">;
|
|
11
|
+
host: StandaloneWorkflowNodeCaptureOptions["host"];
|
|
12
|
+
libraryInstallationId: string;
|
|
13
|
+
catalogHost: StandaloneWorkflowAuthorityReaderOptions["catalogHost"];
|
|
14
|
+
}>;
|
|
15
|
+
/** One live kit registry and its configured canonical owner back every start. */
|
|
16
|
+
export declare function createStandaloneWorkflowRootAuthority(options: StandaloneWorkflowRootOptions & Readonly<{
|
|
17
|
+
/** The Agent's current App tool approval from its policy owner. */
|
|
18
|
+
agentToolPolicy(agentId: string): Promise<AppToolPolicy | null>;
|
|
19
|
+
/** A key's live Ear Tag facts, read fresh at every admission and recheck. */
|
|
20
|
+
keyLive(keyId: string): Promise<LiveOysterKey | null>;
|
|
21
|
+
}>): WorkflowRunAuthority;
|
|
22
|
+
/**
|
|
23
|
+
* The same live installation-Git admission the start ceiling captured, re-run
|
|
24
|
+
* for a Workflow Git pass: the member must still be the canonical owner and the
|
|
25
|
+
* repository's installation must still grant this Git scope.
|
|
26
|
+
*/
|
|
27
|
+
export declare function createStandaloneWorkflowGitAccess(options: StandaloneWorkflowRootOptions): (memberId: string, repository: RepositoryRef, scope: "read" | "write") => Promise<boolean>;
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { createWorkflowRunAuthority, createWorkflowCeilingCapture, planInstallationLevels, } from "@fungi.computer/braid/runtime";
|
|
2
|
+
import { AppInstanceError } from "../index.js";
|
|
3
|
+
import { keyAppAccess } from "../keys.js";
|
|
4
|
+
import { createStandaloneWorkflowAuthorityReaders } from "./workflow-authority-readers.js";
|
|
5
|
+
import { createStandaloneWorkflowNodeCapture, } from "./workflow-node-capture.js";
|
|
6
|
+
import { createStandaloneWorkflowInstallationGitCapture } from "./workflow-git-capture.js";
|
|
7
|
+
/** The configured canonical owner, re-read around the kit registry's member row. */
|
|
8
|
+
function rootReaders(options) {
|
|
9
|
+
const base = createStandaloneWorkflowAuthorityReaders({
|
|
10
|
+
tenantId: options.shared.tenantId,
|
|
11
|
+
tenancy: options.shared.tenancy,
|
|
12
|
+
host: options.host,
|
|
13
|
+
libraryInstallationId: options.libraryInstallationId,
|
|
14
|
+
catalogHost: options.catalogHost,
|
|
15
|
+
});
|
|
16
|
+
const member = async (id) => {
|
|
17
|
+
const before = await options.shared.owner();
|
|
18
|
+
if (before?.actor.memberId !== id)
|
|
19
|
+
return null;
|
|
20
|
+
const current = await base.member(id);
|
|
21
|
+
const after = await options.shared.owner();
|
|
22
|
+
return current !== null &&
|
|
23
|
+
after?.actor.memberId === id &&
|
|
24
|
+
before.membershipVersion === current.membershipVersion &&
|
|
25
|
+
after.membershipVersion === current.membershipVersion
|
|
26
|
+
? current
|
|
27
|
+
: null;
|
|
28
|
+
};
|
|
29
|
+
return { ...base, member };
|
|
30
|
+
}
|
|
31
|
+
/** One live kit registry and its configured canonical owner back every start. */
|
|
32
|
+
export function createStandaloneWorkflowRootAuthority(options) {
|
|
33
|
+
const readers = rootReaders(options);
|
|
34
|
+
return createWorkflowRunAuthority({
|
|
35
|
+
...readers,
|
|
36
|
+
// An Agent run stays admitted only while its grantor's write approval lasts.
|
|
37
|
+
async admitAgent(preparation) {
|
|
38
|
+
const policy = await options.agentToolPolicy(preparation.initiatedBy.agentId);
|
|
39
|
+
return (policy?.level === "write" &&
|
|
40
|
+
policy.grantedBy === preparation.initiatedBy.grantorMemberId);
|
|
41
|
+
},
|
|
42
|
+
// A key run stays admitted only while the key is still live, its owner
|
|
43
|
+
// still matches the run's grantor, `apps.use` still reaches this
|
|
44
|
+
// Workflows library, and every installation the plan touches is still
|
|
45
|
+
// inside the key's own grants -- a key never widens past what it was
|
|
46
|
+
// issued, even when the plan's binding set has not changed.
|
|
47
|
+
async admitKey(preparation) {
|
|
48
|
+
const key = await options.keyLive(preparation.initiatedBy.keyId);
|
|
49
|
+
if (key?.owner !== preparation.initiatedBy.grantorMemberId)
|
|
50
|
+
return false;
|
|
51
|
+
if (keyAppAccess(key, options.libraryInstallationId)?.level !== "write")
|
|
52
|
+
return false;
|
|
53
|
+
return planInstallationLevels(preparation.plan).every(({ installationId, level }) => {
|
|
54
|
+
const access = keyAppAccess(key, installationId);
|
|
55
|
+
return (access !== null && (level !== "write" || access.level === "write"));
|
|
56
|
+
});
|
|
57
|
+
},
|
|
58
|
+
capture: createWorkflowCeilingCapture({
|
|
59
|
+
node: createStandaloneWorkflowNodeCapture({
|
|
60
|
+
host: options.host,
|
|
61
|
+
readers,
|
|
62
|
+
}),
|
|
63
|
+
git: createStandaloneWorkflowInstallationGitCapture({
|
|
64
|
+
host: options.host,
|
|
65
|
+
readers,
|
|
66
|
+
}),
|
|
67
|
+
}),
|
|
68
|
+
denied: () => new AppInstanceError("revoked"),
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* The same live installation-Git admission the start ceiling captured, re-run
|
|
73
|
+
* for a Workflow Git pass: the member must still be the canonical owner and the
|
|
74
|
+
* repository's installation must still grant this Git scope.
|
|
75
|
+
*/
|
|
76
|
+
export function createStandaloneWorkflowGitAccess(options) {
|
|
77
|
+
const readers = rootReaders(options);
|
|
78
|
+
const capture = createStandaloneWorkflowInstallationGitCapture({
|
|
79
|
+
host: options.host,
|
|
80
|
+
readers,
|
|
81
|
+
});
|
|
82
|
+
return async (memberId, repository, scope) => {
|
|
83
|
+
const member = await readers.member(memberId);
|
|
84
|
+
return (member !== null &&
|
|
85
|
+
(await capture({ kind: "member", memberId }, member, repository, scope)));
|
|
86
|
+
};
|
|
87
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { createWorkflowSchedules, type WorkflowInstallationAuthority } from "@fungi.computer/braid/runtime";
|
|
2
|
+
import type { AppMethodCallContext } from "@fungi.computer/puffball/app-method-runtime";
|
|
3
|
+
import { type TriggerArmTarget, type ScheduleDisarmInput } from "./workflow-binding.js";
|
|
4
|
+
/** The host supplies this result only after reopening the protected invocation. */
|
|
5
|
+
export type TriggerAdmission = Readonly<{
|
|
6
|
+
libraryId: string;
|
|
7
|
+
context: AppMethodCallContext;
|
|
8
|
+
/** The admitted frame's selection and verified artifact, pinned on arm. */
|
|
9
|
+
installationAuthority: WorkflowInstallationAuthority;
|
|
10
|
+
}>;
|
|
11
|
+
export type ScheduleOperation = "armSchedule" | "disarmSchedule" | "schedules";
|
|
12
|
+
/** Host controls and alarm delivery compose the shared schedule owner unchanged. */
|
|
13
|
+
export declare function createStandaloneWorkflowSchedules<Frame>(options: Parameters<typeof createWorkflowSchedules>[0] & {
|
|
14
|
+
admitInvocation(frame: Frame, operation: ScheduleOperation): Promise<TriggerAdmission>;
|
|
15
|
+
}): {
|
|
16
|
+
nextWake: () => number | null;
|
|
17
|
+
/** Native DO alarm delivery enters the same shared occurrence/fire path. */
|
|
18
|
+
alarm: () => Promise<void>;
|
|
19
|
+
arm(frame: Frame, raw: TriggerArmTarget): Promise<{
|
|
20
|
+
kind: "armed";
|
|
21
|
+
scheduleId: string;
|
|
22
|
+
} | {
|
|
23
|
+
error: string;
|
|
24
|
+
kind?: undefined;
|
|
25
|
+
reason?: undefined;
|
|
26
|
+
} | {
|
|
27
|
+
kind: "refused";
|
|
28
|
+
reason: string;
|
|
29
|
+
readonly error?: undefined;
|
|
30
|
+
}>;
|
|
31
|
+
disarm(frame: Frame, raw: ScheduleDisarmInput): Promise<{
|
|
32
|
+
kind: "not_found";
|
|
33
|
+
} | {
|
|
34
|
+
kind: "disarmed";
|
|
35
|
+
} | {
|
|
36
|
+
error: string;
|
|
37
|
+
}>;
|
|
38
|
+
list(frame: Frame): Promise<{
|
|
39
|
+
error: string;
|
|
40
|
+
schedules?: undefined;
|
|
41
|
+
} | {
|
|
42
|
+
schedules: {
|
|
43
|
+
scheduleId: string;
|
|
44
|
+
workflowId: string;
|
|
45
|
+
triggerId: string;
|
|
46
|
+
nextDueAt: number;
|
|
47
|
+
state: "active" | "finished" | "paused" | "cancelled";
|
|
48
|
+
lastError: string | null;
|
|
49
|
+
}[];
|
|
50
|
+
readonly error?: undefined;
|
|
51
|
+
}>;
|
|
52
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { createWorkflowSchedules, } from "@fungi.computer/braid/runtime";
|
|
2
|
+
import { scheduleDisarmInputSchema } from "@fungi.computer/workflows-wire";
|
|
3
|
+
import { triggerArmTargetSchema, } from "./workflow-binding.js";
|
|
4
|
+
/** Host controls and alarm delivery compose the shared schedule owner unchanged. */
|
|
5
|
+
export function createStandaloneWorkflowSchedules(options) {
|
|
6
|
+
const schedules = createWorkflowSchedules(options);
|
|
7
|
+
return {
|
|
8
|
+
nextWake: schedules.nextWake,
|
|
9
|
+
/** Native DO alarm delivery enters the same shared occurrence/fire path. */
|
|
10
|
+
alarm: () => schedules.fireDue(),
|
|
11
|
+
async arm(frame, raw) {
|
|
12
|
+
const target = triggerArmTargetSchema.parse(raw);
|
|
13
|
+
const { libraryId, context, installationAuthority } = await options.admitInvocation(frame, "armSchedule");
|
|
14
|
+
if (context.actor.kind !== "member")
|
|
15
|
+
return { error: "not_granted" };
|
|
16
|
+
if (context.computerId === null)
|
|
17
|
+
return {
|
|
18
|
+
kind: "refused",
|
|
19
|
+
reason: "Select a Computer before arming a schedule",
|
|
20
|
+
};
|
|
21
|
+
// Standalone has no Agent arming source: the owner arms as themself.
|
|
22
|
+
return schedules.arm({ armedBy: context.actor.memberId, armedVia: null }, { ...target, installationId: libraryId }, context.computerId, installationAuthority);
|
|
23
|
+
},
|
|
24
|
+
async disarm(frame, raw) {
|
|
25
|
+
const { scheduleId } = scheduleDisarmInputSchema.parse(raw);
|
|
26
|
+
const { libraryId, context } = await options.admitInvocation(frame, "disarmSchedule");
|
|
27
|
+
if (context.actor.kind !== "member")
|
|
28
|
+
return { error: "not_granted" };
|
|
29
|
+
return schedules.disarm({ kind: "member", memberId: context.actor.memberId }, libraryId, scheduleId);
|
|
30
|
+
},
|
|
31
|
+
async list(frame) {
|
|
32
|
+
const { libraryId, context } = await options.admitInvocation(frame, "schedules");
|
|
33
|
+
if (context.actor.kind !== "member")
|
|
34
|
+
return { error: "not_granted" };
|
|
35
|
+
return {
|
|
36
|
+
schedules: await schedules.list({ kind: "member", memberId: context.actor.memberId }, libraryId),
|
|
37
|
+
};
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
}
|