@kizenapps/cli 0.8.0 → 0.10.0

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.
Files changed (88) hide show
  1. package/dist/index.js +388 -90
  2. package/dist/index.js.map +1 -1
  3. package/dist/viewer/assets/AppDetailPage-ObugMPkn.js +1 -0
  4. package/dist/viewer/assets/BootstrapContext-CEWgv2oI.js +1 -0
  5. package/dist/viewer/assets/Card-BSwG5koM.js +1 -0
  6. package/dist/viewer/assets/ConfigurationPage-SnFNzZgJ.js +1 -0
  7. package/dist/viewer/assets/CredentialsContext-BLZX6RwF.js +1 -0
  8. package/dist/viewer/assets/DevSidebar-BiROAoKF.js +1 -0
  9. package/dist/viewer/assets/SandboxPage-BFVVe-i3.js +1 -0
  10. package/dist/viewer/assets/SidebarContext-Byo6PYOF.js +1 -0
  11. package/dist/viewer/assets/VersionsPage-DOd2Hv8M.js +1 -0
  12. package/dist/viewer/assets/configStorage-Bb-A9Nvj.js +14 -0
  13. package/dist/viewer/assets/{fa-core-BU3ZZt7W.js → fa-core-mHW_3JUd.js} +1 -1
  14. package/dist/viewer/assets/index-B-4D4w_w.js +5 -0
  15. package/dist/viewer/assets/index-D6Un_G7k.css +2 -0
  16. package/dist/viewer/assets/{react-C90crogl.js → react-D_K8ni3_.js} +1 -1
  17. package/dist/viewer/assets/validIcons-TcFXfn-E.js +1 -0
  18. package/dist/viewer/index.html +8 -5
  19. package/package.json +8 -4
  20. package/dist/chromium-extension/manifest.json +0 -17
  21. package/dist/chromium-extension/rules.json +0 -17
  22. package/dist/chunk-4OYGMEFM.js +0 -3675
  23. package/dist/chunk-4OYGMEFM.js.map +0 -1
  24. package/dist/chunk-4Z6S6LWP.js +0 -6134
  25. package/dist/chunk-4Z6S6LWP.js.map +0 -1
  26. package/dist/chunk-5WPGAZLP.js +0 -6136
  27. package/dist/chunk-5WPGAZLP.js.map +0 -1
  28. package/dist/chunk-6XGFZ4W5.js +0 -3673
  29. package/dist/chunk-6XGFZ4W5.js.map +0 -1
  30. package/dist/chunk-HJPLDI45.js +0 -6134
  31. package/dist/chunk-HJPLDI45.js.map +0 -1
  32. package/dist/chunk-KMKDY2I6.js +0 -46
  33. package/dist/chunk-KMKDY2I6.js.map +0 -1
  34. package/dist/chunk-LSHHXCC6.js +0 -3639
  35. package/dist/chunk-LSHHXCC6.js.map +0 -1
  36. package/dist/chunk-NNDSADQG.js +0 -3641
  37. package/dist/chunk-NNDSADQG.js.map +0 -1
  38. package/dist/chunk-SEGVTWSK.js +0 -44
  39. package/dist/chunk-SEGVTWSK.js.map +0 -1
  40. package/dist/chunk-ZWE3DS7E.js +0 -39
  41. package/dist/chunk-ZWE3DS7E.js.map +0 -1
  42. package/dist/devtools-HNR7DD7Q.js +0 -68
  43. package/dist/devtools-HNR7DD7Q.js.map +0 -1
  44. package/dist/devtools-HW3UQAHH.js +0 -69
  45. package/dist/devtools-HW3UQAHH.js.map +0 -1
  46. package/dist/devtools-KXIXZGBY.js +0 -64
  47. package/dist/devtools-KXIXZGBY.js.map +0 -1
  48. package/dist/devtools-PDXM3L35.js +0 -67
  49. package/dist/devtools-PDXM3L35.js.map +0 -1
  50. package/dist/devtools-SDEASRYI.js +0 -66
  51. package/dist/devtools-SDEASRYI.js.map +0 -1
  52. package/dist/devtools-XBEEGBQ4.js +0 -66
  53. package/dist/devtools-XBEEGBQ4.js.map +0 -1
  54. package/dist/dist-4P5P64FK.js +0 -8474
  55. package/dist/dist-4P5P64FK.js.map +0 -1
  56. package/dist/dist-AVHDSU4X.js +0 -137
  57. package/dist/dist-AVHDSU4X.js.map +0 -1
  58. package/dist/dist-C3F6UOWF.js +0 -8472
  59. package/dist/dist-C3F6UOWF.js.map +0 -1
  60. package/dist/dist-NUJOSN7W.js +0 -8472
  61. package/dist/dist-NUJOSN7W.js.map +0 -1
  62. package/dist/dist-QHDMIBVE.js +0 -135
  63. package/dist/dist-QHDMIBVE.js.map +0 -1
  64. package/dist/dist-ZIZQOSY3.js +0 -135
  65. package/dist/dist-ZIZQOSY3.js.map +0 -1
  66. package/dist/electron/icon.png +0 -0
  67. package/dist/electron/main.js +0 -201
  68. package/dist/electron/main.js.map +0 -1
  69. package/dist/electron/preload.cjs +0 -9
  70. package/dist/gzip-size-K37OQCQ2.js +0 -137
  71. package/dist/gzip-size-K37OQCQ2.js.map +0 -1
  72. package/dist/gzip-size-QRGIYYEQ.js +0 -137
  73. package/dist/gzip-size-QRGIYYEQ.js.map +0 -1
  74. package/dist/gzip-size-SHZBT5GU.js +0 -139
  75. package/dist/gzip-size-SHZBT5GU.js.map +0 -1
  76. package/dist/viewer/assets/index-B09qenTV.js +0 -17
  77. package/dist/viewer/assets/index-CVd4BLit.js +0 -17
  78. package/dist/viewer/assets/index-C_gRcfsL.js +0 -17
  79. package/dist/viewer/assets/index-CcvbGL5I.js +0 -17
  80. package/dist/viewer/assets/index-Ci5up9QD.js +0 -17
  81. package/dist/viewer/assets/index-DOiSISo1.css +0 -2
  82. package/dist/viewer/assets/index-DSJAwx9Y.js +0 -17
  83. package/dist/viewer/assets/index-DUgUEouL.css +0 -2
  84. package/dist/viewer/assets/index-WnCF0zx3.js +0 -17
  85. package/dist/viewer/assets/index-_K33TupM.js +0 -17
  86. package/dist/viewer/assets/index-dPV4go8k.js +0 -17
  87. package/dist/viewer/assets/index-xFuxlPJA.js +0 -17
  88. /package/dist/viewer/assets/{fa-pro-icons-D4afj0tU.js → fa-pro-icons-CIQGXozY.js} +0 -0
