@fungi.computer/braid 0.0.0-stage → 0.0.1

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 (81) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +260 -2
  3. package/dist/cloudflare/graph-engine.d.ts +25 -0
  4. package/dist/cloudflare/graph-engine.js +46 -0
  5. package/dist/cloudflare/graph-program.d.ts +6 -0
  6. package/dist/cloudflare/graph-program.js +528 -0
  7. package/dist/cloudflare/index.d.ts +1 -0
  8. package/dist/cloudflare/index.js +1 -0
  9. package/dist/code-execution.d.ts +16 -0
  10. package/dist/code-execution.js +50 -0
  11. package/dist/code-result.d.ts +11 -0
  12. package/dist/code-result.js +19 -0
  13. package/dist/code-sandbox.d.ts +9 -0
  14. package/dist/code-sandbox.js +13 -0
  15. package/dist/compile.d.ts +41 -0
  16. package/dist/compile.js +240 -0
  17. package/dist/execution.d.ts +113 -0
  18. package/dist/execution.js +108 -0
  19. package/dist/expressions.d.ts +16 -0
  20. package/dist/expressions.js +322 -0
  21. package/dist/git-access.d.ts +59 -0
  22. package/dist/git-access.js +106 -0
  23. package/dist/index.d.ts +8 -0
  24. package/dist/index.js +8 -0
  25. package/dist/ports.d.ts +58 -0
  26. package/dist/ports.js +256 -0
  27. package/dist/runtime/arming-authority.d.ts +83 -0
  28. package/dist/runtime/arming-authority.js +90 -0
  29. package/dist/runtime/bindings.d.ts +25 -0
  30. package/dist/runtime/bindings.js +41 -0
  31. package/dist/runtime/capture.d.ts +18 -0
  32. package/dist/runtime/capture.js +99 -0
  33. package/dist/runtime/git-pass-contract.d.ts +63 -0
  34. package/dist/runtime/git-pass-contract.js +36 -0
  35. package/dist/runtime/git-pass-store.d.ts +32 -0
  36. package/dist/runtime/git-pass-store.js +82 -0
  37. package/dist/runtime/git-pass.d.ts +41 -0
  38. package/dist/runtime/git-pass.js +72 -0
  39. package/dist/runtime/graph-contract.d.ts +784 -0
  40. package/dist/runtime/graph-contract.js +122 -0
  41. package/dist/runtime/graph-dispatch.d.ts +31 -0
  42. package/dist/runtime/graph-dispatch.js +111 -0
  43. package/dist/runtime/graph-run-store.d.ts +73 -0
  44. package/dist/runtime/graph-run-store.js +267 -0
  45. package/dist/runtime/graph-start.d.ts +90 -0
  46. package/dist/runtime/graph-start.js +124 -0
  47. package/dist/runtime/index.d.ts +20 -0
  48. package/dist/runtime/index.js +20 -0
  49. package/dist/runtime/ports.d.ts +153 -0
  50. package/dist/runtime/ports.js +1 -0
  51. package/dist/runtime/run-authority.d.ts +60 -0
  52. package/dist/runtime/run-authority.js +145 -0
  53. package/dist/runtime/run-ceiling.d.ts +106 -0
  54. package/dist/runtime/run-ceiling.js +23 -0
  55. package/dist/runtime/run-member.d.ts +3 -0
  56. package/dist/runtime/run-member.js +12 -0
  57. package/dist/runtime/run-owner.d.ts +1628 -0
  58. package/dist/runtime/run-owner.js +513 -0
  59. package/dist/runtime/run-report.d.ts +14 -0
  60. package/dist/runtime/run-report.js +23 -0
  61. package/dist/runtime/schedules.d.ts +39 -0
  62. package/dist/runtime/schedules.js +149 -0
  63. package/dist/runtime/stored-trigger-rows.d.ts +12 -0
  64. package/dist/runtime/stored-trigger-rows.js +17 -0
  65. package/dist/runtime/trigger-owner.d.ts +590 -0
  66. package/dist/runtime/trigger-owner.js +280 -0
  67. package/dist/runtime/trigger-plans.d.ts +766 -0
  68. package/dist/runtime/trigger-plans.js +62 -0
  69. package/dist/runtime/trigger-router.d.ts +34 -0
  70. package/dist/runtime/trigger-router.js +143 -0
  71. package/dist/runtime/trigger-run-reservations.d.ts +28 -0
  72. package/dist/runtime/trigger-run-reservations.js +114 -0
  73. package/dist/schema.d.ts +1111 -0
  74. package/dist/schema.js +227 -0
  75. package/dist/triggers.d.ts +46 -0
  76. package/dist/triggers.js +75 -0
  77. package/dist/validate.d.ts +25 -0
  78. package/dist/validate.js +679 -0
  79. package/dist/workflow-id.d.ts +3 -0
  80. package/dist/workflow-id.js +3 -0
  81. package/package.json +95 -3
