@codeyam-editor/codeyam-editor 0.1.0-staging.g6631ba2 → 0.1.0-staging.gac4ca32
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 +77 -16
- package/npm/ingress-invoke.js +84 -13
- package/package.json +5 -5
- package/ui/dist/assets/{ContainerInspector-B3vO6op1.js → ContainerInspector-urDZ9zOC.js} +1 -1
- package/ui/dist/assets/EditorShell-eZm_8CsG.js +230 -0
- package/ui/dist/assets/{ScenarioDataPanel-DIAw2DpS.js → ScenarioDataPanel-DuU3_hdh.js} +2 -2
- package/ui/dist/assets/{ScenarioDataPanelFullPage-CibLNYsH.js → ScenarioDataPanelFullPage-DlAIUrnk.js} +1 -1
- package/ui/dist/assets/ScenarioRenderer-od1vF8-p.js +21 -0
- package/ui/dist/assets/{index-B14nyUpe.js → index-B7_KRKaQ.js} +2 -2
- package/ui/dist/assets/index-D3csSagX.css +1 -0
- package/ui/dist/index.html +2 -2
- package/ui/dist/assets/EditorShell-CndrQhoL.js +0 -191
- package/ui/dist/assets/ScenarioRenderer-rbCbaiLe.js +0 -21
- package/ui/dist/assets/index-DK-ZOfVw.css +0 -1
package/npm/cloud.js
CHANGED
|
@@ -81,6 +81,8 @@ const {
|
|
|
81
81
|
selectOrphanIngressTargets,
|
|
82
82
|
classifyOrphanDownResult,
|
|
83
83
|
backendServicesListInvocation,
|
|
84
|
+
negListInvocation,
|
|
85
|
+
instancesListInvocation,
|
|
84
86
|
ingressConvergeDecision,
|
|
85
87
|
detachedAddTailInvocation,
|
|
86
88
|
ingressInflightMarkerPath,
|
|
@@ -3697,22 +3699,31 @@ async function cmdReconcileIngress(args, projectDir = rootDir) {
|
|
|
3697
3699
|
sweepOrphanIngress({ cfg, domain, roster, projectDir });
|
|
3698
3700
|
}
|
|
3699
3701
|
|
|
3700
|
-
/** Auto-clean orphaned LB ingress (vm--auto-clean-orphaned-lb-ingress):
|
|
3701
|
-
*
|
|
3702
|
-
* has no in-flight add. Enumerates the live
|
|
3703
|
-
* VM
|
|
3704
|
-
*
|
|
3705
|
-
*
|
|
3706
|
-
*
|
|
3707
|
-
*
|
|
3708
|
-
*
|
|
3709
|
-
* never
|
|
3710
|
-
*
|
|
3711
|
-
*
|
|
3712
|
-
*
|
|
3702
|
+
/** Auto-clean orphaned LB ingress (vm--auto-clean-orphaned-lb-ingress): LB
|
|
3703
|
+
* resources whose owning VM number is gone from the roster (a destroyed VM) and
|
|
3704
|
+
* has no in-flight add. Enumerates the live backend services AND zonal NEGs,
|
|
3705
|
+
* UNIONs their owning VM numbers (so a partial teardown whose NEG outlived its
|
|
3706
|
+
* backend service — the VM-5/6 case — still surfaces its VM as a candidate),
|
|
3707
|
+
* maps each name back to its VM number, subtracts the roster + in-flight adds +
|
|
3708
|
+
* live instances (the false-orphan guards), and runs the VERIFIED
|
|
3709
|
+
* `vm-ingress.sh <N> down` (asserts neg=no bes=no host=no, with retries) for
|
|
3710
|
+
* each orphan — so an orphan is genuinely cleared or surfaced as a real failure,
|
|
3711
|
+
* never a silent leak and never a manual command. Emits a machine-readable
|
|
3712
|
+
* `CY_ORPHAN_SWEEP <json>` summary line the dashboard parses to reconcile its
|
|
3713
|
+
* orphan-attention banner; the summary carries `stillPresent` (VM numbers that
|
|
3714
|
+
* still own ≥1 LB resource after the sweep) so the dashboard can clear a stale
|
|
3715
|
+
* banner for a VM that is now back in the roster or has no residual resources.
|
|
3716
|
+
* Best-effort per VM: one `down` failing never aborts the rest of the sweep.
|
|
3717
|
+
* Fail-safe: if the live-instance list can't be fetched, teardown is skipped for
|
|
3718
|
+
* this pass (a live-but-unrostered VM must never be torn down) while the banner
|
|
3719
|
+
* reconcile still runs. Returns the `{ cleaned, failed, skipped, stillPresent }`
|
|
3720
|
+
* summary (null when the backend-service enumeration itself failed) — the same
|
|
3721
|
+
* shape it prints — so the flow is directly assertable. `deps` is injectable so
|
|
3722
|
+
* the enumerate → select → down flow is testable without gcloud. */
|
|
3713
3723
|
function sweepOrphanIngress({ cfg, domain, roster, projectDir = rootDir } = {}, deps = {}) {
|
|
3714
3724
|
const gi = deps.googleIngress || require("./google-ingress.js");
|
|
3715
3725
|
const fcl = deps.fleetCleanup || require("./fleet-cleanup.js");
|
|
3726
|
+
const frecon = deps.fleetReconcile || require("./fleet-reconcile.js");
|
|
3716
3727
|
const run = deps.spawnSync || spawnSync;
|
|
3717
3728
|
|
|
3718
3729
|
const listInv = backendServicesListInvocation({ project: cfg && cfg.project });
|
|
@@ -3724,11 +3735,31 @@ function sweepOrphanIngress({ cfg, domain, roster, projectDir = rootDir } = {},
|
|
|
3724
3735
|
console.error(` orphan sweep skipped — could not list backend services (${tail}).`);
|
|
3725
3736
|
return null;
|
|
3726
3737
|
}
|
|
3727
|
-
const
|
|
3738
|
+
const besVmNumbers = String(listRes.stdout || "")
|
|
3728
3739
|
.split("\n")
|
|
3729
3740
|
.map((line) => gi.vmNumberFromResourceName(line.trim()))
|
|
3730
3741
|
.filter((n) => n != null);
|
|
3731
3742
|
|
|
3743
|
+
// Also enumerate zonal NEGs and union their owners in: a partial teardown can
|
|
3744
|
+
// leave a `vm-<N>-<surface>-neg` behind after its backend service is already
|
|
3745
|
+
// gone, so a backend-service-only candidate set misses that residue. Best-effort
|
|
3746
|
+
// — a NEG-list failure never aborts the backend-service-driven sweep.
|
|
3747
|
+
let negVmNumbers = [];
|
|
3748
|
+
const negInv = negListInvocation({ project: cfg && cfg.project });
|
|
3749
|
+
const negRes = run(negInv.command, negInv.args, { encoding: "utf8" });
|
|
3750
|
+
if (negRes && negRes.status === 0) {
|
|
3751
|
+
negVmNumbers = String(negRes.stdout || "")
|
|
3752
|
+
.split("\n")
|
|
3753
|
+
.map((line) => gi.vmNumberFromResourceName(line.trim()))
|
|
3754
|
+
.filter((n) => n != null);
|
|
3755
|
+
} else {
|
|
3756
|
+
const tail = (
|
|
3757
|
+
(negRes && (negRes.stderr || (negRes.error && negRes.error.message))) || ""
|
|
3758
|
+
).toString().trim().split("\n").pop() || "error";
|
|
3759
|
+
console.error(` orphan sweep: could not list NEGs (${tail}) — sweeping backend services only.`);
|
|
3760
|
+
}
|
|
3761
|
+
const ingressVmNumbers = [...new Set([...besVmNumbers, ...negVmNumbers])].sort((a, b) => a - b);
|
|
3762
|
+
|
|
3732
3763
|
// A VM in the roster, or with an in-flight `add-<n>.json` sidecar (provisioning,
|
|
3733
3764
|
// not yet rostered), is live — never an orphan.
|
|
3734
3765
|
const pendingDir = process.env.PENDING_JOBS_DIR || path.join(projectDir, ".codeyam/logs/pending-jobs");
|
|
@@ -3736,9 +3767,34 @@ function sweepOrphanIngress({ cfg, domain, roster, projectDir = rootDir } = {},
|
|
|
3736
3767
|
.listPendingJobs(pendingDir)
|
|
3737
3768
|
.filter((j) => j.action === "add")
|
|
3738
3769
|
.map((j) => j.n);
|
|
3739
|
-
const orphans = selectOrphanIngressTargets({ ingressVmNumbers, roster, inFlightAdds });
|
|
3740
3770
|
|
|
3741
|
-
|
|
3771
|
+
// Defensive live-instance guard: never tear down a VM whose GCP instance is
|
|
3772
|
+
// actually running (a roster drift could leave a live VM unrostered; a teardown
|
|
3773
|
+
// that SUCCEEDED against it would knock it offline). `liveVmNumbers === null`
|
|
3774
|
+
// means the list couldn't be fetched — fail safe by skipping teardown this pass
|
|
3775
|
+
// (below) rather than risk a false teardown. The banner reconcile still runs.
|
|
3776
|
+
let liveVmNumbers = null;
|
|
3777
|
+
const instInv = instancesListInvocation({ project: cfg && cfg.project });
|
|
3778
|
+
const instRes = run(instInv.command, instInv.args, { encoding: "utf8" });
|
|
3779
|
+
if (instRes && instRes.status === 0) {
|
|
3780
|
+
liveVmNumbers = frecon.parseRunningFleetVms(instRes.stdout, fleetInstancePrefix());
|
|
3781
|
+
} else {
|
|
3782
|
+
const tail = (
|
|
3783
|
+
(instRes && (instRes.stderr || (instRes.error && instRes.error.message))) || ""
|
|
3784
|
+
).toString().trim().split("\n").pop() || "error";
|
|
3785
|
+
console.error(` orphan sweep: could not list instances (${tail}) — skipping teardown this pass (banners still reconciled).`);
|
|
3786
|
+
}
|
|
3787
|
+
|
|
3788
|
+
const orphans = liveVmNumbers === null
|
|
3789
|
+
? []
|
|
3790
|
+
: selectOrphanIngressTargets({ ingressVmNumbers, roster, inFlightAdds, liveVmNumbers });
|
|
3791
|
+
|
|
3792
|
+
// stillPresent = every VM that owned an LB resource going in, minus the ones we
|
|
3793
|
+
// verifiably clean below. The dashboard clears a banner for any orphan VM not in
|
|
3794
|
+
// this set (or now back in the roster). Computed up front so the no-orphans and
|
|
3795
|
+
// fail-safe-skip paths report it too — that is what unfreezes a VM-3-style banner
|
|
3796
|
+
// (re-rostered, so never a sweep target) with no teardown at all.
|
|
3797
|
+
const summary = { cleaned: [], failed: [], skipped: inFlightAdds.slice(), stillPresent: ingressVmNumbers.slice() };
|
|
3742
3798
|
if (!orphans.length) {
|
|
3743
3799
|
console.error(" orphan sweep: no orphaned LB ingress found.");
|
|
3744
3800
|
console.log(`CY_ORPHAN_SWEEP ${JSON.stringify(summary)}`);
|
|
@@ -3775,6 +3831,11 @@ function sweepOrphanIngress({ cfg, domain, roster, projectDir = rootDir } = {},
|
|
|
3775
3831
|
console.error(` VM-${n}: orphaned ingress → auto-clean FAILED (${tail.slice(-200)})`);
|
|
3776
3832
|
}
|
|
3777
3833
|
}
|
|
3834
|
+
// A verifiably-cleaned VM no longer owns any LB resource — drop it from
|
|
3835
|
+
// stillPresent so the dashboard clears its banner (failed VMs stay, keeping
|
|
3836
|
+
// their real attention item).
|
|
3837
|
+
const cleanedSet = new Set(summary.cleaned);
|
|
3838
|
+
summary.stillPresent = ingressVmNumbers.filter((n) => !cleanedSet.has(n));
|
|
3778
3839
|
console.log(`CY_ORPHAN_SWEEP ${JSON.stringify(summary)}`);
|
|
3779
3840
|
return summary;
|
|
3780
3841
|
}
|
package/npm/ingress-invoke.js
CHANGED
|
@@ -178,22 +178,27 @@ function selectIngressReconcileTargets(statuses) {
|
|
|
178
178
|
}
|
|
179
179
|
|
|
180
180
|
/** Pick the genuine orphans from the VM numbers that currently HAVE LB ingress
|
|
181
|
-
* resources (`ingressVmNumbers`, derived from the live backend-service
|
|
182
|
-
* A number is an orphan iff it has ingress but is NOT in the roster
|
|
183
|
-
* in-flight add — the
|
|
184
|
-
* that stop the auto-clean from
|
|
185
|
-
* mid-provision (its `add-<n>.json`
|
|
186
|
-
*
|
|
187
|
-
*
|
|
188
|
-
*
|
|
189
|
-
|
|
181
|
+
* resources (`ingressVmNumbers`, derived from the live backend-service AND NEG
|
|
182
|
+
* names). A number is an orphan iff it has ingress but is NOT in the roster,
|
|
183
|
+
* has NO in-flight add, AND is NOT a live GCP instance — the false-orphan
|
|
184
|
+
* guards (vm--auto-clean-orphaned-lb-ingress) that stop the auto-clean from
|
|
185
|
+
* ripping ingress out from under a VM that is mid-provision (its `add-<n>.json`
|
|
186
|
+
* sidecar and, moments later, its roster entry land before ingress finishes
|
|
187
|
+
* wiring), transiently unlisted, or genuinely running but not yet re-rostered
|
|
188
|
+
* (a roster drift — tearing down a *live* VM's ingress would knock it offline).
|
|
189
|
+
* Numbers not yet rostered but with an in-flight add, or with a live instance,
|
|
190
|
+
* are treated as live, not orphans. `liveVmNumbers` is optional; omit it (or
|
|
191
|
+
* pass a non-array) to disable the live-instance guard. Returns the orphan
|
|
192
|
+
* numbers deduped and sorted ascending. Pure. */
|
|
193
|
+
function selectOrphanIngressTargets({ ingressVmNumbers, roster, inFlightAdds, liveVmNumbers } = {}) {
|
|
190
194
|
const rosterSet = new Set((Array.isArray(roster) ? roster : []).map(Number));
|
|
191
195
|
const inFlightSet = new Set((Array.isArray(inFlightAdds) ? inFlightAdds : []).map(Number));
|
|
196
|
+
const liveSet = new Set((Array.isArray(liveVmNumbers) ? liveVmNumbers : []).map(Number));
|
|
192
197
|
const orphans = new Set();
|
|
193
198
|
for (const raw of Array.isArray(ingressVmNumbers) ? ingressVmNumbers : []) {
|
|
194
199
|
const n = Number(raw);
|
|
195
200
|
if (!Number.isInteger(n) || n <= 0) continue;
|
|
196
|
-
if (rosterSet.has(n) || inFlightSet.has(n)) continue;
|
|
201
|
+
if (rosterSet.has(n) || inFlightSet.has(n) || liveSet.has(n)) continue;
|
|
197
202
|
orphans.add(n);
|
|
198
203
|
}
|
|
199
204
|
return [...orphans].sort((a, b) => a - b);
|
|
@@ -212,9 +217,14 @@ function classifyOrphanDownResult({ verdict, status } = {}) {
|
|
|
212
217
|
}
|
|
213
218
|
|
|
214
219
|
/** Parse the machine-readable `CY_ORPHAN_SWEEP <json>` summary line the orphan
|
|
215
|
-
* sweep prints into `{ cleaned:[n], failed:[{n, cause}], skipped:[n] }
|
|
216
|
-
*
|
|
217
|
-
*
|
|
220
|
+
* sweep prints into `{ cleaned:[n], failed:[{n, cause}], skipped:[n] }` — plus
|
|
221
|
+
* `stillPresent:[n]` (VM numbers that still own ≥1 LB resource) ONLY when the
|
|
222
|
+
* summary carries that field. Its ABSENCE (an older reconcile binary that never
|
|
223
|
+
* emitted it) is preserved as an absent key, NOT normalized to `[]`, so the
|
|
224
|
+
* dashboard's reconcile can tell "sweep reported no residual" (`[]`) apart from
|
|
225
|
+
* "sweep can't say" (undefined) and leave stale banners untouched in the latter.
|
|
226
|
+
* Returns null when no `CY_ORPHAN_SWEEP` line is present (no sweep ran, or one
|
|
227
|
+
* died before emitting) so the dashboard can distinguish "no sweep ran" from
|
|
218
228
|
* "sweep ran, found nothing". The LAST such line wins (a reconcile emits one).
|
|
219
229
|
* Malformed JSON is skipped, not thrown. Pure so the dashboard's orphan-state
|
|
220
230
|
* reconciliation stays unit-testable without spawning the reconcile. */
|
|
@@ -234,10 +244,38 @@ function parseOrphanSweepSummary(stdout) {
|
|
|
234
244
|
.map((f) => ({ n: Number(f.n), cause: String(f.cause || 'auto-clean did not verify clean') })),
|
|
235
245
|
skipped: nums(o.skipped),
|
|
236
246
|
};
|
|
247
|
+
if (Array.isArray(o.stillPresent)) found.stillPresent = nums(o.stillPresent);
|
|
237
248
|
}
|
|
238
249
|
return found;
|
|
239
250
|
}
|
|
240
251
|
|
|
252
|
+
/** The reconcile-direction mirror of {@link selectOrphanIngressTargets}: given
|
|
253
|
+
* the VM numbers that currently carry an orphan-attention banner (`orphanKeys`,
|
|
254
|
+
* the keys of the dashboard's orphanedIngress map), the live `roster`, and the
|
|
255
|
+
* sweep's `stillPresent` set (VM numbers that still own ≥1 LB resource), decide
|
|
256
|
+
* which banners are STALE and must be cleared. A banner is stale iff its VM is
|
|
257
|
+
* (a) back in the roster — a destroy→re-add churn re-rostered it, freezing an
|
|
258
|
+
* old `failed` banner the sweep never revisits (the VM-3 case) — or (b) reported
|
|
259
|
+
* as owning no residual LB resource (absent from `stillPresent`), e.g. a VM
|
|
260
|
+
* cleaned outside a recorded sweep. When `stillPresent` is NOT an array (an older
|
|
261
|
+
* reconcile binary that never emitted the field) this returns `[]` so the caller
|
|
262
|
+
* leaves every banner untouched rather than clearing all of them — the absence of
|
|
263
|
+
* the field means "the sweep can't say," not "nothing is present." Junk keys
|
|
264
|
+
* (non-positive / non-integer) are ignored. Returns the VM numbers to clear,
|
|
265
|
+
* deduped ascending. Pure so the reconcile is unit-testable without the server. */
|
|
266
|
+
function staleOrphanEntries({ orphanKeys, roster, stillPresent } = {}) {
|
|
267
|
+
if (!Array.isArray(stillPresent)) return [];
|
|
268
|
+
const present = new Set(stillPresent.map(Number));
|
|
269
|
+
const rosterSet = new Set((Array.isArray(roster) ? roster : []).map(Number));
|
|
270
|
+
const stale = new Set();
|
|
271
|
+
for (const raw of Array.isArray(orphanKeys) ? orphanKeys : []) {
|
|
272
|
+
const n = Number(raw);
|
|
273
|
+
if (!Number.isInteger(n) || n <= 0) continue;
|
|
274
|
+
if (rosterSet.has(n) || !present.has(n)) stale.add(n);
|
|
275
|
+
}
|
|
276
|
+
return [...stale].sort((a, b) => a - b);
|
|
277
|
+
}
|
|
278
|
+
|
|
241
279
|
/** Decide the next step of the add-time ingress CONVERGE loop
|
|
242
280
|
* (vm--add-time-url-wiring-converges-reliably). The detached add-tail no longer
|
|
243
281
|
* fires `vm-ingress.sh up` ONCE and trusts the slow periodic reconcile to catch
|
|
@@ -455,6 +493,36 @@ function backendServicesListInvocation({ project } = {}) {
|
|
|
455
493
|
return { command: 'gcloud', args };
|
|
456
494
|
}
|
|
457
495
|
|
|
496
|
+
/** Build the `gcloud compute network-endpoint-groups list --format=value(name)`
|
|
497
|
+
* invocation the orphan sweep uses to enumerate every zonal NEG by name. A
|
|
498
|
+
* partial teardown can leave a `vm-<N>-<surface>-neg` behind after its backend
|
|
499
|
+
* service is already gone (the VM-5/6 case), so keying orphan candidates off
|
|
500
|
+
* backend services alone misses that residue — the sweep unions these NEG owners
|
|
501
|
+
* in. `project` adds `--project <id>` when supplied (omitted so gcloud's active
|
|
502
|
+
* config applies). Pure argv shaping, unit-tested without invoking gcloud. */
|
|
503
|
+
function negListInvocation({ project } = {}) {
|
|
504
|
+
const args = ['compute', 'network-endpoint-groups', 'list', '--format=value(name)'];
|
|
505
|
+
if (project != null && String(project).trim() !== '') {
|
|
506
|
+
args.push('--project', String(project).trim());
|
|
507
|
+
}
|
|
508
|
+
return { command: 'gcloud', args };
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
/** Build the `gcloud compute instances list --format=value(name)` invocation the
|
|
512
|
+
* orphan sweep uses for its defensive live-instance guard: a VM whose GCP
|
|
513
|
+
* instance is actually running must never have its ingress torn down, even if a
|
|
514
|
+
* roster drift left it unrostered (a successful teardown would knock a live VM
|
|
515
|
+
* offline). The names map back to VM numbers via fleet-reconcile.parseRunningFleetVms.
|
|
516
|
+
* `project` adds `--project <id>` when supplied. Pure argv shaping, unit-tested
|
|
517
|
+
* without invoking gcloud. */
|
|
518
|
+
function instancesListInvocation({ project } = {}) {
|
|
519
|
+
const args = ['compute', 'instances', 'list', '--format=value(name)'];
|
|
520
|
+
if (project != null && String(project).trim() !== '') {
|
|
521
|
+
args.push('--project', String(project).trim());
|
|
522
|
+
}
|
|
523
|
+
return { command: 'gcloud', args };
|
|
524
|
+
}
|
|
525
|
+
|
|
458
526
|
module.exports = {
|
|
459
527
|
BES_WEBSOCKET_TIMEOUT_SEC,
|
|
460
528
|
backendServiceTimeoutInvocation,
|
|
@@ -470,7 +538,10 @@ module.exports = {
|
|
|
470
538
|
selectOrphanIngressTargets,
|
|
471
539
|
classifyOrphanDownResult,
|
|
472
540
|
parseOrphanSweepSummary,
|
|
541
|
+
staleOrphanEntries,
|
|
473
542
|
backendServicesListInvocation,
|
|
543
|
+
negListInvocation,
|
|
544
|
+
instancesListInvocation,
|
|
474
545
|
ingressConvergeDecision,
|
|
475
546
|
detachedAddTailInvocation,
|
|
476
547
|
ingressInflightMarkerPath,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@codeyam-editor/codeyam-editor",
|
|
3
|
-
"version": "0.1.0-staging.
|
|
3
|
+
"version": "0.1.0-staging.gac4ca32",
|
|
4
4
|
"description": "Language-agnostic managed execution sandbox for scenario-driven development",
|
|
5
5
|
"bin": {
|
|
6
6
|
"codeyam-editor": "./npm/cli.js"
|
|
@@ -9,10 +9,10 @@
|
|
|
9
9
|
"playwright": "^1.58.2"
|
|
10
10
|
},
|
|
11
11
|
"optionalDependencies": {
|
|
12
|
-
"@codeyam-editor/codeyam-editor-darwin-arm64": "0.1.0-staging.
|
|
13
|
-
"@codeyam-editor/codeyam-editor-darwin-x64": "0.1.0-staging.
|
|
14
|
-
"@codeyam-editor/codeyam-editor-linux-x64": "0.1.0-staging.
|
|
15
|
-
"@codeyam-editor/codeyam-editor-win32-x64": "0.1.0-staging.
|
|
12
|
+
"@codeyam-editor/codeyam-editor-darwin-arm64": "0.1.0-staging.gac4ca32",
|
|
13
|
+
"@codeyam-editor/codeyam-editor-darwin-x64": "0.1.0-staging.gac4ca32",
|
|
14
|
+
"@codeyam-editor/codeyam-editor-linux-x64": "0.1.0-staging.gac4ca32",
|
|
15
|
+
"@codeyam-editor/codeyam-editor-win32-x64": "0.1.0-staging.gac4ca32"
|
|
16
16
|
},
|
|
17
17
|
"keywords": [
|
|
18
18
|
"codeyam",
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{j as e}from"./markdown-v0F0UEt3.js";import{b as a}from"./react-CSS0HapR.js";import{u as O}from"./useEvents-DHkLJZR4.js";import{b as E}from"./index-
|
|
1
|
+
import{j as e}from"./markdown-v0F0UEt3.js";import{b as a}from"./react-CSS0HapR.js";import{u as O}from"./useEvents-DHkLJZR4.js";import{b as E}from"./index-B7_KRKaQ.js";function P({connected:t}){return e.jsx("span",{className:`inline-block rounded-full px-2 py-0.5 text-xs font-medium text-[var(--text-primary)] ${t?"bg-[var(--accent-green)]":"bg-[var(--accent-red)]"}`,children:t?"connected":"reconnecting…"})}const re=Object.freeze(Object.defineProperty({__proto__:null,ConnectionBadge:P},Symbol.toStringTag,{value:"Module"}));function N(){const[t,r]=a.useState(null),[o,s]=a.useState(!0),{events:l}=O(),n=a.useCallback(async()=>{try{const c=await(await fetch("/api/inspector/status")).json();r(c)}catch{}finally{s(!1)}},[]);return a.useEffect(()=>{n()},[n]),a.useEffect(()=>{const i=l[l.length-1];i&&i.type==="scenario_switch"&&n()},[l,n]),{scenario:(t==null?void 0:t.scenario)??null,previewScenario:(t==null?void 0:t.previewScenario)??null,routes:(t==null?void 0:t.routes)??[],routeCount:(t==null?void 0:t.routeCount)??0,loading:o,refetch:n}}function v({title:t}){return e.jsx("h2",{style:{fontSize:14,fontWeight:600,color:"#f0f6fc",margin:"0 0 8px 0",borderBottom:"1px solid #30363d",paddingBottom:8},children:t})}function f({color:t,children:r}){return e.jsx("span",{style:{background:`${t}22`,color:t,border:`1px solid ${t}55`,borderRadius:4,padding:"2px 8px",fontSize:11,fontWeight:600,letterSpacing:"0.5px",whiteSpace:"nowrap"},children:r})}const $={GET:"#D7FF63",POST:"#238636",PUT:"#d29922",DELETE:"#da3633",PATCH:"#bc8cff"};function k({method:t}){const r=$[t.toUpperCase()]??"#8b949e";return e.jsx("span",{style:{color:r,fontWeight:600,fontSize:11,minWidth:48,display:"inline-block"},children:t.toUpperCase()})}function C({color:t,children:r}){return e.jsx("div",{style:{padding:"12px 16px",borderRadius:6,border:`1px solid ${t}44`,background:`${t}11`,color:t},children:r})}const u={padding:"8px 12px",textAlign:"center",color:"#8b949e",fontWeight:500,fontSize:11,textTransform:"uppercase",letterSpacing:"0.5px"},h={padding:"6px 12px"},S={background:"#161b22",border:"1px solid #30363d",borderRadius:6,color:"#c9d1d9",padding:"6px 12px",fontSize:13,fontFamily:"inherit"},R={...S,appearance:"none",paddingRight:24,backgroundImage:`url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%238b949e' d='M6 8L1 3h10z'/%3E%3C/svg%3E")`,backgroundRepeat:"no-repeat",backgroundPosition:"right 8px center"},oe=Object.freeze(Object.defineProperty({__proto__:null,Badge:f,MethodBadge:k,SectionHeader:v,StatusBox:C,inputStyle:S,selectStyle:R,tdStyle:h,thStyle:u},Symbol.toStringTag,{value:"Module"}));function z({routes:t}){return e.jsxs("table",{style:{width:"100%",borderCollapse:"collapse",fontSize:12},children:[e.jsx("thead",{children:e.jsxs("tr",{style:{borderBottom:"1px solid #30363d"},children:[e.jsx("th",{style:u,children:"Method"}),e.jsx("th",{style:{...u,textAlign:"left"},children:"Pattern"}),e.jsx("th",{style:u,children:"Status"})]})}),e.jsx("tbody",{children:t.map((r,o)=>e.jsxs("tr",{style:{borderBottom:"1px solid #21262d"},children:[e.jsx("td",{style:{...h,textAlign:"center"},children:e.jsx(k,{method:r.method})}),e.jsx("td",{style:h,children:r.pattern}),e.jsx("td",{style:{...h,textAlign:"center",color:"#8b949e"},children:r.status})]},o))})]})}const se=Object.freeze(Object.defineProperty({__proto__:null,InspectorRouteTable:z},Symbol.toStringTag,{value:"Module"}));function A(){const{scenario:t,previewScenario:r,routes:o,routeCount:s,loading:l}=N();return e.jsxs("section",{children:[e.jsx(v,{title:"Active Scenario"}),l?e.jsx(C,{color:"#8b949e",children:"Loading..."}):t?e.jsxs("div",{style:{display:"flex",flexDirection:"column",gap:12},children:[e.jsxs("div",{style:{display:"flex",alignItems:"center",gap:8},children:[e.jsx(f,{color:"#238636",children:"ACTIVE"}),e.jsx("span",{style:{color:"#f0f6fc",fontWeight:500},children:t.name}),e.jsxs("span",{style:{color:"#8b949e"},children:["(",t.slug,")"]})]}),e.jsxs("div",{style:{color:"#8b949e",fontSize:12},children:[s," mock route",s!==1?"s":""," loaded"]}),o.length>0&&e.jsx(z,{routes:o})]}):r?e.jsxs("div",{style:{display:"flex",flexDirection:"column",gap:12},children:[e.jsxs("div",{style:{display:"flex",alignItems:"center",gap:8},children:[e.jsx(f,{color:"#D7FF63",children:"PREVIEW"}),e.jsx("span",{style:{color:"#f0f6fc",fontWeight:500},children:r.name}),e.jsxs("span",{style:{color:"#8b949e"},children:["(",r.slug,")"]})]}),e.jsx("div",{style:{color:"#8b949e",fontSize:12},children:"Component preview — no HTTP mocks active"})]}):e.jsx(C,{color:"#d29922",children:"No scenario active — all requests pass through to upstream"})]})}const ne=Object.freeze(Object.defineProperty({__proto__:null,InspectorScenarioSection:A},Symbol.toStringTag,{value:"Module"}));function F({textUrlFilter:t,onTextUrlFilterChange:r,methodFilter:o,onMethodFilterChange:s,sourceFilter:l,onSourceFilterChange:n}){return e.jsxs("div",{style:{display:"flex",gap:8,marginBottom:12,alignItems:"center"},children:[e.jsx("input",{type:"text",placeholder:"Filter by URL...",value:t,onChange:i=>r(i.target.value),style:{...S,flex:1,minWidth:0}}),e.jsxs("select",{value:o,onChange:i=>s(i.target.value),style:R,children:[e.jsx("option",{value:"ALL",children:"All Methods"}),e.jsx("option",{value:"GET",children:"GET"}),e.jsx("option",{value:"POST",children:"POST"}),e.jsx("option",{value:"PUT",children:"PUT"}),e.jsx("option",{value:"DELETE",children:"DELETE"}),e.jsx("option",{value:"PATCH",children:"PATCH"})]}),e.jsxs("select",{value:l,onChange:i=>n(i.target.value),style:R,children:[e.jsx("option",{value:"ALL",children:"All Sources"}),e.jsx("option",{value:"MOCKED",children:"Mocked"}),e.jsx("option",{value:"PASSTHROUGH",children:"Passthrough"})]})]})}const ie=Object.freeze(Object.defineProperty({__proto__:null,InspectorTrafficFilterBar:F},Symbol.toStringTag,{value:"Module"})),G={color:"#8b949e",fontSize:11,fontWeight:600,textTransform:"uppercase",letterSpacing:"0.5px",marginBottom:4},M={background:"#161b22",border:"1px solid #30363d",borderRadius:4,padding:"8px 12px",margin:0,fontSize:11,color:"#c9d1d9",fontFamily:"monospace",maxHeight:200,overflow:"auto",whiteSpace:"pre-wrap",wordBreak:"break-all"};function _({label:t,children:r}){return e.jsxs("div",{style:{marginBottom:12},children:[e.jsx("div",{style:G,children:t}),r]})}function B({event:t}){let r=[];try{const n=new URL(t.url,"http://localhost");r=Array.from(n.searchParams.entries())}catch{}const o=t.requestHeaders&&Object.keys(t.requestHeaders).length>0,s=t.requestBody!=null,l=t.responseBody!=null;return e.jsxs("div",{style:{background:"#0d1117",padding:"12px 16px",borderTop:"1px solid #30363d"},children:[r.length>0&&e.jsx(_,{label:"Query Parameters",children:e.jsx("div",{style:{display:"grid",gridTemplateColumns:"auto 1fr",gap:"2px 12px",fontSize:12},children:r.map(([n,i],c)=>e.jsxs("div",{style:{display:"contents"},children:[e.jsx("span",{style:{color:"#D7FF63",fontFamily:"monospace"},children:n}),e.jsx("span",{style:{color:"#c9d1d9",fontFamily:"monospace"},children:i})]},c))})}),o&&e.jsx(_,{label:"Request Headers",children:e.jsx("pre",{style:M,children:Object.entries(t.requestHeaders).map(([n,i])=>`${n}: ${i}`).join(`
|
|
2
2
|
`)})}),s&&e.jsx(_,{label:"Request Body",children:e.jsx("pre",{style:M,children:typeof t.requestBody=="string"?t.requestBody:JSON.stringify(t.requestBody,null,2)})}),l&&e.jsx(_,{label:"Response Body",children:e.jsx("pre",{style:M,children:typeof t.responseBody=="string"?t.responseBody:JSON.stringify(t.responseBody,null,2)})}),r.length===0&&!o&&!s&&!l&&e.jsx("div",{style:{color:"#8b949e",fontSize:12},children:"No additional details available for this request"})]})}const le=Object.freeze(Object.defineProperty({__proto__:null,DetailSection:_,InspectorTrafficDetail:B},Symbol.toStringTag,{value:"Module"}));function L({event:t}){const[r,o]=a.useState(!1),s=new Date(t.timestampMs).toLocaleTimeString(),l=t.mocked;return e.jsxs(e.Fragment,{children:[e.jsxs("tr",{onClick:()=>o(!r),style:{borderBottom:"1px solid #21262d",background:l?"transparent":"rgba(210, 153, 34, 0.05)",cursor:"pointer"},children:[e.jsxs("td",{style:{...h,color:"#8b949e",textAlign:"center"},children:[e.jsx("span",{style:{display:"inline-block",width:12,marginRight:4,fontSize:10,color:"#8b949e",transition:"transform 0.15s",transform:r?"rotate(90deg)":"rotate(0deg)"},children:"▶"}),s]}),e.jsx("td",{style:{...h,textAlign:"center"},children:e.jsx(k,{method:t.method})}),e.jsx("td",{style:{...h,maxWidth:400,overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap"},children:t.url}),e.jsx("td",{style:{...h,textAlign:"center",color:"#8b949e"},children:t.status??"---"}),e.jsxs("td",{style:{...h,textAlign:"center",color:"#8b949e"},children:[t.durationMs,"ms"]}),e.jsx("td",{style:{...h,textAlign:"center"},children:l?t.mockSource==="default"?e.jsx(f,{color:"#1f6feb",children:"MOCKED (default)"}):e.jsx(f,{color:"#238636",children:"MOCKED (scenario)"}):e.jsx(f,{color:"#d29922",children:"PASSTHROUGH"})})]}),r&&e.jsx("tr",{children:e.jsx("td",{colSpan:6,style:{padding:0},children:e.jsx(B,{event:t})})})]})}const ae=Object.freeze(Object.defineProperty({__proto__:null,InspectorTrafficRow:L},Symbol.toStringTag,{value:"Module"}));function D({events:t}){return e.jsx("div",{style:{maxHeight:300,overflowY:"auto",border:"1px solid #30363d",borderRadius:6},children:e.jsxs("table",{style:{width:"100%",borderCollapse:"collapse",fontSize:12},children:[e.jsx("thead",{children:e.jsxs("tr",{style:{borderBottom:"1px solid #30363d",position:"sticky",top:0,background:"#0d1117"},children:[e.jsx("th",{style:u,children:"Time"}),e.jsx("th",{style:u,children:"Method"}),e.jsx("th",{style:{...u,textAlign:"left"},children:"URL"}),e.jsx("th",{style:u,children:"Status"}),e.jsx("th",{style:u,children:"Duration"}),e.jsx("th",{style:u,children:"Source"})]})}),e.jsx("tbody",{children:t.map(r=>e.jsx(L,{event:r},r.id))})]})})}const ce=Object.freeze(Object.defineProperty({__proto__:null,InspectorTrafficTable:D},Symbol.toStringTag,{value:"Module"}));function K(t){if(t===void 0)return"checked";if(t==="checked")return"excluded"}function I({chips:t,chipStates:r,onChipStatesChange:o}){if(t.length===0)return null;const s=n=>{const i=new Map(r),c=K(i.get(n));c===void 0?i.delete(n):i.set(n,c),o(i)},l=()=>{o(new Map)};return e.jsxs("div",{style:{display:"flex",flexWrap:"wrap",gap:6,marginBottom:12},children:[r.size>0&&e.jsx("button",{onClick:l,style:{background:"#da363322",color:"#da3633",border:"1px solid #da363355",borderRadius:4,padding:"2px 8px",fontSize:11,fontWeight:600,cursor:"pointer",fontFamily:"inherit"},children:"Clear all"}),t.map(({path:n,count:i})=>{const c=r.get(n),d=c==="checked",g=c==="excluded";let m="#30363d",b="#8b949e",T="#30363d",y="";return d?(m="#D7FF6322",b="#D7FF63",T="#D7FF6355",y="✓ "):g&&(m="#da363322",b="#da3633",T="#da363355",y="✗ "),e.jsxs("button",{onClick:()=>s(n),style:{background:m,color:b,border:`1px solid ${T}`,borderRadius:4,padding:"2px 8px",fontSize:11,fontFamily:"monospace",cursor:"pointer",whiteSpace:"nowrap"},children:[y,n,e.jsxs("span",{style:{marginLeft:4,opacity:.6,fontFamily:"inherit"},children:["(",i,")"]})]},n)})]})}const de=Object.freeze(Object.defineProperty({__proto__:null,InspectorTrafficUrlChips:I},Symbol.toStringTag,{value:"Module"}));function H(){const{events:t}=O(),[r,o]=a.useState(""),[s,l]=a.useState(()=>new Map),[n,i]=a.useState("ALL"),[c,d]=a.useState("ALL"),g=t.filter(p=>p.type==="http_request"),m=a.useMemo(()=>{const p=new Map;for(const x of g){const j=E(x.url);p.set(j,(p.get(j)||0)+1)}return Array.from(p.entries()).sort((x,j)=>j[1]-x[1]).slice(0,10).map(([x,j])=>({path:x,count:j}))},[g]),b=a.useMemo(()=>Array.from(s.values()).some(p=>p==="checked"),[s]),y=g.filter(p=>{if(r&&!p.url.toLowerCase().includes(r.toLowerCase()))return!1;const x=E(p.url);return!(s.get(x)==="excluded"||b&&s.get(x)!=="checked"||n!=="ALL"&&p.method.toUpperCase()!==n||c==="MOCKED"&&!p.mocked||c==="PASSTHROUGH"&&p.mocked)}).slice(-50).reverse();return e.jsxs("section",{children:[e.jsx(v,{title:"Live Traffic"}),e.jsx(F,{textUrlFilter:r,onTextUrlFilterChange:o,methodFilter:n,onMethodFilterChange:i,sourceFilter:c,onSourceFilterChange:p=>d(p)}),e.jsx(I,{chips:m,chipStates:s,onChipStatesChange:l}),y.length===0?e.jsx(C,{color:"#8b949e",children:g.length>0?"No matching traffic — try adjusting the filters":"No HTTP traffic yet — requests will appear here in real-time"}):e.jsx(D,{events:y})]})}const pe=Object.freeze(Object.defineProperty({__proto__:null,InspectorTrafficSection:H},Symbol.toStringTag,{value:"Module"}));function J(){const[t,r]=a.useState(null),[o,s]=a.useState(!1);return{explain:a.useCallback(async(n,i)=>{s(!0);try{const d=await(await fetch("/api/inspector/explain",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({method:n,url:i})})).json();r(d)}catch{}finally{s(!1)}},[]),result:t,loading:o}}function U({result:t}){return e.jsxs("div",{style:{border:"1px solid #30363d",borderRadius:6,padding:12},children:[e.jsxs("div",{style:{display:"flex",alignItems:"center",gap:8,marginBottom:12},children:[e.jsxs("span",{style:{color:"#f0f6fc",fontWeight:500},children:[t.method," ",t.url]}),t.wouldMatch?e.jsx(f,{color:"#238636",children:"WOULD MATCH"}):e.jsx(f,{color:"#da3633",children:"NO MATCH"})]}),t.attempts.length===0?e.jsx("div",{style:{color:"#8b949e"},children:"No routes loaded to match against"}):e.jsx("div",{style:{display:"flex",flexDirection:"column",gap:6},children:t.attempts.map((r,o)=>e.jsxs("div",{style:{display:"flex",alignItems:"center",gap:8,padding:"4px 8px",borderRadius:4,background:r.failure?"transparent":"rgba(35, 134, 54, 0.1)"},children:[e.jsx("span",{style:{color:r.failure?"#da3633":"#238636",fontWeight:600,width:16},children:r.failure?"x":"v"}),e.jsx(k,{method:r.method}),e.jsx("span",{children:r.pattern}),r.failure&&e.jsx("span",{style:{color:"#8b949e",fontSize:11},children:r.failure.reason==="methodMismatch"?`method: expected ${r.failure.expected}, got ${r.failure.actual}`:`path "${r.failure.path}" does not match "${r.failure.pattern}"`})]},o))})]})}const ue=Object.freeze(Object.defineProperty({__proto__:null,InspectorExplainerResult:U},Symbol.toStringTag,{value:"Module"}));function W(){const{explain:t,result:r,loading:o}=J(),[s,l]=a.useState("GET"),[n,i]=a.useState(""),c=d=>{d.preventDefault(),n.trim()&&t(s,n.trim())};return e.jsxs("section",{children:[e.jsx(v,{title:"Match Explainer"}),e.jsxs("form",{onSubmit:c,style:{display:"flex",gap:8,marginBottom:12},children:[e.jsx("select",{value:s,onChange:d=>l(d.target.value),style:{...S,width:100},children:["GET","POST","PUT","DELETE","PATCH"].map(d=>e.jsx("option",{value:d,children:d},d))}),e.jsx("input",{type:"text",value:n,onChange:d=>i(d.target.value),placeholder:"/api/users",style:{...S,flex:1}}),e.jsx("button",{type:"submit",disabled:o||!n.trim(),style:{background:"#238636",color:"#fff",border:"none",borderRadius:6,padding:"6px 16px",cursor:"pointer",fontSize:12,fontWeight:500,opacity:o||!n.trim()?.5:1},children:o?"Checking...":"Explain"})]}),r&&e.jsx(U,{result:r})]})}const he=Object.freeze(Object.defineProperty({__proto__:null,InspectorExplainerSection:W},Symbol.toStringTag,{value:"Module"})),w={"/api/health":"Health check — confirms the proxy and mock engine are running","/api/events":"SSE stream of real-time events (traffic, errors) powering this Inspector","/api/config":"Returns current CodeYam configuration for the project","/api/editor-dev-server":"Polls/controls the dev server status (start, stop, restart)","/api/inspector/status":"Returns active scenario and loaded mock routes","/api/inspector/explain":"Tests which mock route would match a given method + URL","/api/scenarios":"Lists all defined scenarios with metadata","/api/tests":"Returns the AI-maintained test registry joined with per-test-evidence, grouped by file","/api/test-results":"Returns latest test execution results","/api/screenshots/*":"Serves captured scenario screenshots","/api/session-info":"Returns current editor session metadata","/api/data-structure":"Returns the app's data structure definition","/api/dependency-graph":"Returns the project's component dependency graph","/api/app-route-entities":"Lists app routes and their associated entities","/api/glossary":"Returns the project's component glossary","/api/step":"Returns current editor workflow step","/api/editor-commands":"Lists available editor commands"};function V(t){if(w[t])return w[t];for(const[r,o]of Object.entries(w))if(r.endsWith("/*")){const s=r.slice(0,-1);if(t.startsWith(s))return o}}function Y(t){const r=new Set([...t,...Object.keys(w)]),o=[];for(const s of Array.from(r).sort()){if(s.endsWith("/*"))continue;const l=V(s);o.push({path:s,description:l??"Application endpoint"})}return o}function q(){const{events:t}=O(),r=a.useMemo(()=>{const o=[];for(const s of t)s.type==="http_request"&&o.push(E(s.url));return Y(o)},[t]);return r.length===0?null:e.jsxs("section",{children:[e.jsx(v,{title:"URL Glossary"}),e.jsx("div",{style:{border:"1px solid #30363d",borderRadius:6,overflow:"hidden"},children:e.jsxs("table",{style:{width:"100%",borderCollapse:"collapse",fontSize:12},children:[e.jsx("thead",{children:e.jsxs("tr",{style:{borderBottom:"1px solid #30363d",background:"#161b22"},children:[e.jsx("th",{style:{padding:"6px 12px",textAlign:"left",color:"#8b949e",fontWeight:500,fontSize:11,textTransform:"uppercase",letterSpacing:"0.5px",width:"30%"},children:"Path"}),e.jsx("th",{style:{padding:"6px 12px",textAlign:"left",color:"#8b949e",fontWeight:500,fontSize:11,textTransform:"uppercase",letterSpacing:"0.5px"},children:"Description"})]})}),e.jsx("tbody",{children:r.map(({path:o,description:s})=>e.jsxs("tr",{style:{borderBottom:"1px solid #21262d"},children:[e.jsx("td",{style:{padding:"4px 12px",fontFamily:"monospace",color:"#D7FF63",fontSize:11},children:o}),e.jsx("td",{style:{padding:"4px 12px",color:"#8b949e"},children:s})]},o))})]})})]})}const xe=Object.freeze(Object.defineProperty({__proto__:null,InspectorUrlGlossary:q},Symbol.toStringTag,{value:"Module"}));function Q(){const{connected:t}=O();return e.jsxs("div",{style:{background:"#0d1117",color:"#c9d1d9",minHeight:"100vh",fontFamily:"'SF Mono', 'Fira Code', 'Cascadia Code', Menlo, monospace",fontSize:13,padding:24,display:"flex",flexDirection:"column",gap:24},children:[e.jsxs("header",{style:{display:"flex",alignItems:"center",gap:12},children:[e.jsx("h1",{style:{fontSize:18,fontWeight:600,color:"#f0f6fc",margin:0},children:"Container Inspector"}),e.jsx("span",{style:{color:"#8b949e",fontSize:12},children:"Read-only view of proxy and mock engine state"}),e.jsx(P,{connected:t})]}),e.jsx(A,{}),e.jsx(H,{}),e.jsx(W,{}),e.jsx(q,{})]})}const fe=Object.freeze(Object.defineProperty({__proto__:null,ContainerInspector:Q},Symbol.toStringTag,{value:"Module"}));export{f as B,Q as C,_ as D,L as I,k as M,v as S,xe as _,C as a,q as b,I as c,D as d,F as e,B as f,U as g,W as h,H as i,z as j,A as k,P as l,de as m,ce as n,pe as o,ae as p,ie as q,le as r,ne as s,u as t,se as u,oe as v,he as w,ue as x,fe as y,re as z};
|