package/dist/index.js CHANGED
@@ -229,18 +229,269 @@ import { render as render2 } from "ink";
229
229
  // src/ui/DevUI.tsx
230
230
  import { useCallback as useCallback2, useEffect as useEffect3, useRef, useState as useState3 } from "react";
231
231
  import { Box as Box4, Text as Text4, useInput as useInput2 } from "ink";
232
- import { spawn } from "child_process";
233
232
  import { createReadStream, watch } from "fs";
234
- import { access, readFile as readFile4 } from "fs/promises";
233
+ import { access, readFile as readFile5 } from "fs/promises";
235
234
  import { createServer } from "http";
235
+ import { dirname as dirname3, extname, join as join7 } from "path";
236
+ import { fileURLToPath as fileURLToPath2 } from "url";
237
+ import { WebSocketServer } from "ws";
238
+
239
+ // src/chrome/launcher.ts
240
+ import { launch } from "chrome-launcher";
236
241
  import { createRequire } from "module";
237
- import { dirname as dirname2, extname, join as join6 } from "path";
242
+ import { mkdirSync } from "fs";
243
+ import { readFile as readFile2 } from "fs/promises";
244
+ import { dirname, join as join4 } from "path";
238
245
  import { fileURLToPath } from "url";
239
- import { WebSocketServer } from "ws";
246
+ var CDP = createRequire(import.meta.url)("chrome-remote-interface");
247
+ var CSP_HEADERS = /* @__PURE__ */ new Set([
248
+ "content-security-policy",
249
+ "content-security-policy-report-only",
250
+ "x-frame-options"
251
+ ]);
252
+ var KIZEN_DOMAINS = ["kizen.dev", "kizen.com"];
253
+ function isKizenUrl(url) {
254
+ try {
255
+ const { hostname } = new URL(url);
256
+ return KIZEN_DOMAINS.some((d) => hostname === d || hostname.endsWith("." + d));
257
+ } catch {
258
+ return false;
259
+ }
260
+ }
261
+ function parseProp(prop) {
262
+ if (prop.type === "number") {
263
+ return Number(prop.value);
264
+ }
265
+ if (prop.type === "boolean") {
266
+ return prop.value === "true";
267
+ }
268
+ if (prop.type === "undefined") {
269
+ return void 0;
270
+ }
271
+ if (prop.type === "object" && prop.value === "null") {
272
+ return null;
273
+ }
274
+ if (prop.type === "object" && prop.valuePreview) {
275
+ const { valuePreview } = prop;
276
+ if (valuePreview.subtype === "array") {
277
+ return valuePreview.properties.filter((p) => /^\d+$/.test(p.name)).sort((a, b) => parseInt(a.name) - parseInt(b.name)).map(parseProp);
278
+ }
279
+ const obj = {};
280
+ for (const p of valuePreview.properties) {
281
+ obj[p.name] = parseProp(p);
282
+ }
283
+ return obj;
284
+ }
285
+ return prop.value ?? `[${prop.type}]`;
286
+ }
287
+ function serializeArg(arg) {
288
+ if (arg.value !== void 0) {
289
+ return arg.value;
290
+ }
291
+ if (arg.preview) {
292
+ const { preview } = arg;
293
+ if (preview.subtype === "array") {
294
+ return preview.properties.filter((p) => /^\d+$/.test(p.name)).sort((a, b) => parseInt(a.name) - parseInt(b.name)).map(parseProp);
295
+ }
296
+ const obj = {};
297
+ for (const prop of preview.properties) {
298
+ obj[prop.name] = parseProp(prop);
299
+ }
300
+ return obj;
301
+ }
302
+ return arg.description ?? `[${arg.type}]`;
303
+ }
304
+ async function serializeArgAsync(client, arg) {
305
+ if (arg.type === "object" && arg.objectId) {
306
+ try {
307
+ const result = await client.Runtime.callFunctionOn({
308
+ objectId: arg.objectId,
309
+ functionDeclaration: 'function() { const seen = new WeakSet(); return JSON.stringify(this, function(k,v) { if (typeof v === "object" && v !== null) { if (seen.has(v)) return "[Circular]"; seen.add(v); } return v; }); }',
310
+ returnByValue: true
311
+ });
312
+ const val = result.result.value;
313
+ if (typeof val === "string") {
314
+ return JSON.parse(val);
315
+ }
316
+ } catch {
317
+ }
318
+ }
319
+ return serializeArg(arg);
320
+ }
321
+ async function installDockIcon(client) {
322
+ try {
323
+ const iconPath = join4(dirname(fileURLToPath(import.meta.url)), "viewer", "icon.png");
324
+ const icon = await readFile2(iconPath);
325
+ await client.Browser.setDockTile({ image: icon.toString("base64") });
326
+ } catch {
327
+ }
328
+ }
329
+ async function launchChromeViewer(opts) {
330
+ mkdirSync(opts.userDataDir, { recursive: true });
331
+ const chrome = await launch({
332
+ startingUrl: `http://localhost:${String(opts.port)}`,
333
+ chromeFlags: [
334
+ `--app=http://localhost:${String(opts.port)}`,
335
+ "--no-first-run",
336
+ "--no-default-browser-check",
337
+ "--disable-extensions"
338
+ ],
339
+ userDataDir: opts.userDataDir,
340
+ handleSIGINT: false,
341
+ logLevel: "silent"
342
+ });
343
+ let client = null;
344
+ let cookieTimer = null;
345
+ const cdpPort = chrome.port;
346
+ for (let attempt = 0; attempt < 10; attempt++) {
347
+ await new Promise((r) => setTimeout(r, 300));
348
+ try {
349
+ client = await CDP({ port: cdpPort });
350
+ break;
351
+ } catch {
352
+ if (attempt === 9) {
353
+ return {
354
+ kill: () => {
355
+ chrome.kill();
356
+ }
357
+ };
358
+ }
359
+ }
360
+ }
361
+ if (!client) {
362
+ return {
363
+ kill: () => {
364
+ chrome.kill();
365
+ }
366
+ };
367
+ }
368
+ const c = client;
369
+ await installDockIcon(c);
370
+ await c.Fetch.enable({
371
+ patterns: KIZEN_DOMAINS.flatMap((d) => [
372
+ { urlPattern: `*://${d}/*`, requestStage: "Response" },
373
+ { urlPattern: `*://*.${d}/*`, requestStage: "Response" }
374
+ ])
375
+ });
376
+ c.on("Fetch.requestPaused", (rawParams) => {
377
+ void (async () => {
378
+ const params = rawParams;
379
+ const { requestId, request, responseHeaders, responseStatusCode } = params;
380
+ const url = request.url;
381
+ const filtered = [];
382
+ for (const h of responseHeaders ?? []) {
383
+ if (CSP_HEADERS.has(h.name.toLowerCase())) {
384
+ continue;
385
+ }
386
+ if (h.name.toLowerCase() === "set-cookie" && isKizenUrl(url)) {
387
+ const patched = h.value.split("\n").map((cookie) => {
388
+ if (!/SameSite=/i.test(cookie)) {
389
+ return cookie + "; SameSite=None; Secure";
390
+ }
391
+ return cookie.replace(/SameSite=\w+/i, "SameSite=None");
392
+ }).join("\n");
393
+ filtered.push({ name: h.name, value: patched });
394
+ } else {
395
+ filtered.push(h);
396
+ }
397
+ }
398
+ try {
399
+ const { body, base64Encoded } = await c.Fetch.getResponseBody({ requestId });
400
+ await c.Fetch.fulfillRequest({
401
+ requestId,
402
+ responseCode: responseStatusCode ?? 200,
403
+ responseHeaders: filtered,
404
+ body,
405
+ ...base64Encoded && { base64Encoded: true }
406
+ });
407
+ } catch {
408
+ try {
409
+ await c.Fetch.continueRequest({ requestId });
410
+ } catch {
411
+ }
412
+ }
413
+ })();
414
+ });
415
+ cookieTimer = setInterval(() => {
416
+ void (async () => {
417
+ try {
418
+ const { cookies } = await c.Network.getAllCookies();
419
+ for (const cookie of cookies) {
420
+ if (!isKizenUrl(`https://${cookie.domain ?? ""}`)) {
421
+ continue;
422
+ }
423
+ if (cookie.sameSite === "None") {
424
+ continue;
425
+ }
426
+ const raw = cookie.domain ?? "";
427
+ const bare = raw.startsWith(".") ? raw.slice(1) : raw;
428
+ await c.Network.setCookie({
429
+ url: `https://${bare}`,
430
+ name: cookie.name,
431
+ value: cookie.value,
432
+ domain: raw,
433
+ path: cookie.path ?? "/",
434
+ secure: true,
435
+ ...cookie.httpOnly !== void 0 && { httpOnly: cookie.httpOnly },
436
+ ...cookie.expirationDate !== void 0 && {
437
+ expires: cookie.expirationDate
438
+ },
439
+ sameSite: "None"
440
+ });
441
+ }
442
+ } catch {
443
+ }
444
+ })();
445
+ }, 2e3);
446
+ await c.send("Runtime.enable", { generatePreview: true });
447
+ c.on("Target.attachedToTarget", (rawParams) => {
448
+ const { sessionId } = rawParams;
449
+ void c.send("Runtime.enable", { generatePreview: true }, sessionId).catch(() => {
450
+ });
451
+ });
452
+ await c.Target.setAutoAttach({
453
+ autoAttach: true,
454
+ waitForDebuggerOnStart: false,
455
+ flatten: true
456
+ });
457
+ await c.Target.setDiscoverTargets({ discover: true });
458
+ c.on("Target.targetCreated", (rawParams) => {
459
+ const { targetInfo } = rawParams;
460
+ if (targetInfo.type === "page" && targetInfo.url === "chrome://newtab/") {
461
+ void c.Target.closeTarget({ targetId: targetInfo.targetId }).catch(() => {
462
+ });
463
+ }
464
+ });
465
+ c.on("Runtime.consoleAPICalled", (rawParams) => {
466
+ void (async () => {
467
+ const { type, args } = rawParams;
468
+ if (args[0]?.type === "string" && String(args[0].value).includes("Chrome Security")) {
469
+ return;
470
+ }
471
+ const level = type === "warning" ? "warn" : ["log", "warn", "error", "info"].includes(type) ? type : "log";
472
+ const serialized = await Promise.all(args.map((arg) => serializeArgAsync(c, arg)));
473
+ opts.broadcast({ type: "console-message", level, args: serialized });
474
+ })().catch(() => {
475
+ });
476
+ });
477
+ const kill = () => {
478
+ clearInterval(cookieTimer);
479
+ try {
480
+ void c.close();
481
+ } catch {
482
+ }
483
+ chrome.kill();
484
+ };
485
+ return { kill };
486
+ }
240
487
 