@@ -0,0 +1,63 @@
1
+ import { z } from "zod";
2
+ import type { GitWriter } from "@fungi.computer/agar/git";
3
+ /** Non-secret Team-issued authority for one stored Workflow step on its pinned Computer. */
4
+ export declare const workflowGitPassGrantSchema: z.ZodObject<{
5
+ teamId: z.ZodString;
6
+ computerId: z.ZodString;
7
+ memberId: z.ZodString;
8
+ workflow: import("zod/mini").ZodMiniObject<{
9
+ workflowId: import("zod/mini").ZodMiniString<string>;
10
+ runId: import("zod/mini").ZodMiniString<string>;
11
+ nodeId: import("zod/mini").ZodMiniString<string>;
12
+ armedBy: import("zod/mini").ZodMiniDiscriminatedUnion<[import("zod/mini").ZodMiniObject<{
13
+ kind: import("zod/mini").ZodMiniLiteral<"member">;
14
+ memberId: import("zod/mini").ZodMiniString<string>;
15
+ }, z.core.$strip>, import("zod/mini").ZodMiniObject<{
16
+ kind: import("zod/mini").ZodMiniLiteral<"agent">;
17
+ agentId: import("zod/mini").ZodMiniString<string>;
18
+ }, z.core.$strip>, import("zod/mini").ZodMiniObject<{
19
+ kind: import("zod/mini").ZodMiniLiteral<"team-key">;
20
+ keyId: import("zod/mini").ZodMiniString<string>;
21
+ }, z.core.$strip>], "kind">;
22
+ }, z.core.$strip>;
23
+ runId: z.ZodString;
24
+ graphId: z.ZodOptional<z.ZodString>;
25
+ nodeId: z.ZodString;
26
+ iteration: z.ZodString;
27
+ scope: z.ZodEnum<{
28
+ write: "write";
29
+ read: "read";
30
+ }>;
31
+ gitAccess: z.ZodUnion<readonly [z.ZodObject<{
32
+ repository: z.ZodDiscriminatedUnion<[z.ZodObject<{
33
+ kind: z.ZodLiteral<"installation">;
34
+ installationId: z.ZodString;
35
+ repository: z.ZodString;
36
+ }, z.core.$strict>, z.ZodObject<{
37
+ kind: z.ZodLiteral<"team-source">;
38
+ repository: z.ZodString;
39
+ }, z.core.$strict>], "kind">;
40
+ branches: z.ZodArray<z.ZodString>;
41
+ }, z.core.$strict>, z.ZodObject<{
42
+ repository: z.ZodDiscriminatedUnion<[z.ZodObject<{
43
+ kind: z.ZodLiteral<"installation">;
44
+ installationId: z.ZodString;
45
+ repository: z.ZodString;
46
+ }, z.core.$strict>, z.ZodObject<{
47
+ kind: z.ZodLiteral<"team-source">;
48
+ repository: z.ZodString;
49
+ }, z.core.$strict>], "kind">;
50
+ access: z.ZodLiteral<"read">;
51
+ }, z.core.$strict>]>;
52
+ triggerId: z.ZodOptional<z.ZodString>;
53
+ }, z.core.$strict>;
54
+ export type WorkflowGitPassGrant = z.output<typeof workflowGitPassGrantSchema>;
55
+ /** Run cleanup remains valid after the Computer's product data is erased. */
56
+ export declare const workflowGitPassRevocationSchema: z.ZodObject<{
57
+ teamId: z.ZodString;
58
+ runId: z.ZodString;
59
+ }, z.core.$strict>;
60
+ /** Project a stored, admitted pass; projection alone never admits authority. */
61
+ export declare function workflowGitPassWriter(grant: WorkflowGitPassGrant): Extract<GitWriter, {
62
+ kind: "actor";
63
+ }>;
@@ -0,0 +1,36 @@
1
+ import { z } from "zod";
2
+ import { workflowAttestationSchema } from "@fungi.computer/puffball/app-method-runtime";
3
+ import { gitAccessScope, resolvedGitAccessSchema } from "../git-access.js";
4
+ const id = z.string().min(1).max(256);
5
+ /** Non-secret Team-issued authority for one stored Workflow step on its pinned Computer. */
6
+ export const workflowGitPassGrantSchema = z
7
+ .strictObject({
8
+ teamId: id,
9
+ computerId: id,
10
+ memberId: id,
11
+ workflow: workflowAttestationSchema,
12
+ runId: id,
13
+ graphId: id.optional(),
14
+ nodeId: id,
15
+ iteration: id,
16
+ scope: z.enum(["read", "write"]),
17
+ gitAccess: resolvedGitAccessSchema,
18
+ triggerId: id.optional(),
19
+ })
20
+ .refine((grant) => grant.scope === gitAccessScope(grant.gitAccess), "A pass's scope is its admitted Git access");
21
+ /** Run cleanup remains valid after the Computer's product data is erased. */
22
+ export const workflowGitPassRevocationSchema = z.strictObject({
23
+ teamId: id,
24
+ runId: id,
25
+ });
26
+ /** Project a stored, admitted pass; projection alone never admits authority. */
27
+ export function workflowGitPassWriter(grant) {
28
+ // A read pass never writes, so it carries no branch ceiling to attest.
29
+ return "branches" in grant.gitAccess
30
+ ? {
31
+ kind: "actor",
32
+ actor: grant.workflow.armedBy,
33
+ workflow: { ...grant.workflow, branches: grant.gitAccess.branches },
34
+ }
35
+ : { kind: "actor", actor: grant.workflow.armedBy };
36
+ }
@@ -0,0 +1,32 @@
1
+ import { type RepositoryRef } from "@fungi.computer/agar/git";
2
+ import type { WatchdogSqliteOwner } from "@fungi.computer/watchdog";
3
+ import { type WorkflowGitPassGrant } from "./git-pass-contract.js";
4
+ /** Scoped pass state shared by both Computer hosts; credentials never enter the grant. */
5
+ export declare function createWorkflowGitPassStore(options: Readonly<{
6
+ sql: WatchdogSqliteOwner["sql"];
7
+ transactionSync: <T>(body: () => T) => T;
8
+ ownerId(): string | null;
9
+ computerId(): string | undefined;
10
+ mintPassId(): string;
11
+ remote(passId: string, repository: RepositoryRef): string;
12
+ denied(): Error;
13
+ now?: () => number;
14
+ }>): Readonly<{
15
+ openWorkflowPass(raw: WorkflowGitPassGrant): {
16
+ passId: string;
17
+ remote: string;
18
+ expiresAt: number;
19
+ bearer?: string;
20
+ };
21
+ /** Retain a tombstone so a delayed mint cannot resurrect a terminal run. */
22
+ revokeWorkflowRunGitPasses(input: {
23
+ teamId: string;
24
+ runId: string;
25
+ }): void;
26
+ /** Re-admit the stored scope for every Git request; an expired pass grants nothing. */
27
+ admit(input: {
28
+ passId: string;
29
+ repository: RepositoryRef;
30
+ scope: "read" | "write";
31
+ }): WorkflowGitPassGrant | null;
32
+ }>;
@@ -0,0 +1,82 @@
1
+ import { repositoryRefsEqual, } from "@fungi.computer/agar/git";
2
+ import { workflowGitPassResultSchema } from "@fungi.computer/agar/computer-contracts";
3
+ import { z } from "zod";
4
+ import { workflowGitPassGrantSchema, workflowGitPassRevocationSchema, } from "./git-pass-contract.js";
5
+ /** Scoped pass state shared by both Computer hosts; credentials never enter the grant. */
6
+ export function createWorkflowGitPassStore(options) {
7
+ const { sql } = options;
8
+ const now = options.now ?? Date.now;
9
+ sql.exec(`CREATE TABLE IF NOT EXISTS computer_workflow_git_passes (
10
+ pass_id TEXT PRIMARY KEY, node_key TEXT NOT NULL UNIQUE, run_id TEXT NOT NULL,
11
+ grant_json TEXT NOT NULL, expires_at INTEGER NOT NULL
12
+ ) STRICT`);
13
+ sql.exec(`CREATE TABLE IF NOT EXISTS computer_workflow_git_ended_runs (run_id TEXT PRIMARY KEY) STRICT`);
14
+ return Object.freeze({
15
+ openWorkflowPass(raw) {
16
+ const grant = workflowGitPassGrantSchema.parse(raw);
17
+ if (grant.teamId !== options.ownerId() ||
18
+ grant.computerId !== options.computerId() ||
19
+ grant.workflow.runId !== grant.runId ||
20
+ grant.workflow.nodeId !== grant.nodeId ||
21
+ grant.workflow.armedBy.kind !== "member" ||
22
+ grant.workflow.armedBy.memberId !== grant.memberId)
23
+ throw options.denied();
24
+ return options.transactionSync(() => {
25
+ if (sql
26
+ .exec("SELECT 1 FROM computer_workflow_git_ended_runs WHERE run_id=?", grant.runId)
27
+ .toArray().length > 0)
28
+ throw options.denied();
29
+ const key = JSON.stringify([
30
+ grant.runId,
31
+ grant.graphId ?? null,
32
+ grant.nodeId,
33
+ grant.iteration,
34
+ grant.scope,
35
+ ]);
36
+ const row = sql
37
+ .exec("SELECT pass_id,grant_json,expires_at FROM computer_workflow_git_passes WHERE node_key=?", key)
38
+ .toArray()[0];
39
+ if (row !== undefined &&
40
+ z.string().parse(row.grant_json) !== JSON.stringify(grant))
41
+ throw options.denied();
42
+ const passId = row === undefined
43
+ ? options.mintPassId()
44
+ : z.string().parse(row.pass_id);
45
+ const expiresAt = now() + 60_000;
46
+ sql.exec(`INSERT INTO computer_workflow_git_passes VALUES(?,?,?,?,?)
47
+ ON CONFLICT(node_key) DO UPDATE SET expires_at=excluded.expires_at`, passId, key, grant.runId, JSON.stringify(grant), expiresAt);
48
+ return workflowGitPassResultSchema.parse({
49
+ passId,
50
+ expiresAt,
51
+ remote: options.remote(passId, grant.gitAccess.repository),
52
+ });
53
+ });
54
+ },
55
+ /** Retain a tombstone so a delayed mint cannot resurrect a terminal run. */
56
+ revokeWorkflowRunGitPasses(input) {
57
+ const { teamId, runId } = workflowGitPassRevocationSchema.parse(input);
58
+ if (teamId !== options.ownerId())
59
+ throw options.denied();
60
+ options.transactionSync(() => {
61
+ sql.exec("INSERT OR IGNORE INTO computer_workflow_git_ended_runs VALUES(?)", runId);
62
+ sql.exec("DELETE FROM computer_workflow_git_passes WHERE run_id=?", runId);
63
+ });
64
+ },
65
+ /** Re-admit the stored scope for every Git request; an expired pass grants nothing. */
66
+ admit(input) {
67
+ const at = now();
68
+ const row = sql
69
+ .exec("SELECT grant_json,expires_at FROM computer_workflow_git_passes WHERE pass_id=?", input.passId)
70
+ .toArray()[0];
71
+ if (row === undefined || z.number().parse(row.expires_at) <= at)
72
+ return null;
73
+ const grant = workflowGitPassGrantSchema.parse(JSON.parse(z.string().parse(row.grant_json)));
74
+ if (grant.teamId !== options.ownerId() ||
75
+ grant.computerId !== options.computerId() ||
76
+ !repositoryRefsEqual(grant.gitAccess.repository, input.repository) ||
77
+ (input.scope === "write" && grant.scope !== "write"))
78
+ return null;
79
+ return grant;
80
+ },
81
+ });
82
+ }
@@ -0,0 +1,41 @@
1
+ import type { RepositoryRef } from "@fungi.computer/agar/git";
2
+ import { type WorkflowGitPassResult } from "@fungi.computer/agar/computer-contracts";
3
+ import type { WorkflowRunContext } from "./graph-contract.js";
4
+ import type { StoredWorkflowNode } from "./graph-dispatch.js";
5
+ import { type WorkflowGitPassGrant } from "./git-pass-contract.js";
6
+ /** Admitted stored-node identity; host resource admission remains outside the shared owner. */
7
+ export type WorkflowGitPassNode = Pick<StoredWorkflowNode, "installationId" | "appId" | "method" | "methodDigest" | "computerId"> & {
8
+ teamId: string;
9
+ memberId: string;
10
+ context: WorkflowRunContext;
11
+ };
12
+ export type WorkflowGitScope = WorkflowGitPassGrant["scope"];
13
+ /** Compose the sole stored-node admission and retained terminal cleanup owners. */
14
+ export declare function createWorkflowGitPasses(ports: Readonly<{
15
+ /** The host preserves its own typed refusal vocabulary. */
16
+ denied(): Error;
17
+ admitNode(context: WorkflowRunContext, target: {
18
+ installationId: string;
19
+ method: string;
20
+ }): WorkflowGitPassNode | null;
21
+ admitMemberGitAccess(memberId: string, repository: RepositoryRef, scope: WorkflowGitScope): Promise<void>;
22
+ open(computerId: string, grant: WorkflowGitPassGrant): Promise<WorkflowGitPassResult>;
23
+ revoke(computerId: string, input: {
24
+ teamId: string;
25
+ runId: string;
26
+ }): Promise<void>;
27
+ pending(): {
28
+ teamId: string;
29
+ runId: string;
30
+ computerId: string;
31
+ }[];
32
+ acknowledge(runId: string): void;
33
+ wake?(due: number | null): Promise<void>;
34
+ }>): {
35
+ cleanupWake: () => number | null;
36
+ openWorkflowPass(context: WorkflowRunContext, target: {
37
+ installationId: string;
38
+ method: string;
39
+ }): Promise<WorkflowGitPassResult>;
40
+ deliverCleanups(): Promise<void>;
41
+ };
@@ -0,0 +1,72 @@
1
+ import { workflowGitPassResultSchema, } from "@fungi.computer/agar/computer-contracts";
2
+ import { canonicalJson } from "../index.js";
3
+ import { gitAccessScope } from "../git-access.js";
4
+ import { workflowGitPassGrantSchema, } from "./git-pass-contract.js";
5
+ /** Compose the sole stored-node admission and retained terminal cleanup owners. */
6
+ export function createWorkflowGitPasses(ports) {
7
+ const cleanupWake = () => ports.pending().length === 0 ? null : Date.now() + 5000;
8
+ return {
9
+ cleanupWake,
10
+ async openWorkflowPass(context, target) {
11
+ const admitted = ports.admitNode(context, target);
12
+ if (admitted?.computerId == null ||
13
+ admitted.context.gitAccess === undefined ||
14
+ admitted.context.armedBy.kind !== "member" ||
15
+ admitted.context.armedBy.memberId !== admitted.memberId)
16
+ throw ports.denied();
17
+ // The admitted node's own access decides the scope; no caller widens it.
18
+ const scope = gitAccessScope(admitted.context.gitAccess);
19
+ const grant = workflowGitPassGrantSchema.parse({
20
+ teamId: admitted.teamId,
21
+ computerId: admitted.computerId,
22
+ memberId: admitted.memberId,
23
+ workflow: {
24
+ workflowId: admitted.context.workflowId,
25
+ runId: admitted.context.runId,
26
+ nodeId: admitted.context.nodeId,
27
+ armedBy: admitted.context.armedBy,
28
+ },
29
+ runId: admitted.context.runId,
30
+ graphId: admitted.context.graphId,
31
+ nodeId: admitted.context.nodeId,
32
+ iteration: admitted.context.iteration,
33
+ scope,
34
+ gitAccess: admitted.context.gitAccess,
35
+ triggerId: admitted.context.trigger?.triggerId,
36
+ });
37
+ await ports.admitMemberGitAccess(grant.memberId, grant.gitAccess.repository, scope);
38
+ if (canonicalJson(ports.admitNode(context, target)) !==
39
+ canonicalJson(admitted))
40
+ throw ports.denied();
41
+ const pass = workflowGitPassResultSchema.parse(await ports.open(grant.computerId, grant));
42
+ try {
43
+ await ports.admitMemberGitAccess(grant.memberId, grant.gitAccess.repository, scope);
44
+ if (canonicalJson(ports.admitNode(context, target)) !==
45
+ canonicalJson(admitted))
46
+ throw ports.denied();
47
+ }
48
+ catch (cause) {
49
+ await ports.revoke(grant.computerId, {
50
+ teamId: grant.teamId,
51
+ runId: grant.runId,
52
+ });
53
+ throw cause;
54
+ }
55
+ return pass;
56
+ },
57
+ async deliverCleanups() {
58
+ try {
59
+ for (const cleanup of ports.pending()) {
60
+ await ports.revoke(cleanup.computerId, {
61
+ teamId: cleanup.teamId,
62
+ runId: cleanup.runId,
63
+ });
64
+ ports.acknowledge(cleanup.runId);
65
+ }
66
+ }
67
+ finally {
68
+ await ports.wake?.(cleanupWake());
69
+ }
70
+ },
71
+ };
72
+ }