@jr2/orchestrator 0.2.1 → 0.4.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.
- package/bin/server.ts +7 -4
- package/console/canvas.ts +27 -14
- package/console/components/app.ts +10 -1
- package/console/components/fleet.ts +39 -6
- package/console/components/machine-pane.ts +89 -2
- package/console/components/nav.ts +1 -1
- package/console/components/schema-form.ts +12 -4
- package/console/json-field.ts +23 -0
- package/console/main.ts +110 -39
- package/console/store.ts +221 -17
- package/console/style.css +90 -0
- package/package.json +4 -5
- package/src/actor.ts +507 -54
- package/src/agent.ts +2 -2
- package/src/ambient.ts +2 -0
- package/src/assembly-checks.ts +165 -0
- package/src/assembly-tar.ts +494 -0
- package/src/assembly.ts +721 -0
- package/src/chart.ts +2050 -0
- package/src/charts.ts +720 -0
- package/src/config.ts +233 -22
- package/src/content-hash.ts +105 -0
- package/src/custodian.lua +313 -0
- package/src/custodian.ts +951 -0
- package/src/customize.ts +216 -25
- package/src/effect-checks.ts +557 -0
- package/src/effect.ts +905 -0
- package/src/effector-wire.ts +525 -0
- package/src/fingerprint.ts +4 -3
- package/src/gate.ts +8 -0
- package/src/git-clone.ts +65 -0
- package/src/git-push.ts +336 -0
- package/src/harness-client.ts +247 -25
- package/src/held-secrets.ts +643 -0
- package/src/holds.ts +156 -0
- package/src/http-effect.ts +981 -0
- package/src/http.ts +514 -119
- package/src/images.ts +58 -31
- package/src/index.ts +49 -4
- package/src/instance.ts +445 -59
- package/src/kube-client.ts +333 -0
- package/src/machine-doc.ts +88 -6
- package/src/model-keys.ts +92 -0
- package/src/mount-digest.ts +53 -0
- package/src/names.ts +89 -7
- package/src/notices.ts +88 -0
- package/src/open-body.ts +1444 -0
- package/src/open-effect.ts +144 -0
- package/src/open.ts +4 -4
- package/src/parts.ts +530 -33
- package/src/party.ts +468 -0
- package/src/pick.ts +309 -0
- package/src/pool.ts +1 -1
- package/src/registration.ts +301 -17
- package/src/repo-fetch.ts +61 -86
- package/src/repos.ts +63 -74
- package/src/rest.ts +133 -0
- package/src/run-host.ts +1841 -105
- package/src/sandbox-effect.ts +891 -0
- package/src/sandbox-effector.ts +111 -0
- package/src/sandbox-kube.ts +1719 -0
- package/src/sandbox-watch.ts +260 -0
- package/src/secret-placement.ts +494 -0
- package/src/server.ts +285 -89
- package/src/setup.ts +589 -70
- package/src/size.ts +342 -0
- package/src/snapshot-store.ts +64 -13
- package/src/stub-harness.ts +5 -0
- package/src/sync-client.ts +54 -0
- package/src/timers.ts +178 -0
- package/src/tokens.ts +44 -12
- package/src/vocabulary.ts +66 -1
- package/src/wire.ts +156 -2
- package/src/workspace.ts +529 -194
- package/src/sandbox-kubectl.ts +0 -1144
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
// An Effect's OPEN parts (ADR-0054, ADR-0067): a packaged Machine cannot know the host its http
|
|
2
|
+
// Effect calls, the program its command runs, or the secret its composer spends, so it leaves that
|
|
3
|
+
// part Open — the `open` sentinel in place of an `http` url (whatever its body) or of a command's
|
|
4
|
+
// argv, or `secret(open)` in place of a secret's name in its auth, a header or a command's env — and
|
|
5
|
+
// the composer binds it once, with `customize(machine, { effects: { <slot>: { url } | { argv } |
|
|
6
|
+
// { secret } } })`. An Effect may leave its url or its argv Open AND name an Open secret: one
|
|
7
|
+
// binding object takes both keys. Never per run: the binding is a value, not a mapper over the door,
|
|
8
|
+
// so the host stays fixed before any run starts and `jr2 up` reads the bound part as it reads one
|
|
9
|
+
// the author wrote — the placement, the secrets it names, the Effector's fence.
|
|
10
|
+
//
|
|
11
|
+
// An Open Effect is a slot whose logic is unbound: it carries the parts the walk reads (the
|
|
12
|
+
// kind, the placement, the secrets its other parts name, each Open part marked) and a binder, and
|
|
13
|
+
// it runs nothing. Binding calls the Effect's own constructor again with the definition and the
|
|
14
|
+
// bound parts, so a bound Effect is checked by every rule the constructor checks for an Effect
|
|
15
|
+
// written whole, and is the same logic; a binding that leaves one of two Open parts unbound gives an
|
|
16
|
+
// Effect still Open in that part. The compiler's check is the one it misses: an Open argv's input is
|
|
17
|
+
// one string per key, so a slot the invoke does not supply faults when the Effect starts. `jr2 up`
|
|
18
|
+
// refuses a registered Machine whose Open part nothing bound, naming the `customize` line; the
|
|
19
|
+
// unbound logic is the second fence, for every path that walk never saw — a run of it faults
|
|
20
|
+
// before anything is sent.
|
|
21
|
+
//
|
|
22
|
+
// This module holds only the sentinel's Effect half: the binder's key, the unbound logic, and the
|
|
23
|
+
// phantom type `customize()` reads. It imports no Effect kind, so every kind imports it.
|
|
24
|
+
|
|
25
|
+
import type { ActorLogic, AnyActorRef, EventObject } from "xstate";
|
|
26
|
+
import { open } from "./open.ts";
|
|
27
|
+
import type { Arg, EffectLogic, EffectParts, EffectSnapshot } from "./effect.ts";
|
|
28
|
+
import type { Text } from "./http-effect.ts";
|
|
29
|
+
|
|
30
|
+
/** The parts of an Effect a package may leave Open: an http url, a command's argv, a secret's name. */
|
|
31
|
+
export type OpenEffectPart = "url" | "argv" | "secret";
|
|
32
|
+
|
|
33
|
+
/** What a composer binds an Effect's Open parts to, each key one part: a url (a string, or a `text`
|
|
34
|
+
* with a secret in its path), an argv (each entry one argument, never shell text), a secret's name
|
|
35
|
+
* (its file in the Effector's mount, ADR-0068). One object for an Effect with two Open parts. */
|
|
36
|
+
export type EffectBinding = {
|
|
37
|
+
readonly url?: string | Text;
|
|
38
|
+
readonly argv?: readonly (string | Arg)[];
|
|
39
|
+
readonly secret?: string;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
declare const openPart: unique symbol;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* The TYPE half of an Open Effect: its logic type says which parts are Open, so `customize()`'s
|
|
46
|
+
* `effects` offers exactly the Open slots, each with the binding its parts take — the intersection
|
|
47
|
+
* of each part's, for an Effect with two. Phantom, like `JR2Wrapper`: the property exists in the
|
|
48
|
+
* type alone.
|
|
49
|
+
*/
|
|
50
|
+
export type OpenEffect<P extends OpenEffectPart> = { readonly [openPart]: P };
|
|
51
|
+
|
|
52
|
+
/** The phantom for these Open parts, or nothing for none: what a constructor's type adds. */
|
|
53
|
+
export type OpenOf<P extends OpenEffectPart> = [P] extends [never] ? unknown : OpenEffect<P>;
|
|
54
|
+
|
|
55
|
+
/** The binder an Open Effect carries, under a `Symbol.for` key so a second copy of this module (an
|
|
56
|
+
* Instance's own `@jr2/orchestrator` beside the CLI's) reads it. Not enumerable. */
|
|
57
|
+
const BIND: unique symbol = Symbol.for("jr2.effect.bind");
|
|
58
|
+
|
|
59
|
+
type Binder = (binding: EffectBinding) => EffectLogic<unknown, unknown>;
|
|
60
|
+
|
|
61
|
+
/** Which parts of these parts are Open, in binding order: the url or the argv, then the secret. */
|
|
62
|
+
export function openPartsOf(parts: EffectParts): OpenEffectPart[] {
|
|
63
|
+
const out: OpenEffectPart[] = [];
|
|
64
|
+
if (parts.kind === "http" && parts.url === open) out.push("url");
|
|
65
|
+
if (parts.kind === "command" && parts.argv === open) out.push("argv");
|
|
66
|
+
if (parts.kind !== "push" && parts.openSecret) out.push("secret");
|
|
67
|
+
return out;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** The binder of an Open Effect's logic; undefined for an Effect whose parts are all fixed. */
|
|
71
|
+
export function binderOf(logic: unknown): Binder | undefined {
|
|
72
|
+
const bind = (logic as { [BIND]?: unknown } | null | undefined)?.[BIND];
|
|
73
|
+
return typeof bind === "function" ? (bind as Binder) : undefined;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/** What the fix line writes for each Open part: a sample of the value its binding takes. */
|
|
77
|
+
const BINDING_SAMPLE: Record<OpenEffectPart, string> = {
|
|
78
|
+
url: 'url: "https://<host>/<path>"',
|
|
79
|
+
argv: 'argv: ["<program>", "<arg>"]',
|
|
80
|
+
secret: 'secret: "<name>"',
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
/** One binding object for these Open parts — `{ url: "…", secret: "<name>" }` for two. */
|
|
84
|
+
export function effectBindingShape(parts: OpenEffectPart | readonly OpenEffectPart[]): string {
|
|
85
|
+
const list = typeof parts === "string" ? [parts] : parts;
|
|
86
|
+
return `{ ${list.map((p) => BINDING_SAMPLE[p]).join(", ")} }`;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** The fix line for an Effect's Open parts, as `customize()` takes it under `effects`. */
|
|
90
|
+
export function effectBindingLine(slot: string, parts: OpenEffectPart | readonly OpenEffectPart[]): string {
|
|
91
|
+
return `{ effects: { ${slot}: ${effectBindingShape(parts)} } }`;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** The Open parts in words: "url", "url and secret". */
|
|
95
|
+
export function openPartsText(parts: OpenEffectPart | readonly OpenEffectPart[]): string {
|
|
96
|
+
return (typeof parts === "string" ? [parts] : parts).join(" and ");
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
type Fault = { type: "jr2.effect.error"; error: unknown };
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* The unbound logic of an Effect with Open parts: the parts the walk reads, the binder
|
|
103
|
+
* `customize()` calls, and a start that faults (a config defect, ADR-0067) naming the line that
|
|
104
|
+
* binds them. It sends nothing and writes no ledger row.
|
|
105
|
+
*/
|
|
106
|
+
export function openEffectLogic<I, O, P extends OpenEffectPart>(
|
|
107
|
+
parts: EffectParts,
|
|
108
|
+
open: readonly P[],
|
|
109
|
+
bind: Binder,
|
|
110
|
+
): EffectLogic<I, O> & OpenEffect<P> {
|
|
111
|
+
const logic: ActorLogic<EffectSnapshot<O, I>, EventObject, I> = {
|
|
112
|
+
getInitialSnapshot: (_scope, input) => ({
|
|
113
|
+
status: "active",
|
|
114
|
+
output: undefined,
|
|
115
|
+
error: undefined,
|
|
116
|
+
input,
|
|
117
|
+
effectId: "",
|
|
118
|
+
}),
|
|
119
|
+
transition: (snapshot, event) => {
|
|
120
|
+
if (snapshot.status !== "active") return snapshot;
|
|
121
|
+
if (event.type === "jr2.effect.error") {
|
|
122
|
+
return { ...snapshot, status: "error", error: (event as Fault).error, input: undefined };
|
|
123
|
+
}
|
|
124
|
+
if (event.type === "xstate.stop") return { ...snapshot, status: "stopped", input: undefined };
|
|
125
|
+
return snapshot;
|
|
126
|
+
},
|
|
127
|
+
start: (snapshot, { self }) => {
|
|
128
|
+
if (snapshot.status !== "active") return;
|
|
129
|
+
const slot = slotOf(self);
|
|
130
|
+
const error = new Error(
|
|
131
|
+
`${parts.kind} "${slot}" leaves its ${openPartsText(open)} Open — nobody bound it; bind it where the ` +
|
|
132
|
+
`Machine is registered (\`customize(<machine>, ${effectBindingLine(slot, open)})\`), because a packaged ` +
|
|
133
|
+
"Machine cannot know it; nothing was sent (ADR-0054)",
|
|
134
|
+
);
|
|
135
|
+
const fault: Fault = { type: "jr2.effect.error", error };
|
|
136
|
+
self.send(fault);
|
|
137
|
+
},
|
|
138
|
+
getPersistedSnapshot: (snapshot) => snapshot,
|
|
139
|
+
};
|
|
140
|
+
Object.defineProperty(logic, BIND, { value: bind, enumerable: false });
|
|
141
|
+
return Object.assign(logic, { effect: parts }) as unknown as EffectLogic<I, O> & OpenEffect<P>;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const slotOf = (self: AnyActorRef): string => (typeof self.src === "string" ? self.src : self.id);
|
package/src/open.ts
CHANGED
|
@@ -6,10 +6,10 @@
|
|
|
6
6
|
// nothing but the sentinel in it is what lets both import it and neither import the other.
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
|
-
* Open (
|
|
10
|
-
* url, an Agent with no model. The shape a packaged Machine ships in, because a
|
|
11
|
-
* know the repository or pay for the model. `jr2 up` refuses an Open part nobody
|
|
12
|
-
* the line that binds it; a run never sees one.
|
|
9
|
+
* Open (GLOSSARY.md, ADR-0054): this part is a composer's to bind, with `customize()` — a Repo
|
|
10
|
+
* Slot with no url, an Agent with no model. The shape a packaged Machine ships in, because a
|
|
11
|
+
* package cannot know the repository or pay for the model. `jr2 up` refuses an Open part nobody
|
|
12
|
+
* bound and names the line that binds it; a run never sees one.
|
|
13
13
|
*
|
|
14
14
|
* `Symbol.for`, so an Instance's own copy of this module and the CLI's walk agree on it — a
|
|
15
15
|
* packaged Machine may be built against one and walked by the other.
|