241
488
  // src/lib/proxyCache.ts
242
- function createProxyCache() {
489
+ function createProxyCache(options = {}) {
243
490
  const cache = /* @__PURE__ */ new Map();
491
+ const { onChange } = options;
492
+ const emitChange = () => {
493
+ onChange?.(cache.size);
494
+ };
244
495
  return {
245
496
  get(key, fetcher) {
246
497
  const existing = cache.get(key);
@@ -258,24 +509,33 @@ function createProxyCache() {
258
509
  };
259
510
  }).catch((error) => {
260
511
  cache.delete(key);
512
+ emitChange();
261
513
  throw error;
262
514
  });
263
515
  cache.set(key, pending);
264
- return pending.then((response) => ({ response, fromCache: false }));
516
+ emitChange();
517
+ return pending.then((response) => {
518
+ if (response.status >= 400) {
519
+ cache.delete(key);
520
+ emitChange();
521
+ }
522
+ return { response, fromCache: false };
523
+ });
265
524
  },
266
525
  clear() {
267
526
  cache.clear();
527
+ emitChange();
268
528
  }
269
529
  };
270
530
  }
271
531
 
272
532
  // src/lib/credentials.ts
273
- import { mkdir as mkdir2, readFile as readFile2, readdir as readdir2, writeFile as writeFile2 } from "fs/promises";
533
+ import { mkdir as mkdir2, readFile as readFile3, readdir as readdir2, writeFile as writeFile2 } from "fs/promises";
274
534
  import { homedir } from "os";
