@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.
- package/npm/cloud.js +16 -3
- package/npm/fleet-auth-account.js +61 -0
- package/npm/fleet-auth.js +69 -1
- package/npm/fleet-public-url.js +81 -12
- package/npm/fleet-reconcile.js +46 -11
- package/npm/group-bounded-spawn.js +111 -0
- package/npm/launch-config.js +90 -21
- package/npm/orphan-ssh-reaper.js +81 -0
- package/npm/scenario-playwright.js +107 -6
- package/package.json +5 -5
- package/ui/dist/assets/{ContainerInspector-C7I9wctz.js → ContainerInspector-DiN91foX.js} +1 -1
- package/ui/dist/assets/{EditorShell-CE8iG_EM.css → EditorShell-BEIdHnHd.css} +1 -1
- package/ui/dist/assets/{EditorShell-9aYMeOdZ.js → EditorShell-CPV5vhjo.js} +70 -70
- package/ui/dist/assets/ScenarioDataPanel-DHAHyraq.js +18 -0
- package/ui/dist/assets/{ScenarioDataPanelFullPage-DT0TZC4M.js → ScenarioDataPanelFullPage-CHIWip3n.js} +1 -1
- package/ui/dist/assets/ScenarioRenderer-D9a91iwS.js +21 -0
- package/ui/dist/assets/{index-BRoAenc1.css → index--cE0hUJw.css} +1 -1
- package/ui/dist/assets/{index-iMR5siiG.js → index-sW8wcg7v.js} +2 -2
- package/ui/dist/index.html +2 -2
- package/ui/dist/assets/ScenarioDataPanel-CLwkrex0.js +0 -18
- package/ui/dist/assets/ScenarioRenderer-Bsarhcrl.js +0 -21
package/npm/launch-config.js
CHANGED
|
@@ -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
|
-
* (
|
|
483
|
-
|
|
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
|
-
/**
|
|
490
|
-
*
|
|
491
|
-
*
|
|
492
|
-
|
|
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 =
|
|
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 `
|
|
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. `
|
|
509
|
-
*
|
|
510
|
-
*
|
|
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).
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
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
|
-
|
|
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
|
|
349
|
-
//
|
|
350
|
-
//
|
|
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:
|
|
394
|
-
//
|
|
395
|
-
//
|
|
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.
|
|
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.
|
|
13
|
-
"@codeyam-editor/codeyam-editor-darwin-x64": "0.1.
|
|
14
|
-
"@codeyam-editor/codeyam-editor-linux-x64": "0.1.
|
|
15
|
-
"@codeyam-editor/codeyam-editor-win32-x64": "0.1.
|
|
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-
|
|
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};
|