@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 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): backend
3701
- * services whose owning VM number is gone from the roster (a destroyed VM) and
3702
- * has no in-flight add. Enumerates the live backends, maps each name back to its
3703
- * VM number, subtracts the roster + in-flight adds (the false-orphan guards),
3704
- * and runs the VERIFIED `vm-ingress.sh <N> down` (asserts neg=no bes=no host=no,
3705
- * with retries) for each orphan — so an orphan is genuinely cleared or surfaced
3706
- * as a real failure, never a silent leak and never a manual command. Emits a
3707
- * machine-readable `CY_ORPHAN_SWEEP <json>` summary line the dashboard parses to
3708
- * reconcile its orphan-attention banner. Best-effort per VM: one `down` failing
3709
- * never aborts the rest of the sweep. Returns the `{ cleaned, failed, skipped }`
3710
- * summary (null when the enumeration itself failed) — the same shape it prints —
3711
- * so the flow is directly assertable. `deps` is injectable so the enumerate →
3712
- * select → down flow is testable without gcloud. */
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 ingressVmNumbers = String(listRes.stdout || "")
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
- const summary = { cleaned: [], failed: [], skipped: inFlightAdds.slice() };
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
  }
@@ -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 names).
182
- * A number is an orphan iff it has ingress but is NOT in the roster AND has NO
183
- * in-flight add — the two false-orphan guards (vm--auto-clean-orphaned-lb-ingress)
184
- * that stop the auto-clean from ripping ingress out from under a VM that is
185
- * mid-provision (its `add-<n>.json` sidecar and, moments later, its roster entry
186
- * land before ingress finishes wiring) or transiently unlisted. Numbers not yet
187
- * rostered but with an in-flight add are treated as live, not orphans. Returns
188
- * the orphan numbers deduped and sorted ascending. Pure. */
189
- function selectOrphanIngressTargets({ ingressVmNumbers, roster, inFlightAdds } = {}) {
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] }`. Returns
216
- * null when no such line is present (an older reconcile binary, or a sweep that
217
- * died before emitting it) so the dashboard can distinguish "no sweep ran" from
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.g6631ba2",
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.g6631ba2",
13
- "@codeyam-editor/codeyam-editor-darwin-x64": "0.1.0-staging.g6631ba2",
14
- "@codeyam-editor/codeyam-editor-linux-x64": "0.1.0-staging.g6631ba2",
15
- "@codeyam-editor/codeyam-editor-win32-x64": "0.1.0-staging.g6631ba2"
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-B14nyUpe.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(`
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};