275
- import { dirname, join as join4 } from "path";
535
+ import { dirname as dirname2, join as join5 } from "path";
276
536
  var ENVIRONMENTS = ["go", "fmo", "staging", "integration", "test1"];
277
- var GLOBAL_CREDENTIALS_DIR = join4(homedir(), ".kizenappbuilder");
278
- var GLOBAL_CREDENTIALS_PATH = join4(GLOBAL_CREDENTIALS_DIR, "credentials.json");
537
+ var GLOBAL_CREDENTIALS_DIR = join5(homedir(), ".kizenappbuilder");
538
+ var GLOBAL_CREDENTIALS_PATH = join5(GLOBAL_CREDENTIALS_DIR, "credentials.json");
279
539
  var DEFAULT_PROFILE_NAME = "credentials";
280
540
  function isValidEnvironment(value) {
281
541
  return ENVIRONMENTS.includes(value);
@@ -294,7 +554,7 @@ function parseCredentials(raw) {
294
554
  };
295
555
  }
296
556
  async function loadCredentialsFromFile(filePath) {
297
- const content = await readFile2(filePath, "utf-8");
557
+ const content = await readFile3(filePath, "utf-8");
298
558
  return parseCredentials(JSON.parse(content));
299
559
  }
300
560
  async function loadGlobalCredentials() {
@@ -305,11 +565,11 @@ async function loadGlobalCredentials() {
305
565
  }
306
566
  }
307
567
  async function saveGlobalCredentials(credentials) {
308
- await mkdir2(dirname(GLOBAL_CREDENTIALS_PATH), { recursive: true });
568
+ await mkdir2(dirname2(GLOBAL_CREDENTIALS_PATH), { recursive: true });
309
569
  await writeFile2(GLOBAL_CREDENTIALS_PATH, JSON.stringify(credentials, null, 2), "utf-8");
310
570
  }
311
571
  function getProfilePath(name) {
312
- return join4(GLOBAL_CREDENTIALS_DIR, `${name}.json`);
572
+ return join5(GLOBAL_CREDENTIALS_DIR, `${name}.json`);
313
573
  }
