@fungi.computer/braid 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/dist/cloudflare/graph-program.js +29 -7
- package/dist/code-execution.js +2 -0
- package/dist/compile.d.ts +2 -0
- package/dist/compile.js +9 -1
- package/dist/execution.d.ts +81 -0
- package/dist/execution.js +22 -0
- package/dist/git-access.d.ts +15 -15
- package/dist/git-access.js +8 -8
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/review-text.d.ts +15 -0
- package/dist/review-text.js +40 -0
- package/dist/runtime/arming-authority.d.ts +5 -5
- package/dist/runtime/arming-authority.js +5 -5
- package/dist/runtime/git-pass-contract.d.ts +30 -9
- package/dist/runtime/git-pass-contract.js +26 -2
- package/dist/runtime/git-pass-store.d.ts +16 -13
- package/dist/runtime/git-pass-store.js +126 -50
- package/dist/runtime/git-pass.d.ts +3 -4
- package/dist/runtime/git-pass.js +3 -17
- package/dist/runtime/graph-contract.d.ts +5 -1
- package/dist/runtime/graph-run-store.d.ts +6 -0
- package/dist/runtime/ports.d.ts +11 -1
- package/dist/runtime/run-ceiling.d.ts +1 -1
- package/dist/runtime/run-owner.d.ts +89 -4
- package/dist/runtime/run-owner.js +27 -12
- package/dist/runtime/schedules.d.ts +1 -0
- package/dist/runtime/schedules.js +1 -0
- package/dist/runtime/trigger-owner.d.ts +10 -6
- package/dist/runtime/trigger-owner.js +2 -2
- package/dist/runtime/trigger-plans.d.ts +8 -0
- package/dist/runtime/trigger-run-reservations.d.ts +67 -3
- package/dist/runtime/trigger-run-reservations.js +126 -7
- package/dist/schema.d.ts +80 -8
- package/dist/schema.js +14 -2
- package/dist/validate.js +7 -0
- package/dist/workflow-id.d.ts +1 -1
- package/dist/workflow-id.js +1 -1
- package/package.json +8 -7
|
@@ -103,6 +103,9 @@ export async function runGraphWorkflow(step, params, instanceId, workflows, disp
|
|
|
103
103
|
throw new NonRetryableError("invalid_input");
|
|
104
104
|
const input = admitted.input;
|
|
105
105
|
const iteration = checked.kind === "run" ? "0" : checked.iteration;
|
|
106
|
+
const position = checked.kind === "run" ? [] : checked.position;
|
|
107
|
+
/** Steps inside a loop say which item or round they belong to. */
|
|
108
|
+
const placed = (record) => position.length === 0 ? record : { ...record, position };
|
|
106
109
|
const values = {};
|
|
107
110
|
values.$input = input;
|
|
108
111
|
const baseEvent = (node, state, attempt = 0, poll) => {
|
|
@@ -116,20 +119,21 @@ export async function runGraphWorkflow(step, params, instanceId, workflows, disp
|
|
|
116
119
|
if (poll !== undefined)
|
|
117
120
|
event = { ...event, poll };
|
|
118
121
|
return checked.kind === "map-item"
|
|
119
|
-
? { ...event, graphId: checked.body }
|
|
122
|
+
? placed({ ...event, graphId: checked.body })
|
|
120
123
|
: event;
|
|
121
124
|
};
|
|
122
|
-
const complete = async (node, output, attempt = 0, costMilliunits = 0, durationMs = 0, poll) => {
|
|
125
|
+
const complete = async (node, output, attempt = 0, costMilliunits = 0, durationMs = 0, poll, row) => {
|
|
123
126
|
const ports = { ...output, after: true };
|
|
124
127
|
if (exceedsResultLimit(ports))
|
|
125
128
|
return fail(node, "step_result_limit", undefined, poll);
|
|
126
|
-
|
|
129
|
+
const event = {
|
|
127
130
|
...baseEvent(node, "complete", attempt, poll),
|
|
128
131
|
costMilliunits,
|
|
129
132
|
durationMs,
|
|
130
133
|
outputDigest: await contentDigest(ports),
|
|
131
134
|
output,
|
|
132
|
-
}
|
|
135
|
+
};
|
|
136
|
+
await dispatcher.recordEvent(row === undefined ? event : { ...event, row });
|
|
133
137
|
return ports;
|
|
134
138
|
};
|
|
135
139
|
const fail = async (node, code, cause, poll) => {
|
|
@@ -289,7 +293,7 @@ export async function runGraphWorkflow(step, params, instanceId, workflows, disp
|
|
|
289
293
|
if (ports === undefined)
|
|
290
294
|
return fail(node, "invalid_output", undefined, poll);
|
|
291
295
|
return {
|
|
292
|
-
ports: await complete(node, ports, ctx.attempt, result.costMilliunits, Date.now() - start, poll),
|
|
296
|
+
ports: await complete(node, ports, ctx.attempt, result.costMilliunits, Date.now() - start, poll, result.row),
|
|
293
297
|
};
|
|
294
298
|
});
|
|
295
299
|
// A pending App call is polled with the same idempotency key, within
|
|
@@ -331,7 +335,7 @@ export async function runGraphWorkflow(step, params, instanceId, workflows, disp
|
|
|
331
335
|
const eventType = "review_" +
|
|
332
336
|
(await contentDigest([instanceId, node.id, iteration])).slice(7);
|
|
333
337
|
const expiresAt = await step.do(name + ":pending", async () => {
|
|
334
|
-
const deadline = await dispatcher.openReview({
|
|
338
|
+
const deadline = await dispatcher.openReview(placed({
|
|
335
339
|
runId: checked.runId,
|
|
336
340
|
nodeId: node.id,
|
|
337
341
|
iteration,
|
|
@@ -342,7 +346,7 @@ export async function runGraphWorkflow(step, params, instanceId, workflows, disp
|
|
|
342
346
|
timeout: node.timeout,
|
|
343
347
|
show: node.show.map((ref) => readPort(values, ref) ?? null),
|
|
344
348
|
...reviewChoices(node),
|
|
345
|
-
});
|
|
349
|
+
}));
|
|
346
350
|
await dispatcher.recordEvent(baseEvent(node, "waiting"));
|
|
347
351
|
return deadline;
|
|
348
352
|
});
|
|
@@ -404,6 +408,15 @@ export async function runGraphWorkflow(step, params, instanceId, workflows, disp
|
|
|
404
408
|
parentInstanceId: instanceId,
|
|
405
409
|
completionEvent: eventType,
|
|
406
410
|
input: { item, round, previous },
|
|
411
|
+
position: [
|
|
412
|
+
...position,
|
|
413
|
+
{
|
|
414
|
+
node: node.id,
|
|
415
|
+
loop: "repeat",
|
|
416
|
+
index: round,
|
|
417
|
+
count: node.maxRounds,
|
|
418
|
+
},
|
|
419
|
+
],
|
|
407
420
|
};
|
|
408
421
|
try {
|
|
409
422
|
await workflows.create({ id: childId, params });
|
|
@@ -467,6 +480,15 @@ export async function runGraphWorkflow(step, params, instanceId, workflows, disp
|
|
|
467
480
|
itemIndex,
|
|
468
481
|
])).slice(7),
|
|
469
482
|
input: { item, index: itemIndex },
|
|
483
|
+
position: [
|
|
484
|
+
...position,
|
|
485
|
+
{
|
|
486
|
+
node: node.id,
|
|
487
|
+
loop: "map",
|
|
488
|
+
index: itemIndex,
|
|
489
|
+
count: list.length,
|
|
490
|
+
},
|
|
491
|
+
],
|
|
470
492
|
};
|
|
471
493
|
try {
|
|
472
494
|
await workflows.create({ id: childId, params: childParams });
|
package/dist/code-execution.js
CHANGED
package/dist/compile.d.ts
CHANGED
|
@@ -19,6 +19,8 @@ export type DeclaredMethod = Readonly<{
|
|
|
19
19
|
pending?: true | undefined;
|
|
20
20
|
/** Discovery-only metadata from the App declaration; hidden methods have no graph ports. */
|
|
21
21
|
hidden?: true | undefined;
|
|
22
|
+
/** Presentation is display metadata and never part of a method's graph contract. */
|
|
23
|
+
present?: object | undefined;
|
|
22
24
|
description: string;
|
|
23
25
|
input: object;
|
|
24
26
|
output: object;
|
package/dist/compile.js
CHANGED
|
@@ -190,6 +190,12 @@ function completionView(method) {
|
|
|
190
190
|
})(output);
|
|
191
191
|
return Option.isSome(union) ? schemaView(union.value.anyOf[0]) : output;
|
|
192
192
|
}
|
|
193
|
+
/** The graph-facing contract of one method: no discovery or display metadata. */
|
|
194
|
+
function graphContract(method) {
|
|
195
|
+
const { description, input, output, effect, requires } = method;
|
|
196
|
+
const contract = { description, input, output, effect, requires };
|
|
197
|
+
return method.pending === true ? { ...contract, pending: true } : contract;
|
|
198
|
+
}
|
|
193
199
|
/**
|
|
194
200
|
* The catalog entry for one App's declared methods. The digest covers what a
|
|
195
201
|
* node depends on (ports, effect, approval, requires), not the description, so
|
|
@@ -198,7 +204,9 @@ function completionView(method) {
|
|
|
198
204
|
*/
|
|
199
205
|
export async function catalogMethods(methods) {
|
|
200
206
|
const entries = await Promise.all(Object.entries(methods)
|
|
201
|
-
.flatMap(([name,
|
|
207
|
+
.flatMap(([name, declared]) => declared.hidden === true
|
|
208
|
+
? []
|
|
209
|
+
: [[name, graphContract(declared)]])
|
|
202
210
|
.map(async ([name, declared]) => {
|
|
203
211
|
const method = S.decodeUnknownSync(methodSchema)({
|
|
204
212
|
...declared,
|
package/dist/execution.d.ts
CHANGED
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
import * as S from "effect/Schema";
|
|
2
2
|
import type { ExecutionPlan, Json, Method } from "./schema.js";
|
|
3
|
+
/**
|
|
4
|
+
* One loop a step runs inside: which Map item or Repeat round this pass is,
|
|
5
|
+
* counted from 0, of how many. The runtime knows both while it executes; the
|
|
6
|
+
* iteration id is a digest and says neither.
|
|
7
|
+
*/
|
|
8
|
+
export declare const loopPlaceSchema: S.Struct<{
|
|
9
|
+
readonly node: S.String;
|
|
10
|
+
readonly loop: S.Literals<readonly ["map", "repeat"]>;
|
|
11
|
+
readonly index: S.Number;
|
|
12
|
+
readonly count: S.Number;
|
|
13
|
+
}>;
|
|
14
|
+
export type LoopPlace = typeof loopPlaceSchema.Type;
|
|
15
|
+
/** Outermost loop first. Nesting needs one loop node per level, so the 64-node graph bound holds. */
|
|
16
|
+
export declare const loopPositionSchema: S.$Array<S.Struct<{
|
|
17
|
+
readonly node: S.String;
|
|
18
|
+
readonly loop: S.Literals<readonly ["map", "repeat"]>;
|
|
19
|
+
readonly index: S.Number;
|
|
20
|
+
readonly count: S.Number;
|
|
21
|
+
}>>;
|
|
22
|
+
export type LoopPosition = typeof loopPositionSchema.Type;
|
|
3
23
|
export declare const graphParamsSchema: S.Union<readonly [S.Struct<{
|
|
4
24
|
readonly kind: S.Literal<"run">;
|
|
5
25
|
readonly teamId: S.String;
|
|
@@ -15,6 +35,12 @@ export declare const graphParamsSchema: S.Union<readonly [S.Struct<{
|
|
|
15
35
|
readonly parentInstanceId: S.String;
|
|
16
36
|
readonly completionEvent: S.String;
|
|
17
37
|
readonly input: S.$Record<S.String, S.Codec<S.Json, S.Json, never, never>>;
|
|
38
|
+
readonly position: S.$Array<S.Struct<{
|
|
39
|
+
readonly node: S.String;
|
|
40
|
+
readonly loop: S.Literals<readonly ["map", "repeat"]>;
|
|
41
|
+
readonly index: S.Number;
|
|
42
|
+
readonly count: S.Number;
|
|
43
|
+
}>>;
|
|
18
44
|
}>]>;
|
|
19
45
|
export type GraphParams = typeof graphParamsSchema.Type;
|
|
20
46
|
export declare const nodeEventSchema: S.Struct<{
|
|
@@ -22,6 +48,13 @@ export declare const nodeEventSchema: S.Struct<{
|
|
|
22
48
|
readonly graphId: S.optionalKey<S.String>;
|
|
23
49
|
readonly nodeId: S.String;
|
|
24
50
|
readonly iteration: S.String;
|
|
51
|
+
/** The loops around this step; absent at the top of the run. */
|
|
52
|
+
readonly position: S.optionalKey<S.$Array<S.Struct<{
|
|
53
|
+
readonly node: S.String;
|
|
54
|
+
readonly loop: S.Literals<readonly ["map", "repeat"]>;
|
|
55
|
+
readonly index: S.Number;
|
|
56
|
+
readonly count: S.Number;
|
|
57
|
+
}>>>;
|
|
25
58
|
readonly poll: S.optionalKey<S.Number>;
|
|
26
59
|
readonly state: S.Literals<readonly ["queued", "running", "waiting", "complete", "failed", "skipped"]>;
|
|
27
60
|
readonly attempt: S.Number;
|
|
@@ -31,6 +64,27 @@ export declare const nodeEventSchema: S.Struct<{
|
|
|
31
64
|
readonly costMilliunits: S.optionalKey<S.Number>;
|
|
32
65
|
readonly outputDigest: S.optionalKey<S.String>;
|
|
33
66
|
readonly output: S.optionalKey<S.$Record<S.String, S.Codec<S.Json, S.Json, never, never>>>;
|
|
67
|
+
/** How the finished App call reads to a person; absent when the App declares no presentation. */
|
|
68
|
+
readonly row: S.optionalKey<S.Struct<{
|
|
69
|
+
readonly media: S.optional<S.Struct<{
|
|
70
|
+
readonly path: S.String;
|
|
71
|
+
readonly format: S.Literals<readonly ["fungi:image", "fungi:video"]>;
|
|
72
|
+
}>>;
|
|
73
|
+
readonly id: S.String;
|
|
74
|
+
readonly title: S.String;
|
|
75
|
+
readonly detail: S.optional<S.String>;
|
|
76
|
+
readonly tone: S.optional<S.Literals<readonly ["attention", "running", "error", "settled", "muted"]>>;
|
|
77
|
+
readonly status: S.optional<S.Struct<{
|
|
78
|
+
readonly label: S.String;
|
|
79
|
+
readonly tone: S.Literals<readonly ["neutral", "primary", "secondary", "accent", "success", "info", "warning", "error"]>;
|
|
80
|
+
}>>;
|
|
81
|
+
readonly open: S.optional<S.Struct<{
|
|
82
|
+
readonly params: S.$Array<S.Struct<{
|
|
83
|
+
readonly key: S.String;
|
|
84
|
+
readonly value: S.String;
|
|
85
|
+
}>>;
|
|
86
|
+
}>>;
|
|
87
|
+
}>>;
|
|
34
88
|
readonly errorMessage: S.optionalKey<S.String>;
|
|
35
89
|
readonly refusal: S.optionalKey<S.String>;
|
|
36
90
|
}>;
|
|
@@ -39,6 +93,13 @@ export declare const graphReviewSchema: S.Struct<{
|
|
|
39
93
|
readonly runId: S.String;
|
|
40
94
|
readonly nodeId: S.String;
|
|
41
95
|
readonly iteration: S.String;
|
|
96
|
+
/** The loops around this review; absent at the top of the run. */
|
|
97
|
+
readonly position: S.optionalKey<S.$Array<S.Struct<{
|
|
98
|
+
readonly node: S.String;
|
|
99
|
+
readonly loop: S.Literals<readonly ["map", "repeat"]>;
|
|
100
|
+
readonly index: S.Number;
|
|
101
|
+
readonly count: S.Number;
|
|
102
|
+
}>>>;
|
|
42
103
|
readonly instanceId: S.String;
|
|
43
104
|
readonly eventType: S.String;
|
|
44
105
|
readonly title: S.String;
|
|
@@ -66,6 +127,26 @@ export declare const dispatchResultSchema: S.Union<readonly [S.Struct<{
|
|
|
66
127
|
readonly kind: S.Literal<"complete">;
|
|
67
128
|
readonly output: S.Codec<S.Json, S.Json, never, never>;
|
|
68
129
|
readonly costMilliunits: S.Number;
|
|
130
|
+
readonly row: S.optionalKey<S.Struct<{
|
|
131
|
+
readonly media: S.optional<S.Struct<{
|
|
132
|
+
readonly path: S.String;
|
|
133
|
+
readonly format: S.Literals<readonly ["fungi:image", "fungi:video"]>;
|
|
134
|
+
}>>;
|
|
135
|
+
readonly id: S.String;
|
|
136
|
+
readonly title: S.String;
|
|
137
|
+
readonly detail: S.optional<S.String>;
|
|
138
|
+
readonly tone: S.optional<S.Literals<readonly ["attention", "running", "error", "settled", "muted"]>>;
|
|
139
|
+
readonly status: S.optional<S.Struct<{
|
|
140
|
+
readonly label: S.String;
|
|
141
|
+
readonly tone: S.Literals<readonly ["neutral", "primary", "secondary", "accent", "success", "info", "warning", "error"]>;
|
|
142
|
+
}>>;
|
|
143
|
+
readonly open: S.optional<S.Struct<{
|
|
144
|
+
readonly params: S.$Array<S.Struct<{
|
|
145
|
+
readonly key: S.String;
|
|
146
|
+
readonly value: S.String;
|
|
147
|
+
}>>;
|
|
148
|
+
}>>;
|
|
149
|
+
}>>;
|
|
69
150
|
}>, S.Struct<{
|
|
70
151
|
readonly kind: S.Literal<"refused">;
|
|
71
152
|
readonly code: S.Literals<readonly ["not_granted", "invalid_input", "invalid_output", "revoked", "member_revoked", "workflow_run_revoked", "computer_revoked", "budget_exhausted", "unknown_method"]>;
|
package/dist/execution.js
CHANGED
|
@@ -1,6 +1,20 @@
|
|
|
1
1
|
import * as S from "effect/Schema";
|
|
2
|
+
import { resultRowSchemaV1 } from "@fungi.computer/gills";
|
|
2
3
|
const id = S.String.pipe(S.check(S.isMinLength(1)), S.check(S.isMaxLength(256)));
|
|
3
4
|
const natural = S.Number.pipe(S.check(S.isInt()), S.check(S.isGreaterThanOrEqualTo(0)));
|
|
5
|
+
/**
|
|
6
|
+
* One loop a step runs inside: which Map item or Repeat round this pass is,
|
|
7
|
+
* counted from 0, of how many. The runtime knows both while it executes; the
|
|
8
|
+
* iteration id is a digest and says neither.
|
|
9
|
+
*/
|
|
10
|
+
export const loopPlaceSchema = S.Struct({
|
|
11
|
+
node: id,
|
|
12
|
+
loop: S.Literals(["map", "repeat"]),
|
|
13
|
+
index: natural,
|
|
14
|
+
count: natural,
|
|
15
|
+
});
|
|
16
|
+
/** Outermost loop first. Nesting needs one loop node per level, so the 64-node graph bound holds. */
|
|
17
|
+
export const loopPositionSchema = S.Array(loopPlaceSchema).check(S.isMaxLength(64));
|
|
4
18
|
export const graphParamsSchema = S.Union([
|
|
5
19
|
S.Struct({ kind: S.Literal("run"), teamId: id, runId: id, planDigest: id }),
|
|
6
20
|
S.Struct({
|
|
@@ -13,6 +27,7 @@ export const graphParamsSchema = S.Union([
|
|
|
13
27
|
parentInstanceId: id,
|
|
14
28
|
completionEvent: id,
|
|
15
29
|
input: S.JsonObject,
|
|
30
|
+
position: loopPositionSchema,
|
|
16
31
|
}),
|
|
17
32
|
]);
|
|
18
33
|
export const nodeEventSchema = S.Struct({
|
|
@@ -20,6 +35,8 @@ export const nodeEventSchema = S.Struct({
|
|
|
20
35
|
graphId: S.optionalKey(id),
|
|
21
36
|
nodeId: id,
|
|
22
37
|
iteration: id,
|
|
38
|
+
/** The loops around this step; absent at the top of the run. */
|
|
39
|
+
position: S.optionalKey(loopPositionSchema),
|
|
23
40
|
poll: S.optionalKey(natural),
|
|
24
41
|
state: S.Literals([
|
|
25
42
|
"queued",
|
|
@@ -36,6 +53,8 @@ export const nodeEventSchema = S.Struct({
|
|
|
36
53
|
costMilliunits: S.optionalKey(natural),
|
|
37
54
|
outputDigest: S.optionalKey(id),
|
|
38
55
|
output: S.optionalKey(S.JsonObject),
|
|
56
|
+
/** How the finished App call reads to a person; absent when the App declares no presentation. */
|
|
57
|
+
row: S.optionalKey(resultRowSchemaV1),
|
|
39
58
|
errorMessage: S.optionalKey(S.String.check(S.isMaxLength(1024))),
|
|
40
59
|
refusal: S.optionalKey(id),
|
|
41
60
|
});
|
|
@@ -43,6 +62,8 @@ export const graphReviewSchema = S.Struct({
|
|
|
43
62
|
runId: id,
|
|
44
63
|
nodeId: id,
|
|
45
64
|
iteration: id,
|
|
65
|
+
/** The loops around this review; absent at the top of the run. */
|
|
66
|
+
position: S.optionalKey(loopPositionSchema),
|
|
46
67
|
instanceId: id,
|
|
47
68
|
eventType: id,
|
|
48
69
|
title: S.String,
|
|
@@ -89,6 +110,7 @@ export const dispatchResultSchema = S.Union([
|
|
|
89
110
|
kind: S.Literal("complete"),
|
|
90
111
|
output: S.Json,
|
|
91
112
|
costMilliunits: natural,
|
|
113
|
+
row: S.optionalKey(resultRowSchemaV1),
|
|
92
114
|
}),
|
|
93
115
|
S.Struct({ kind: S.Literal("refused"), code: S.Literals(refusalCodes) }),
|
|
94
116
|
S.Struct({ kind: S.Literal("pending_approval"), actionId: id }),
|
package/dist/git-access.d.ts
CHANGED
|
@@ -1,48 +1,48 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as z from "zod";
|
|
2
2
|
/** Frozen installation identity or explicit registered Team-source namespace. */
|
|
3
3
|
export declare const workflowBindingSchema: z.ZodUnion<readonly [z.ZodObject<{
|
|
4
4
|
appId: z.ZodString;
|
|
5
5
|
installationId: z.ZodString;
|
|
6
|
-
}, z.core.$strict>, z.ZodObject<{
|
|
6
|
+
}, z.core.$strict>, z.ZodLazy<z.ZodObject<{
|
|
7
7
|
kind: z.ZodLiteral<"team-source">;
|
|
8
|
-
}, z.core.$strict
|
|
8
|
+
}, z.core.$strict>>]>;
|
|
9
9
|
/** Installation selections are admitted through the host's catalog before compilation. */
|
|
10
|
-
export declare const bindingsSelectionSchema: z.ZodRecord<z.ZodString
|
|
10
|
+
export declare const bindingsSelectionSchema: z.ZodRecord<z.ZodLazy<z.ZodString>, z.ZodUnion<readonly [z.ZodString, z.ZodLazy<z.ZodObject<{
|
|
11
11
|
kind: z.ZodLiteral<"team-source">;
|
|
12
|
-
}, z.core.$strict
|
|
12
|
+
}, z.core.$strict>>]>>;
|
|
13
13
|
export type BindingsSelection = z.output<typeof bindingsSelectionSchema>;
|
|
14
14
|
/** Published node declaration: a write branch ceiling or a read-only scope. */
|
|
15
15
|
export declare const gitAccessSchema: z.ZodUnion<readonly [z.ZodObject<{
|
|
16
|
-
binding: z.ZodString
|
|
16
|
+
binding: z.ZodLazy<z.ZodString>;
|
|
17
17
|
repository: z.ZodString;
|
|
18
|
-
branches: z.ZodArray<z.ZodString
|
|
18
|
+
branches: z.ZodLazy<z.ZodArray<z.ZodString>>;
|
|
19
19
|
}, z.core.$strict>, z.ZodObject<{
|
|
20
|
-
binding: z.ZodString
|
|
20
|
+
binding: z.ZodLazy<z.ZodString>;
|
|
21
21
|
repository: z.ZodUnion<readonly [z.ZodString, z.ZodLiteral<"$trigger.repository">]>;
|
|
22
22
|
access: z.ZodLiteral<"read">;
|
|
23
23
|
}, z.core.$strict>]>;
|
|
24
24
|
/** Host-resolved scope carried to a process; data alone never grants this authority. */
|
|
25
|
-
export declare const resolvedGitAccessSchema: z.ZodUnion<readonly [z.ZodObject<{
|
|
26
|
-
repository: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
25
|
+
export declare const resolvedGitAccessSchema: z.ZodLazy<z.ZodUnion<readonly [z.ZodObject<{
|
|
26
|
+
repository: z.ZodLazy<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
27
27
|
kind: z.ZodLiteral<"installation">;
|
|
28
28
|
installationId: z.ZodString;
|
|
29
29
|
repository: z.ZodString;
|
|
30
30
|
}, z.core.$strict>, z.ZodObject<{
|
|
31
31
|
kind: z.ZodLiteral<"team-source">;
|
|
32
32
|
repository: z.ZodString;
|
|
33
|
-
}, z.core.$strict>], "kind"
|
|
34
|
-
branches: z.ZodArray<z.ZodString
|
|
33
|
+
}, z.core.$strict>], "kind">>;
|
|
34
|
+
branches: z.ZodLazy<z.ZodArray<z.ZodString>>;
|
|
35
35
|
}, z.core.$strict>, z.ZodObject<{
|
|
36
|
-
repository: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
36
|
+
repository: z.ZodLazy<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
37
37
|
kind: z.ZodLiteral<"installation">;
|
|
38
38
|
installationId: z.ZodString;
|
|
39
39
|
repository: z.ZodString;
|
|
40
40
|
}, z.core.$strict>, z.ZodObject<{
|
|
41
41
|
kind: z.ZodLiteral<"team-source">;
|
|
42
42
|
repository: z.ZodString;
|
|
43
|
-
}, z.core.$strict>], "kind"
|
|
43
|
+
}, z.core.$strict>], "kind">>;
|
|
44
44
|
access: z.ZodLiteral<"read">;
|
|
45
|
-
}, z.core.$strict>]
|
|
45
|
+
}, z.core.$strict>]>>;
|
|
46
46
|
export type GitAccess = z.infer<typeof gitAccessSchema>;
|
|
47
47
|
export type ResolvedGitAccess = z.infer<typeof resolvedGitAccessSchema>;
|
|
48
48
|
/** The Git scope a pass for this resolved access admits. */
|
package/dist/git-access.js
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as z from "zod";
|
|
2
2
|
import { repositoryRefSchema, repositoryRefsEqual, } from "@fungi.computer/agar/git";
|
|
3
3
|
const repository = z
|
|
4
4
|
.string()
|
|
5
5
|
.regex(/^[a-zA-Z0-9._-]{1,128}$/)
|
|
6
6
|
.refine((value) => value !== "." && value !== "..");
|
|
7
|
-
const bindingName = z
|
|
7
|
+
const bindingName = z.lazy(() => z
|
|
8
8
|
.string()
|
|
9
9
|
.min(1)
|
|
10
10
|
.max(80)
|
|
11
|
-
.regex(/^[A-Za-z][A-Za-z0-9_-]*$/);
|
|
11
|
+
.regex(/^[A-Za-z][A-Za-z0-9_-]*$/));
|
|
12
12
|
const identity = z.string().min(1).max(256);
|
|
13
|
-
const teamSourceBinding = z.strictObject({ kind: z.literal("team-source") });
|
|
13
|
+
const teamSourceBinding = z.lazy(() => z.strictObject({ kind: z.literal("team-source") }));
|
|
14
14
|
/** Frozen installation identity or explicit registered Team-source namespace. */
|
|
15
15
|
export const workflowBindingSchema = z.union([
|
|
16
16
|
z.strictObject({ appId: identity, installationId: identity }),
|
|
@@ -30,11 +30,11 @@ const branch = z
|
|
|
30
30
|
value
|
|
31
31
|
.split("/")
|
|
32
32
|
.every((part) => !part.startsWith(".") && !part.endsWith(".lock")));
|
|
33
|
-
const branches = z
|
|
33
|
+
const branches = z.lazy(() => z
|
|
34
34
|
.array(branch)
|
|
35
35
|
.min(1)
|
|
36
36
|
.max(256)
|
|
37
|
-
.refine((values) => new Set(values).size === values.length, "Branches must be unique");
|
|
37
|
+
.refine((values) => new Set(values).size === values.length, "Branches must be unique"));
|
|
38
38
|
/** Write access names a fixed repository and its exact branch ceiling, admitted at arm time. */
|
|
39
39
|
const writeGitAccessSchema = z.strictObject({
|
|
40
40
|
binding: bindingName,
|
|
@@ -57,13 +57,13 @@ export const gitAccessSchema = z.union([
|
|
|
57
57
|
readGitAccessSchema,
|
|
58
58
|
]);
|
|
59
59
|
/** Host-resolved scope carried to a process; data alone never grants this authority. */
|
|
60
|
-
export const resolvedGitAccessSchema = z.union([
|
|
60
|
+
export const resolvedGitAccessSchema = z.lazy(() => z.union([
|
|
61
61
|
z.strictObject({ repository: repositoryRefSchema, branches }),
|
|
62
62
|
z.strictObject({
|
|
63
63
|
repository: repositoryRefSchema,
|
|
64
64
|
access: z.literal("read"),
|
|
65
65
|
}),
|
|
66
|
-
]);
|
|
66
|
+
]));
|
|
67
67
|
/** The Git scope a pass for this resolved access admits. */
|
|
68
68
|
export function gitAccessScope(access) {
|
|
69
69
|
return "access" in access ? access.access : "write";
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { LoopPosition } from "./execution.js";
|
|
2
|
+
/**
|
|
3
|
+
* The words a review's `detail` and choice descriptions may ask for, filled
|
|
4
|
+
* from the loops around the review: the innermost Map gives the item, the
|
|
5
|
+
* innermost Repeat the try. Each counts from 1, as a person does.
|
|
6
|
+
*/
|
|
7
|
+
export declare const reviewTextPlaceholders: readonly ["item", "items", "try", "tries"];
|
|
8
|
+
/** Placeholder names in `text` that no review can fill. */
|
|
9
|
+
export declare function unknownReviewPlaceholders(text: string): string[];
|
|
10
|
+
/**
|
|
11
|
+
* Fill a review's text from where it runs. Text whose placeholders the
|
|
12
|
+
* position cannot fill (a `{try}` outside any Repeat) is not shown at all,
|
|
13
|
+
* rather than shown with a hole or a guess.
|
|
14
|
+
*/
|
|
15
|
+
export declare function fillReviewText(text: string, position?: LoopPosition): string | undefined;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The words a review's `detail` and choice descriptions may ask for, filled
|
|
3
|
+
* from the loops around the review: the innermost Map gives the item, the
|
|
4
|
+
* innermost Repeat the try. Each counts from 1, as a person does.
|
|
5
|
+
*/
|
|
6
|
+
export const reviewTextPlaceholders = [
|
|
7
|
+
"item",
|
|
8
|
+
"items",
|
|
9
|
+
"try",
|
|
10
|
+
"tries",
|
|
11
|
+
];
|
|
12
|
+
const placeholder = /\{([A-Za-z]+)\}/g;
|
|
13
|
+
const known = (name) => reviewTextPlaceholders.some((candidate) => candidate === name);
|
|
14
|
+
/** Placeholder names in `text` that no review can fill. */
|
|
15
|
+
export function unknownReviewPlaceholders(text) {
|
|
16
|
+
return [...text.matchAll(placeholder)].flatMap(([, name = ""]) => known(name) ? [] : [name]);
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Fill a review's text from where it runs. Text whose placeholders the
|
|
20
|
+
* position cannot fill (a `{try}` outside any Repeat) is not shown at all,
|
|
21
|
+
* rather than shown with a hole or a guess.
|
|
22
|
+
*/
|
|
23
|
+
export function fillReviewText(text, position = []) {
|
|
24
|
+
const map = position.findLast((place) => place.loop === "map");
|
|
25
|
+
const repeat = position.findLast((place) => place.loop === "repeat");
|
|
26
|
+
const values = {
|
|
27
|
+
item: map === undefined ? undefined : map.index + 1,
|
|
28
|
+
items: map?.count,
|
|
29
|
+
try: repeat === undefined ? undefined : repeat.index + 1,
|
|
30
|
+
tries: repeat?.count,
|
|
31
|
+
};
|
|
32
|
+
const valueOf = (name) => (known(name) ? values[name] : undefined);
|
|
33
|
+
const names = [...text.matchAll(placeholder)].map(([, name = ""]) => name);
|
|
34
|
+
if (names.some((name) => valueOf(name) === undefined))
|
|
35
|
+
return undefined;
|
|
36
|
+
return text.replace(placeholder, (whole, name) => {
|
|
37
|
+
const value = valueOf(name);
|
|
38
|
+
return value === undefined ? whole : String(value);
|
|
39
|
+
});
|
|
40
|
+
}
|
|
@@ -15,7 +15,7 @@ export declare const workflowArmedViaSchema: z.ZodDiscriminatedUnion<[z.ZodObjec
|
|
|
15
15
|
/** Provenance from the actual connector or approved-policy owner. */
|
|
16
16
|
export type WorkflowArmedVia = z.output<typeof workflowArmedViaSchema>;
|
|
17
17
|
/** The grantor stays the armer; decoding this shape alone grants no authority. */
|
|
18
|
-
export declare const workflowArmingReceiptSchema: z.ZodObject<{
|
|
18
|
+
export declare const workflowArmingReceiptSchema: z.ZodLazy<z.ZodObject<{
|
|
19
19
|
armedBy: z.ZodString;
|
|
20
20
|
armedVia: z.ZodNullable<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
21
21
|
kind: z.ZodLiteral<"connector">;
|
|
@@ -28,12 +28,12 @@ export declare const workflowArmingReceiptSchema: z.ZodObject<{
|
|
|
28
28
|
policyRevision: z.ZodString;
|
|
29
29
|
grantorMemberId: z.ZodString;
|
|
30
30
|
}, z.core.$strict>], "kind">>;
|
|
31
|
-
grantorMembershipVersion: z.ZodNumber
|
|
32
|
-
}, z.core.$strict
|
|
31
|
+
grantorMembershipVersion: z.ZodLazy<z.ZodNumber>;
|
|
32
|
+
}, z.core.$strict>>;
|
|
33
33
|
/** Captured by a host after current frame, installation and member admission. */
|
|
34
34
|
export type WorkflowArmingReceipt = z.output<typeof workflowArmingReceiptSchema>;
|
|
35
35
|
/** Current host-owner readback, after live grantor and approval checks. */
|
|
36
|
-
export declare const workflowArmingAccessSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
36
|
+
export declare const workflowArmingAccessSchema: z.ZodLazy<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
37
37
|
kind: z.ZodLiteral<"connector">;
|
|
38
38
|
agentId: z.ZodString;
|
|
39
39
|
attachmentId: z.ZodString;
|
|
@@ -52,7 +52,7 @@ export declare const workflowArmingAccessSchema: z.ZodDiscriminatedUnion<[z.ZodO
|
|
|
52
52
|
write: "write";
|
|
53
53
|
read: "read";
|
|
54
54
|
}>;
|
|
55
|
-
}, z.core.$strict>], "kind"
|
|
55
|
+
}, z.core.$strict>], "kind">>;
|
|
56
56
|
/** The actual source identity plus its currently approved access level. */
|
|
57
57
|
export type WorkflowArmingAccess = z.output<typeof workflowArmingAccessSchema>;
|
|
58
58
|
/** Lookup keys exclude captured versions, revisions and grantors. */
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { decodeWorkflowRunMember } from "./run-member.js";
|
|
3
3
|
const identity = z.string().min(1).max(256);
|
|
4
|
-
const membershipVersion = z.number().int().nonnegative();
|
|
4
|
+
const membershipVersion = z.lazy(() => z.number().int().nonnegative());
|
|
5
5
|
const connector = z.strictObject({
|
|
6
6
|
kind: z.literal("connector"),
|
|
7
7
|
agentId: identity,
|
|
@@ -21,7 +21,7 @@ export const workflowArmedViaSchema = z.discriminatedUnion("kind", [
|
|
|
21
21
|
policy,
|
|
22
22
|
]);
|
|
23
23
|
/** The grantor stays the armer; decoding this shape alone grants no authority. */
|
|
24
|
-
export const workflowArmingReceiptSchema = z
|
|
24
|
+
export const workflowArmingReceiptSchema = z.lazy(() => z
|
|
25
25
|
.strictObject({
|
|
26
26
|
armedBy: identity,
|
|
27
27
|
/** A human arm has an explicit null; Agent arms retain their actual live source. */
|
|
@@ -36,15 +36,15 @@ export const workflowArmingReceiptSchema = z
|
|
|
36
36
|
path: ["armedVia", "grantorMemberId"],
|
|
37
37
|
message: "The approved policy grantor must be the armer",
|
|
38
38
|
});
|
|
39
|
-
});
|
|
39
|
+
}));
|
|
40
40
|
/** Current host-owner readback, after live grantor and approval checks. */
|
|
41
|
-
export const workflowArmingAccessSchema = z.discriminatedUnion("kind", [
|
|
41
|
+
export const workflowArmingAccessSchema = z.lazy(() => z.discriminatedUnion("kind", [
|
|
42
42
|
connector.extend({
|
|
43
43
|
grantorMemberId: identity,
|
|
44
44
|
level: z.enum(["read", "write"]),
|
|
45
45
|
}),
|
|
46
46
|
policy.extend({ level: z.enum(["read", "write"]) }),
|
|
47
|
-
]);
|
|
47
|
+
]));
|
|
48
48
|
/** Recheck Agent authority only; the host also admits member, Computer and installation grants. */
|
|
49
49
|
export async function recheckWorkflowArming(receipt, ports) {
|
|
50
50
|
const captured = workflowArmingReceiptSchema.safeParse(receipt);
|
|
@@ -1,7 +1,28 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import type { GitWriter } from "@fungi.computer/agar/git";
|
|
3
|
+
/** One credential grammar for both hosts; the owner is public, the token is not. */
|
|
4
|
+
export declare const workflowGitPassPrefix = "wgp.";
|
|
5
|
+
export declare const workflowGitPassTokenSchema: z.ZodPipe<z.ZodString, z.ZodTransform<{
|
|
6
|
+
ownerId: string;
|
|
7
|
+
token: string;
|
|
8
|
+
}, string>>;
|
|
9
|
+
/** Format the existing standalone bearer shape, never a credential-bearing URL. */
|
|
10
|
+
export declare function formatWorkflowGitPassToken(ownerId: string, token: string): string;
|
|
11
|
+
/** Returned once to the native dispatcher, never to an App or a durable record. */
|
|
12
|
+
export type WorkflowGitCredential = Readonly<{
|
|
13
|
+
bearer: string;
|
|
14
|
+
remote: string;
|
|
15
|
+
expiresAt: number;
|
|
16
|
+
}>;
|
|
17
|
+
/** Git reads this command-local config without writing a credential or repository config. */
|
|
18
|
+
export declare function workflowGitExecutionEnvironment(credential: WorkflowGitCredential): {
|
|
19
|
+
FUNGI_GIT_REMOTE: string;
|
|
20
|
+
GIT_CONFIG_COUNT: string;
|
|
21
|
+
GIT_CONFIG_KEY_0: string;
|
|
22
|
+
GIT_CONFIG_VALUE_0: string;
|
|
23
|
+
};
|
|
3
24
|
/** Non-secret Team-issued authority for one stored Workflow step on its pinned Computer. */
|
|
4
|
-
export declare const workflowGitPassGrantSchema: z.ZodObject<{
|
|
25
|
+
export declare const workflowGitPassGrantSchema: z.ZodLazy<z.ZodObject<{
|
|
5
26
|
teamId: z.ZodString;
|
|
6
27
|
computerId: z.ZodString;
|
|
7
28
|
memberId: z.ZodString;
|
|
@@ -28,29 +49,29 @@ export declare const workflowGitPassGrantSchema: z.ZodObject<{
|
|
|
28
49
|
write: "write";
|
|
29
50
|
read: "read";
|
|
30
51
|
}>;
|
|
31
|
-
gitAccess: z.ZodUnion<readonly [z.ZodObject<{
|
|
32
|
-
repository: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
52
|
+
gitAccess: z.ZodLazy<z.ZodUnion<readonly [z.ZodObject<{
|
|
53
|
+
repository: z.ZodLazy<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
33
54
|
kind: z.ZodLiteral<"installation">;
|
|
34
55
|
installationId: z.ZodString;
|
|
35
56
|
repository: z.ZodString;
|
|
36
57
|
}, z.core.$strict>, z.ZodObject<{
|
|
37
58
|
kind: z.ZodLiteral<"team-source">;
|
|
38
59
|
repository: z.ZodString;
|
|
39
|
-
}, z.core.$strict>], "kind"
|
|
40
|
-
branches: z.ZodArray<z.ZodString
|
|
60
|
+
}, z.core.$strict>], "kind">>;
|
|
61
|
+
branches: z.ZodLazy<z.ZodArray<z.ZodString>>;
|
|
41
62
|
}, z.core.$strict>, z.ZodObject<{
|
|
42
|
-
repository: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
63
|
+
repository: z.ZodLazy<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
43
64
|
kind: z.ZodLiteral<"installation">;
|
|
44
65
|
installationId: z.ZodString;
|
|
45
66
|
repository: z.ZodString;
|
|
46
67
|
}, z.core.$strict>, z.ZodObject<{
|
|
47
68
|
kind: z.ZodLiteral<"team-source">;
|
|
48
69
|
repository: z.ZodString;
|
|
49
|
-
}, z.core.$strict>], "kind"
|
|
70
|
+
}, z.core.$strict>], "kind">>;
|
|
50
71
|
access: z.ZodLiteral<"read">;
|
|
51
|
-
}, z.core.$strict>]
|
|
72
|
+
}, z.core.$strict>]>>;
|
|
52
73
|
triggerId: z.ZodOptional<z.ZodString>;
|
|
53
|
-
}, z.core.$strict
|
|
74
|
+
}, z.core.$strict>>;
|
|
54
75
|
export type WorkflowGitPassGrant = z.output<typeof workflowGitPassGrantSchema>;
|
|
55
76
|
/** Run cleanup remains valid after the Computer's product data is erased. */
|
|
56
77
|
export declare const workflowGitPassRevocationSchema: z.ZodObject<{
|