@codeyam-editor/codeyam-editor 0.1.14 → 0.1.15
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/agent-cli-install.js +102 -0
- package/npm/cloud.js +50 -53
- package/npm/editor.js +23 -0
- package/npm/fleet-add-failure.js +53 -2
- package/npm/fleet-auth.js +17 -1
- package/npm/launch-config.js +20 -3
- package/npm/launcher-boot-log.js +57 -0
- package/npm/operator-host-disk.js +44 -0
- package/npm/scenario-check.js +172 -1
- package/npm/scenario-metrics.js +101 -0
- package/npm/scenario-playwright.js +77 -5
- package/npm/utils.js +140 -21
- package/package.json +5 -5
- package/ui/dist/assets/{ContainerInspector-9w8TlRwl.js → ContainerInspector-Ceop11Fv.js} +1 -1
- package/ui/dist/assets/EditorShell-DjI5cEF_.js +366 -0
- package/ui/dist/assets/ScenarioDataPanel-D-nWT7xD.js +21 -0
- package/ui/dist/assets/{ScenarioDataPanelFullPage-DkysGs6X.js → ScenarioDataPanelFullPage-ZNFZcTQU.js} +1 -1
- package/ui/dist/assets/ScenarioRenderer-BW6QLGFW.js +26 -0
- package/ui/dist/assets/index-B3SMxYUB.css +1 -0
- package/ui/dist/assets/{index-CqKB9WVm.js → index-ClrdYzlw.js} +6 -6
- package/ui/dist/index.html +2 -2
- package/ui/dist/assets/EditorShell-CmEoMxKh.js +0 -366
- package/ui/dist/assets/ScenarioDataPanel-BM7COEfv.js +0 -21
- package/ui/dist/assets/ScenarioRenderer-M3HRiuEn.js +0 -26
- package/ui/dist/assets/index-Bk0fHYF3.css +0 -1
package/npm/utils.js
CHANGED
|
@@ -3,6 +3,7 @@ const net = require("net");
|
|
|
3
3
|
const path = require("path");
|
|
4
4
|
const fs = require("fs");
|
|
5
5
|
const readline = require("readline");
|
|
6
|
+
const { logLauncherBootEvent } = require("./launcher-boot-log");
|
|
6
7
|
|
|
7
8
|
const rootDir = path.resolve(__dirname, "..");
|
|
8
9
|
|
|
@@ -823,6 +824,24 @@ function formatServerStartTimeoutError(port, timeoutMs) {
|
|
|
823
824
|
].join("\n");
|
|
824
825
|
}
|
|
825
826
|
|
|
827
|
+
/**
|
|
828
|
+
* Build the "server is not answering, so the browser was not opened" failure
|
|
829
|
+
* message — the launcher's last word when its final identity check finds no
|
|
830
|
+
* live server of this project. Pure string builder: names the outcome (no
|
|
831
|
+
* editor page was opened) and the three logs that say why, as absolute paths
|
|
832
|
+
* under `projectDir` so they are clickable from any cwd.
|
|
833
|
+
*/
|
|
834
|
+
function formatServerNotAnsweringError(port, projectDir) {
|
|
835
|
+
const logs = path.join(projectDir, ".codeyam", "logs");
|
|
836
|
+
return [
|
|
837
|
+
`codeyam-editor server is not answering on port ${port}, so the editor was not opened.`,
|
|
838
|
+
"Where to look:",
|
|
839
|
+
` ${path.join(logs, "editor-server.log")}`,
|
|
840
|
+
` ${path.join(logs, "editor-server.out.log")}`,
|
|
841
|
+
` ${path.join(logs, "launcher-boot.log")}`,
|
|
842
|
+
].join("\n");
|
|
843
|
+
}
|
|
844
|
+
|
|
826
845
|
/**
|
|
827
846
|
* Classify whatever is listening on `port` relative to `projectDir`.
|
|
828
847
|
* Returns one of:
|
|
@@ -1097,6 +1116,34 @@ function logLauncherTeardownSignal(signal, projectDir, { interactive, forwarded
|
|
|
1097
1116
|
}
|
|
1098
1117
|
}
|
|
1099
1118
|
|
|
1119
|
+
/**
|
|
1120
|
+
* The identity of the server on `port` when it belongs to THIS launch's
|
|
1121
|
+
* project — a same-project editor, or the Project Launcher (selector) that a
|
|
1122
|
+
* multi-project launch diverts to — else `null`. This is the "ask the server"
|
|
1123
|
+
* check every launcher-initiated kill and every browser open is gated on: a TCP
|
|
1124
|
+
* probe cannot tell this project's live server from a stranger or a corpse.
|
|
1125
|
+
*/
|
|
1126
|
+
async function ownServerIdentity(port, projectDir, classify = classifyPortHolder) {
|
|
1127
|
+
const holder = await classify(port, projectDir);
|
|
1128
|
+
return holder.kind === "same-project" || holder.kind === "launcher" ? holder.identity : null;
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1131
|
+
/**
|
|
1132
|
+
* Poll `ownServerIdentityFn(port)` until it answers or `timeoutMs` elapses.
|
|
1133
|
+
* Returns the identity, or `null` when nothing of this project's answered in the
|
|
1134
|
+
* window. A single sub-second identity probe can miss a live server that is busy
|
|
1135
|
+
* finishing its boot, so the browser gate polls instead of trusting one miss.
|
|
1136
|
+
*/
|
|
1137
|
+
async function waitForOwnServer(port, { ownServerIdentityFn, timeoutMs = 5000, intervalMs = 250 }) {
|
|
1138
|
+
const deadline = Date.now() + timeoutMs;
|
|
1139
|
+
for (;;) {
|
|
1140
|
+
const identity = await ownServerIdentityFn(port);
|
|
1141
|
+
if (identity) return identity;
|
|
1142
|
+
if (Date.now() >= deadline) return null;
|
|
1143
|
+
await new Promise((r) => setTimeout(r, intervalMs));
|
|
1144
|
+
}
|
|
1145
|
+
}
|
|
1146
|
+
|
|
1100
1147
|
/**
|
|
1101
1148
|
* Send the platform-correct termination signal to a spawned child process.
|
|
1102
1149
|
* Used by editor.js / editor-dev.js / container.js when the user hits Ctrl+C —
|
|
@@ -1227,6 +1274,16 @@ async function restartServer(port = 14199, projectDir = process.cwd()) {
|
|
|
1227
1274
|
return ensureServer(port, projectDir);
|
|
1228
1275
|
}
|
|
1229
1276
|
|
|
1277
|
+
/**
|
|
1278
|
+
* The argv `ensureServer` spawns the server with. `--port` only says where to
|
|
1279
|
+
* bind; with 2+ registered projects the server still lands on the project
|
|
1280
|
+
* selector there (`boot_route` in `start_boot_route.rs`, whose test pins this
|
|
1281
|
+
* same argv from the Rust side).
|
|
1282
|
+
*/
|
|
1283
|
+
function serverStartArgs(port) {
|
|
1284
|
+
return ["start", "--no-open", "--port", String(port)];
|
|
1285
|
+
}
|
|
1286
|
+
|
|
1230
1287
|
/**
|
|
1231
1288
|
* Start the Rust backend server if not already running, refusing to attach
|
|
1232
1289
|
* if the existing listener belongs to a different project (or to a foreign
|
|
@@ -1293,6 +1350,7 @@ async function ensureServer(port = 14199, projectDir = process.cwd()) {
|
|
|
1293
1350
|
|
|
1294
1351
|
const binary = ensureBinary();
|
|
1295
1352
|
const startTimeoutMs = resolveStartTimeoutMs();
|
|
1353
|
+
const trace = (event, fields) => logLauncherBootEvent(projectDir, event, fields);
|
|
1296
1354
|
|
|
1297
1355
|
// Spawn a fresh server child and race its boot against `timeoutMs`. Factored
|
|
1298
1356
|
// into a closure so the auto-retry path re-runs an identical spawn with a
|
|
@@ -1313,7 +1371,7 @@ async function ensureServer(port = 14199, projectDir = process.cwd()) {
|
|
|
1313
1371
|
const logFd = openServerOutputLog(projectDir);
|
|
1314
1372
|
const child = spawn(
|
|
1315
1373
|
binary,
|
|
1316
|
-
|
|
1374
|
+
serverStartArgs(port),
|
|
1317
1375
|
buildServerSpawnOptions({
|
|
1318
1376
|
cwd: projectDir,
|
|
1319
1377
|
env: serverSpawnEnv(process.env, { devCheckout: isDevCheckout() }),
|
|
@@ -1328,25 +1386,36 @@ async function ensureServer(port = 14199, projectDir = process.cwd()) {
|
|
|
1328
1386
|
}
|
|
1329
1387
|
}
|
|
1330
1388
|
child.unref();
|
|
1389
|
+
trace("spawn", { child_pid: child.pid, port, budget_ms: timeoutMs });
|
|
1331
1390
|
const race = await awaitServerReadyOrChildExit(child, port, (p) =>
|
|
1332
1391
|
waitForPort(p, timeoutMs),
|
|
1333
1392
|
);
|
|
1334
1393
|
return { child, ...race };
|
|
1335
1394
|
};
|
|
1336
1395
|
|
|
1337
|
-
// Orchestrate the boot (
|
|
1338
|
-
// effects injected, then map the discriminated result onto the
|
|
1339
|
-
// policy. Keeping the orchestration in `resolveBootedServer`
|
|
1340
|
-
// flow over injected effects) is what makes
|
|
1396
|
+
// Orchestrate the boot (ask-before-kill + adopt-on-lost-race + one auto-retry)
|
|
1397
|
+
// with the real effects injected, then map the discriminated result onto the
|
|
1398
|
+
// process-exit policy. Keeping the orchestration in `resolveBootedServer`
|
|
1399
|
+
// (pure control flow over injected effects) is what makes those guards
|
|
1341
1400
|
// unit-testable without spawning the real binary or stubbing process.exit.
|
|
1342
1401
|
const result = await resolveBootedServer({
|
|
1343
1402
|
port,
|
|
1344
1403
|
startTimeoutMs,
|
|
1345
1404
|
spawnAndAwait,
|
|
1346
1405
|
isPortInUseFn: isPortInUse,
|
|
1406
|
+
fetchIdentityFn: (p) => ownServerIdentity(p, projectDir),
|
|
1407
|
+
waitForPortToCloseFn: (p) => waitForPortToClose(p),
|
|
1408
|
+
trace,
|
|
1347
1409
|
});
|
|
1410
|
+
trace("outcome", { kind: result.kind, port });
|
|
1348
1411
|
switch (result.kind) {
|
|
1349
1412
|
case "ready":
|
|
1413
|
+
if (result.adopted) {
|
|
1414
|
+
console.error(
|
|
1415
|
+
`Another launch of this project bound port ${port} first ` +
|
|
1416
|
+
`(pid ${result.adopted.pid}); using that server.`,
|
|
1417
|
+
);
|
|
1418
|
+
}
|
|
1350
1419
|
return result.child;
|
|
1351
1420
|
case "early-exit": {
|
|
1352
1421
|
// A genuine early exit (preflight bail) is always a hard fail — even on
|
|
@@ -1370,39 +1439,79 @@ async function ensureServer(port = 14199, projectDir = process.cwd()) {
|
|
|
1370
1439
|
|
|
1371
1440
|
/**
|
|
1372
1441
|
* Boot orchestration for `ensureServer`, isolated from the process-exit policy
|
|
1373
|
-
* and from the spawn/port effects so the
|
|
1374
|
-
* single auto-retry are unit-testable. Pure control flow over
|
|
1375
|
-
* effects:
|
|
1442
|
+
* and from the spawn/port effects so the keep-before-kill guard, the adoption
|
|
1443
|
+
* path and the single auto-retry are unit-testable. Pure control flow over
|
|
1444
|
+
* injected effects:
|
|
1376
1445
|
* - `spawnAndAwait(timeoutMs)` → `{ child, outcome, exitedBeforeBind }` —
|
|
1377
1446
|
* one boot attempt racing the port against the child exit.
|
|
1378
|
-
* - `
|
|
1447
|
+
* - `fetchIdentityFn(port)` → identity | null — does THIS project's server
|
|
1448
|
+
* (or the Project Launcher) answer on the port? (`ownServerIdentity`)
|
|
1449
|
+
* - `isPortInUseFn(port)` → bool — the TCP fallback re-check.
|
|
1450
|
+
* - `waitForPortToCloseFn(port)` → bool — lets a killed attempt release the
|
|
1451
|
+
* port before the retry binds it.
|
|
1452
|
+
* - `trace(event, fields)` — launch-attempt forensics (`logLauncherBootEvent`).
|
|
1379
1453
|
*
|
|
1380
1454
|
* Returns a discriminated result the caller maps to an action:
|
|
1381
1455
|
* - `{ kind: "ready", child }` — server bound; keep this child.
|
|
1456
|
+
* - `{ kind: "ready", child: null, adopted }` — this attempt lost a bind race
|
|
1457
|
+
* to a live server of this project; that server is adopted, not fought.
|
|
1382
1458
|
* - `{ kind: "early-exit", exitedBeforeBind }` — child exited before bind
|
|
1383
|
-
*
|
|
1459
|
+
* and nothing of this project answers the port; a hard fail, never retried.
|
|
1384
1460
|
* - `{ kind: "timeout-exhausted", retryTimeoutMs }` — both the budget and a
|
|
1385
1461
|
* longer-budget retry missed; give up with actionable guidance.
|
|
1386
1462
|
*
|
|
1387
|
-
*
|
|
1388
|
-
*
|
|
1389
|
-
*
|
|
1463
|
+
* Invariant: a server that has bound is never torn down by its own launch. The
|
|
1464
|
+
* destructive `child.kill()` only fires after the server is asked
|
|
1465
|
+
* (`fetchIdentityFn`) AND the port is probed, and both come back empty. The
|
|
1466
|
+
* retry waits for the killed attempt to release the port, and runs at most once.
|
|
1390
1467
|
*/
|
|
1391
|
-
async function resolveBootedServer({
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1468
|
+
async function resolveBootedServer({
|
|
1469
|
+
port,
|
|
1470
|
+
startTimeoutMs,
|
|
1471
|
+
spawnAndAwait,
|
|
1472
|
+
isPortInUseFn,
|
|
1473
|
+
fetchIdentityFn = async () => null,
|
|
1474
|
+
waitForPortToCloseFn = async () => true,
|
|
1475
|
+
trace = () => {},
|
|
1476
|
+
}) {
|
|
1477
|
+
// One boot attempt, with the keep/adopt checks folded in: a post-deadline bind
|
|
1478
|
+
// is reported as "ready" rather than "timeout" so the caller never kills it.
|
|
1479
|
+
const attempt = async (timeoutMs, label) => {
|
|
1395
1480
|
const { child, outcome, exitedBeforeBind } = await spawnAndAwait(timeoutMs);
|
|
1481
|
+
trace("attempt-result", {
|
|
1482
|
+
attempt: label,
|
|
1483
|
+
child_pid: child && child.pid,
|
|
1484
|
+
port,
|
|
1485
|
+
budget_ms: timeoutMs,
|
|
1486
|
+
outcome,
|
|
1487
|
+
exited: exitedBeforeBind ?? undefined,
|
|
1488
|
+
});
|
|
1396
1489
|
if (outcome === "ready") return { kind: "ready", child };
|
|
1397
1490
|
if (outcome === "exited" || (outcome === "timeout" && exitedBeforeBind !== null)) {
|
|
1491
|
+
// The child died before binding. When the port is held by a live server of
|
|
1492
|
+
// THIS project (a concurrent launch won the bind), adopt it rather than
|
|
1493
|
+
// ending the launch with zero servers.
|
|
1494
|
+
const holder = await fetchIdentityFn(port);
|
|
1495
|
+
if (holder) {
|
|
1496
|
+
trace("adopt", { attempt: label, port, holder_pid: holder.pid, reason: "lost-bind-race" });
|
|
1497
|
+
return { kind: "ready", child: null, adopted: holder };
|
|
1498
|
+
}
|
|
1398
1499
|
return { kind: "early-exit", exitedBeforeBind };
|
|
1399
1500
|
}
|
|
1400
|
-
// Genuine timeout —
|
|
1401
|
-
|
|
1501
|
+
// Genuine timeout — ask the server before reporting a miss.
|
|
1502
|
+
const identity = await fetchIdentityFn(port);
|
|
1503
|
+
if (identity) {
|
|
1504
|
+
trace("keep", { attempt: label, child_pid: child.pid, port, decided_by: "identity" });
|
|
1505
|
+
return { kind: "ready", child };
|
|
1506
|
+
}
|
|
1507
|
+
if (await isPortInUseFn(port)) {
|
|
1508
|
+
trace("keep", { attempt: label, child_pid: child.pid, port, decided_by: "tcp" });
|
|
1509
|
+
return { kind: "ready", child };
|
|
1510
|
+
}
|
|
1402
1511
|
return { kind: "timeout", child };
|
|
1403
1512
|
};
|
|
1404
1513
|
|
|
1405
|
-
const first = await attempt(startTimeoutMs);
|
|
1514
|
+
const first = await attempt(startTimeoutMs, "first");
|
|
1406
1515
|
if (first.kind !== "timeout") return first;
|
|
1407
1516
|
|
|
1408
1517
|
// The boot was just slow (cold caches, broker respawn). Kill the stalled
|
|
@@ -1413,11 +1522,17 @@ async function resolveBootedServer({ port, startTimeoutMs, spawnAndAwait, isPort
|
|
|
1413
1522
|
`Server did not bind port ${port} within ${Math.round(startTimeoutMs / 1000)}s; ` +
|
|
1414
1523
|
`retrying once with a longer budget...`,
|
|
1415
1524
|
);
|
|
1525
|
+
trace("kill", { child_pid: first.child.pid, port, reason: "no-identity-no-listener-after-budget" });
|
|
1416
1526
|
first.child.kill();
|
|
1527
|
+
// Let the killed attempt release the port, so a late bind of the dying child
|
|
1528
|
+
// cannot make the retry fail with "port held" (the double-spawn shape).
|
|
1529
|
+
const released = await waitForPortToCloseFn(port);
|
|
1530
|
+
trace("retry-spawn", { port, budget_ms: retryTimeoutMs, prior_port_released: released });
|
|
1417
1531
|
|
|
1418
|
-
const second = await attempt(retryTimeoutMs);
|
|
1532
|
+
const second = await attempt(retryTimeoutMs, "retry");
|
|
1419
1533
|
if (second.kind !== "timeout") return second;
|
|
1420
1534
|
|
|
1535
|
+
trace("kill", { child_pid: second.child.pid, port, reason: "retry-budget-exhausted" });
|
|
1421
1536
|
second.child.kill();
|
|
1422
1537
|
return { kind: "timeout-exhausted", retryTimeoutMs };
|
|
1423
1538
|
}
|
|
@@ -1480,6 +1595,7 @@ module.exports = {
|
|
|
1480
1595
|
formatLaunchFailureError,
|
|
1481
1596
|
ensureProxyPortAvailable,
|
|
1482
1597
|
formatServerStartTimeoutError,
|
|
1598
|
+
formatServerNotAnsweringError,
|
|
1483
1599
|
resolveStartTimeoutMs,
|
|
1484
1600
|
tryKillPid,
|
|
1485
1601
|
findListeningPids,
|
|
@@ -1487,6 +1603,7 @@ module.exports = {
|
|
|
1487
1603
|
stopServer,
|
|
1488
1604
|
restartServer,
|
|
1489
1605
|
ensureServer,
|
|
1606
|
+
serverStartArgs,
|
|
1490
1607
|
resolveBootedServer,
|
|
1491
1608
|
awaitServerReadyOrChildExit,
|
|
1492
1609
|
isInteractiveLauncher,
|
|
@@ -1496,6 +1613,8 @@ module.exports = {
|
|
|
1496
1613
|
openServerOutputLog,
|
|
1497
1614
|
formatLauncherSignalRecord,
|
|
1498
1615
|
logLauncherTeardownSignal,
|
|
1616
|
+
ownServerIdentity,
|
|
1617
|
+
waitForOwnServer,
|
|
1499
1618
|
killChildProcess,
|
|
1500
1619
|
deepMergeJson,
|
|
1501
1620
|
stripUnderscoreTopLevelKeys,
|
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.15",
|
|
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.15",
|
|
13
|
+
"@codeyam-editor/codeyam-editor-darwin-x64": "0.1.15",
|
|
14
|
+
"@codeyam-editor/codeyam-editor-linux-x64": "0.1.15",
|
|
15
|
+
"@codeyam-editor/codeyam-editor-win32-x64": "0.1.15"
|
|
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-DNb1olaj.js";import{m 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-DNb1olaj.js";import{m as E}from"./index-ClrdYzlw.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};
|