314
574
  async function listCredentialProfiles() {
315
575
  const profiles = [
@@ -318,10 +578,14 @@ async function listCredentialProfiles() {
318
578
  try {
319
579
  const entries = await readdir2(GLOBAL_CREDENTIALS_DIR);
320
580
  for (const entry of entries) {
321
- if (!entry.endsWith(".json")) continue;
581
+ if (!entry.endsWith(".json")) {
582
+ continue;
583
+ }
322
584
  const name = entry.slice(0, -5);
323
- if (name === DEFAULT_PROFILE_NAME) continue;
324
- profiles.push({ name, path: join4(GLOBAL_CREDENTIALS_DIR, entry), isDefault: false });
585
+ if (name === DEFAULT_PROFILE_NAME) {
586
+ continue;
587
+ }
588
+ profiles.push({ name, path: join5(GLOBAL_CREDENTIALS_DIR, entry), isDefault: false });
325
589
  }
326
590
  } catch {
327
591
  }
@@ -344,11 +608,11 @@ async function loadCredentialProfile(name) {
344
608
  }
345
609
 
346
610
  // src/lib/config.ts
347
- import { mkdir as mkdir3, readFile as readFile3, writeFile as writeFile3 } from "fs/promises";
348
- import { join as join5 } from "path";
611
+ import { mkdir as mkdir3, readFile as readFile4, writeFile as writeFile3 } from "fs/promises";
612
+ import { join as join6 } from "path";
349
613
  async function loadConfig(outputDir) {
350
614
  try {
351
- const content = await readFile3(join5(outputDir, "config.json"), "utf-8");
615
+ const content = await readFile4(join6(outputDir, "config.json"), "utf-8");
352
616
  return JSON.parse(content);
353
617
  } catch {
354
618
  return {};
@@ -356,7 +620,7 @@ async function loadConfig(outputDir) {
356
620
  }
357
621
  async function saveConfig(outputDir, config) {
358
622
  await mkdir3(outputDir, { recursive: true });
359
- await writeFile3(join5(outputDir, "config.json"), JSON.stringify(config, null, 2), "utf-8");
623
+ await writeFile3(join6(outputDir, "config.json"), JSON.stringify(config, null, 2), "utf-8");
360
624
  }
361
625
 
362
626
  // src/ui/CredentialSetupUI.tsx
@@ -383,7 +647,7 @@ var CredentialSetupUI = ({
383
647
  const [error, setError] = useState2(null);
384
648
  const [activeProfileName, setActiveProfileName] = useState2(void 0);
385
649
  const handleModeChosen = useCallback(
386
- async (mode) => {
650
+ (mode) => {
387
651
  if (mode === "local") {
388
652
  onComplete({ mode: "local", credentials: null });
389
653
  return;
@@ -452,7 +716,7 @@ var CredentialSetupUI = ({
452
716
  setPhase({ type: "mode-select", cursor: 1 });
453
717
  } else if (key.return) {
454
718
  const mode = phase.cursor === 0 ? "global" : "local";
455
- void handleModeChosen(mode);
719
+ handleModeChosen(mode);
456
720
  }
457
721
  return;
458
722
  }
@@ -468,13 +732,16 @@ var CredentialSetupUI = ({
468
732
  if (cursor === addNewIdx) {
469
733
  setPhase({ type: "name-entry", nameBuffer: "", existingProfiles: profiles });
470
734
  } else {
471
- void handleProfileChosen(profiles[cursor] ?? profiles[0]);
735
+ const selected = profiles[cursor] ?? profiles[0];
736
+ if (selected) {
737
+ void handleProfileChosen(selected);
738
+ }
472
739
  }
473
740
  }
474
741
  return;
475
742
  }
476
743
  if (phase.type === "name-entry") {
477
- const { nameBuffer, nameError: _nameError, existingProfiles } = phase;
744
+ const { nameBuffer, existingProfiles } = phase;
478
745
  if (key.backspace || key.delete) {
479
746
  setPhase({ type: "name-entry", nameBuffer: nameBuffer.slice(0, -1), existingProfiles });
480
747
  } else if (key.return) {
@@ -619,7 +886,14 @@ var CredentialSetupUI = ({
619
886
  }),
620
887
  /* @__PURE__ */ jsxs2(Box3, { gap: 2, children: [
621
888
  /* @__PURE__ */ jsx3(Text3, { ...cursor === addNewIdx && { color: "cyan" }, children: cursor === addNewIdx ? "\u276F" : " " }),
622
- /* @__PURE__ */ jsx3(Text3, { bold: cursor === addNewIdx, ...cursor === addNewIdx && { color: "cyan" }, children: "+ Add new profile" })
889
+ /* @__PURE__ */ jsx3(
890
+ Text3,
891
+ {
892
+ bold: cursor === addNewIdx,
893
+ ...cursor === addNewIdx && { color: "cyan" },
894
+ children: "+ Add new profile"
895
+ }
896
+ )
623
897
  ] })
624
898
  ] }),
625
899
  /* @__PURE__ */ jsx3(Hint, { text: "\u2191\u2193 to move \xB7 Enter to select \xB7 Esc to cancel" })
@@ -649,7 +923,7 @@ var CredentialSetupUI = ({
649
923
  }
650
924
  const { field: activeField, values, envCursor } = phase;
651
925
  const isEnvField = activeField === FIELDS.length;
652
- const profileLabel = activeProfileName !== void 0 ? activeProfileName : DEFAULT_PROFILE_NAME;
926
+ const profileLabel = activeProfileName ?? DEFAULT_PROFILE_NAME;
653
927
  return /* @__PURE__ */ jsxs2(Box3, { flexDirection: "column", paddingY: 1, paddingX: 2, gap: 1, children: [
654
928
  /* @__PURE__ */ jsx3(Logo, {}),
655
929
  /* @__PURE__ */ jsxs2(Box3, { flexDirection: "column", marginTop: 1, children: [
@@ -659,7 +933,8 @@ var CredentialSetupUI = ({
659
933
  /* @__PURE__ */ jsxs2(Box3, { flexDirection: "column", gap: 0, children: [
660
934
  /* @__PURE__ */ jsx3(Text3, { bold: true, children: activeProfileName !== void 0 ? `Profile: ${activeProfileName}` : "Global credentials" }),
661
935
  /* @__PURE__ */ jsxs2(Text3, { dimColor: true, children: [
662
- "Saved to: ",
936
+ "Saved to:",
937
+ " ",
663
938
  activeProfileName !== void 0 ? `~/.kizenappbuilder/${activeProfileName}.json` : GLOBAL_CREDENTIALS_PATH
664
939
  ] })
665
940
  ] }),
@@ -739,12 +1014,8 @@ var Spinner2 = () => {
739
1014
  return /* @__PURE__ */ jsx4(Text4, { color: "cyan", children: SPINNER_FRAMES2[frame] ?? "\u280B" });
740
1015
  };
741
1016
  function getViewerPath() {
742
- const filename = fileURLToPath(import.meta.url);
743
- return join6(dirname2(filename), "viewer");
744
- }
745
- function getElectronMainPath() {
746
- const filename = fileURLToPath(import.meta.url);
747
- return join6(dirname2(filename), "electron", "main.js");
1017
+ const filename = fileURLToPath2(import.meta.url);
1018
+ return join7(dirname3(filename), "viewer");
748
1019
  }
749
1020
  async function fileExists(filePath) {
750
1021
  try {
@@ -754,8 +1025,8 @@ async function fileExists(filePath) {
754
1025
  return false;
755
1026
  }
756
1027
  }
757
- function createRequestHandler(viewerPath, createServerLog, createProxyLog, credentialsRef, activeProfileRef, outputDir, broadcast) {
758
- const proxyCache = createProxyCache();
1028
+ function createRequestHandler(viewerPath, createServerLog, createProxyLog, credentialsRef, activeProfileRef, outputDir, broadcast, onProxyCacheChange) {
1029
+ const proxyCache = createProxyCache({ onChange: onProxyCacheChange });
759
1030
  return (req, res) => {
760
1031
  void (async () => {
761
1032
  const url = req.url ?? "/";
@@ -782,7 +1053,7 @@ function createRequestHandler(viewerPath, createServerLog, createProxyLog, crede
782
1053
  const creds = profileName === void 0 || profileName === "" ? await loadGlobalCredentials() : await loadCredentialProfile(profileName);
783
1054
  if (creds !== null) {
784
1055
  credentialsRef.current = creds;
785
- activeProfileRef.current = profileName || void 0;
1056
+ activeProfileRef.current = profileName ?? void 0;
786
1057
  const active = activeProfileRef.current;
787
1058
  await saveConfig(outputDir, {
788
1059
  credentialMode: "global",
@@ -795,9 +1066,9 @@ function createRequestHandler(viewerPath, createServerLog, createProxyLog, crede
795
1066
  return;
796
1067
  }
797
1068
  if (url === "/api/bundle") {
798
- const bundlePath = join6(process.cwd(), ".kizenapp", "bundle.json");
1069
+ const bundlePath = join7(process.cwd(), ".kizenapp", "bundle.json");
799
1070
  try {
800
- const content = await readFile4(bundlePath, "utf-8");
1071
+ const content = await readFile5(bundlePath, "utf-8");
801
1072
  res.writeHead(200, { "Content-Type": "application/json; charset=utf-8" });
802
1073
  res.end(content);
803
1074
  } catch {
@@ -870,8 +1141,8 @@ function createRequestHandler(viewerPath, createServerLog, createProxyLog, crede
870
1141
  return;
871
1142
  }
872
1143
  const rawPath = url === "/" ? "/index.html" : url;
873
- const filePath = join6(viewerPath, rawPath);
874
- const resolvedPath = await fileExists(filePath) ? filePath : join6(viewerPath, "index.html");
1144
+ const filePath = join7(viewerPath, rawPath);
1145
+ const resolvedPath = await fileExists(filePath) ? filePath : join7(viewerPath, "index.html");
875
1146
  const ext = extname(resolvedPath);
876
1147
  const mimeType = MIME_TYPES[ext] ?? "application/octet-stream";
877
1148
  res.writeHead(200, { "Content-Type": mimeType });
@@ -922,11 +1193,46 @@ var DevUI = ({
922
1193
  const [buildError, setBuildError] = useState3(null);
923
1194
  const [lastBuilt, setLastBuilt] = useState3(null);
924
1195
  const [wsClientCount, setWsClientCount] = useState3(0);
1196
+ const [proxyCacheSize, setProxyCacheSize] = useState3(0);
1197
+ const [proxyCacheHits, setProxyCacheHits] = useState3(0);
1198
+ const [proxyCacheMisses, setProxyCacheMisses] = useState3(0);
925
1199
  const buildingRef = useRef(false);
926
- const electronLaunchedRef = useRef(false);
1200
+ const viewerLaunchedRef = useRef(false);
1201
+ const chromeHandleRef = useRef(null);
927
1202
  const debounceTimerRef = useRef(null);
928
1203
  const wsClientsRef = useRef(/* @__PURE__ */ new Set());
929
1204
  const pendingMessagesRef = useRef([]);
1205
+ const broadcast = useCallback2((msg) => {
1206
+ const json = JSON.stringify(msg);
1207
+ if (wsClientsRef.current.size === 0) {
1208
+ pendingMessagesRef.current = [...pendingMessagesRef.current, json].slice(-LOG_LIMIT);
1209
+ return;
1210
+ }
1211
+ for (const client of wsClientsRef.current) {
1212
+ if (client.readyState === 1) {
1213
+ client.send(json);
1214
+ }
1215
+ }
1216
+ }, []);
1217
+ const createServerLog = useCallback2((message) => {
1218
+ setServerLogHistory(
1219
+ (h) => [...h, `${(/* @__PURE__ */ new Date()).toLocaleTimeString()}: ${message}`].slice(-LOG_LIMIT)
1220
+ );
1221
+ }, []);
1222
+ const launchChromeViewerCb = useCallback2(() => {
1223
+ chromeHandleRef.current?.kill();
1224
+ chromeHandleRef.current = null;
1225
+ void launchChromeViewer({
1226
+ port,
1227
+ userDataDir: join7(outputDir, ".chrome"),
1228
+ broadcast
1229
+ }).then((handle) => {
1230
+ chromeHandleRef.current = handle;
1231
+ process.on("exit", () => {
1232
+ handle.kill();
1233
+ });
1234
+ });
1235
+ }, [port, outputDir, broadcast]);
930
1236
  useInput2(
931
1237
  (input, key) => {
932
1238
  if (input === "q" || key.ctrl && input === "c") {
@@ -935,50 +1241,19 @@ var DevUI = ({
935
1241
  if (input === "c") {
936
1242
  setCredMode("editing");
937
1243
  }
938
- if (input === "d") {
939
- broadcast({ type: "open-devtools" });
1244
+ if (input === "v") {
1245
+ launchChromeViewerCb();
940
1246
  }
941
1247
  },
942
1248
  { isActive: credMode === "main" }
943
1249
  );
944
1250
  useEffect3(() => {
945
- if (status !== "running" || electronLaunchedRef.current) {
1251
+ if (status !== "running" || viewerLaunchedRef.current) {
946
1252
  return;
947
1253
  }
948
- electronLaunchedRef.current = true;
949
- const _require = createRequire(import.meta.url);
950
- const electronBin = _require("electron");
951
- const electronMain = getElectronMainPath();
952
- const userDataDir = join6(outputDir, ".electron");
953
- const proc = spawn(
954
- electronBin,
955
- [electronMain, `--port=${String(port)}`, `--user-data-dir=${userDataDir}`],
956
- {
957
- stdio: "ignore"
958
- }
959
- );
960
- proc.unref();
961
- process.on("exit", () => {
962
- proc.kill();
963
- });
964
- }, [status, port, outputDir]);
965
- const createServerLog = useCallback2((message) => {
966
- setServerLogHistory(
967
- (h) => [...h, `${(/* @__PURE__ */ new Date()).toLocaleTimeString()}: ${message}`].slice(-LOG_LIMIT)
968
- );
969
- }, []);
970
- const broadcast = useCallback2((msg) => {
971
- const json = JSON.stringify(msg);
972
- if (wsClientsRef.current.size === 0) {
973
- pendingMessagesRef.current = [...pendingMessagesRef.current, json].slice(-LOG_LIMIT);
974
- return;
975
- }
976
- for (const client of wsClientsRef.current) {
977
- if (client.readyState === 1) {
978
- client.send(json);
979
- }
980
- }
981
- }, []);
1254
+ viewerLaunchedRef.current = true;
1255
+ launchChromeViewerCb();
1256
+ }, [status, launchChromeViewerCb]);
982
1257
  const handleCredentialsDone = useCallback2(
983
1258
  (result) => {
984
1259
  credentialsRef.current = result.credentials;
@@ -1001,6 +1276,13 @@ var DevUI = ({
1001
1276
  -LOG_LIMIT
1002
1277
  )
1003
1278
  );
1279
+ if (entry.kind === "request") {
1280
+ if (entry.fromCache) {
1281
+ setProxyCacheHits((n) => n + 1);
1282
+ } else {
1283
+ setProxyCacheMisses((n) => n + 1);
1284
+ }
1285
+ }
1004
1286
  broadcast({ type: "proxy-log", entry });
1005
1287
  },
1006
1288
  [broadcast]
@@ -1065,7 +1347,7 @@ var DevUI = ({
1065
1347
  }, [pluginDir, triggerBuild, createBuildLog]);
1066
1348
  useEffect3(() => {
1067
1349
  const viewerPath = getViewerPath();
1068
- void fileExists(join6(viewerPath, "index.html")).then((viewerBuilt) => {
1350
+ void fileExists(join7(viewerPath, "index.html")).then((viewerBuilt) => {
1069
1351
  if (!viewerBuilt) {
1070
1352
  setErrorMessage("Viewer not built. Run 'pnpm build:viewer' first.");
1071
1353
  setStatus("error");
@@ -1078,7 +1360,8 @@ var DevUI = ({
1078
1360
  credentialsRef,
1079
1361
  activeProfileRef,
1080
1362
  outputDir,
1081
- broadcast
1363
+ broadcast,
1364
+ setProxyCacheSize
1082
1365
  );
1083
1366
  const server = createServer(handler);
1084
1367
  const wss = new WebSocketServer({ server });
@@ -1110,7 +1393,7 @@ var DevUI = ({
1110
1393
  server.close();
1111
1394
  };
1112
1395
  });
1113
- }, [port, createServerLog, createProxyLog]);
1396
+ }, [port, createServerLog, createProxyLog, broadcast, outputDir]);
1114
1397
  if (credMode === "editing") {
1115
1398
  return /* @__PURE__ */ jsx4(
1116
1399
  CredentialSetupUI,
@@ -1134,7 +1417,7 @@ var DevUI = ({
1134
1417
  ] }),
1135
1418
  /* @__PURE__ */ jsxs3(Box4, { flexDirection: "column", children: [
1136
1419
  /* @__PURE__ */ jsxs3(Box4, { gap: 2, marginBottom: 0, children: [
1137
- /* @__PURE__ */ jsx4(Text4, { bold: true, dimColor: true, children: "Build" }),
1420
+ /* @__PURE__ */ jsx4(Text4, { bold: true, dimColor: true, children: "App Build" }),
1138
1421
  buildStatus === "pending" && /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: "waiting\u2026" }),
1139
1422
  buildStatus === "building" && /* @__PURE__ */ jsxs3(Box4, { gap: 1, children: [
1140
1423
  /* @__PURE__ */ jsx4(Spinner2, {}),
@@ -1157,7 +1440,7 @@ var DevUI = ({
1157
1440
  Box4,
1158
1441
  {
1159
1442
  flexDirection: "column",
1160
- height: LOG_DISPLAY,
1443
+ height: LOG_DISPLAY + 2,
1161
1444
  borderStyle: "single",
1162
1445
  borderColor: "gray",
1163
1446
  overflow: "hidden",
@@ -1167,7 +1450,7 @@ var DevUI = ({
1167
1450
  ] }),
1168
1451
  /* @__PURE__ */ jsxs3(Box4, { marginTop: 1, flexDirection: "column", children: [
1169
1452
  /* @__PURE__ */ jsxs3(Box4, { gap: 2, children: [
1170
- /* @__PURE__ */ jsx4(Text4, { bold: true, dimColor: true, children: "Server" }),
1453
+ /* @__PURE__ */ jsx4(Text4, { bold: true, dimColor: true, children: "Web Server" }),
1171
1454
  status === "starting" && /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: "starting\u2026" }),
1172
1455
  status === "running" && /* @__PURE__ */ jsxs3(Box4, { gap: 2, children: [
1173
1456
  /* @__PURE__ */ jsx4(Text4, { color: "green", children: "\u2713 Running" }),
@@ -1187,7 +1470,7 @@ var DevUI = ({
1187
1470
  Box4,
1188
1471
  {
1189
1472
  flexDirection: "column",
1190
- height: LOG_DISPLAY,
1473
+ height: LOG_DISPLAY + 2,
1191
1474
  borderStyle: "single",
1192
1475
  borderColor: "gray",
1193
1476
  overflow: "hidden",
@@ -1197,14 +1480,26 @@ var DevUI = ({
1197
1480
  ] }),
1198
1481
  /* @__PURE__ */ jsxs3(Box4, { marginTop: 1, flexDirection: "column", children: [
1199
1482
  /* @__PURE__ */ jsxs3(Box4, { gap: 2, children: [
1200
- /* @__PURE__ */ jsx4(Text4, { bold: true, dimColor: true, children: "Proxy" }),
1201
- /* @__PURE__ */ jsx4(Text4, { color: "green", children: "\u2713 Active" })
1483
+ /* @__PURE__ */ jsx4(Text4, { bold: true, dimColor: true, children: "Network Proxy" }),
1484
+ /* @__PURE__ */ jsx4(Text4, { color: "green", children: "\u2713 Active" }),
1485
+ /* @__PURE__ */ jsxs3(Text4, { dimColor: true, children: [
1486
+ "\u25CF ",
1487
+ proxyCacheSize,
1488
+ " key",
1489
+ proxyCacheSize !== 1 ? "s" : "",
1490
+ " in the network cache \xB7",
1491
+ " ",
1492
+ proxyCacheHits,
1493
+ " hit \xB7 ",
1494
+ proxyCacheMisses,
1495
+ " miss"
1496
+ ] })
1202
1497
  ] }),
1203
1498
  /* @__PURE__ */ jsx4(
1204
1499
  Box4,
1205
1500
  {
1206
1501
  flexDirection: "column",
1207
- height: LOG_DISPLAY,
1502
+ height: LOG_DISPLAY + 2,
1208
1503
  borderStyle: "single",
1209
1504
  borderColor: "gray",
1210
1505
  overflow: "hidden",
@@ -1218,8 +1513,11 @@ var DevUI = ({
1218
1513
  /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: "] quit [" }),
1219
1514
  /* @__PURE__ */ jsx4(Text4, { children: "c" }),
1220
1515
  /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: "] credentials [" }),
1221
- /* @__PURE__ */ jsx4(Text4, { children: "d" }),
1222
- /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: "] devtools" })
1516
+ /* @__PURE__ */ jsx4(Text4, { children: "v" }),
1517
+ /* @__PURE__ */ jsxs3(Text4, { dimColor: true, children: [
1518
+ "] ",
1519
+ wsClientCount === 0 ? "open viewer" : "relaunch viewer"
1520
+ ] })
1223
1521
  ] })
1224
1522
  ] });
1225
1523
  };