@codeyam-editor/codeyam-editor 0.1.9 → 0.1.10

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.
@@ -11,6 +11,7 @@
11
11
  const { CANONICAL_EDITOR_REPO, EDITOR_VARIANTS, repoBasename, AGENT_ENV_VARS } = require("./cloud");
12
12
  const { normalizeVmSlug } = require("./vm-urls");
13
13
  const { normalizeAccessList } = require("./fleet-vm-access");
14
+ const { normalizeRepoUrl } = require("./fleet-env");
14
15
 
15
16
  /** The build agents a VM's editor terminal can run, as the keys of cloud.js's
16
17
  * AGENT_ENV_VARS. Deriving the selectable set from that map keeps it a single
@@ -479,21 +480,51 @@ function queuedStatusForAction(action) {
479
480
  /** Push a launch entry onto the newest-first history, capped to `cap`. No
480
481
  * cross-VM dedup at write time — the full log is what powers per-VM config
481
482
  * lookups (configForVm); the "recent launches" picker de-dups at read time
482
- * (distinctConfigs). Returns a new array; does not mutate the input. */
483
- function pushHistory(history, entry, cap = 30) {
483
+ * (distinctProjectConfigs). Returns a new array; does not mutate the input.
484
+ *
485
+ * The cap is deliberately generous: this log is the ONLY source the picker can
486
+ * draw distinct projects from, so it bounds how far back "recent projects" can
487
+ * reach, and one project relaunched in a burst must not be able to evict every
488
+ * other project from the window. Entries are ~390 bytes, so 100 of them is
489
+ * ~39 KB — no meaningful cost, and it also lengthens the reach of the per-VM
490
+ * lookups (configForVm / latestLaunchTs) for VMs not relaunched recently. */
491
+ function pushHistory(history, entry, cap = 100) {
484
492
  const arr = Array.isArray(history) ? history.slice() : [];
485
493
  arr.unshift(entry);
486
494
  return arr.slice(0, Math.max(1, cap));
487
495
  }
488
496
 
489
- /** Distinct configs, newest-first, for the "recent launches" picker (so the
490
- * same config launched on several VMs shows once — the most-recent
491
- * occurrence). */
492
- function distinctConfigs(history, limit = 12) {
497
+ /** The identity the "recent launches" picker de-dups on: WHICH PROJECT a launch
498
+ * was against, not which exact configuration it used.
499
+ *
500
+ * Per-launch and per-VM fields (clientBranch, editorVariant, editorBranch,
501
+ * agent, vmSlug, vmAccess) must NOT participate: they are the variant details
502
+ * the operator edits in the form *after* picking a project, so keying on them
503
+ * fragments one hot project into many near-identical rows and crowds every
504
+ * other project off the list.
505
+ *
506
+ * Repo identity goes through `normalizeRepoUrl` (fleet-env.js), so the scp-like
507
+ * and https spellings of one repo — and a `.git` suffix — collapse to one row.
508
+ * `repoBasename` is deliberately NOT the key: two owners' repos can share a
509
+ * basename. Greenfield launches key on the project name, so two different
510
+ * greenfield projects stay two rows. Normalizes through EMPTY_CONFIG so a
511
+ * legacy history entry with missing fields cannot throw. Pure; no I/O. */
512
+ function launchProjectKey(config) {
513
+ const cfg = { ...EMPTY_CONFIG, ...(config || {}) };
514
+ if (cfg.clientRepo) return `repo:${normalizeRepoUrl(cfg.clientRepo)}`;
515
+ if (cfg.newProjectName) return `new:${String(cfg.newProjectName).trim().toLowerCase()}`;
516
+ return "none";
517
+ }
518
+
519
+ /** Newest config per PROJECT, newest-first, for the "recent launches" picker —
520
+ * repo identity or greenfield name (launchProjectKey), so one project launched
521
+ * many times shows once, carrying its most recent launch as the row's prefill.
522
+ * `limit` bounds the rows returned. */
523
+ function distinctProjectConfigs(history, limit = 12) {
493
524
  const seen = new Set();
494
525
  const out = [];
495
526
  for (const e of Array.isArray(history) ? history : []) {
496
- const k = JSON.stringify(e && e.config);
527
+ const k = launchProjectKey(e && e.config);
497
528
  if (seen.has(k)) continue;
498
529
  seen.add(k);
499
530
  out.push(e);
@@ -502,12 +533,12 @@ function distinctConfigs(history, limit = 12) {
502
533
  return out;
503
534
  }
504
535
 
505
- /** Display-side companion to `distinctConfigs`: derive the two label strings the
536
+ /** Display-side companion to `distinctProjectConfigs`: derive the two label strings the
506
537
  * "recent launches" picker renders for one config, so two configs that differ
507
538
  * only by build agent (or greenfield project name) look different instead of
508
- * collapsing to identical rows. `distinctConfigs` de-dups on the FULL config, so
509
- * the rows are genuinely distinct launches — the old inline label just never
510
- * surfaced the distinguishing fields (agent, greenfield name).
539
+ * collapsing to identical rows. `distinctProjectConfigs` de-dups on project identity,
540
+ * so each row is one project carrying its most recent launch — and these labels
541
+ * are what say which branch / editor variant / agent that launch used.
511
542
  *
512
543
  * Returns { primary, secondary }:
513
544
  * - primary: greenfield (`!clientRepo && newProjectName`) → `new: <name>` so
@@ -919,13 +950,29 @@ function decideStartJob({ existing, requested } = {}) {
919
950
  * Modes "token" and "none" carry no claude auth, so the caller marks the VM
920
951
  * add-degraded with a `claude /login` remediation. `reachableDonors` must be
921
952
  * pre-filtered to VMs that actually answered (a destroyed-but-rostered orphan
922
- * is not a donor — fragility #24). Pure; no I/O. */
923
- function resolveCredsSource({ reachableDonors = [], claudeCredsSrc = "", githubToken = "" } = {}) {
924
- const donor = Array.isArray(reachableDonors) && reachableDonors.length ? reachableDonors[0] : null;
925
- if (donor != null) return { mode: "donor", donor };
926
- if (claudeCredsSrc) return { mode: "host-stash", donor: null };
927
- if (githubToken) return { mode: "token", donor: null };
928
- return { mode: "none", donor: null };
953
+ * is not a donor — fragility #24).
954
+ *
955
+ * `preferredDonor` (the reachable VM holding the operator's chosen Claude
956
+ * account) wins over `reachableDonors[0]` when it is itself reachable. When a
957
+ * `requestedAccount` was asked for, the result also carries
958
+ * `requestedAccountUnavailable` — true when no preferred donor could honor it —
959
+ * so the caller can raise a degraded reason without re-deriving why. With no
960
+ * account requested the result shape is unchanged. Pure; no I/O. */
961
+ function resolveCredsSource({
962
+ reachableDonors = [],
963
+ preferredDonor = null,
964
+ requestedAccount = "",
965
+ claudeCredsSrc = "",
966
+ githubToken = "",
967
+ } = {}) {
968
+ const donors = Array.isArray(reachableDonors) ? reachableDonors : [];
969
+ const preferred = preferredDonor != null && donors.includes(preferredDonor) ? preferredDonor : null;
970
+ const extra = requestedAccount ? { requestedAccountUnavailable: preferred == null } : {};
971
+ const donor = preferred != null ? preferred : (donors.length ? donors[0] : null);
972
+ if (donor != null) return { mode: "donor", donor, ...extra };
973
+ if (claudeCredsSrc) return { mode: "host-stash", donor: null, ...extra };
974
+ if (githubToken) return { mode: "token", donor: null, ...extra };
975
+ return { mode: "none", donor: null, ...extra };
929
976
  }
930
977
 
931
978
  /** True when the resolved creds `mode` (see `resolveCredsSource`) carries a real
@@ -1038,7 +1085,12 @@ function verifyBeforeMoveSink(
1038
1085
  * can extend host-stash with a sibling onboarding-stash env knob.
1039
1086
  *
1040
1087
  * Pure; no I/O. */
1041
- function buildCredsSteps({ mode, refInst = "", inst, container, gcloud, zone, n, claudeCredsSrc = "" } = {}) {
1088
+ function buildCredsSteps({ mode, refInst = "", inst, container, gcloud, zone, n, claudeCredsSrc = "", account = "" } = {}) {
1089
+ // The donor's Claude account, named in the add log so it says WHICH account
1090
+ // was seeded, not only which VM it came from. Reduced to email-safe characters
1091
+ // because it is interpolated into a double-quoted shell `echo`.
1092
+ const acct = String(account).replace(/[^A-Za-z0-9@._+-]/g, "");
1093
+ const acctNote = acct ? ` (account ${acct})` : "";
1042
1094
  // The `.credentials.json` copy additionally validates the donor's credential
1043
1095
  // shape (positive `expiresAt`, non-empty tokens) before overwriting — a
1044
1096
  // non-empty-but-corrupt donor must not clobber a working target. The
@@ -1053,7 +1105,7 @@ function buildCredsSteps({ mode, refInst = "", inst, container, gcloud, zone, n,
1053
1105
  return [
1054
1106
  `echo "copying git creds from ${refInst}..."`,
1055
1107
  `${gcloud} compute ssh ${refInst} --tunnel-through-iap --zone=${zone} --command='cat $HOME/.codeyam-cloud-git-credentials' 2>/dev/null | ${gcloud} compute ssh ${inst} --tunnel-through-iap --zone=${zone} --command='umask 077; cat > $HOME/.codeyam-cloud-git-credentials' 2>/dev/null || echo "CY_DEGRADED git creds copy failed on VM-${n} — re-copy creds from a live donor (the agent can't git pull/push until then)"`,
1056
- `echo "copying claude auth from ${refInst} (container->container)..."`,
1108
+ `echo "copying claude auth from ${refInst}${acctNote} (container->container)..."`,
1057
1109
  `${gcloud} compute ssh ${refInst} --tunnel-through-iap --zone=${zone} --command='docker exec ${container} base64 /root/.claude/.credentials.json' 2>/dev/null | ${gcloud} compute ssh ${inst} --tunnel-through-iap --zone=${zone} --command=${_shq(claudeSink)} 2>/dev/null && echo "claude auth copied" || echo "CY_DEGRADED claude auth copy failed on VM-${n} (empty/unreadable/malformed donor — existing creds left untouched, not overwritten with a corrupt credential) — ${loginHint}"`,
1058
1110
  `echo "copying claude.json (onboarding state) from ${refInst} (container->container)..."`,
1059
1111
  `${gcloud} compute ssh ${refInst} --tunnel-through-iap --zone=${zone} --command='docker exec ${container} base64 /root/.claude.json' 2>/dev/null | ${gcloud} compute ssh ${inst} --tunnel-through-iap --zone=${zone} --command=${_shq(claudeJsonSink)} 2>/dev/null && echo "claude.json copied" || echo "CY_DEGRADED claude.json copy failed on VM-${n} — first interactive claude launch may show 'Select login method' menu; ${loginHint} once to complete onboarding"`,
@@ -1075,6 +1127,21 @@ function buildCredsSteps({ mode, refInst = "", inst, container, gcloud, zone, n,
1075
1127
  ];
1076
1128
  }
1077
1129
 
1130
+ /** The CY_DEGRADED reason for an Add whose requested Claude account no
1131
+ * reachable VM holds: names the account asked for and what was seeded instead
1132
+ * (the fallback donor's account, the host stash's account, or none for the
1133
+ * token/none modes), plus the remediation. Fall back loudly, never silently —
1134
+ * the dashboard lifts this line onto the new VM's card as an amber badge. The
1135
+ * caller shell-quotes it. Pure. */
1136
+ function accountFallbackMessage({ requested = "", seeded = "", mode = "none", donor = null, inst = "" } = {}) {
1137
+ const fallback = {
1138
+ donor: donor != null ? `VM-${donor}'s account` : "an unknown account",
1139
+ "host-stash": "the host credential stash's account",
1140
+ };
1141
+ const got = seeded || fallback[mode] || "no Claude account";
1142
+ return `CY_DEGRADED requested Claude account ${requested} is not on any reachable VM — seeded ${got} instead; run claude /login on ${inst} to switch it to ${requested}`;
1143
+ }
1144
+
1078
1145
  /** Build the two host-side shell snippets that PRESERVE the container's
1079
1146
  * /root/.claude.json (claude onboarding state) across a non-self-host Rebuild's
1080
1147
  * container recreate. The whole /root/.claude DIRECTORY is bind-mounted from the
@@ -1351,7 +1418,8 @@ module.exports = {
1351
1418
  HEAVY_ACTIONS,
1352
1419
  queuedStatusForAction,
1353
1420
  pushHistory,
1354
- distinctConfigs,
1421
+ launchProjectKey,
1422
+ distinctProjectConfigs,
1355
1423
  recentLaunchDisplay,
1356
1424
  defaultConfig,
1357
1425
  configForVm,
@@ -1366,6 +1434,7 @@ module.exports = {
1366
1434
  resolveCredsSource,
1367
1435
  claudeAuthSeeded,
1368
1436
  buildCredsSteps,
1437
+ accountFallbackMessage,
1369
1438
  verifyBeforeMoveSink,
1370
1439
  buildClaudeJsonPreserveSteps,
1371
1440
  buildEnvApplyCommand,
@@ -0,0 +1,81 @@
1
+ /**
2
+ * orphan-ssh-reaper.js — sweep the fleet SSH sessions a timed-out gcloud probe
3
+ * already leaked, before this machine's next sweep adds to them.
4
+ *
5
+ * group-bounded-spawn.js stops NEW leaks. A machine that ran the sweep before
6
+ * that fix may still carry orphans: `gcloud compute ssh` requests a TTY (`-t`),
7
+ * so an orphaned session has no EOF to notice and no parent to report to — it
8
+ * simply waits, and they accumulate rather than drain.
9
+ *
10
+ * A process is reaped only when BOTH hold:
11
+ * - it is an `ssh` carrying the fleet's `HostKeyAlias=compute.` signature
12
+ * (gcloud sets the alias to `compute.<instance-id>`), which separates it
13
+ * from any other ssh the operator runs; AND
14
+ * - it has been re-parented to PID 1. A live sweep's in-flight ssh still has
15
+ * its gcloud parent, so the command line alone is never enough.
16
+ * Limitation: on a Linux host with a user-session subreaper, orphans re-parent
17
+ * to that subreaper instead of PID 1 and are left alone — the safe direction.
18
+ */
19
+
20
+ const { spawnSync } = require("child_process");
21
+
22
+ const FLEET_SSH_SIGNATURE = /HostKeyAlias=compute\./;
23
+ // The ssh binary itself (`ssh …` or `/usr/bin/ssh …`) — never the gcloud
24
+ // wrapper, whose command line reads `… gcloud.py compute ssh …`.
25
+ const SSH_BINARY = /(^|\/)ssh\s/;
26
+
27
+ /** Parse `ps -A -o pid=,ppid=,command=` stdout into `{ pid, ppid, command }`
28
+ * rows, skipping anything malformed. Pure. */
29
+ function parsePsRows(stdout) {
30
+ return String(stdout || "")
31
+ .split("\n")
32
+ .map((line) => /^\s*(\d+)\s+(\d+)\s+(.*)$/.exec(line))
33
+ .filter(Boolean)
34
+ .map((m) => ({ pid: Number(m[1]), ppid: Number(m[2]), command: m[3] }));
35
+ }
36
+
37
+ /** Whether one `ps` row is a leaked fleet ssh session. Pure. */
38
+ function isOrphanedFleetSsh(row) {
39
+ return (
40
+ row.ppid === 1 &&
41
+ SSH_BINARY.test(row.command) &&
42
+ FLEET_SSH_SIGNATURE.test(row.command)
43
+ );
44
+ }
45
+
46
+ function defaultRunPs() {
47
+ return spawnSync("ps", ["-A", "-o", "pid=,ppid=,command="], { encoding: "utf8" });
48
+ }
49
+
50
+ /** SIGKILL every orphaned fleet ssh and report each one. Returns the reaped
51
+ * rows. Best-effort: a `ps` failure logs and reaps nothing, never throws. */
52
+ function reapOrphanedFleetSsh({
53
+ runPs = defaultRunPs,
54
+ kill = (pid, signal) => process.kill(pid, signal),
55
+ log = (line) => console.error(line),
56
+ } = {}) {
57
+ // Platform assumption: PID-1 re-parenting and `ps -o` are POSIX.
58
+ if (process.platform === "win32") return [];
59
+ const res = runPs();
60
+ if (res.error || res.status !== 0) {
61
+ const why = (res.error && res.error.message) || `ps exited ${res.status}`;
62
+ log(` orphaned-ssh sweep skipped: could not list processes (${why})`);
63
+ return [];
64
+ }
65
+ const reaped = [];
66
+ for (const row of parsePsRows(res.stdout).filter(isOrphanedFleetSsh)) {
67
+ try {
68
+ kill(row.pid, "SIGKILL");
69
+ } catch {
70
+ continue; // exited between the listing and the kill
71
+ }
72
+ reaped.push(row);
73
+ log(` reaped orphaned fleet ssh pid=${row.pid} :: ${row.command.slice(0, 120)}`);
74
+ }
75
+ if (reaped.length) {
76
+ log(` orphaned-ssh sweep: reaped ${reaped.length} leaked session(s) left by earlier timed-out probes`);
77
+ }
78
+ return reaped;
79
+ }
80
+
81
+ module.exports = { parsePsRows, isOrphanedFleetSsh, reapOrphanedFleetSsh };
@@ -345,9 +345,18 @@ async function collectVisibleTextLength(target) {
345
345
  // style id) and best-effort. Returns true when the style is present after the
346
346
  // call.
347
347
  //
348
- // The reveal is per-element and conditional ON THE ELEMENT BEING INVISIBLE,
349
- // never a blanket `opacity: 1 !important` / `transform: none !important` over
350
- // `*`. A blanket rule cannot tell an entrance animation's `opacity: 0` from
348
+ // The reveal is conditional on ANIMATION EVIDENCE, not on transparency alone:
349
+ // an element is revealed only when, before the reset lands, an animation or a
350
+ // transition was actually holding it back. An element resting at `opacity: 0`
351
+ // with neither is hidden BY INTENT — a closed dropdown, a hover-only action, an
352
+ // empty toast slot, an off-screen drawer — and is left exactly as the app
353
+ // rendered it. Elements carrying an explicit hidden-intent signal
354
+ // (`aria-hidden="true"`, `inert`, or `pointer-events: none`), on themselves or
355
+ // on an ancestor, are skipped even when a transition is declared — that is the
356
+ // resting shape of a closed popover which animates on open.
357
+ //
358
+ // It is never a blanket `opacity: 1 !important` / `transform: none !important`
359
+ // over `*`. A blanket rule cannot tell an entrance animation's `opacity: 0` from
351
360
  // DELIBERATE, resting state — a disabled control's dim, a muted row, a
352
361
  // collapsed chevron's rotation — so it silently flattened every one of them out
353
362
  // of every screenshot the capture pipeline produced. Two scenarios differing
@@ -385,14 +394,104 @@ async function forceFinalVisualState(target) {
385
394
  "}";
386
395
  const head = document.head || document.documentElement;
387
396
  if (!head || typeof head.appendChild !== "function") return false;
397
+
398
+ // Which elements is an animation or a transition ACTUALLY holding back?
399
+ // Snapshot that BEFORE the stylesheet lands, because injecting it destroys
400
+ // the evidence: `animation: none` / `transition: none` clears
401
+ // `animationName`, empties the running-animation list, and zeroes every
402
+ // duration. Only elements in this set are eligible for the reveal below.
403
+ const isAnimationHeld = (el) => {
404
+ let animations = null;
405
+ if (typeof el.getAnimations === "function") {
406
+ try {
407
+ animations = el.getAnimations();
408
+ } catch (_) {
409
+ animations = null;
410
+ }
411
+ }
412
+ if (animations && animations.length > 0) return true;
413
+ // No `getAnimations` (older engines) falls through to the computed-style
414
+ // evidence below — never to "reveal every invisible element".
415
+ let computed = null;
416
+ try {
417
+ computed = getComputedStyle(el);
418
+ } catch (_) {
419
+ return false;
420
+ }
421
+ if (!computed) return false;
422
+ const animationName = computed.animationName;
423
+ if (
424
+ typeof animationName === "string" &&
425
+ animationName !== "" &&
426
+ animationName !== "none"
427
+ ) {
428
+ return true;
429
+ }
430
+ const properties = computed.transitionProperty;
431
+ if (
432
+ typeof properties !== "string" ||
433
+ properties === "" ||
434
+ properties === "none"
435
+ ) {
436
+ return false;
437
+ }
438
+ const coversVisibility = properties
439
+ .split(",")
440
+ .map((property) => property.trim())
441
+ .some(
442
+ (property) =>
443
+ property === "opacity" ||
444
+ property === "transform" ||
445
+ property === "all",
446
+ );
447
+ if (!coversVisibility) return false;
448
+ const durations = computed.transitionDuration;
449
+ if (typeof durations !== "string") return false;
450
+ // A declared transition with a zero duration animates nothing, so it is
451
+ // not holding anything back.
452
+ return durations.split(",").some((duration) => parseFloat(duration) > 0);
453
+ };
454
+
455
+ // Explicit "this is hidden on purpose" signals, which outrank animation
456
+ // evidence. A closed popover carries its open-transition while resting
457
+ // closed, so a transition alone cannot distinguish it from an entrance the
458
+ // capture should finish. An ancestor counts: hiding a whole subtree is the
459
+ // common spelling.
460
+ const hasHiddenIntent = (el, computed) => {
461
+ if (computed && computed.pointerEvents === "none") return true;
462
+ if (typeof el.closest === "function") {
463
+ try {
464
+ if (el.closest('[aria-hidden="true"], [inert]')) return true;
465
+ } catch (_) {
466
+ // Fall through to the self-only checks below.
467
+ }
468
+ }
469
+ if (el.inert === true) return true;
470
+ if (
471
+ typeof el.getAttribute === "function" &&
472
+ el.getAttribute("aria-hidden") === "true"
473
+ ) {
474
+ return true;
475
+ }
476
+ return false;
477
+ };
478
+
479
+ const heldByAnimation = new Set();
480
+ if (typeof document.querySelectorAll === "function") {
481
+ for (const el of document.querySelectorAll("*")) {
482
+ if (isAnimationHeld(el)) heldByAnimation.add(el);
483
+ }
484
+ }
485
+
388
486
  head.appendChild(style);
389
487
 
390
488
  // With animations disabled above, an element held back by an entrance
391
489
  // animation now computes to its pre-animation resting state — typically
392
490
  // `opacity: 0`, often paired with a translate/scale that parks it offscreen.
393
- // Reveal exactly those, and only those: a fully transparent element shows
394
- // nothing either way, so forcing it can hide no real state, while an element
395
- // at any visible opacity is left untouched.
491
+ // Reveal exactly those, and only those: membership in the pre-reset
492
+ // `heldByAnimation` snapshot is what separates them from UI the app is
493
+ // deliberately holding at `opacity: 0`, which stays hidden. An element at
494
+ // any visible opacity is left untouched either way.
396
495
  //
397
496
  // The transform half of the reveal STOPS at the SVG boundary. A CSS
398
497
  // `transform` overrides the SVG `transform` presentation attribute, so
@@ -417,6 +516,8 @@ async function forceFinalVisualState(target) {
417
516
  if (!computed) continue;
418
517
  const opacity = parseFloat(computed.opacity);
419
518
  if (!Number.isFinite(opacity) || opacity > INVISIBLE_EPSILON) continue;
519
+ if (!heldByAnimation.has(el)) continue;
520
+ if (hasHiddenIntent(el, computed)) continue;
420
521
  if (!el.style || typeof el.style.setProperty !== "function") continue;
421
522
  const inSvg =
422
523
  el.ownerSVGElement != null ||
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codeyam-editor/codeyam-editor",
3
- "version": "0.1.9",
3
+ "version": "0.1.10",
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.9",
13
- "@codeyam-editor/codeyam-editor-darwin-x64": "0.1.9",
14
- "@codeyam-editor/codeyam-editor-linux-x64": "0.1.9",
15
- "@codeyam-editor/codeyam-editor-win32-x64": "0.1.9"
12
+ "@codeyam-editor/codeyam-editor-darwin-arm64": "0.1.10",
13
+ "@codeyam-editor/codeyam-editor-darwin-x64": "0.1.10",
14
+ "@codeyam-editor/codeyam-editor-linux-x64": "0.1.10",
15
+ "@codeyam-editor/codeyam-editor-win32-x64": "0.1.10"
16
16
  },
17
17
  "keywords": [
18
18
  "codeyam",
@@ -1,2 +1,2 @@
1
- import{j as e}from"./markdown-C9tbsVHX.js";import{b as a}from"./react-nrLBr15I.js";import{u as O}from"./useEvents-S3Qk1Y_d.js";import{h as E}from"./index-iMR5siiG.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-C9tbsVHX.js";import{b as a}from"./react-nrLBr15I.js";import{u as O}from"./useEvents-S3Qk1Y_d.js";import{h as E}from"./index-sW8wcg7v.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};