@intentius/behold 0.9.1 → 0.10.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/dist/cli.js +437 -115
- package/example-argo-estate/app-a/package.json +2 -2
- package/example-argo-estate/app-b/package.json +2 -2
- package/example-argo-estate/control-plane/package.json +2 -2
- package/example-argo-estate/package-lock.json +17 -17
- package/example-carve/README.md +15 -8
- package/example-flux-estate/app-a/package.json +2 -2
- package/example-flux-estate/app-b/package.json +2 -2
- package/example-flux-estate/control-plane/package.json +2 -2
- package/example-flux-estate/package-lock.json +17 -17
- package/package.json +2 -2
- package/web/app.js +26 -0
- package/web/carve-steps.js +53 -6
|
@@ -14,22 +14,22 @@
|
|
|
14
14
|
"app-a": {
|
|
15
15
|
"name": "behold-example-argo-app-a",
|
|
16
16
|
"dependencies": {
|
|
17
|
-
"@intentius/chant": "^0.44.
|
|
18
|
-
"@intentius/chant-lexicon-k8s": "^0.44.
|
|
17
|
+
"@intentius/chant": "^0.44.8",
|
|
18
|
+
"@intentius/chant-lexicon-k8s": "^0.44.8"
|
|
19
19
|
}
|
|
20
20
|
},
|
|
21
21
|
"app-b": {
|
|
22
22
|
"name": "behold-example-argo-app-b",
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@intentius/chant": "^0.44.
|
|
25
|
-
"@intentius/chant-lexicon-k8s": "^0.44.
|
|
24
|
+
"@intentius/chant": "^0.44.8",
|
|
25
|
+
"@intentius/chant-lexicon-k8s": "^0.44.8"
|
|
26
26
|
}
|
|
27
27
|
},
|
|
28
28
|
"control-plane": {
|
|
29
29
|
"name": "behold-example-argo-control-plane",
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@intentius/chant": "^0.44.
|
|
32
|
-
"@intentius/chant-lexicon-k8s": "^0.44.
|
|
31
|
+
"@intentius/chant": "^0.44.8",
|
|
32
|
+
"@intentius/chant-lexicon-k8s": "^0.44.8"
|
|
33
33
|
}
|
|
34
34
|
},
|
|
35
35
|
"node_modules/@dagrejs/dagre": {
|
|
@@ -464,9 +464,9 @@
|
|
|
464
464
|
}
|
|
465
465
|
},
|
|
466
466
|
"node_modules/@intentius/chant": {
|
|
467
|
-
"version": "0.44.
|
|
468
|
-
"resolved": "https://registry.npmjs.org/@intentius/chant/-/chant-0.44.
|
|
469
|
-
"integrity": "sha512-
|
|
467
|
+
"version": "0.44.8",
|
|
468
|
+
"resolved": "https://registry.npmjs.org/@intentius/chant/-/chant-0.44.8.tgz",
|
|
469
|
+
"integrity": "sha512-c44GY1RExBBEfPB495qwFR09L4CxYXm/CVDYQQbcUtq5QZ/fSQYLymKxSiHKFbhTd3UllHv70Ic3maC+ivm1vg==",
|
|
470
470
|
"license": "Apache-2.0",
|
|
471
471
|
"dependencies": {
|
|
472
472
|
"@dagrejs/dagre": "^3.0.0",
|
|
@@ -482,9 +482,9 @@
|
|
|
482
482
|
}
|
|
483
483
|
},
|
|
484
484
|
"node_modules/@intentius/chant-k8s-client": {
|
|
485
|
-
"version": "0.44.
|
|
486
|
-
"resolved": "https://registry.npmjs.org/@intentius/chant-k8s-client/-/chant-k8s-client-0.44.
|
|
487
|
-
"integrity": "sha512-
|
|
485
|
+
"version": "0.44.8",
|
|
486
|
+
"resolved": "https://registry.npmjs.org/@intentius/chant-k8s-client/-/chant-k8s-client-0.44.8.tgz",
|
|
487
|
+
"integrity": "sha512-k+w06ewOdUvaak9lnRRauy+xobDNfljht6/VVO0UtgCEv97xd1bqWWjzo16Zi0A0Ce0T1hls6h95JSGrxoRmpQ==",
|
|
488
488
|
"license": "Apache-2.0",
|
|
489
489
|
"optional": true,
|
|
490
490
|
"dependencies": {
|
|
@@ -492,19 +492,19 @@
|
|
|
492
492
|
}
|
|
493
493
|
},
|
|
494
494
|
"node_modules/@intentius/chant-lexicon-k8s": {
|
|
495
|
-
"version": "0.44.
|
|
496
|
-
"resolved": "https://registry.npmjs.org/@intentius/chant-lexicon-k8s/-/chant-lexicon-k8s-0.44.
|
|
497
|
-
"integrity": "sha512-
|
|
495
|
+
"version": "0.44.8",
|
|
496
|
+
"resolved": "https://registry.npmjs.org/@intentius/chant-lexicon-k8s/-/chant-lexicon-k8s-0.44.8.tgz",
|
|
497
|
+
"integrity": "sha512-4jU+NzhyobLQZVBAnG40RgrD6AFnnJzLIoHPzZhXzitLsXgGuLblhpPDI2ZXWKdUOxZX9nkeqNkhoQF9tuFV0A==",
|
|
498
498
|
"license": "Apache-2.0",
|
|
499
499
|
"dependencies": {
|
|
500
500
|
"@types/js-yaml": "^4.0.9",
|
|
501
501
|
"js-yaml": "^4.1.1"
|
|
502
502
|
},
|
|
503
503
|
"optionalDependencies": {
|
|
504
|
-
"@intentius/chant-k8s-client": "^0.44.
|
|
504
|
+
"@intentius/chant-k8s-client": "^0.44.8"
|
|
505
505
|
},
|
|
506
506
|
"peerDependencies": {
|
|
507
|
-
"@intentius/chant": "^0.44.
|
|
507
|
+
"@intentius/chant": "^0.44.8",
|
|
508
508
|
"typescript": "^5.9.3",
|
|
509
509
|
"zod": "^4.3.6"
|
|
510
510
|
}
|
package/example-carve/README.md
CHANGED
|
@@ -170,14 +170,21 @@ no AWS account and no network. The only dependency beyond this directory is
|
|
|
170
170
|
`@cdktf/hcl2json`, which chant lazy-loads and names in its error if absent. This
|
|
171
171
|
tier guarantees the first thirty seconds of the video.
|
|
172
172
|
|
|
173
|
-
**`--live` is the full video.**
|
|
174
|
-
`
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
it, chant adopted it, and it
|
|
173
|
+
**`--live` is the full video.** `behold demo carve --live` boots a scratch
|
|
174
|
+
Floci (`behold-carve-floci`, its own port, deleted on exit), arms
|
|
175
|
+
`legacy-tf/floci-override.tf.disabled` (see its header) into the demo copy, and
|
|
176
|
+
`terraform apply -target`s the starred resources into it — so the tfstate the
|
|
177
|
+
advisor reads is one terraform really wrote. At Handoff the stepper gains a
|
|
178
|
+
read-only **terraform plan** button: before `state rm` it shows a no-op, after
|
|
179
|
+
it the carved bucket is simply gone from Terraform's world — `0 to destroy`.
|
|
180
|
+
The line the tier exists for is "Terraform forgot it, chant adopted it, and it
|
|
181
|
+
never blinked."
|
|
182
|
+
|
|
183
|
+
One beat stays caption rather than footage for now: `chant lifecycle diff
|
|
184
|
+
--live` reading the bucket clean while Terraform still owns it. chant's AWS
|
|
185
|
+
observe is CFN-stack-scoped by logical id, so a Terraform-owned resource reads
|
|
186
|
+
confirmed-missing regardless of its existence — chant#1647 tracks the
|
|
187
|
+
physical-identity read path that unlocks it.
|
|
181
188
|
|
|
182
189
|
The live tier needs `docker` and `terraform` on PATH, boots its own throwaway
|
|
183
190
|
Floci and deletes it after, and never touches an existing `floci*` container.
|
|
@@ -14,22 +14,22 @@
|
|
|
14
14
|
"app-a": {
|
|
15
15
|
"name": "behold-example-flux-app-a",
|
|
16
16
|
"dependencies": {
|
|
17
|
-
"@intentius/chant": "^0.44.
|
|
18
|
-
"@intentius/chant-lexicon-k8s": "^0.44.
|
|
17
|
+
"@intentius/chant": "^0.44.8",
|
|
18
|
+
"@intentius/chant-lexicon-k8s": "^0.44.8"
|
|
19
19
|
}
|
|
20
20
|
},
|
|
21
21
|
"app-b": {
|
|
22
22
|
"name": "behold-example-flux-app-b",
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@intentius/chant": "^0.44.
|
|
25
|
-
"@intentius/chant-lexicon-k8s": "^0.44.
|
|
24
|
+
"@intentius/chant": "^0.44.8",
|
|
25
|
+
"@intentius/chant-lexicon-k8s": "^0.44.8"
|
|
26
26
|
}
|
|
27
27
|
},
|
|
28
28
|
"control-plane": {
|
|
29
29
|
"name": "behold-example-flux-control-plane",
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@intentius/chant": "^0.44.
|
|
32
|
-
"@intentius/chant-lexicon-k8s": "^0.44.
|
|
31
|
+
"@intentius/chant": "^0.44.8",
|
|
32
|
+
"@intentius/chant-lexicon-k8s": "^0.44.8"
|
|
33
33
|
}
|
|
34
34
|
},
|
|
35
35
|
"node_modules/@dagrejs/dagre": {
|
|
@@ -464,9 +464,9 @@
|
|
|
464
464
|
}
|
|
465
465
|
},
|
|
466
466
|
"node_modules/@intentius/chant": {
|
|
467
|
-
"version": "0.44.
|
|
468
|
-
"resolved": "https://registry.npmjs.org/@intentius/chant/-/chant-0.44.
|
|
469
|
-
"integrity": "sha512-
|
|
467
|
+
"version": "0.44.8",
|
|
468
|
+
"resolved": "https://registry.npmjs.org/@intentius/chant/-/chant-0.44.8.tgz",
|
|
469
|
+
"integrity": "sha512-c44GY1RExBBEfPB495qwFR09L4CxYXm/CVDYQQbcUtq5QZ/fSQYLymKxSiHKFbhTd3UllHv70Ic3maC+ivm1vg==",
|
|
470
470
|
"license": "Apache-2.0",
|
|
471
471
|
"dependencies": {
|
|
472
472
|
"@dagrejs/dagre": "^3.0.0",
|
|
@@ -482,9 +482,9 @@
|
|
|
482
482
|
}
|
|
483
483
|
},
|
|
484
484
|
"node_modules/@intentius/chant-k8s-client": {
|
|
485
|
-
"version": "0.44.
|
|
486
|
-
"resolved": "https://registry.npmjs.org/@intentius/chant-k8s-client/-/chant-k8s-client-0.44.
|
|
487
|
-
"integrity": "sha512-
|
|
485
|
+
"version": "0.44.8",
|
|
486
|
+
"resolved": "https://registry.npmjs.org/@intentius/chant-k8s-client/-/chant-k8s-client-0.44.8.tgz",
|
|
487
|
+
"integrity": "sha512-k+w06ewOdUvaak9lnRRauy+xobDNfljht6/VVO0UtgCEv97xd1bqWWjzo16Zi0A0Ce0T1hls6h95JSGrxoRmpQ==",
|
|
488
488
|
"license": "Apache-2.0",
|
|
489
489
|
"optional": true,
|
|
490
490
|
"dependencies": {
|
|
@@ -492,19 +492,19 @@
|
|
|
492
492
|
}
|
|
493
493
|
},
|
|
494
494
|
"node_modules/@intentius/chant-lexicon-k8s": {
|
|
495
|
-
"version": "0.44.
|
|
496
|
-
"resolved": "https://registry.npmjs.org/@intentius/chant-lexicon-k8s/-/chant-lexicon-k8s-0.44.
|
|
497
|
-
"integrity": "sha512-
|
|
495
|
+
"version": "0.44.8",
|
|
496
|
+
"resolved": "https://registry.npmjs.org/@intentius/chant-lexicon-k8s/-/chant-lexicon-k8s-0.44.8.tgz",
|
|
497
|
+
"integrity": "sha512-4jU+NzhyobLQZVBAnG40RgrD6AFnnJzLIoHPzZhXzitLsXgGuLblhpPDI2ZXWKdUOxZX9nkeqNkhoQF9tuFV0A==",
|
|
498
498
|
"license": "Apache-2.0",
|
|
499
499
|
"dependencies": {
|
|
500
500
|
"@types/js-yaml": "^4.0.9",
|
|
501
501
|
"js-yaml": "^4.1.1"
|
|
502
502
|
},
|
|
503
503
|
"optionalDependencies": {
|
|
504
|
-
"@intentius/chant-k8s-client": "^0.44.
|
|
504
|
+
"@intentius/chant-k8s-client": "^0.44.8"
|
|
505
505
|
},
|
|
506
506
|
"peerDependencies": {
|
|
507
|
-
"@intentius/chant": "^0.44.
|
|
507
|
+
"@intentius/chant": "^0.44.8",
|
|
508
508
|
"typescript": "^5.9.3",
|
|
509
509
|
"zod": "^4.3.6"
|
|
510
510
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@intentius/behold",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "behold — a live control plane on chant. See your whole estate (every substrate in one graph), coloured by drift; act through delegated, gated Ops.",
|
|
6
6
|
"bin": {
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"@hono/node-server": "^2.0.5",
|
|
38
38
|
"@intentius/chant": "^0.38.0",
|
|
39
39
|
"@intentius/chant-k8s-client": "^0.44.5",
|
|
40
|
-
"@intentius/pinhole": "^0.3.
|
|
40
|
+
"@intentius/pinhole": "^0.3.4",
|
|
41
41
|
"hono": "^4.12.34"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
package/web/app.js
CHANGED
|
@@ -1233,6 +1233,32 @@ const carveActions = {
|
|
|
1233
1233
|
},
|
|
1234
1234
|
runEmit: () => runCarveStep("emit"),
|
|
1235
1235
|
runBridge: () => runCarveStep("bridge"),
|
|
1236
|
+
async runPlan() {
|
|
1237
|
+
if (carveState.busy) return;
|
|
1238
|
+
carveState.busy = "plan";
|
|
1239
|
+
carveState.error = null;
|
|
1240
|
+
renderPanelCarve();
|
|
1241
|
+
try {
|
|
1242
|
+
const res = await fetch("/api/carve/plan", {
|
|
1243
|
+
method: "POST",
|
|
1244
|
+
headers: { "content-type": "application/json" },
|
|
1245
|
+
body: "{}",
|
|
1246
|
+
});
|
|
1247
|
+
const body = await res.json().catch(() => ({ error: "plan returned an unreadable body", remedy: "" }));
|
|
1248
|
+
if (!res.ok || body.error) {
|
|
1249
|
+
carveState.error = { step: "handoff", ...body };
|
|
1250
|
+
showToast(`✗ terraform plan: ${body.error || res.status}`, false);
|
|
1251
|
+
} else {
|
|
1252
|
+
carveState.plan = body;
|
|
1253
|
+
showToast(body.noDestroy ? "✓ plan: nothing to destroy" : "✗ plan wants to destroy something", body.noDestroy);
|
|
1254
|
+
}
|
|
1255
|
+
} catch (err) {
|
|
1256
|
+
carveState.error = { step: "handoff", error: String((err && err.message) || err), remedy: "Is the behold server still running?" };
|
|
1257
|
+
} finally {
|
|
1258
|
+
carveState.busy = null;
|
|
1259
|
+
renderPanelCarve();
|
|
1260
|
+
}
|
|
1261
|
+
},
|
|
1236
1262
|
copy(text, el) {
|
|
1237
1263
|
const done = () => {
|
|
1238
1264
|
el.dataset.copied = "1";
|
package/web/carve-steps.js
CHANGED
|
@@ -63,7 +63,10 @@ export function initialCarveState() {
|
|
|
63
63
|
bridge: null,
|
|
64
64
|
/** Set when the user says they ran the handoff commands. */
|
|
65
65
|
handoff: false,
|
|
66
|
-
/**
|
|
66
|
+
/** POST /api/carve/plan's answer (live tier only). Estate-wide, so a new
|
|
67
|
+
* pick keeps it — the plan is about the armed Terraform, not the pick. */
|
|
68
|
+
plan: null,
|
|
69
|
+
/** Which step is mid-run ("emit" | "bridge" | "plan" | null). */
|
|
67
70
|
busy: null,
|
|
68
71
|
/** The last structured refusal, keyed by step id. */
|
|
69
72
|
error: null,
|
|
@@ -378,7 +381,7 @@ export function renderCarvePanel(host, state, ctx, actions) {
|
|
|
378
381
|
pick: () => renderPick(body, state, ctx, actions),
|
|
379
382
|
emit: () => renderEmit(body, state, ctx, actions),
|
|
380
383
|
bridge: () => renderBridge(body, state, ctx, actions),
|
|
381
|
-
handoff: () => renderHandoff(body, state, actions),
|
|
384
|
+
handoff: () => renderHandoff(body, state, ctx, actions),
|
|
382
385
|
done: () => renderDone(body, state, ctx, actions),
|
|
383
386
|
};
|
|
384
387
|
renderers[step.id]();
|
|
@@ -426,6 +429,17 @@ function renderAdvise(body, ctx) {
|
|
|
426
429
|
`Terraform read from ${ctx.demo.fromLabel}/ in your demo copy. Emit and bridge write only into ${ctx.demo.outLabel}/.`,
|
|
427
430
|
),
|
|
428
431
|
);
|
|
432
|
+
if (ctx.demo.live) {
|
|
433
|
+
body.appendChild(
|
|
434
|
+
el(
|
|
435
|
+
"p",
|
|
436
|
+
"panel-muted carve-live-note",
|
|
437
|
+
`Live tier: the starred resources are REALLY applied into a scratch Floci (${ctx.demo.live.endpoint}, ` +
|
|
438
|
+
"deleted when this server exits) and the tfstate the advisor read was written by terraform, not shipped. " +
|
|
439
|
+
"The observe beat — chant reading the bucket live while Terraform still owns it — waits on chant#1647.",
|
|
440
|
+
),
|
|
441
|
+
);
|
|
442
|
+
}
|
|
429
443
|
}
|
|
430
444
|
}
|
|
431
445
|
|
|
@@ -544,12 +558,38 @@ function renderBridge(body, state, ctx, actions) {
|
|
|
544
558
|
for (const a of b.proposals || []) body.appendChild(artifactBlock(a, actions));
|
|
545
559
|
}
|
|
546
560
|
|
|
547
|
-
function renderHandoff(body, state, actions) {
|
|
561
|
+
function renderHandoff(body, state, ctx, actions) {
|
|
548
562
|
const runbook = state.bridge && state.bridge.runbook;
|
|
549
563
|
if (!runbook) {
|
|
550
564
|
body.appendChild(el("p", "panel-muted", blockedReason(state, "handoff") || "no runbook yet"));
|
|
551
565
|
return;
|
|
552
566
|
}
|
|
567
|
+
|
|
568
|
+
// The live tier's read-only beat (#254): a REAL `terraform plan`. Before the
|
|
569
|
+
// operator pastes `state rm` it shows a no-op; after, the carved resource is
|
|
570
|
+
// simply absent from Terraform's world — not destroyed. That verdict line is
|
|
571
|
+
// the reason the tier exists.
|
|
572
|
+
if (ctx.demo && ctx.demo.live) {
|
|
573
|
+
const planBtn = el("button", "act carve-plan", state.busy === "plan" ? "planning…" : "▶ terraform plan (read-only)");
|
|
574
|
+
planBtn.disabled = !!state.busy;
|
|
575
|
+
planBtn.title = "Runs terraform plan in the demo copy against the scratch Floci. Reads everything, changes nothing.";
|
|
576
|
+
planBtn.addEventListener("click", actions.runPlan);
|
|
577
|
+
body.appendChild(planBtn);
|
|
578
|
+
if (state.plan) {
|
|
579
|
+
const verdict = el("div", "carve-plan-verdict");
|
|
580
|
+
verdict.appendChild(el("code", null, state.plan.planLine));
|
|
581
|
+
verdict.appendChild(
|
|
582
|
+
el(
|
|
583
|
+
"p",
|
|
584
|
+
"panel-muted",
|
|
585
|
+
state.plan.noDestroy
|
|
586
|
+
? "0 to destroy — the resource never blinked."
|
|
587
|
+
: "The plan wants to destroy something. Stop and read it before touching the estate.",
|
|
588
|
+
),
|
|
589
|
+
);
|
|
590
|
+
body.appendChild(verdict);
|
|
591
|
+
}
|
|
592
|
+
}
|
|
553
593
|
// The one place the walkthrough refuses to be a button, stated out loud.
|
|
554
594
|
const why = el("div", "carve-refusal carve-human");
|
|
555
595
|
why.appendChild(el("div", "carve-refusal-title", "These two are not buttons, on purpose."));
|
|
@@ -605,13 +645,20 @@ function renderDone(body, state, ctx, actions) {
|
|
|
605
645
|
if (!state.handoff) {
|
|
606
646
|
body.appendChild(el("p", "panel-muted", "The handoff commands haven't been marked as run — this is the shape of the ending, not a claim about your estate."));
|
|
607
647
|
}
|
|
648
|
+
if (state.pick) {
|
|
649
|
+
const watch = el("a", "act carve-morph-link", "▶ watch it move");
|
|
650
|
+
watch.href = "/carve/morph?select=" + encodeURIComponent(state.pick.node.id);
|
|
651
|
+
watch.target = "_blank";
|
|
652
|
+
watch.rel = "noopener";
|
|
653
|
+
watch.title = "The estate frame, morphing: the card slides out of the Terraform box and into the chant project box beside last month's carves.";
|
|
654
|
+
body.appendChild(watch);
|
|
655
|
+
}
|
|
608
656
|
body.appendChild(
|
|
609
657
|
el(
|
|
610
658
|
"p",
|
|
611
659
|
"panel-muted carve-honesty",
|
|
612
|
-
"Deferred to the follow-up: the
|
|
613
|
-
"
|
|
614
|
-
"plan showing no destroy) is the other half of that follow-up.",
|
|
660
|
+
"Deferred to the follow-up: the Floci `--live` tier — a real terraform apply into a scratch emulator, " +
|
|
661
|
+
"a real plan showing no destroy.",
|
|
615
662
|
),
|
|
616
663
|
);
|
|
617
664
|
const again = el("button", "act", "↺ start over");
|