@codeyam-editor/codeyam-editor 0.1.11 → 0.1.12-staging.g3468c54

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/npm/cloud.js CHANGED
@@ -3243,10 +3243,18 @@ async function seedPreviewOrigin(
3243
3243
  * Best-effort and non-fatal by contract: a probe failure, a still-absent
3244
3244
  * stack.json (verdict stays `true`), or a heal that can only reach a loopback
3245
3245
  * bootstrap origin (the operator's `<N>-preview` tunnel not published yet) all
3246
- * leave the add green. The cloudflared-monitor's periodic tick (section 2) is
3247
- * the durable owner that heals post-add flips — onboarding finishing minutes
3248
- * after READY. */
3249
- async function healPreviewOriginAtReady(cfg, appPort = VITE_PORT) {
3246
+ * leave the add green.
3247
+ *
3248
+ * TWO TRIGGERS, and the second is the point. This used to be called only from
3249
+ * the add/READY path, and its own comment named "the cloudflared-monitor's
3250
+ * periodic tick" as the durable owner of post-add flips. That owner does not
3251
+ * exist on this fleet: the fleet runs on the GCP HTTPS LB, there is no
3252
+ * cloudflared tick, and so a VM that developed the failure MID-LIFE — the
3253
+ * normal case, since the failure needs an app to exist before it can fail to
3254
+ * hydrate — was never healed by anything. It is now also called per-VM from
3255
+ * `cmdReconcileIngress`, the ~15-minute roster sweep that genuinely does run,
3256
+ * which is what makes a mid-life flip self-heal without an Add. */
3257
+ async function healPreviewOriginAtReady(cfg, appPort = VITE_PORT, { resolveAppPort = null } = {}) {
3250
3258
  const probe = runGcloud(
3251
3259
  buildGcloudArgs("ssh-probe", cfg, {
3252
3260
  command: `curl -fsS --max-time 5 http://localhost:${EDITOR_PORT}/api/config 2>/dev/null || echo '{}'`,
@@ -3282,7 +3290,14 @@ async function healPreviewOriginAtReady(cfg, appPort = VITE_PORT) {
3282
3290
  // inside the seed answers `true` and re-writes the same-origin sentinel that
3283
3291
  // just failed to hydrate. We got here precisely BECAUSE the editor said
3284
3292
  // `false`, so `false` is the answer the seed must use.
3285
- await seedPreviewOrigin(cfg, appPort, false);
3293
+ // Resolve the real app port only NOW, past the two guards above, because
3294
+ // reading it costs an SSH round-trip per VM. The add path already has the port
3295
+ // in hand and passes it directly; the periodic sweep passes a resolver instead,
3296
+ // so a fleet of healthy VMs pays nothing and only a VM actually being healed
3297
+ // pays the read. Falls back to whatever `appPort` already is if the resolver
3298
+ // fails, which is the same VITE_PORT bootstrap default it had before.
3299
+ const seedPort = resolveAppPort ? ((await resolveAppPort()) ?? appPort) : appPort;
3300
+ await seedPreviewOrigin(cfg, seedPort, false);
3286
3301
  // Tell the running server to re-read editor.local.json — no restart (the
3287
3302
  // footgun that killed VM7's live Claude session).
3288
3303
  const reload = runGcloud(
@@ -5015,6 +5030,40 @@ async function cmdIngressUp(args, projectDir = rootDir) {
5015
5030
  );
5016
5031
  }
5017
5032
 
5033
+ /** Re-seed the preview origin on any rostered VM whose editor reports the
5034
+ * subpath-hydration escalation but whose configured origin never followed.
5035
+ *
5036
+ * The second trigger for `healPreviewOriginAtReady`, whose only other caller is
5037
+ * the add/READY path — so a VM that developed the failure MID-LIFE was never
5038
+ * reached by it. That is the normal way this failure appears (it needs an app to
5039
+ * exist before the app can fail to hydrate), and it is why the same bug recurred
5040
+ * on four VMs that had each been added long before they broke.
5041
+ *
5042
+ * Runs over the WHOLE roster, not just the sweep's re-wire targets: a VM can be
5043
+ * perfectly wired at the load balancer and still be serving its preview from the
5044
+ * wrong origin — the two conditions are unrelated, and narrowing to the re-wire
5045
+ * set would miss exactly the healthy-but-wrong VM this exists for. Safe to do so
5046
+ * because the heal's own two guards return early unless the editor reports a
5047
+ * not-same-origin-safe stack AND a wrong-mode origin, so a healthy VM costs one
5048
+ * bounded probe and writes nothing; the app-port read is deferred behind those
5049
+ * guards, so only a VM actually being healed pays for it.
5050
+ *
5051
+ * Per-VM failures are logged and swallowed: a preview-origin heal must never
5052
+ * abort an ingress sweep that has other VMs left to reconcile. */
5053
+ async function healPreviewOriginsAcrossRoster(cfg, roster) {
5054
+ for (const n of roster) {
5055
+ const label = slugFor(n) || `vm-${n}`;
5056
+ const perVmCfg = { ...cfg, instanceName: `${fleetInstancePrefix()}${n}` };
5057
+ try {
5058
+ await healPreviewOriginAtReady(perVmCfg, VITE_PORT, {
5059
+ resolveAppPort: () => readVmAppPort(perVmCfg),
5060
+ });
5061
+ } catch (e) {
5062
+ console.error(` VM-${n} ${label}: preview-origin heal skipped (${e && e.message})`);
5063
+ }
5064
+ }
5065
+ }
5066
+
5018
5067
  /** Detect unwired / half-wired rostered VMs and re-wire them idempotently.
5019
5068
  * Enumerates the fleet roster (the same source the dashboard reads), runs the
5020
5069
  * read-only `vm-ingress.sh <N> status` probe per VM, and for any reporting
@@ -5104,6 +5153,8 @@ async function cmdReconcileIngress(args, projectDir = rootDir) {
5104
5153
  );
5105
5154
  }
5106
5155
 
5156
+ await healPreviewOriginsAcrossRoster(cfg, roster);
5157
+
5107
5158
  // Converge each rostered backend-service's timeout to 86400 (24h). A fully-wired
5108
5159
  // BES still at GCP's 30s default reports `wired` above and is skipped by the
5109
5160
  // re-wire loop, yet on an External HTTPS LB that 30s is the MAX WebSocket lifetime
@@ -5764,6 +5815,7 @@ module.exports = {
5764
5815
  buildEditorLocalSeedWriteCommand,
5765
5816
  seedPreviewOrigin,
5766
5817
  healPreviewOriginAtReady,
5818
+ healPreviewOriginsAcrossRoster,
5767
5819
  buildGcloudArgs,
5768
5820
  sshScriptCommand,
5769
5821
  bootstrapRunnerScript,
@@ -0,0 +1,227 @@
1
+ 'use strict';
2
+ // Pure IAP identity verification for the fleet dashboard
3
+ // (fleet: Let any codeyam.com account into the VM Dashboard).
4
+ //
5
+ // The dashboard sits behind a Google load balancer with IAP in front. IAP stamps
6
+ // two things onto every request it admits: a convenience header
7
+ // (X-Goog-Authenticated-User-Email) and a signed assertion
8
+ // (X-Goog-IAP-JWT-Assertion). Only the second is trustworthy — the plain header
9
+ // is forgeable by anything that can reach the dashboard port directly, which the
10
+ // firewall narrows to Google's LB/IAP ranges but does not reduce to nothing (the
11
+ // operator host itself still reaches it). So this module verifies the assertion
12
+ // cryptographically and never reads the convenience header.
13
+ //
14
+ // This is the PURE half — no network, no clock of its own, no config reads. The
15
+ // caller supplies the JWK set, the expected audience, and the current time;
16
+ // server.js owns fetching and caching Google's keys. That split mirrors the
17
+ // other fleet-* modules and is what makes every branch here unit-testable
18
+ // without a network (npm/fleet-iap-identity.test.js).
19
+ //
20
+ // Stack note: this is fleet-operator dashboard infrastructure (the GCE fleet
21
+ // that runs codeyam-editor itself). A project that never runs the dashboard
22
+ // never reaches it.
23
+ const crypto = require('crypto');
24
+
25
+ // The issuer IAP stamps on every assertion it signs. A token that verifies
26
+ // against Google's keys but carries a different issuer is not an IAP assertion
27
+ // and is refused.
28
+ const IAP_ISSUER = 'https://cloud.google.com/iap';
29
+
30
+ // Where Google publishes the ES256 public keys IAP signs with. Exported so
31
+ // server.js and the documentation cannot drift on the URL.
32
+ const IAP_JWK_URL = 'https://www.gstatic.com/iap/verify/public_key-jwk';
33
+
34
+ // Leeway, in seconds, allowed when comparing `iat` against the supplied clock.
35
+ // Without it a token minted a fraction of a second in the future (ordinary clock
36
+ // skew between Google's signer and the operator host) reads as forged.
37
+ const CLOCK_SKEW_SEC = 60;
38
+
39
+ // decodeJwt splits an assertion and base64url-decodes its header and payload
40
+ // WITHOUT verifying anything. It exists so the caller can read `kid` to choose a
41
+ // key, and so malformed input fails as `null` at one place rather than throwing
42
+ // from the middle of verification. Never make an authorization decision on what
43
+ // this returns — that is what verifyAssertion is for.
44
+ function decodeJwt(assertion) {
45
+ if (typeof assertion !== 'string') return null;
46
+ const parts = assertion.split('.');
47
+ if (parts.length !== 3 || !parts[0] || !parts[1] || !parts[2]) return null;
48
+ try {
49
+ const header = JSON.parse(Buffer.from(parts[0], 'base64url').toString('utf8'));
50
+ const payload = JSON.parse(Buffer.from(parts[1], 'base64url').toString('utf8'));
51
+ if (!header || typeof header !== 'object' || Array.isArray(header)) return null;
52
+ if (!payload || typeof payload !== 'object' || Array.isArray(payload)) return null;
53
+ return { header, payload };
54
+ } catch {
55
+ return null;
56
+ }
57
+ }
58
+
59
+ // Accept either the JWK document Google serves (`{ keys: [...] }`) or a bare
60
+ // array of JWKs, so a caller holding the fetched document does not have to
61
+ // remember to reach into it.
62
+ function keyList(keys) {
63
+ if (Array.isArray(keys)) return keys;
64
+ if (keys && Array.isArray(keys.keys)) return keys.keys;
65
+ return [];
66
+ }
67
+
68
+ // Pick the JWK matching this token's `kid`. A token whose kid is absent from the
69
+ // set is refused rather than tried against every key: an unknown kid means the
70
+ // cached set is stale (the caller's cue to refresh), not that the token is valid
71
+ // under some other key.
72
+ function findKey(keys, kid) {
73
+ if (typeof kid !== 'string' || kid === '') return null;
74
+ return keyList(keys).find((k) => k && k.kid === kid) || null;
75
+ }
76
+
77
+ // verifyAssertion returns the verified email address, or null. Every failure
78
+ // path returns null — there is deliberately no "valid but unidentified" result,
79
+ // because the only caller uses this to decide whether to admit a request.
80
+ //
81
+ // `keys` is Google's JWK set, `audience` the expected `aud` string, and `now`
82
+ // the current time in epoch MILLISECONDS (defaults to Date.now(), supplied
83
+ // explicitly by the tests so expiry is deterministic).
84
+ function verifyAssertion(assertion, { keys, audience, now } = {}) {
85
+ const decoded = decodeJwt(assertion);
86
+ if (!decoded) return null;
87
+ const { header, payload } = decoded;
88
+
89
+ // IAP signs with ES256. Pinning the algorithm refuses a token that asks to be
90
+ // verified as something else — including `alg: "none"`, and including the
91
+ // classic confusion where an attacker re-signs with HS256 using the public key
92
+ // as the HMAC secret.
93
+ if (header.alg !== 'ES256') return null;
94
+
95
+ const jwk = findKey(keys, header.kid);
96
+ if (!jwk) return null;
97
+
98
+ const parts = assertion.split('.');
99
+ const signingInput = `${parts[0]}.${parts[1]}`;
100
+ let signature;
101
+ try {
102
+ signature = Buffer.from(parts[2], 'base64url');
103
+ } catch {
104
+ return null;
105
+ }
106
+ if (signature.length === 0) return null;
107
+
108
+ let signatureOk = false;
109
+ try {
110
+ const publicKey = crypto.createPublicKey({ key: jwk, format: 'jwk' });
111
+ // `dsaEncoding: 'ieee-p1363'` is load-bearing and is the trap in this whole
112
+ // module. A JWT carries an ECDSA signature as the raw r‖s pair, while Node's
113
+ // default for EC keys is DER. Without this option every genuine token fails
114
+ // verification, and the symptom — all tokens rejected — reads like a key or
115
+ // audience problem and sends the debugging somewhere else entirely.
116
+ signatureOk = crypto.verify(
117
+ 'sha256',
118
+ Buffer.from(signingInput),
119
+ { key: publicKey, dsaEncoding: 'ieee-p1363' },
120
+ signature,
121
+ );
122
+ } catch {
123
+ return null;
124
+ }
125
+ if (!signatureOk) return null;
126
+
127
+ if (payload.iss !== IAP_ISSUER) return null;
128
+ // An unset expected audience is a misconfiguration, not a wildcard. Admitting
129
+ // a signature-valid token from a DIFFERENT Google-fronted backend would be the
130
+ // worst possible reading of "no audience configured".
131
+ if (typeof audience !== 'string' || audience === '') return null;
132
+ if (payload.aud !== audience) return null;
133
+
134
+ const nowSec = Math.floor((now == null ? Date.now() : now) / 1000);
135
+ if (typeof payload.exp !== 'number' || nowSec >= payload.exp) return null;
136
+ if (typeof payload.iat === 'number' && payload.iat - CLOCK_SKEW_SEC > nowSec) return null;
137
+
138
+ const email = typeof payload.email === 'string' ? payload.email.trim().toLowerCase() : '';
139
+ return email === '' ? null : email;
140
+ }
141
+
142
+ // parseAllowedDomains turns the configured comma-separated list into a
143
+ // normalized array. Split out from isAllowedEmail so the server parses the env
144
+ // var once at boot instead of on every request, and so the parsing rule is
145
+ // testable on its own.
146
+ function parseAllowedDomains(raw) {
147
+ if (Array.isArray(raw)) raw = raw.join(',');
148
+ return String(raw == null ? '' : raw)
149
+ .split(',')
150
+ .map((d) => d.trim().toLowerCase())
151
+ .filter(Boolean);
152
+ }
153
+
154
+ // isAllowedEmail decides whether a VERIFIED email is one this dashboard admits.
155
+ // The domain is compared WHOLE, never as a suffix: a suffix test would admit
156
+ // `x@evil-codeyam.com`, and a naive `includes` would admit
157
+ // `x@codeyam.com.evil.com`. Both are real lookalikes an attacker controls.
158
+ function isAllowedEmail(email, allowedDomains) {
159
+ if (typeof email !== 'string') return false;
160
+ const lower = email.trim().toLowerCase();
161
+ const at = lower.indexOf('@');
162
+ // Exactly one "@", and neither side empty.
163
+ if (at <= 0 || lower.indexOf('@', at + 1) !== -1) return false;
164
+ const domain = lower.slice(at + 1);
165
+ if (domain === '') return false;
166
+ return parseAllowedDomains(allowedDomains).includes(domain);
167
+ }
168
+
169
+ // shouldRefreshKeySet decides whether the caller's cached JWK set needs
170
+ // refetching. Split out from the fetch itself so the cold-cache and expiry
171
+ // branches are assertable without a network: the caller's `authed` path is
172
+ // synchronous, so this decides, and the caller does the I/O in the background.
173
+ //
174
+ // `hasKeys` false always refreshes — a cache that has never been filled is the
175
+ // state in which every request is refused, so it is the one worth retrying.
176
+ function shouldRefreshKeySet({ hasKeys, fetchedAt, now, ttlMs } = {}) {
177
+ if (!hasKeys) return true;
178
+ const at = typeof fetchedAt === 'number' ? fetchedAt : 0;
179
+ const ttl = typeof ttlMs === 'number' ? ttlMs : 0;
180
+ const nowMs = typeof now === 'number' ? now : Date.now();
181
+ return nowMs - at > ttl;
182
+ }
183
+
184
+ // decideAdmission is the dashboard's whole authorization rule, as a pure
185
+ // function of four facts: the email IAP verified (or null), the domains this
186
+ // dashboard admits, whether the shared token has been retired, and whether the
187
+ // presented token matched.
188
+ //
189
+ // Returns `allow`, the `viewer` to attribute actions to (null on the token path,
190
+ // which carries no identity), and `rejectedEmail` — a verified person who was
191
+ // not admitted on their own merits, so the refusal can say why instead of
192
+ // telling them to find a token they will never be given.
193
+ function decideAdmission({ verifiedEmail, allowedDomains, requireIap, tokenOk } = {}) {
194
+ if (verifiedEmail && isAllowedEmail(verifiedEmail, allowedDomains)) {
195
+ return { allow: true, viewer: verifiedEmail, rejectedEmail: null };
196
+ }
197
+ const rejectedEmail = verifiedEmail || null;
198
+ // Identity-only mode retires the token outright, so a wrong-domain account
199
+ // cannot fall through to it.
200
+ if (requireIap) return { allow: false, viewer: null, rejectedEmail };
201
+ return { allow: Boolean(tokenOk), viewer: null, rejectedEmail };
202
+ }
203
+
204
+ // forbiddenDomainMessage is what a signed-in person on an unadmitted domain
205
+ // sees. It names the account that was actually verified, because the common
206
+ // cause is a browser signed in to a personal account alongside a work one — and
207
+ // the generic "append a token" refusal sent exactly those people hunting for a
208
+ // credential that was never the answer.
209
+ function forbiddenDomainMessage(email, allowedDomains) {
210
+ const domains = parseAllowedDomains(allowedDomains);
211
+ const allowed = domains.length ? domains.join(', ') : 'none configured';
212
+ return `Forbidden — ${email} is not on a domain this dashboard admits (${allowed}). `
213
+ + 'Sign in with an account on one of those domains.\n';
214
+ }
215
+
216
+ module.exports = {
217
+ IAP_ISSUER,
218
+ IAP_JWK_URL,
219
+ CLOCK_SKEW_SEC,
220
+ decodeJwt,
221
+ verifyAssertion,
222
+ parseAllowedDomains,
223
+ isAllowedEmail,
224
+ shouldRefreshKeySet,
225
+ decideAdmission,
226
+ forbiddenDomainMessage,
227
+ };
@@ -670,6 +670,42 @@ async function applyBrowserState(context, config) {
670
670
  await context.setExtraHTTPHeaders(headers);
671
671
  }
672
672
 
673
+ // Tell the page it is under capture, before any app script runs. Browsers do
674
+ // not let page JS set headers on a WebSocket handshake, so the
675
+ // `X-Codeyam-Capture` header above cannot reach `/ws/terminal` — this flag is
676
+ // how a chat surface learns to render a placeholder instead of opening a
677
+ // socket, and how it knows to mark the socket it does open. Landing first
678
+ // also means app code cannot spoof it.
679
+ //
680
+ // `liveSocket` mirrors `scenarioScriptsLiveSocket`: the page's WebSocket is
681
+ // only left un-stubbed when the scenario scripts a transcript or a stream.
682
+ // The page needs BOTH bits, because the right behavior differs:
683
+ //
684
+ // liveSocket true — connect. The server replays the scripted transcript
685
+ // (a real conversation in the screenshot), or refuses
686
+ // the spawn if none matched. These are the scenarios
687
+ // that could reach a real agent, so they are the ones
688
+ // the server-side refusal actually guards.
689
+ // liveSocket false — the harness stubs the socket, so a connect can never
690
+ // reach the server and never gets refused; it just
691
+ // retries and bakes a varying "Reconnecting… (attempt N
692
+ // of 15)" counter into the screenshot. Don't connect.
693
+ //
694
+ // Registered UNCONDITIONALLY, not inside the storage branch below: every
695
+ // capture needs it, for the same reason `codeyamHeaders` is always present.
696
+ await context.addInitScript(
697
+ (liveSocket) => {
698
+ try {
699
+ window.__codeyamCapture = true;
700
+ window.__codeyamCaptureLiveSocket = liveSocket;
701
+ } catch (_) {
702
+ // Never fail a capture over the marker; the server-side refusal is the
703
+ // load-bearing half and does not depend on this.
704
+ }
705
+ },
706
+ scenarioScriptsLiveSocket(config),
707
+ );
708
+
673
709
  // Strip the codeyam capture markers (and any scenario request headers) from
674
710
  // CROSS-ORIGIN requests. setExtraHTTPHeaders applies context-wide, so the
675
711
  // custom `X-Codeyam-*` headers ride along on third-party subresource
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codeyam-editor/codeyam-editor",
3
- "version": "0.1.11",
3
+ "version": "0.1.12-staging.g3468c54",
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.11",
13
- "@codeyam-editor/codeyam-editor-darwin-x64": "0.1.11",
14
- "@codeyam-editor/codeyam-editor-linux-x64": "0.1.11",
15
- "@codeyam-editor/codeyam-editor-win32-x64": "0.1.11"
12
+ "@codeyam-editor/codeyam-editor-darwin-arm64": "0.1.12-staging.g3468c54",
13
+ "@codeyam-editor/codeyam-editor-darwin-x64": "0.1.12-staging.g3468c54",
14
+ "@codeyam-editor/codeyam-editor-linux-x64": "0.1.12-staging.g3468c54",
15
+ "@codeyam-editor/codeyam-editor-win32-x64": "0.1.12-staging.g3468c54"
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-wh9iIy0M.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
- `)})}),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};
1
+ import{j as e}from"./markdown-C9tbsVHX.js";import{b as a}from"./react-nrLBr15I.js";import{u as k}from"./useEvents-S3Qk1Y_d.js";import{k as E}from"./index-CJLPJ5n_.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}=k(),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 O({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:O,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(O,{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
+ `)})}),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(O,{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}=k(),[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(O,{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}=k(),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}=k();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,O 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};