@intentius/behold 0.2.3 → 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/AGENTS.md +64 -0
- package/README.md +25 -1
- package/dist/cli.js +298 -88
- package/example-writes/README.md +88 -0
- package/example-writes/chant.config.ts +12 -0
- package/example-writes/ops/apply.op.ts +7 -0
- package/example-writes/ops/floci.op.ts +20 -0
- package/example-writes/ops/reconcile.op.ts +6 -0
- package/example-writes/package-lock.json +1249 -0
- package/example-writes/package.json +14 -0
- package/example-writes/src/bucket.ts +54 -0
- package/example-writes/tsconfig.json +1 -0
- package/package.json +4 -2
- package/web/app.js +468 -115
- package/web/index.html +143 -91
- package/web/panel.js +163 -0
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# Your first apply with behold
|
|
2
|
+
|
|
3
|
+
The smallest real thing: one S3 bucket, deployed from the browser by clicking
|
|
4
|
+
**Sync**. It exists to show where the delegated-write buttons come from and what
|
|
5
|
+
they do — behold never applies anything itself, it triggers the `ApplyOp` you
|
|
6
|
+
committed here, running `chant` on your machine.
|
|
7
|
+
|
|
8
|
+
```
|
|
9
|
+
src/bucket.ts one S3 Bucket + a TLS-only policy (the whole "infra")
|
|
10
|
+
ops/apply.op.ts ApplyOp "prod-apply" — code → real AWS (aws cloudformation deploy)
|
|
11
|
+
ops/floci.op.ts Op "floci-apply" — code → local Floci (CloudFormation API), no account
|
|
12
|
+
ops/reconcile.op.ts ReconcileOp "prod-reconcile" — cloud → code PR (the Adopt button)
|
|
13
|
+
chant.config.ts lexicons [aws, temporal], environments [prod]
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
The **Sync** button appears because a project declares an `ApplyOp`. A project with
|
|
17
|
+
no `*.op.ts` (like `../example`) shows no Sync — that's expected, not a bug.
|
|
18
|
+
|
|
19
|
+
## Creds-free: deploy to a local emulator (`--local`)
|
|
20
|
+
|
|
21
|
+
No AWS account? Serve with `--local` and behold boots a local Floci emulator
|
|
22
|
+
(needs Docker), then the **floci-apply** Op deploys the bucket to it — the same
|
|
23
|
+
delegated-write path, zero cloud creds:
|
|
24
|
+
|
|
25
|
+
```sh
|
|
26
|
+
# from the behold repo root
|
|
27
|
+
npm run dev -- serve example-writes --local
|
|
28
|
+
# → boots chant-floci, header shows "● local · chant-floci up"
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Click **floci-apply** (or `curl -XPOST localhost:4600/api/ops/floci-apply/run`).
|
|
32
|
+
The now-line streams Build → Apply → Verify; the bucket is created in Floci via
|
|
33
|
+
the CloudFormation API (`awsApply`, no `aws` CLI). The emulator is torn down when
|
|
34
|
+
you Ctrl-C. Everything below uses real AWS instead.
|
|
35
|
+
|
|
36
|
+
## Prerequisites
|
|
37
|
+
|
|
38
|
+
- Node 20+, and **AWS credentials** in your environment (`aws sts get-caller-identity`
|
|
39
|
+
should work). The apply creates a real S3 bucket via CloudFormation — a few cents,
|
|
40
|
+
torn down at the end. behold holds no creds; `chant` uses yours on this machine.
|
|
41
|
+
- One edit: S3 bucket names are **globally unique**, so change `BucketName` in
|
|
42
|
+
[`src/bucket.ts`](src/bucket.ts) to something of your own (e.g. include your AWS
|
|
43
|
+
account id or a random suffix).
|
|
44
|
+
|
|
45
|
+
## Steps
|
|
46
|
+
|
|
47
|
+
```sh
|
|
48
|
+
# from this directory — installs the project's own chant + lexicons
|
|
49
|
+
npm install
|
|
50
|
+
|
|
51
|
+
# from the behold repo root — serve this project, with the prod overlay on
|
|
52
|
+
npm run dev -- serve example-writes --env prod
|
|
53
|
+
# → behold → http://localhost:4600
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
1. Open **http://localhost:4600**. You see the declared graph. With `--env prod`,
|
|
57
|
+
the bucket node is **blue (pending)** — declared, not deployed yet. (No AWS creds?
|
|
58
|
+
drop `--env` for the plain source graph; Sync still works, you just won't see the
|
|
59
|
+
colour flip.)
|
|
60
|
+
2. In the header, click **Sync**. The now-line streams the Op's phases:
|
|
61
|
+
`▶ chant run prod-apply` → Build → Plan → **Apply** (`aws cloudformation deploy`).
|
|
62
|
+
`prod-apply` is **ungated**, so it applies straight away — no approval step.
|
|
63
|
+
3. When it finishes, the `store` node flips **blue → green (managed)**. Click it: the
|
|
64
|
+
inspect panel's **live** section shows the bucket's observed **physical id** and
|
|
65
|
+
status. That's the hydration — the cloud's view of your declared resource.
|
|
66
|
+
4. Click **Refresh** any time to re-check drift and drop a lanes frame.
|
|
67
|
+
|
|
68
|
+
## What "gated" looks like
|
|
69
|
+
|
|
70
|
+
`prod-apply` here is additive and ungated, so Sync applies in one click. Make it
|
|
71
|
+
gated and Sync starts the Op but **pauses** at an approval gate — behold then shows
|
|
72
|
+
an **Approve** button that signals the gate (a durable Temporal wait). Authority for
|
|
73
|
+
the destructive step stays with a human:
|
|
74
|
+
|
|
75
|
+
```ts
|
|
76
|
+
ApplyOp({ name: "prod-apply", env: "prod", target: "cloudformation", delete: "gated" });
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Tear down
|
|
80
|
+
|
|
81
|
+
```sh
|
|
82
|
+
aws cloudformation delete-stack --stack-name prod
|
|
83
|
+
aws cloudformation wait stack-delete-complete --stack-name prod
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Or try the **Adopt** button on a *foreign* node (one that's in the cloud but not in
|
|
87
|
+
source) — it triggers `prod-reconcile`, which opens a PR pulling live back into
|
|
88
|
+
typed source. behold never writes source directly; a human merges the PR.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { TemporalChantConfig } from "@intentius/chant-lexicon-temporal";
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
lexicons: ["aws", "temporal"],
|
|
5
|
+
sourceDir: "src",
|
|
6
|
+
environments: ["prod"],
|
|
7
|
+
ownership: { stack: "behold-writes", env: "prod" },
|
|
8
|
+
temporal: {
|
|
9
|
+
profiles: { prod: { address: "localhost:7233", namespace: "default", taskQueue: "behold-writes", autoStart: true } },
|
|
10
|
+
defaultProfile: "prod",
|
|
11
|
+
} satisfies TemporalChantConfig,
|
|
12
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { ApplyOp } from "@intentius/chant-lexicon-temporal";
|
|
2
|
+
|
|
3
|
+
// code → cloud. Additive apply (no gate) runs one-shot on the local executor —
|
|
4
|
+
// behold's Sync button shells `chant run prod-apply`. Target cloudformation;
|
|
5
|
+
// output defaults to template.json (matching this project's `build` script).
|
|
6
|
+
const { op } = ApplyOp({ name: "prod-apply", env: "prod", target: "cloudformation" });
|
|
7
|
+
export default op;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Op, phase, build, awsApply, httpCheck } from "@intentius/chant-lexicon-temporal";
|
|
2
|
+
|
|
3
|
+
// Deploy the S3 bucket to the local Floci emulator via the CloudFormation API
|
|
4
|
+
// (awsApply — direct create-or-update + poll, no aws CLI, honours the endpoint).
|
|
5
|
+
// `behold serve … --local` boots Floci; this Op's Run button deploys to it. No
|
|
6
|
+
// cloud account, no creds — the creds-free first apply. (prod-apply is the
|
|
7
|
+
// real-AWS path; it shells the aws CLI.)
|
|
8
|
+
export default Op({
|
|
9
|
+
name: "floci-apply",
|
|
10
|
+
overview: "S3 bucket → local Floci (CloudFormation API), no cloud account",
|
|
11
|
+
taskQueue: "behold-local",
|
|
12
|
+
phases: [
|
|
13
|
+
phase("Build", [build(".", { script: "build" })]),
|
|
14
|
+
// Stack name = the env ("prod"), so `serve --local --env prod`'s overlay —
|
|
15
|
+
// which queries the CFN stack named after the env — observes this deploy and
|
|
16
|
+
// flips the node green (chant #926 points the live query at the emulator).
|
|
17
|
+
phase("Apply", [awsApply("template.json", { stackName: "prod", endpoint: "http://localhost:4566" })]),
|
|
18
|
+
phase("Verify", [httpCheck("http://localhost:4566/behold-floci-demo")]),
|
|
19
|
+
],
|
|
20
|
+
});
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { ReconcileOp } from "@intentius/chant-lexicon-temporal";
|
|
2
|
+
|
|
3
|
+
// cloud → code. Snapshot live prod, diff vs source, open a PR for drift/orphans.
|
|
4
|
+
// Scoped to chant-owned resources. behold's Adopt button triggers this.
|
|
5
|
+
const { op } = ReconcileOp({ name: "prod-reconcile", env: "prod", onDrift: "pull-request", scope: { owned: true } });
|
|
6
|
+
export default op;
|