@codeyam-editor/codeyam-editor 0.1.8 → 0.1.9
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/npm/cloud.js +361 -18
- package/npm/editor-dev.js +24 -2
- package/npm/editor.js +15 -5
- package/npm/fleet-add-failure.js +355 -21
- package/npm/fleet-branch.js +124 -1
- package/npm/fleet-build-data.js +12 -1
- package/npm/fleet-cleanup.js +84 -3
- package/npm/fleet-datastore-health.js +107 -0
- package/npm/fleet-revision.js +108 -0
- package/npm/fleet-run-setup.js +66 -0
- package/npm/fleet-session-activity.js +162 -0
- package/npm/fleet-vm-readiness.js +57 -16
- package/npm/launch-config.js +126 -2
- package/npm/scenario-check.js +697 -34
- package/npm/scenario-interactivity.js +477 -23
- package/npm/scenario-issues.js +61 -0
- package/npm/scenario-playwright.js +67 -14
- package/npm/utils.js +262 -22
- package/package.json +5 -5
- package/ui/dist/assets/{ContainerInspector-DSVZvEkB.js → ContainerInspector-C7I9wctz.js} +1 -1
- package/ui/dist/assets/EditorShell-9aYMeOdZ.js +330 -0
- package/ui/dist/assets/EditorShell-CE8iG_EM.css +1 -0
- package/ui/dist/assets/ScenarioDataPanel-CLwkrex0.js +18 -0
- package/ui/dist/assets/{ScenarioDataPanelFullPage-Cyk6efiI.js → ScenarioDataPanelFullPage-DT0TZC4M.js} +1 -1
- package/ui/dist/assets/ScenarioRenderer-Bsarhcrl.js +21 -0
- package/ui/dist/assets/index-BRoAenc1.css +1 -0
- package/ui/dist/assets/{index-BPLKgwon.js → index-iMR5siiG.js} +9 -9
- package/ui/dist/index.html +2 -2
- package/ui/dist/preview-subdir-demo/index.html +62 -0
- package/ui/dist/preview-subdir-demo/sibling.html +48 -0
- package/ui/dist/assets/EditorShell-DnXdHrar.js +0 -256
- package/ui/dist/assets/EditorShell-FvbXyqqD.css +0 -1
- package/ui/dist/assets/ScenarioDataPanel-6qJE-0lA.js +0 -15
- package/ui/dist/assets/ScenarioRenderer-Uyt54vHI.js +0 -21
- package/ui/dist/assets/index-DKpgJLo3.css +0 -1
|
@@ -71,6 +71,12 @@
|
|
|
71
71
|
// 'cli-missing' — the editor is up but the build-agent CLI isn't installed
|
|
72
72
|
// (`providerAuth.cliInstalled === false`): a provisioning
|
|
73
73
|
// fault, not a usable VM.
|
|
74
|
+
// 'cli-broken' — the CLI IS installed but will not execute
|
|
75
|
+
// (`providerAuth.cliRunnable === false`): a provisioning
|
|
76
|
+
// fault whose remedy is repair/reinstall, not a fresh
|
|
77
|
+
// install and not re-authentication. Distinct from
|
|
78
|
+
// 'cli-missing' because sending an operator to "install it"
|
|
79
|
+
// when it is already installed wastes the incident.
|
|
74
80
|
// 'auth-unsettled' — the auth probe is wedged at `unknown` (timing out — the
|
|
75
81
|
// exact VM5 auth shape): we can't even tell if login is
|
|
76
82
|
// needed. A provisioning fault distinct from a clean
|
|
@@ -89,6 +95,7 @@ const READINESS_STATES = [
|
|
|
89
95
|
'broker-down',
|
|
90
96
|
'ws-dead',
|
|
91
97
|
'cli-missing',
|
|
98
|
+
'cli-broken',
|
|
92
99
|
'auth-unsettled',
|
|
93
100
|
'needs-login',
|
|
94
101
|
'ready',
|
|
@@ -100,23 +107,41 @@ const fpub = require('./fleet-public-url');
|
|
|
100
107
|
|
|
101
108
|
// Classify the build-agent usability axis from the editor's own `providerAuth`
|
|
102
109
|
// subtree (vm: Readiness Verifies Real Usability). control-api emits the SAME
|
|
103
|
-
// `providerAuth` (with `cliInstalled` + settled `state`) on BOTH
|
|
104
|
-
// /api/session-info via build_provider_auth_json, so the
|
|
105
|
-
// straight off the gathered VM record — no re-derivation.
|
|
110
|
+
// `providerAuth` (with `cliInstalled` + `cliRunnable` + settled `state`) on BOTH
|
|
111
|
+
// /api/config and /api/session-info via build_provider_auth_json, so the
|
|
112
|
+
// dashboard reads it straight off the gathered VM record — no re-derivation.
|
|
113
|
+
// Returns one verdict:
|
|
106
114
|
// 'cli-missing' — `cliInstalled === false`: a provisioning fault.
|
|
107
|
-
// '
|
|
108
|
-
//
|
|
109
|
-
// '
|
|
115
|
+
// 'cli-broken' — CLI installed but `cliRunnable === false`: it resolves
|
|
116
|
+
// and will not execute. A provisioning fault.
|
|
117
|
+
// 'auth-unsettled' — CLI installed and runnable (or unprobed) but
|
|
118
|
+
// `state === 'unknown'` (probe wedged / timing out — the
|
|
119
|
+
// VM5 shape): a provisioning fault.
|
|
120
|
+
// 'needs-login' — CLI usable, `state === 'needs-login'`: a LEGITIMATE,
|
|
110
121
|
// actionable state, not a fault.
|
|
111
|
-
// 'ready' — CLI
|
|
122
|
+
// 'ready' — CLI usable, `state === 'authenticated'`: usable.
|
|
112
123
|
// 'unknown' — providerAuth absent (older editor / probe never landed),
|
|
113
124
|
// or any other shape: absence of evidence, never a fault.
|
|
114
|
-
// Only an EXPLICIT `cliInstalled === false` / `
|
|
115
|
-
// missing fields stay 'unknown' so an older
|
|
125
|
+
// Only an EXPLICIT `cliInstalled === false` / `cliRunnable === false` /
|
|
126
|
+
// `state === 'unknown'` is a fault; missing fields stay 'unknown' so an older
|
|
127
|
+
// editor never reads as broken.
|
|
128
|
+
//
|
|
129
|
+
// The `cli-broken` check sits BEFORE the state checks deliberately, and that
|
|
130
|
+
// ordering IS the fix. A CLI that will not execute cannot have authenticated,
|
|
131
|
+
// so `state: 'authenticated'` alongside `cliRunnable: false` is not a
|
|
132
|
+
// contradiction to be resolved in the state's favour — it is precisely the
|
|
133
|
+
// false green observed on VM-2 and VM-7 through the whole 2026-08-19/20 npm
|
|
134
|
+
// publish race, where the card read `cliInstalled: true, state: "authenticated"`
|
|
135
|
+
// while both boxes were unusable and the breakage was found only by ssh.
|
|
136
|
+
//
|
|
137
|
+
// `cliRunnable` is deliberately NOT required to be `true` to proceed: an older
|
|
138
|
+
// editor server omits it entirely (`undefined`), and treating that as a fault
|
|
139
|
+
// would mark every not-yet-upgraded VM in the fleet broken.
|
|
116
140
|
function classifyAgentUsability(providerAuth) {
|
|
117
141
|
if (!providerAuth || typeof providerAuth !== 'object') return 'unknown';
|
|
118
142
|
if (providerAuth.cliInstalled === false) return 'cli-missing';
|
|
119
143
|
if (providerAuth.cliInstalled !== true) return 'unknown';
|
|
144
|
+
if (providerAuth.cliRunnable === false) return 'cli-broken';
|
|
120
145
|
if (providerAuth.state === 'unknown') return 'auth-unsettled';
|
|
121
146
|
if (providerAuth.state === 'needs-login') return 'needs-login';
|
|
122
147
|
if (providerAuth.state === 'authenticated') return 'ready';
|
|
@@ -126,8 +151,13 @@ function classifyAgentUsability(providerAuth) {
|
|
|
126
151
|
// Map the agent-usability verdict to the tri-state the rostering gate
|
|
127
152
|
// (fleet-cleanup's reapVerdict) consumes, mirroring `projectWired`'s
|
|
128
153
|
// true|false|null contract so a flaky probe can never strand a healthy VM:
|
|
129
|
-
// false — a PROVISIONING FAULT ('cli-missing' / '
|
|
130
|
-
// not usable, so don't roster it as
|
|
154
|
+
// false — a PROVISIONING FAULT ('cli-missing' / 'cli-broken' /
|
|
155
|
+
// 'auth-unsettled'): the box is not usable, so don't roster it as
|
|
156
|
+
// healthy (the VM5 false-green). 'cli-broken' joins the fault set
|
|
157
|
+
// because a CLI that will not execute makes the VM exactly as
|
|
158
|
+
// unusable as one that was never installed — the two differ only in
|
|
159
|
+
// the remedy an operator should reach for, not in whether the box can
|
|
160
|
+
// do work.
|
|
131
161
|
// true — usable enough to roster: 'ready', or a legitimate 'needs-login'
|
|
132
162
|
// (the user must auth, but that's an expected state, not a
|
|
133
163
|
// half-provisioned box that should be held back).
|
|
@@ -135,7 +165,13 @@ function classifyAgentUsability(providerAuth) {
|
|
|
135
165
|
// evidence must NEVER block rostering, exactly like reapVerdict's null
|
|
136
166
|
// tolerance for projectWired.
|
|
137
167
|
function agentRosterReady(verdict) {
|
|
138
|
-
if (
|
|
168
|
+
if (
|
|
169
|
+
verdict === 'cli-missing' ||
|
|
170
|
+
verdict === 'cli-broken' ||
|
|
171
|
+
verdict === 'auth-unsettled'
|
|
172
|
+
) {
|
|
173
|
+
return false;
|
|
174
|
+
}
|
|
139
175
|
if (verdict === 'ready' || verdict === 'needs-login') return true;
|
|
140
176
|
return null;
|
|
141
177
|
}
|
|
@@ -206,12 +242,17 @@ function deriveVmReadiness(v, ctx) {
|
|
|
206
242
|
if (v.publicWsHealth === 'unwired' && !(bh && bh.editor === 'healthy')) return 'ws-dead';
|
|
207
243
|
}
|
|
208
244
|
// 6. Agent usability: the box is up and the WS path carries frames, but the
|
|
209
|
-
// build agent itself may not be usable — CLI not installed,
|
|
210
|
-
// probe wedged at 'unknown'. A legitimate
|
|
211
|
-
// actionable state (Authenticate),
|
|
212
|
-
// Absence of providerAuth (older
|
|
245
|
+
// build agent itself may not be usable — CLI not installed, CLI installed
|
|
246
|
+
// but not executable, or the auth probe wedged at 'unknown'. A legitimate
|
|
247
|
+
// 'needs-login' surfaces as its own actionable state (Authenticate),
|
|
248
|
+
// distinct from a provisioning fault. Absence of providerAuth (older
|
|
249
|
+
// editor / unprobed) falls through to ready.
|
|
213
250
|
const usability = classifyAgentUsability(v.providerAuth);
|
|
214
251
|
if (usability === 'cli-missing') return 'cli-missing';
|
|
252
|
+
// Surfaced as its own readiness state rather than folded into 'cli-missing':
|
|
253
|
+
// the card's whole job here is to send an operator to the right remedy, and
|
|
254
|
+
// "reinstall the broken CLI" is not "install the missing one".
|
|
255
|
+
if (usability === 'cli-broken') return 'cli-broken';
|
|
215
256
|
if (usability === 'auth-unsettled') return 'auth-unsettled';
|
|
216
257
|
if (usability === 'needs-login') return 'needs-login';
|
|
217
258
|
// 6b. Preview-host IAP gap: the editor surface grants access (the URL routes
|
package/npm/launch-config.js
CHANGED
|
@@ -119,6 +119,79 @@ function validateLaunchConfig(cfg) {
|
|
|
119
119
|
return { ok: true };
|
|
120
120
|
}
|
|
121
121
|
|
|
122
|
+
/** The branch refs a launch config asks git to resolve, as
|
|
123
|
+
* `[{ field, repo, branch }]` — one entry per branch-carrying field that is
|
|
124
|
+
* actually populated. This is the input to a ref-existence preflight; it lives
|
|
125
|
+
* beside normalizeLaunchConfig (rather than in a second parser at the add
|
|
126
|
+
* endpoint or in cloud.js) because it reads the SAME canonical field names that
|
|
127
|
+
* normalizer produces, so a field rename can never leave the two disagreeing.
|
|
128
|
+
*
|
|
129
|
+
* BOTH fields are reported, deliberately. In the 2026-08-25 incident the dead
|
|
130
|
+
* ref sat in `clientBranch` AND `editorBranch`, and it was `clientBranch` that
|
|
131
|
+
* actually killed the bootstrap — an editor-only check would have missed it.
|
|
132
|
+
*
|
|
133
|
+
* A greenfield add (`newProjectName`, no `clientRepo`) has no client repo to
|
|
134
|
+
* resolve a branch against, so its clientBranch is skipped rather than probed
|
|
135
|
+
* against a repo URL that does not exist. `editorRepo` is passed in because the
|
|
136
|
+
* launch config carries an editor BRANCH but not an editor REPO — the repo is
|
|
137
|
+
* the caller's (cloud:up resolves it via resolveEditorConfig; the dashboard
|
|
138
|
+
* holds it in REPO). Pure; no I/O. */
|
|
139
|
+
function branchRefTargets(cfg, { editorRepo } = {}) {
|
|
140
|
+
const c = cfg || {};
|
|
141
|
+
const targets = [];
|
|
142
|
+
if (c.clientRepo && c.clientBranch) {
|
|
143
|
+
targets.push({ field: "clientBranch", repo: c.clientRepo, branch: c.clientBranch });
|
|
144
|
+
}
|
|
145
|
+
if (editorRepo && c.editorBranch) {
|
|
146
|
+
targets.push({ field: "editorBranch", repo: editorRepo, branch: c.editorBranch });
|
|
147
|
+
}
|
|
148
|
+
return targets;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/** Partition branchRefTargets by whether each ref still resolves, using an
|
|
152
|
+
* INJECTED probe so the decision logic is unit-tested with no network and no
|
|
153
|
+
* git. `refExists({ repo, branch })` returns true when the ref is present,
|
|
154
|
+
* false when it is provably gone.
|
|
155
|
+
*
|
|
156
|
+
* A probe that cannot reach the remote at all must NOT report false — a network
|
|
157
|
+
* or credential fault is not evidence a branch was deleted, and treating it as
|
|
158
|
+
* such would refuse every add the moment GitHub blipped. The probe signals that
|
|
159
|
+
* by returning `null`/`undefined`, which this treats as "unknown, let it
|
|
160
|
+
* through": the classifier arm downstream still catches a genuinely dead ref,
|
|
161
|
+
* at the cost this preflight normally saves. Fail-open is the right default
|
|
162
|
+
* here precisely because the preflight is an OPTIMIZATION over a failure that
|
|
163
|
+
* is already handled, never the only line of defence. Pure; no I/O. */
|
|
164
|
+
function missingBranchRefs(targets, refExists) {
|
|
165
|
+
const missing = [];
|
|
166
|
+
for (const t of targets || []) {
|
|
167
|
+
if (refExists(t) === false) missing.push(t);
|
|
168
|
+
}
|
|
169
|
+
return missing;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/** Operator-facing copy for a failed branch-ref preflight. States the OUTCOME
|
|
173
|
+
* first (nothing was created), then the mechanism, then the remedy — and never
|
|
174
|
+
* the "a partial instance may exist — reconcile or destroy it" language the
|
|
175
|
+
* generic provisioning failure uses, which is what made the real incident
|
|
176
|
+
* confusing: at preflight time no instance, disk, NEG or backend service has
|
|
177
|
+
* been created, so there is nothing to reconcile and nothing to destroy.
|
|
178
|
+
*
|
|
179
|
+
* Names which FIELD carried the dead ref, because the two are configured in
|
|
180
|
+
* different places and an operator who only corrects one re-issues a request
|
|
181
|
+
* that fails identically. Pure; no I/O. */
|
|
182
|
+
function missingBranchRefMessage(missing) {
|
|
183
|
+
const list = (missing || [])
|
|
184
|
+
.map((m) => `${m.field}: branch '${m.branch}' is not on ${m.repo}`)
|
|
185
|
+
.join("; ");
|
|
186
|
+
return (
|
|
187
|
+
`Branch no longer exists on origin — ${list}. ` +
|
|
188
|
+
"Nothing was provisioned: this is checked before any instance is created, so " +
|
|
189
|
+
"there is no VM, disk or backend service to clean up. This repo prunes branches " +
|
|
190
|
+
"when they merge, so a stored branch name expires silently. Re-add on a live " +
|
|
191
|
+
"branch (the current fleet branch, or any branch that still exists on origin)."
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
|
|
122
195
|
/** Pre-flight the operator-host CONFIG a dashboard Add depends on, BEFORE
|
|
123
196
|
* spawning the ~20-min cloud:up. Pure classifier: it takes the ALREADY-resolved
|
|
124
197
|
* zone, project, and GitHub token (the side-effecting resolution — env lookups,
|
|
@@ -522,11 +595,42 @@ function recentlyLaunchedWithin(ts, nowMs, graceMs) {
|
|
|
522
595
|
* variant(@branch) fallback.
|
|
523
596
|
* - source "live" when editorIdentity drove editorLabel, else
|
|
524
597
|
* "history" — the affordance the card uses to mark inferred
|
|
525
|
-
* data.
|
|
598
|
+
* data.
|
|
599
|
+
* - deployedBranch the branch the fleet ADVANCED this VM to, from the editor's
|
|
600
|
+
* `editorDeployment` (which reads the record the refresh script
|
|
601
|
+
* writes at install time); null when the editor reports none.
|
|
602
|
+
* - deployedTag the full artifact tag behind that branch, for the hover.
|
|
603
|
+
* - deployedSource "live" when the editor reported a deployment, else
|
|
604
|
+
* "unknown".
|
|
605
|
+
* - deployedState the card's three-way presentation verdict, resolved HERE
|
|
606
|
+
* rather than in the browser so the rule is unit-tested in one
|
|
607
|
+
* place and index.html carries no copy of it (the compromise
|
|
608
|
+
* groupVmsByProject's own comment apologizes for, avoidable
|
|
609
|
+
* here because this value rides in the /data payload):
|
|
610
|
+
* "agrees" deployment and provenance name the same branch
|
|
611
|
+
* "differs" they disagree — the EXPECTED plan-only-retag
|
|
612
|
+
* state, to be shown as a pair, never badged
|
|
613
|
+
* "unknown" no deployment reported
|
|
614
|
+
*
|
|
615
|
+
* editorLabel and deployedBranch answer DIFFERENT questions and are routinely
|
|
616
|
+
* expected to disagree. editorLabel is build provenance — what source the
|
|
617
|
+
* binary was compiled from — and stays exactly as it was; it is the right
|
|
618
|
+
* answer to "what code is this actually running?" and is what fragility #33's
|
|
619
|
+
* fix was built for. deployedBranch is the deployment target. After a plan-only
|
|
620
|
+
* advance, where fleet-advance-to.sh correctly retags the base branch's CI
|
|
621
|
+
* binary, provenance `main` + deployment `editor-improvements-78` is the
|
|
622
|
+
* CORRECT state, so the card must present the pair rather than badge it.
|
|
623
|
+
*
|
|
624
|
+
* A missing deployment resolves to null / "unknown" and is NEVER filled in from
|
|
625
|
+
* cfg.editorBranch, though that value is right there: it is written at
|
|
626
|
+
* reconfigure and is not updated by a binary refresh, so it is stale for
|
|
627
|
+
* precisely this question — the same live-over-history discipline the rest of
|
|
628
|
+
* this function applies, and the same `source` honesty. Pure; no I/O. */
|
|
526
629
|
function resolveCardIdentity(live, historyCfg) {
|
|
527
630
|
const cfg = historyCfg || {};
|
|
528
631
|
const lcp = live && live.clientProject;
|
|
529
632
|
const lei = live && live.editorIdentity;
|
|
633
|
+
const led = live && live.editorDeployment;
|
|
530
634
|
|
|
531
635
|
const historyName = cfg.clientRepo ? repoBasename(cfg.clientRepo) : (cfg.newProjectName || "");
|
|
532
636
|
const project = (lcp && lcp.name) || historyName || null;
|
|
@@ -547,7 +651,24 @@ function resolveCardIdentity(live, historyCfg) {
|
|
|
547
651
|
source = "history";
|
|
548
652
|
}
|
|
549
653
|
|
|
550
|
-
|
|
654
|
+
const deployedBranch = (led && led.branch) || null;
|
|
655
|
+
const deployedTag = (led && led.tag) || null;
|
|
656
|
+
const deployedSource = deployedBranch || deployedTag ? "live" : "unknown";
|
|
657
|
+
let deployedState;
|
|
658
|
+
if (!deployedBranch) deployedState = "unknown";
|
|
659
|
+
else if (deployedBranch === editorLabel) deployedState = "agrees";
|
|
660
|
+
else deployedState = "differs";
|
|
661
|
+
|
|
662
|
+
return {
|
|
663
|
+
project,
|
|
664
|
+
clientBranch,
|
|
665
|
+
editorLabel,
|
|
666
|
+
source,
|
|
667
|
+
deployedBranch,
|
|
668
|
+
deployedTag,
|
|
669
|
+
deployedSource,
|
|
670
|
+
deployedState,
|
|
671
|
+
};
|
|
551
672
|
}
|
|
552
673
|
|
|
553
674
|
/** Sticky-merge a VM poll's `clientProject` against the last-known-good value
|
|
@@ -1215,6 +1336,9 @@ module.exports = {
|
|
|
1215
1336
|
EMPTY_CONFIG,
|
|
1216
1337
|
normalizeLaunchConfig,
|
|
1217
1338
|
validateLaunchConfig,
|
|
1339
|
+
branchRefTargets,
|
|
1340
|
+
missingBranchRefs,
|
|
1341
|
+
missingBranchRefMessage,
|
|
1218
1342
|
addPreflight,
|
|
1219
1343
|
isVariantProvisionable,
|
|
1220
1344
|
isVariantRebuildable,
|