@ultrst-armada/krnl 0.0.0-stage → 1.0.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 (236) hide show
  1. package/README.md +84 -2
  2. package/package.json +75 -4
  3. package/packages/cli/dist/K_ENT_Entity-PQ5MURQY-ZG6FZXXX.js +13 -0
  4. package/packages/cli/dist/K_LIB_Krnl_Aiko_Client-OPZAVZCW-K4CRQYA5.js +8 -0
  5. package/packages/cli/dist/K_LIB_Krnl_Ep_Tools-ROOE2TQF-LNPXZYOE.js +110 -0
  6. package/packages/cli/dist/K_LIB_Krnl_Frontend_Builder-2ZDUH6PF-3ALZPNX4.js +11 -0
  7. package/packages/cli/dist/K_LIB_Krnl_Frontend_Generator-UYNRNX2X-PJTICYSW.js +11 -0
  8. package/packages/cli/dist/K_LIB_Krnl_Registry_Client-NXV6D2QW-M5TYU55O.js +8 -0
  9. package/packages/cli/dist/K_LIB_Krnl_SubApp_Engine-7HR5HWW2-H6STOXRK.js +20 -0
  10. package/packages/cli/dist/K_LIB_Krnl_SubApp_Registry-7XYGDKJK-XPJXSF7D.js +19 -0
  11. package/packages/cli/dist/K_LIB_Krnl_SubApp_Worker_Host-CQVTGGG7-CUYGTWOW.js +7 -0
  12. package/packages/cli/dist/K_LIB_Krnl_System_App_Adapter-M6FJKLGU-VR6T4N6J.js +17 -0
  13. package/packages/cli/dist/K_LIB_Krnl_System_Base_Functionality_APIs-ZZUZ6ZG5-RLJ4QWDP.js +58 -0
  14. package/packages/cli/dist/K_LIB_Krnl_System_Magnetic_Loader-XBQO2KXF-BOQGUASC.js +22 -0
  15. package/packages/cli/dist/K_LIB_Krnl_System_Unified_Http_Adapter-RCEGP3OG-XJI7KIT5.js +8 -0
  16. package/packages/cli/dist/K_LIB_Krnl_Telemetry-77FRZ3KV-K5VCWAFW.js +9 -0
  17. package/packages/cli/dist/bin/krnl.cjs +9758 -0
  18. package/packages/cli/dist/bin/krnl.d.cts +1 -0
  19. package/packages/cli/dist/bin/krnl.d.ts +1 -0
  20. package/packages/cli/dist/bin/krnl.js +2006 -0
  21. package/packages/cli/dist/chunk-3TRM6CYL.js +152 -0
  22. package/packages/cli/dist/chunk-5JDK35TG.js +3872 -0
  23. package/packages/cli/dist/chunk-63PTK33I.js +997 -0
  24. package/packages/cli/dist/chunk-6NIKAMUZ.js +82 -0
  25. package/packages/cli/dist/chunk-BOXWWXQG.js +400 -0
  26. package/packages/cli/dist/chunk-EEKLK7QJ.js +565 -0
  27. package/packages/cli/dist/chunk-EG7T2IAD.js +107 -0
  28. package/packages/cli/dist/chunk-F5EHXE2O.js +1036 -0
  29. package/packages/cli/dist/chunk-F65ANSQG.js +269 -0
  30. package/packages/cli/dist/chunk-FDNCADFE.js +71 -0
  31. package/packages/cli/dist/chunk-GDLRMDAN.js +127 -0
  32. package/packages/cli/dist/chunk-HWMTCMOS.js +656 -0
  33. package/packages/cli/dist/chunk-I74QIQOD.js +5536 -0
  34. package/packages/cli/dist/chunk-JTLSTO25.js +71 -0
  35. package/packages/cli/dist/chunk-JULBBJ27.js +1033 -0
  36. package/packages/cli/dist/chunk-OHVMVRBP.js +117 -0
  37. package/packages/cli/dist/chunk-P2PCLEKX.js +666 -0
  38. package/packages/cli/dist/chunk-PJQIUOX2.js +7767 -0
  39. package/packages/cli/dist/chunk-PPOF3DJW.js +471 -0
  40. package/packages/cli/dist/chunk-Q45257P6.js +173 -0
  41. package/packages/cli/dist/chunk-QNROWGBI.js +1036 -0
  42. package/packages/cli/dist/chunk-RJNYUXZD.js +977 -0
  43. package/packages/cli/dist/chunk-SRTOPJ5W.js +104 -0
  44. package/packages/cli/dist/chunk-TCEX3MPS.js +702 -0
  45. package/packages/cli/dist/chunk-TNOAJO2J.js +675 -0
  46. package/packages/cli/dist/chunk-UR2SUCSF.js +701 -0
  47. package/packages/cli/dist/chunk-X5LOIFM7.js +68 -0
  48. package/packages/cli/dist/chunk-XIRFOO3H.js +589 -0
  49. package/packages/cli/dist/chunk-ZAAV3JXQ.js +809 -0
  50. package/packages/cli/dist/index.cjs +7798 -0
  51. package/packages/cli/dist/index.d.cts +7 -0
  52. package/packages/cli/dist/index.d.ts +7 -0
  53. package/packages/cli/dist/index.js +29 -0
  54. package/packages/cli/package.json +34 -0
  55. package/packages/core/dist/K_ENT_Entity-4MB7CU4A.js +12 -0
  56. package/packages/core/dist/K_ENT_Entity-DEJGIDGH.js +12 -0
  57. package/packages/core/dist/K_ENT_Entity-FXZO5UZ2.js +12 -0
  58. package/packages/core/dist/K_ENT_Entity-HDSCXC5F.js +12 -0
  59. package/packages/core/dist/K_ENT_Entity-JILABHLW.js +12 -0
  60. package/packages/core/dist/K_ENT_Entity-PQ5MURQY.js +13 -0
  61. package/packages/core/dist/K_ENT_Entity-ROAKDIHW.js +12 -0
  62. package/packages/core/dist/K_ENT_Entity-TBVFSKDV.js +12 -0
  63. package/packages/core/dist/K_ENT_Entity-ZFLKG3JO.js +12 -0
  64. package/packages/core/dist/K_LIB_Krnl_Aiko_Client-OPZAVZCW.js +8 -0
  65. package/packages/core/dist/K_LIB_Krnl_Ep_Tools-ROOE2TQF.js +110 -0
  66. package/packages/core/dist/K_LIB_Krnl_Frontend_Builder-2ZDUH6PF.js +9 -0
  67. package/packages/core/dist/K_LIB_Krnl_Frontend_Generator-UYNRNX2X.js +9 -0
  68. package/packages/core/dist/K_LIB_Krnl_Registry_Client-NXV6D2QW.js +8 -0
  69. package/packages/core/dist/K_LIB_Krnl_SubApp_Engine-7HR5HWW2.js +20 -0
  70. package/packages/core/dist/K_LIB_Krnl_SubApp_Registry-4C6GBQAG.js +18 -0
  71. package/packages/core/dist/K_LIB_Krnl_SubApp_Registry-6V23A6F6.js +18 -0
  72. package/packages/core/dist/K_LIB_Krnl_SubApp_Registry-7XYGDKJK.js +19 -0
  73. package/packages/core/dist/K_LIB_Krnl_SubApp_Registry-DYLLC5G2.js +18 -0
  74. package/packages/core/dist/K_LIB_Krnl_SubApp_Registry-F4FOFAUP.js +18 -0
  75. package/packages/core/dist/K_LIB_Krnl_SubApp_Registry-FBOXXMI3.js +18 -0
  76. package/packages/core/dist/K_LIB_Krnl_SubApp_Registry-Q73BWLNP.js +18 -0
  77. package/packages/core/dist/K_LIB_Krnl_SubApp_Registry-QM2C4MVA.js +18 -0
  78. package/packages/core/dist/K_LIB_Krnl_SubApp_Registry-QUGI6SMP.js +18 -0
  79. package/packages/core/dist/K_LIB_Krnl_SubApp_Registry-RGMTBHNP.js +18 -0
  80. package/packages/core/dist/K_LIB_Krnl_SubApp_Registry-RWR7UNDW.js +18 -0
  81. package/packages/core/dist/K_LIB_Krnl_SubApp_Worker_Host-CQVTGGG7.js +7 -0
  82. package/packages/core/dist/K_LIB_Krnl_System_App_Adapter-32MFJJ5Z.js +17 -0
  83. package/packages/core/dist/K_LIB_Krnl_System_App_Adapter-4J4CEWRY.js +17 -0
  84. package/packages/core/dist/K_LIB_Krnl_System_App_Adapter-A366YHF2.js +17 -0
  85. package/packages/core/dist/K_LIB_Krnl_System_App_Adapter-LK4LYJHI.js +17 -0
  86. package/packages/core/dist/K_LIB_Krnl_System_App_Adapter-M6FJKLGU.js +17 -0
  87. package/packages/core/dist/K_LIB_Krnl_System_App_Adapter-NW3K63II.js +17 -0
  88. package/packages/core/dist/K_LIB_Krnl_System_App_Adapter-QDHTI5ZV.js +17 -0
  89. package/packages/core/dist/K_LIB_Krnl_System_App_Adapter-VHCT6VIW.js +17 -0
  90. package/packages/core/dist/K_LIB_Krnl_System_App_Adapter-WBRKF24R.js +17 -0
  91. package/packages/core/dist/K_LIB_Krnl_System_App_Adapter-XKYQYT6Q.js +17 -0
  92. package/packages/core/dist/K_LIB_Krnl_System_App_Adapter-Z2PWZYDZ.js +17 -0
  93. package/packages/core/dist/K_LIB_Krnl_System_Base_Functionality_APIs-ZZUZ6ZG5.js +58 -0
  94. package/packages/core/dist/K_LIB_Krnl_System_Magnetic_Loader-2EZU7QF4.js +21 -0
  95. package/packages/core/dist/K_LIB_Krnl_System_Magnetic_Loader-6G3AQNAV.js +21 -0
  96. package/packages/core/dist/K_LIB_Krnl_System_Magnetic_Loader-B2NZMQUG.js +21 -0
  97. package/packages/core/dist/K_LIB_Krnl_System_Magnetic_Loader-C2RABMZP.js +21 -0
  98. package/packages/core/dist/K_LIB_Krnl_System_Magnetic_Loader-HHE3NZ4F.js +21 -0
  99. package/packages/core/dist/K_LIB_Krnl_System_Magnetic_Loader-LHFGR6WK.js +21 -0
  100. package/packages/core/dist/K_LIB_Krnl_System_Magnetic_Loader-M74BLM3N.js +21 -0
  101. package/packages/core/dist/K_LIB_Krnl_System_Magnetic_Loader-PT6ZA6EA.js +21 -0
  102. package/packages/core/dist/K_LIB_Krnl_System_Magnetic_Loader-SP52V3CX.js +21 -0
  103. package/packages/core/dist/K_LIB_Krnl_System_Magnetic_Loader-XBQO2KXF.js +22 -0
  104. package/packages/core/dist/K_LIB_Krnl_System_Magnetic_Loader-YNSEUHJJ.js +21 -0
  105. package/packages/core/dist/K_LIB_Krnl_System_Unified_Http_Adapter-M6SBHIVA.js +8 -0
  106. package/packages/core/dist/K_LIB_Krnl_System_Unified_Http_Adapter-RCEGP3OG.js +8 -0
  107. package/packages/core/dist/K_LIB_Krnl_Telemetry-77FRZ3KV.js +9 -0
  108. package/packages/core/dist/chunk-33L2J7V7.js +82 -0
  109. package/packages/core/dist/chunk-3ES4K2ZP.js +817 -0
  110. package/packages/core/dist/chunk-3SMHVVLL.js +82 -0
  111. package/packages/core/dist/chunk-3UEZ7JQA.js +4877 -0
  112. package/packages/core/dist/chunk-3XIE6KBD.js +4877 -0
  113. package/packages/core/dist/chunk-46ZLCML6.js +107 -0
  114. package/packages/core/dist/chunk-4UEL3DWJ.js +107 -0
  115. package/packages/core/dist/chunk-54GWZ5PC.js +82 -0
  116. package/packages/core/dist/chunk-6QCYRS5L.js +127 -0
  117. package/packages/core/dist/chunk-6ULMHNXR.js +104 -0
  118. package/packages/core/dist/chunk-7WUETJEV.js +117 -0
  119. package/packages/core/dist/chunk-A5KRAYDB.js +107 -0
  120. package/packages/core/dist/chunk-ANEAZ3PO.js +107 -0
  121. package/packages/core/dist/chunk-BE76RL6F.js +82 -0
  122. package/packages/core/dist/chunk-CIARVYDO.js +107 -0
  123. package/packages/core/dist/chunk-CR2ANE62.js +107 -0
  124. package/packages/core/dist/chunk-DI27MOR7.js +173 -0
  125. package/packages/core/dist/chunk-E6RLFHM5.js +596 -0
  126. package/packages/core/dist/chunk-EELAPMNV.js +4877 -0
  127. package/packages/core/dist/chunk-ENZ2RQVJ.js +107 -0
  128. package/packages/core/dist/chunk-F2U6CX4F.js +4877 -0
  129. package/packages/core/dist/chunk-FKG64OHL.js +82 -0
  130. package/packages/core/dist/chunk-FYAZJYM5.js +772 -0
  131. package/packages/core/dist/chunk-G4NP4RDQ.js +82 -0
  132. package/packages/core/dist/chunk-GADIRLRS.js +473 -0
  133. package/packages/core/dist/chunk-GR6CSLQJ.js +772 -0
  134. package/packages/core/dist/chunk-HDS7AT6O.js +772 -0
  135. package/packages/core/dist/chunk-HIMEZUU2.js +71 -0
  136. package/packages/core/dist/chunk-IB4UHG5I.js +596 -0
  137. package/packages/core/dist/chunk-J7ZYNVRS.js +772 -0
  138. package/packages/core/dist/chunk-JVLZU4K7.js +604 -0
  139. package/packages/core/dist/chunk-JYXXI7Q3.js +82 -0
  140. package/packages/core/dist/chunk-K54K2KZW.js +192 -0
  141. package/packages/core/dist/chunk-K6VHLBNH.js +3316 -0
  142. package/packages/core/dist/chunk-KCBP7ZYA.js +596 -0
  143. package/packages/core/dist/chunk-KFQGP6VL.js +33 -0
  144. package/packages/core/dist/chunk-KIUEUI7K.js +82 -0
  145. package/packages/core/dist/chunk-LB67NIZP.js +949 -0
  146. package/packages/core/dist/chunk-LDTYFUNW.js +596 -0
  147. package/packages/core/dist/chunk-MLVWARD4.js +596 -0
  148. package/packages/core/dist/chunk-MPYVXHKO.js +2777 -0
  149. package/packages/core/dist/chunk-MRQ3MITU.js +772 -0
  150. package/packages/core/dist/chunk-MVUN7I7O.js +107 -0
  151. package/packages/core/dist/chunk-NDDOIRW7.js +596 -0
  152. package/packages/core/dist/chunk-O3KDB5MH.js +3330 -0
  153. package/packages/core/dist/chunk-OEINUDSE.js +400 -0
  154. package/packages/core/dist/chunk-OPDENPT4.js +4877 -0
  155. package/packages/core/dist/chunk-P6XEM2LA.js +429 -0
  156. package/packages/core/dist/chunk-QKMRSQV4.js +82 -0
  157. package/packages/core/dist/chunk-R6CCWALL.js +4877 -0
  158. package/packages/core/dist/chunk-RAMMOO73.js +774 -0
  159. package/packages/core/dist/chunk-S3FTZFG6.js +772 -0
  160. package/packages/core/dist/chunk-T2ZVN434.js +4877 -0
  161. package/packages/core/dist/chunk-TFHLDMIA.js +107 -0
  162. package/packages/core/dist/chunk-TI3QUCIJ.js +107 -0
  163. package/packages/core/dist/chunk-TR2BAIGI.js +107 -0
  164. package/packages/core/dist/chunk-UGVVJKC6.js +4877 -0
  165. package/packages/core/dist/chunk-V2DX7YIU.js +984 -0
  166. package/packages/core/dist/chunk-W4IHYUYD.js +772 -0
  167. package/packages/core/dist/chunk-W6WBG5OY.js +4877 -0
  168. package/packages/core/dist/chunk-WN3M7O7H.js +3312 -0
  169. package/packages/core/dist/chunk-WR3NCU7U.js +4877 -0
  170. package/packages/core/dist/chunk-XC6B6L27.js +82 -0
  171. package/packages/core/dist/chunk-XNSE64ZY.js +3316 -0
  172. package/packages/core/dist/chunk-XROES2WN.js +634 -0
  173. package/packages/core/dist/chunk-XSCKNRCZ.js +5020 -0
  174. package/packages/core/dist/chunk-XVABPAUZ.js +82 -0
  175. package/packages/core/dist/chunk-YOBCB5C5.js +596 -0
  176. package/packages/core/dist/chunk-YUK6Q563.js +666 -0
  177. package/packages/core/dist/chunk-YXGL3ZEU.js +1030 -0
  178. package/packages/core/dist/chunk-ZINJ5OUX.js +596 -0
  179. package/packages/core/dist/chunk-ZPFD4BIQ.js +604 -0
  180. package/packages/core/dist/chunk-ZQZBFG6F.js +4826 -0
  181. package/packages/core/dist/chunk-ZZFRJGXM.js +4877 -0
  182. package/packages/core/dist/index.cjs +757 -0
  183. package/packages/core/dist/index.d.cts +360 -0
  184. package/packages/core/dist/index.d.ts +360 -0
  185. package/packages/core/dist/index.js +704 -0
  186. package/packages/core/package.json +34 -0
  187. package/packages/create-krnl/dist/K_ENT_Entity-PQ5MURQY-R2OH4VXE.js +14 -0
  188. package/packages/create-krnl/dist/K_LIB_Krnl_Aiko_Client-OPZAVZCW-AZM5HF4M.js +9 -0
  189. package/packages/create-krnl/dist/K_LIB_Krnl_Ep_Tools-ROOE2TQF-X5N6SV2S.js +111 -0
  190. package/packages/create-krnl/dist/K_LIB_Krnl_Frontend_Builder-2ZDUH6PF-5PFHF4JU.js +12 -0
  191. package/packages/create-krnl/dist/K_LIB_Krnl_Frontend_Generator-UYNRNX2X-ED33OQPH.js +12 -0
  192. package/packages/create-krnl/dist/K_LIB_Krnl_Registry_Client-NXV6D2QW-YGQLVR4P.js +9 -0
  193. package/packages/create-krnl/dist/K_LIB_Krnl_SubApp_Engine-7HR5HWW2-VM7CTU2L.js +21 -0
  194. package/packages/create-krnl/dist/K_LIB_Krnl_SubApp_Registry-7XYGDKJK-Y2GQFJ7I.js +21 -0
  195. package/packages/create-krnl/dist/K_LIB_Krnl_SubApp_Worker_Host-CQVTGGG7-L3JQWO54.js +8 -0
  196. package/packages/create-krnl/dist/K_LIB_Krnl_System_App_Adapter-M6FJKLGU-KQARASPR.js +19 -0
  197. package/packages/create-krnl/dist/K_LIB_Krnl_System_Base_Functionality_APIs-ZZUZ6ZG5-OF3IGLVL.js +59 -0
  198. package/packages/create-krnl/dist/K_LIB_Krnl_System_Magnetic_Loader-XBQO2KXF-JOLHKOEX.js +24 -0
  199. package/packages/create-krnl/dist/K_LIB_Krnl_System_Unified_Http_Adapter-RCEGP3OG-L46LSHKQ.js +10 -0
  200. package/packages/create-krnl/dist/K_LIB_Krnl_Telemetry-77FRZ3KV-4JUPRR3X.js +10 -0
  201. package/packages/create-krnl/dist/chunk-3TRM6CYL.js +152 -0
  202. package/packages/create-krnl/dist/chunk-3URCB3QS.js +127 -0
  203. package/packages/create-krnl/dist/chunk-5JDK35TG.js +3872 -0
  204. package/packages/create-krnl/dist/chunk-A2XAXDOW.js +812 -0
  205. package/packages/create-krnl/dist/chunk-AN6TMQHG.js +1033 -0
  206. package/packages/create-krnl/dist/chunk-BOXWWXQG.js +400 -0
  207. package/packages/create-krnl/dist/chunk-BPTBREBG.js +38 -0
  208. package/packages/create-krnl/dist/chunk-FDNCADFE.js +71 -0
  209. package/packages/create-krnl/dist/chunk-JTLSTO25.js +71 -0
  210. package/packages/create-krnl/dist/chunk-M3NCUIPL.js +107 -0
  211. package/packages/create-krnl/dist/chunk-N2EONSOC.js +7769 -0
  212. package/packages/create-krnl/dist/chunk-OHVMVRBP.js +117 -0
  213. package/packages/create-krnl/dist/chunk-Q45257P6.js +173 -0
  214. package/packages/create-krnl/dist/chunk-QGM4M3NI.js +37 -0
  215. package/packages/create-krnl/dist/chunk-RJNYUXZD.js +977 -0
  216. package/packages/create-krnl/dist/chunk-SRTOPJ5W.js +104 -0
  217. package/packages/create-krnl/dist/chunk-UPBIGEC3.js +666 -0
  218. package/packages/create-krnl/dist/chunk-V2DB7UD7.js +82 -0
  219. package/packages/create-krnl/dist/chunk-WMHLIUI6.js +3644 -0
  220. package/packages/create-krnl/dist/chunk-XIRFOO3H.js +589 -0
  221. package/packages/create-krnl/dist/chunk-Y2CEEVZP.js +5536 -0
  222. package/packages/create-krnl/dist/index.cjs +395 -0
  223. package/packages/create-krnl/dist/index.js +372 -0
  224. package/packages/create-krnl/dist/main-DXUMPBND.js +2217 -0
  225. package/packages/create-krnl/dist/wrapper-SDF2VKX3.js +31 -0
  226. package/packages/create-krnl/package.json +23 -0
  227. package/packages/eslint-plugin/dist/index.cjs +439 -0
  228. package/packages/eslint-plugin/dist/index.d.cts +336 -0
  229. package/packages/eslint-plugin/dist/index.d.ts +336 -0
  230. package/packages/eslint-plugin/dist/index.js +416 -0
  231. package/packages/eslint-plugin/package.json +24 -0
  232. package/packages/validate/dist/index.cjs +499 -0
  233. package/packages/validate/dist/index.d.cts +49 -0
  234. package/packages/validate/dist/index.d.ts +49 -0
  235. package/packages/validate/dist/index.js +462 -0
  236. package/packages/validate/package.json +27 -0
@@ -0,0 +1,1030 @@
1
+ import {
2
+ __esm,
3
+ __export,
4
+ __require
5
+ } from "./chunk-KFQGP6VL.js";
6
+
7
+ // src/core/K_LIB_Krnl_Frontend_Generator.ts
8
+ var K_LIB_Krnl_Frontend_Generator_exports = {};
9
+ __export(K_LIB_Krnl_Frontend_Generator_exports, {
10
+ KrnlFrontendGenerator: () => KrnlFrontendGenerator
11
+ });
12
+ import fs from "fs";
13
+ import path from "path";
14
+ import { fileURLToPath } from "url";
15
+ var _metaUrl, _filename, _dirname, KrnlFrontendGenerator;
16
+ var init_K_LIB_Krnl_Frontend_Generator = __esm({
17
+ "src/core/K_LIB_Krnl_Frontend_Generator.ts"() {
18
+ _metaUrl = typeof import.meta !== "undefined" && import.meta.url ? import.meta.url : null;
19
+ _filename = _metaUrl ? fileURLToPath(_metaUrl) : "";
20
+ _dirname = _metaUrl ? path.dirname(_filename) : "";
21
+ KrnlFrontendGenerator = class {
22
+ /**
23
+ * Main entrypoint: ensures skeleton exists, generates React components & index.tsx
24
+ */
25
+ static async generate(entity, frontendDir) {
26
+ this.ensureFrontendSkeleton(entity, frontendDir);
27
+ const uiAllowlist = entity._frontend?.options?.uiEndpoints;
28
+ const uiEndpointsMap = /* @__PURE__ */ new Map();
29
+ const collectEndpoints = (ent) => {
30
+ const funcs = [
31
+ ...ent.localFunctionalities?.values() || [],
32
+ ...ent.remoteFunctionalities?.values() || []
33
+ ];
34
+ for (const fn of funcs) {
35
+ if (uiAllowlist && !uiAllowlist.includes(fn.id)) {
36
+ continue;
37
+ }
38
+ const uiSpec = fn.uiSpec || fn.ui_spec;
39
+ if (!uiSpec) {
40
+ continue;
41
+ }
42
+ if (uiSpec && uiSpec.ui_component_enabled) {
43
+ if (!uiEndpointsMap.has(fn.id)) {
44
+ uiEndpointsMap.set(fn.id, { id: fn.id, uiSpec, schema: fn.schema });
45
+ }
46
+ }
47
+ }
48
+ const children = ent.hierarchyManager?.ownedComponents || ent.components;
49
+ if (children && typeof children.values === "function") {
50
+ for (const child of children.values()) {
51
+ collectEndpoints(child);
52
+ }
53
+ }
54
+ };
55
+ collectEndpoints(entity);
56
+ const uiEndpoints = Array.from(uiEndpointsMap.values());
57
+ if (uiEndpoints.length > 0) {
58
+ this.checkReactDependencies(frontendDir);
59
+ const contractVal = this.validateExecutionContract(entity, uiEndpoints);
60
+ if (!contractVal.valid) {
61
+ console.error(`[KrnlFrontendGenerator] Execution Contract Validation Failed:
62
+ ${contractVal.errors.map((e) => ` - ${e}`).join("\n")}`);
63
+ throw new Error(`[KrnlExecutionContractError] ${contractVal.errors.join("; ")}`);
64
+ }
65
+ }
66
+ this.generateAtoms(frontendDir);
67
+ const componentsDir = path.join(frontendDir, "src", "components");
68
+ fs.mkdirSync(componentsDir, { recursive: true });
69
+ for (const ep of uiEndpoints) {
70
+ const epDir = path.join(componentsDir, ep.id);
71
+ fs.mkdirSync(epDir, { recursive: true });
72
+ const compSource = this.generateComponentSource(ep.id, ep.uiSpec, ep.schema);
73
+ fs.writeFileSync(path.join(epDir, "index.ts"), compSource, "utf-8");
74
+ }
75
+ const subAppPrefixesSet = new Set(entity._subAppRegistry?.keys() || []);
76
+ if (entity.hierarchyManager?.ownedComponents) {
77
+ for (const [childId, childEntity] of entity.hierarchyManager.ownedComponents.entries()) {
78
+ if (childEntity && childEntity._frontend) {
79
+ subAppPrefixesSet.add(childId.toLowerCase());
80
+ }
81
+ }
82
+ }
83
+ const subAppPrefixes = Array.from(subAppPrefixesSet);
84
+ const indexSource = this.generateIndexSource(entity.entityName, uiEndpoints, subAppPrefixes);
85
+ fs.writeFileSync(path.join(frontendDir, "src", "index.ts"), indexSource, "utf-8");
86
+ }
87
+ /**
88
+ * Ensures essential files (package.json, index.html, index.css, atoms/) exist in frontendDir
89
+ */
90
+ static ensureFrontendSkeleton(entity, frontendDir) {
91
+ const srcDir = path.join(frontendDir, "src");
92
+ const atomsDir = path.join(srcDir, "atoms");
93
+ fs.mkdirSync(atomsDir, { recursive: true });
94
+ const candidates = [
95
+ path.resolve(_dirname, "../src/krnl/frontend/atoms"),
96
+ path.resolve(_dirname, "../../src/krnl/frontend/atoms"),
97
+ path.resolve(_dirname, "../frontend/atoms"),
98
+ path.resolve(process.cwd(), "node_modules/krnlts/src/krnl/frontend/atoms"),
99
+ path.resolve(process.cwd(), "node_modules/krnlts/dist/frontend/atoms")
100
+ ];
101
+ const libAtomsDir = candidates.find((c) => fs.existsSync(c));
102
+ if (libAtomsDir && fs.existsSync(libAtomsDir)) {
103
+ const files = fs.readdirSync(libAtomsDir);
104
+ for (const file of files) {
105
+ const srcFile = path.join(libAtomsDir, file);
106
+ const destFile = path.join(atomsDir, file);
107
+ fs.copyFileSync(srcFile, destFile);
108
+ }
109
+ }
110
+ const pkgPath = path.join(frontendDir, "package.json");
111
+ if (!fs.existsSync(pkgPath)) {
112
+ const pkgContent = {
113
+ name: `${entity.entityName.toLowerCase()}-frontend`,
114
+ version: "1.0.0",
115
+ type: "module",
116
+ dependencies: {
117
+ react: "^18.2.0",
118
+ "react-dom": "^18.2.0"
119
+ },
120
+ devDependencies: {
121
+ "@types/react": "^18.2.0",
122
+ "@types/react-dom": "^18.2.0"
123
+ }
124
+ };
125
+ fs.writeFileSync(pkgPath, JSON.stringify(pkgContent, null, 2), "utf-8");
126
+ }
127
+ const htmlPath = path.join(frontendDir, "index.html");
128
+ if (!fs.existsSync(htmlPath)) {
129
+ const htmlContent = `<!DOCTYPE html>
130
+ <html lang="en" data-theme="glass-brutalism">
131
+ <head>
132
+ <meta charset="UTF-8">
133
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
134
+ <title>${entity.entityName} Frontend</title>
135
+ <link rel="preconnect" href="https://fonts.googleapis.com">
136
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
137
+ <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet">
138
+ <link rel="stylesheet" href="styles.css">
139
+ <link rel="stylesheet" href="/frontend/index.css">
140
+ </head>
141
+ <body>
142
+ <div class="krnl-sphere krnl-sphere-accent"></div>
143
+ <div class="krnl-sphere krnl-sphere-neutral"></div>
144
+ <div id="root"></div>
145
+ <script src="bundle.js"></script>
146
+ </body>
147
+ </html>`;
148
+ fs.writeFileSync(htmlPath, htmlContent, "utf-8");
149
+ }
150
+ const cssPath = path.join(frontendDir, "index.css");
151
+ if (!fs.existsSync(cssPath)) {
152
+ const defaultCss = `/* =============================================================
153
+ KRNL Glass-Brutalism Fallback Design System
154
+ Apple precision + glassmorphism depth + neo-brutalist structure
155
+ Override any --krnl-* token in your own index.css to restyle.
156
+ ============================================================= */
157
+
158
+ /* \u2500\u2500 Google Fonts fallback \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
159
+ @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap');
160
+
161
+ /* \u2500\u2500 Design Tokens \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
162
+ :root {
163
+ /* Typography */
164
+ --krnl-font: 'Inter', -apple-system, 'SF Pro Display', 'Segoe UI', sans-serif;
165
+ --krnl-font-mono: 'SF Mono', 'JetBrains Mono', 'Fira Code', monospace;
166
+
167
+ /* Page base \u2014 Pastel Matte Black */
168
+ --krnl-bg: #16181d;
169
+ --krnl-text: #f1f5f9;
170
+ --krnl-text-muted: #8e95a5;
171
+
172
+ /* Matte glass \u2014 panels, cards, containers (recede back) */
173
+ --krnl-matte-bg: rgba(26, 29, 36, 0.75);
174
+ --krnl-matte-bg-hover: rgba(34, 38, 48, 0.88);
175
+ --krnl-matte-blur: blur(24px);
176
+
177
+ /* Gloss \u2014 buttons, focused inputs (float forward) */
178
+ --krnl-gloss-bg: linear-gradient(160deg, rgba(255,255,255,0.12) 0%, rgba(255,255,255,0.03) 100%);
179
+ --krnl-gloss-shine: inset 0 1px 0 rgba(255,255,255,0.20);
180
+
181
+ /* Neo-brutalist borders & shadows */
182
+ --krnl-border: 2px solid rgba(255, 255, 255, 0.85);
183
+ --krnl-border-input: 2px solid rgba(255, 255, 255, 0.85);
184
+ --krnl-radius: 14px;
185
+ --krnl-radius-sm: 8px;
186
+ --krnl-shadow: 5px 5px 0px rgba(0, 0, 0, 0.90);
187
+ --krnl-shadow-hover: 8px 8px 0px rgba(0, 0, 0, 0.95);
188
+ --krnl-shadow-btn: 4px 4px 0px rgba(0, 0, 0, 0.90);
189
+ --krnl-shadow-btn-hover: 6px 6px 0px rgba(0, 0, 0, 0.95);
190
+ --krnl-shadow-btn-active: 2px 2px 0px rgba(0, 0, 0, 0.90);
191
+
192
+ /* Semantic accents \u2014 desaturated glow pastels for dark bg */
193
+ --krnl-accent: #2b4c7e; /* Dark powder blue */
194
+ --krnl-accent-fg: #f1f5f9;
195
+ --krnl-success: #1f4d36; /* Dark mint */
196
+ --krnl-success-fg: #f1f5f9;
197
+ --krnl-danger: #5c2424; /* Dark blush red */
198
+ --krnl-danger-fg: #f1f5f9;
199
+ --krnl-warning: #5e4e19; /* Dark butter yellow */
200
+ --krnl-warning-fg: #f1f5f9;
201
+
202
+ /* Phantom / live-mode indicator */
203
+ --krnl-phantom-glow: 0 0 24px rgba(48, 209, 88, 0.50);
204
+ --krnl-phantom-border: 2px solid #30d158;
205
+ }
206
+
207
+ /* \u2500\u2500 Reset \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
208
+ *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
209
+
210
+ body {
211
+ font-family: var(--krnl-font);
212
+ background-color: var(--krnl-bg);
213
+ color: var(--krnl-text);
214
+ min-height: 100vh;
215
+ overflow-x: hidden;
216
+ position: relative;
217
+ }
218
+
219
+ /* \u2500\u2500 Ambient Sphere Orbs (hidden for solid pastel black theme) \u2500\u2500 */
220
+ .krnl-sphere { display: none; }
221
+
222
+ /* \u2500\u2500 App Shell \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
223
+ .krnl-app-shell {
224
+ position: relative;
225
+ z-index: 1;
226
+ display: flex;
227
+ flex-direction: column;
228
+ min-height: 100vh;
229
+ align-items: center;
230
+ }
231
+
232
+ /* \u2500\u2500 Header (matte glass) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
233
+ .krnl-header {
234
+ display: flex;
235
+ align-items: center;
236
+ justify-content: center;
237
+ width: 100%;
238
+ padding: 1rem 2rem;
239
+ background: var(--krnl-matte-bg);
240
+ backdrop-filter: var(--krnl-matte-blur);
241
+ -webkit-backdrop-filter: var(--krnl-matte-blur);
242
+ border-bottom: var(--krnl-border);
243
+ box-shadow: 0 3px 0px rgba(0, 0, 0, 0.90);
244
+ letter-spacing: -0.02em;
245
+ position: sticky;
246
+ top: 0;
247
+ z-index: 100;
248
+ }
249
+ .krnl-header h1 {
250
+ font-size: 1.15rem;
251
+ font-weight: 800;
252
+ letter-spacing: -0.04em;
253
+ color: var(--krnl-text);
254
+ text-align: center;
255
+ }
256
+
257
+ /* \u2500\u2500 Nav Tabs (matte glass bar, wrapping + icon-only) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
258
+ .krnl-nav-tabs {
259
+ display: flex;
260
+ flex-wrap: wrap;
261
+ justify-content: center;
262
+ width: 100%;
263
+ gap: 0.5rem;
264
+ padding: 0.75rem 2rem;
265
+ background: var(--krnl-matte-bg);
266
+ backdrop-filter: var(--krnl-matte-blur);
267
+ -webkit-backdrop-filter: var(--krnl-matte-blur);
268
+ border-bottom: var(--krnl-border);
269
+ }
270
+
271
+ /* Tab buttons \u2014 icon-only by default, reveal label on hover */
272
+ .krnl-tab-btn {
273
+ position: relative;
274
+ display: inline-flex;
275
+ align-items: center;
276
+ justify-content: center;
277
+ gap: 0;
278
+ overflow: hidden;
279
+ background: var(--krnl-gloss-bg);
280
+ box-shadow: var(--krnl-gloss-shine);
281
+ border: var(--krnl-border);
282
+ color: var(--krnl-text);
283
+ font-family: var(--krnl-font);
284
+ font-size: 0.82rem;
285
+ font-weight: 600;
286
+ letter-spacing: -0.01em;
287
+ /* Collapsed icon-only size */
288
+ padding: 0.45rem 0.65rem;
289
+ min-width: 2.4rem;
290
+ max-width: 2.4rem;
291
+ border-radius: var(--krnl-radius-sm);
292
+ cursor: pointer;
293
+ white-space: nowrap;
294
+ transition: box-shadow 0.15s ease, transform 0.15s ease, max-width 0.25s ease, padding 0.25s ease, gap 0.25s ease;
295
+ }
296
+
297
+ /* Tab button label \u2014 hidden until hover */
298
+ .krnl-tab-btn .krnl-tab-label {
299
+ display: inline-block;
300
+ max-width: 0;
301
+ opacity: 0;
302
+ overflow: hidden;
303
+ transition: max-width 0.25s ease, opacity 0.2s ease;
304
+ white-space: nowrap;
305
+ pointer-events: none;
306
+ }
307
+
308
+ /* Expand on hover */
309
+ .krnl-tab-btn:hover,
310
+ .krnl-tab-btn:focus-visible {
311
+ max-width: 14rem;
312
+ padding: 0.45rem 1rem;
313
+ gap: 0.4rem;
314
+ transform: translateY(-1px);
315
+ box-shadow: var(--krnl-gloss-shine), 4px 4px 0px rgba(0, 0, 0, 0.95);
316
+ }
317
+ .krnl-tab-btn:hover .krnl-tab-label,
318
+ .krnl-tab-btn:focus-visible .krnl-tab-label {
319
+ max-width: 10rem;
320
+ opacity: 1;
321
+ }
322
+
323
+ /* Active tab \u2014 always show icon + label */
324
+ .krnl-tab-btn.active {
325
+ background: var(--krnl-accent);
326
+ color: var(--krnl-accent-fg);
327
+ box-shadow: var(--krnl-gloss-shine), var(--krnl-shadow-btn);
328
+ transform: translateY(-1px);
329
+ max-width: 14rem;
330
+ padding: 0.45rem 1rem;
331
+ gap: 0.4rem;
332
+ }
333
+ .krnl-tab-btn.active .krnl-tab-label {
334
+ max-width: 10rem;
335
+ opacity: 1;
336
+ }
337
+
338
+ /* \u2500\u2500 Main Content Area \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
339
+ .krnl-main-body {
340
+ flex: 1;
341
+ width: 100%;
342
+ max-width: 1100px;
343
+ padding: 2.5rem 2rem;
344
+ display: flex;
345
+ flex-wrap: wrap;
346
+ justify-content: center;
347
+ align-items: flex-start;
348
+ gap: 2rem;
349
+ overflow-y: auto;
350
+ }
351
+
352
+ /* \u2500\u2500 Endpoint Card (matte glass panel) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
353
+ .krnl-endpoint-card {
354
+ background: var(--krnl-matte-bg);
355
+ backdrop-filter: var(--krnl-matte-blur);
356
+ -webkit-backdrop-filter: var(--krnl-matte-blur);
357
+ border: var(--krnl-border);
358
+ border-radius: var(--krnl-radius);
359
+ box-shadow: var(--krnl-shadow);
360
+ padding: 1.75rem;
361
+ min-width: 320px;
362
+ max-width: 480px;
363
+ width: 100%;
364
+ transition: box-shadow 0.15s ease, transform 0.15s ease;
365
+ position: relative;
366
+ margin: 0 auto;
367
+ }
368
+ .krnl-endpoint-card:hover {
369
+ transform: translate(-2px, -2px);
370
+ box-shadow: var(--krnl-shadow-hover);
371
+ background: var(--krnl-matte-bg-hover);
372
+ }
373
+ .krnl-endpoint-card.krnl-phantom-active {
374
+ border: var(--krnl-phantom-border);
375
+ box-shadow: var(--krnl-phantom-glow);
376
+ }
377
+
378
+ .krnl-endpoint-title {
379
+ font-size: 0.72rem;
380
+ font-weight: 700;
381
+ letter-spacing: 0.08em;
382
+ text-transform: uppercase;
383
+ color: var(--krnl-text-muted);
384
+ margin-bottom: 1.25rem;
385
+ }
386
+
387
+ /* \u2500\u2500 Form Atoms (centered, icon-first, hover-reveal text) \u2500\u2500\u2500\u2500\u2500 */
388
+ .krnl-form-group {
389
+ display: flex;
390
+ flex-direction: column;
391
+ align-items: center;
392
+ gap: 0.45rem;
393
+ margin-bottom: 1.25rem;
394
+ width: 100%;
395
+ }
396
+
397
+ .krnl-label {
398
+ font-size: 0.72rem;
399
+ font-weight: 700;
400
+ letter-spacing: 0.06em;
401
+ text-transform: uppercase;
402
+ color: var(--krnl-text-muted);
403
+ text-align: center;
404
+ opacity: 0;
405
+ max-height: 0px;
406
+ overflow: hidden;
407
+ transform: translateY(-4px);
408
+ transition: opacity 0.2s ease, max-height 0.2s ease, transform 0.2s ease;
409
+ }
410
+
411
+ .krnl-form-group:hover .krnl-label,
412
+ .krnl-form-group:focus-within .krnl-label {
413
+ opacity: 1;
414
+ max-height: 30px;
415
+ transform: translateY(0);
416
+ margin-bottom: 0.2rem;
417
+ }
418
+
419
+ /* Inputs \u2014 gloss surface (floats above matte panel) */
420
+ .krnl-input,
421
+ .krnl-select,
422
+ .krnl-textarea {
423
+ background: var(--krnl-gloss-bg);
424
+ box-shadow: var(--krnl-gloss-shine);
425
+ border: var(--krnl-border-input);
426
+ border-radius: var(--krnl-radius-sm);
427
+ color: var(--krnl-text);
428
+ font-family: var(--krnl-font);
429
+ font-size: 0.9rem;
430
+ padding: 0.65rem 0.85rem;
431
+ width: 100%;
432
+ text-align: center;
433
+ outline: none;
434
+ transition: border-color 0.12s ease, box-shadow 0.12s ease;
435
+ }
436
+ .krnl-input:focus,
437
+ .krnl-select:focus,
438
+ .krnl-textarea:focus {
439
+ border-color: #1d1d1f;
440
+ box-shadow: var(--krnl-gloss-shine), 0 0 0 3px rgba(200, 223, 245, 0.50);
441
+ }
442
+ .krnl-textarea { resize: vertical; min-height: 90px; text-align: left; }
443
+
444
+ .krnl-toggle-group {
445
+ flex-direction: row;
446
+ justify-content: center;
447
+ align-items: center;
448
+ gap: 0.75rem;
449
+ }
450
+
451
+ /* \u2500\u2500 Buttons (gloss, centered icon-first, hover-to-reveal text) \u2500\u2500 */
452
+ .krnl-btn {
453
+ display: inline-flex;
454
+ align-items: center;
455
+ justify-content: center;
456
+ gap: 0.5rem;
457
+ font-family: var(--krnl-font);
458
+ font-size: 0.88rem;
459
+ font-weight: 700;
460
+ letter-spacing: -0.01em;
461
+ padding: 0.65rem 1rem;
462
+ border-radius: var(--krnl-radius-sm);
463
+ border: var(--krnl-border);
464
+ cursor: pointer;
465
+ user-select: none;
466
+ margin: 0 auto;
467
+ transition: box-shadow 0.15s ease, transform 0.15s ease, padding 0.2s ease;
468
+ background: var(--krnl-gloss-bg);
469
+ box-shadow: var(--krnl-gloss-shine), var(--krnl-shadow-btn);
470
+ color: var(--krnl-text);
471
+ }
472
+
473
+ .krnl-btn-icon {
474
+ font-size: 1.1rem;
475
+ filter: grayscale(100%);
476
+ display: inline-block;
477
+ line-height: 1;
478
+ }
479
+
480
+ .krnl-btn-label {
481
+ display: inline-block;
482
+ max-width: 0px;
483
+ opacity: 0;
484
+ overflow: hidden;
485
+ white-space: nowrap;
486
+ transition: max-width 0.25s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.2s ease;
487
+ }
488
+
489
+ .krnl-btn:hover .krnl-btn-label,
490
+ .krnl-btn:focus .krnl-btn-label,
491
+ .krnl-btn:disabled .krnl-btn-label {
492
+ max-width: 200px;
493
+ opacity: 1;
494
+ margin-left: 0.25rem;
495
+ }
496
+
497
+ .krnl-btn:hover .krnl-btn-icon {
498
+ filter: grayscale(0%);
499
+ }
500
+
501
+ .krnl-btn:hover:not(:disabled) {
502
+ transform: translate(-1px, -1px);
503
+ box-shadow: var(--krnl-gloss-shine), var(--krnl-shadow-btn-hover);
504
+ padding: 0.65rem 1.35rem;
505
+ }
506
+ .krnl-btn:active:not(:disabled) {
507
+ transform: translate(2px, 2px);
508
+ box-shadow: var(--krnl-gloss-shine), var(--krnl-shadow-btn-active);
509
+ }
510
+ .krnl-btn:disabled {
511
+ opacity: 0.45;
512
+ cursor: not-allowed;
513
+ }
514
+
515
+ /* Semantic button variants */
516
+ .krnl-btn-primary { background: var(--krnl-accent); color: var(--krnl-accent-fg); }
517
+ .krnl-btn-success { background: var(--krnl-success); color: var(--krnl-success-fg); }
518
+ .krnl-btn-danger { background: var(--krnl-danger); color: var(--krnl-danger-fg); }
519
+ .krnl-btn-warning { background: var(--krnl-warning); color: var(--krnl-warning-fg); }
520
+
521
+ /* \u2500\u2500 JSON Viewer \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
522
+ .krnl-json-viewer {
523
+ background: rgba(245, 245, 247, 0.30);
524
+ backdrop-filter: blur(8px);
525
+ border: var(--krnl-border);
526
+ border-radius: var(--krnl-radius-sm);
527
+ padding: 1rem;
528
+ margin-top: 0.5rem;
529
+ }
530
+ .krnl-json-code {
531
+ font-family: var(--krnl-font-mono);
532
+ font-size: 0.8rem;
533
+ white-space: pre-wrap;
534
+ word-break: break-all;
535
+ color: var(--krnl-text);
536
+ max-height: 320px;
537
+ overflow-y: auto;
538
+ }
539
+
540
+ /* \u2500\u2500 Tile \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
541
+ .krnl-tile {
542
+ background: var(--krnl-matte-bg);
543
+ backdrop-filter: var(--krnl-matte-blur);
544
+ border: var(--krnl-border);
545
+ border-radius: var(--krnl-radius);
546
+ box-shadow: var(--krnl-shadow);
547
+ padding: 1.25rem;
548
+ display: flex;
549
+ flex-direction: column;
550
+ gap: 0.4rem;
551
+ }
552
+ .krnl-tile-title { font-size: 0.72rem; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase; color: var(--krnl-text-muted); }
553
+ .krnl-tile-value { font-size: 1.6rem; font-weight: 800; letter-spacing: -0.04em; color: var(--krnl-text); }
554
+ .krnl-tile-subtitle { font-size: 0.75rem; color: var(--krnl-text-muted); }
555
+
556
+ /* \u2500\u2500 Carousel \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
557
+ .krnl-carousel {
558
+ display: flex;
559
+ gap: 1rem;
560
+ overflow-x: auto;
561
+ scroll-snap-type: x mandatory;
562
+ padding-bottom: 0.5rem;
563
+ scrollbar-width: thin;
564
+ }
565
+ .krnl-carousel-item {
566
+ scroll-snap-align: start;
567
+ flex: 0 0 220px;
568
+ background: var(--krnl-gloss-bg);
569
+ box-shadow: var(--krnl-gloss-shine), var(--krnl-shadow);
570
+ border: var(--krnl-border);
571
+ border-radius: var(--krnl-radius-sm);
572
+ padding: 1rem;
573
+ font-size: 0.88rem;
574
+ }
575
+
576
+ /* \u2500\u2500 Phantom Badge \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
577
+ .krnl-phantom-badge {
578
+ position: absolute;
579
+ top: -10px; right: -10px;
580
+ background: #30d158;
581
+ color: #fff;
582
+ font-family: var(--krnl-font-mono);
583
+ font-size: 0.65rem;
584
+ font-weight: 700;
585
+ letter-spacing: 0.04em;
586
+ padding: 2px 8px;
587
+ border-radius: 999px;
588
+ border: 2px solid #1d1d1f;
589
+ box-shadow: 2px 2px 0px #1d1d1f;
590
+ }
591
+
592
+ /* \u2500\u2500 JSON Builder Rows \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
593
+ .krnl-json-rows { display: flex; flex-direction: column; gap: 0.5rem; }
594
+ .krnl-json-row { display: flex; gap: 0.5rem; align-items: center; }
595
+ .krnl-json-key { flex: 2; }
596
+ .krnl-json-type { flex: 1; }
597
+ .krnl-json-val { flex: 3; }
598
+
599
+ /* \u2500\u2500 Empty State \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
600
+ .krnl-empty {
601
+ color: var(--krnl-text-muted);
602
+ font-size: 0.88rem;
603
+ text-align: center;
604
+ padding: 2rem;
605
+ }
606
+
607
+ /* \u2500\u2500 Responsive \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
608
+ @media (max-width: 768px) {
609
+ .krnl-header, .krnl-nav-tabs { padding-left: 1rem; padding-right: 1rem; }
610
+ .krnl-main-body { padding: 1rem; }
611
+ .krnl-endpoint-card { max-width: 100%; min-width: 0; }
612
+ .krnl-sphere-accent { width: 320px; height: 320px; }
613
+ .krnl-sphere-neutral { width: 260px; height: 260px; }
614
+ }
615
+ `;
616
+ fs.writeFileSync(cssPath, defaultCss, "utf-8");
617
+ }
618
+ }
619
+ /**
620
+ * Verifies React dependencies are installed in frontend/
621
+ */
622
+ static checkReactDependencies(frontendDir) {
623
+ const nodeModulesReact = path.join(frontendDir, "node_modules", "react");
624
+ const rootNodeModulesReact = path.resolve(frontendDir, "../../node_modules/react");
625
+ let hasReact = fs.existsSync(nodeModulesReact) || fs.existsSync(rootNodeModulesReact);
626
+ if (!hasReact) {
627
+ try {
628
+ __require.resolve("react", { paths: [frontendDir, process.cwd()] });
629
+ hasReact = true;
630
+ } catch (_) {
631
+ }
632
+ }
633
+ if (!hasReact) {
634
+ console.error(`
635
+ \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510
636
+ \u2502 [KrnlFrontend] WARNING: UI components are enabled but React is missing! \u2502
637
+ \u2502 Please run inside your frontend/ directory: \u2502
638
+ \u2502 npm install react react-dom \u2502
639
+ \u2502 npm install -D @types/react @types/react-dom \u2502
640
+ \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518
641
+ `);
642
+ }
643
+ }
644
+ /**
645
+ * Validates uiSpec definitions at build time to enforce protocol constraints and catch errors early
646
+ */
647
+ static validateUiSpec(fid, uiSpec) {
648
+ const errors = [];
649
+ const ALLOWED_INPUT_COMPONENTS = /* @__PURE__ */ new Set([
650
+ "KrnlTextInput",
651
+ "KrnlNumberInput",
652
+ "KrnlToggle",
653
+ "KrnlDropdown",
654
+ "KrnlTextArea",
655
+ "KrnlJsonInput"
656
+ ]);
657
+ const ALLOWED_OUTPUT_COMPONENTS = /* @__PURE__ */ new Set([
658
+ "KrnlJsonViewer",
659
+ "KrnlCarousel",
660
+ "KrnlTile",
661
+ "KrnlSSELog"
662
+ ]);
663
+ (uiSpec.inputs || []).forEach((spec) => {
664
+ if (!spec.field || typeof spec.field !== "string") {
665
+ errors.push(`[ERR_UISPEC_MISSING_FIELD] Endpoint '${fid}' input is missing required 'field' name.`);
666
+ }
667
+ if (spec.component && !ALLOWED_INPUT_COMPONENTS.has(spec.component)) {
668
+ errors.push(`[ERR_UISPEC_INVALID_INPUT_COMPONENT] Endpoint '${fid}' input '${spec.field}' uses invalid component '${spec.component}'. Allowed input components: ${Array.from(ALLOWED_INPUT_COMPONENTS).join(", ")}`);
669
+ }
670
+ if (spec.sharedComponentId && typeof spec.sharedComponentId !== "string") {
671
+ errors.push(`[ERR_UISPEC_INVALID_SLOT] Endpoint '${fid}' input '${spec.field}' sharedComponentId must be a string.`);
672
+ }
673
+ if (spec.sourceEndpointId && typeof spec.sourceEndpointId !== "string") {
674
+ errors.push(`[ERR_UISPEC_INVALID_SOURCE_ENDPOINT] Endpoint '${fid}' input '${spec.field}' sourceEndpointId must be a string.`);
675
+ }
676
+ if (spec.sourceEndpointId && spec.component !== "KrnlDropdown") {
677
+ errors.push(`[ERR_UISPEC_SOURCE_ENDPOINT_REQUIRES_DROPDOWN] Endpoint '${fid}' input '${spec.field}' sets sourceEndpointId='${spec.sourceEndpointId}' but uses component '${spec.component}'. Selection-source fields MUST use component 'KrnlDropdown'.`);
678
+ }
679
+ if (spec.component === "KrnlDropdown" && !spec.sharedComponentId && !spec.sourceEndpointId && (!spec.options || spec.options.length === 0)) {
680
+ errors.push(`[ERR_UISPEC_DROPDOWN_MISSING_SOURCE] Endpoint '${fid}' input '${spec.field}' uses component 'KrnlDropdown' but provides no selection source. Must set 'sharedComponentId', 'sourceEndpointId', or static 'options'.`);
681
+ }
682
+ const SELECTION_FIELD_PATTERNS = ["provider", "model", "modelName", "model_name", "profile", "key_id", "keyId"];
683
+ if (spec.component === "KrnlTextInput" && SELECTION_FIELD_PATTERNS.includes(spec.field)) {
684
+ console.warn(`
685
+ \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510`);
686
+ console.warn(`\u2502 [KrnlUiSpecValidator] SELECTION-SOURCE PATTERN WARNING: \u2502`);
687
+ console.warn(`\u2502 Endpoint '${fid}' input '${spec.field}' uses KrnlTextInput. \u2502`);
688
+ console.warn(`\u2502 Field '${spec.field}' is a finite-choice selection candidate. \u2502`);
689
+ console.warn(`\u2502 KRNL ARCHITECTURE RULE: Users MUST NOT type finite selections manually. \u2502`);
690
+ console.warn(`\u2502 FIX: Change component to 'KrnlDropdown' and specify 'sharedComponentId' \u2502`);
691
+ console.warn(`\u2502 or 'sourceEndpointId' pointing to the options-providing endpoint. \u2502`);
692
+ console.warn(`\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518
693
+ `);
694
+ }
695
+ if (spec.bindMode && spec.bindMode !== "selected" && spec.bindMode !== "all") {
696
+ errors.push(`[ERR_UISPEC_INVALID_BINDMODE] Endpoint '${fid}' input '${spec.field}' bindMode '${spec.bindMode}' is invalid. Allowed: 'selected' | 'all'`);
697
+ }
698
+ if (spec.autoFetch) {
699
+ errors.push(`[ERR_UISPEC_INVALID_AUTOFETCH] Endpoint '${fid}' input '${spec.field}' cannot use autoFetch. autoFetch is only allowed on output provider components.`);
700
+ }
701
+ });
702
+ (uiSpec.outputs || []).forEach((spec) => {
703
+ if (!spec.field || typeof spec.field !== "string") {
704
+ errors.push(`[ERR_UISPEC_MISSING_FIELD] Endpoint '${fid}' output is missing required 'field' name.`);
705
+ }
706
+ if (spec.component && !ALLOWED_OUTPUT_COMPONENTS.has(spec.component)) {
707
+ errors.push(`[ERR_UISPEC_INVALID_OUTPUT_COMPONENT] Endpoint '${fid}' output '${spec.field}' uses input component '${spec.component}'. Output fields must use display components: ${Array.from(ALLOWED_OUTPUT_COMPONENTS).join(", ")}.`);
708
+ }
709
+ if (spec.autoFetch && !spec.sharedComponentId) {
710
+ errors.push(`[ERR_UISPEC_AUTOFETCH_MISSING_SLOT] Endpoint '${fid}' output '${spec.field}' sets autoFetch=true but lacks sharedComponentId.`);
711
+ }
712
+ });
713
+ return {
714
+ valid: errors.length === 0,
715
+ errors
716
+ };
717
+ }
718
+ /**
719
+ * Validates that all UI components reference real registered endpoints on the entity.
720
+ */
721
+ static validateExecutionContract(entity, uiEndpoints) {
722
+ const errors = [];
723
+ const registeredFids = /* @__PURE__ */ new Set([
724
+ ...entity.localFunctionalities?.keys() || [],
725
+ ...entity.remoteFunctionalities?.keys() || []
726
+ ]);
727
+ for (const ep of uiEndpoints) {
728
+ if (!registeredFids.has(ep.id)) {
729
+ errors.push(`[ERR_UI_DEAD_FETCH] UI component generated for endpoint '${ep.id}', but no handler is registered on entity '${entity.entityName}'.`);
730
+ }
731
+ for (const input of ep.uiSpec.inputs || []) {
732
+ if (input.sourceEndpointId && !registeredFids.has(input.sourceEndpointId)) {
733
+ errors.push(`[ERR_UI_STALE_ENDPOINT] Endpoint '${ep.id}' input '${input.field}' references sourceEndpointId '${input.sourceEndpointId}', which is not a registered endpoint.`);
734
+ }
735
+ }
736
+ }
737
+ return { valid: errors.length === 0, errors };
738
+ }
739
+ /**
740
+ * Generates TS source code for a single endpoint UI component using VanJS
741
+ */
742
+ static generateComponentSource(fid, uiSpec, schema) {
743
+ const validation = this.validateUiSpec(fid, uiSpec);
744
+ if (!validation.valid) {
745
+ console.error(`
746
+ \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510
747
+ \u2502 [KrnlUiSpecValidator] BUILD ERROR IN ENDPOINT '${fid}':
748
+ ${validation.errors.map((e) => `\u2502 - ${e}`).join("\n")}
749
+ \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518
750
+ `);
751
+ throw new Error(`[KrnlUiSpecValidationError] Invalid uiSpec in endpoint '${fid}': ${validation.errors.join("; ")}`);
752
+ }
753
+ let inputs = uiSpec.inputs || [];
754
+ const outputs = uiSpec.outputs || [];
755
+ if (inputs.length === 0 && schema && schema.input) {
756
+ try {
757
+ let shape = {};
758
+ if (typeof schema.input._def?.shape === "function") {
759
+ shape = schema.input._def.shape();
760
+ } else if (schema.input.shape) {
761
+ shape = schema.input.shape;
762
+ }
763
+ inputs = Object.keys(shape).map((field) => ({
764
+ field,
765
+ label: field,
766
+ component: "KrnlTextInput",
767
+ placeholder: `Enter ${field}`
768
+ }));
769
+ } catch (e) {
770
+ console.warn(`[KrnlFrontendGenerator] Could not infer input fields for '${fid}':`, e);
771
+ }
772
+ }
773
+ const sharedInputIds = inputs.filter((i) => i.sharedComponentId).map((i) => i.sharedComponentId);
774
+ const sharedOutputIds = outputs.filter((o) => o.sharedComponentId).map((o) => o.sharedComponentId);
775
+ const allSharedIds = Array.from(/* @__PURE__ */ new Set([...sharedInputIds, ...sharedOutputIds]));
776
+ const sourceEndpointInputs = inputs.filter((i) => i.sourceEndpointId);
777
+ const autoFetchOutput = outputs.find((o) => o.autoFetch && o.sharedComponentId);
778
+ return `import van from 'vanjs-core';
779
+ import { KrnlButton, KrnlTextInput, KrnlDropdown, KrnlTextArea, KrnlJsonViewer, KrnlTile, getSharedSlot, KrnlAgentBridge } from '../../atoms/index.js';
780
+
781
+ const { div, h3, h4, form } = van.tags;
782
+
783
+ export default function Endpoint_${fid.replace(/[^a-zA-Z0-9]/g, "_")}() {
784
+ const formData = van.state<Record<string, any>>({});
785
+ const response = van.state<any>(null);
786
+ const loading = van.state(false);
787
+ const error = van.state<string | null>(null);
788
+ const phantomActive = van.state(false);
789
+
790
+ ${allSharedIds.map(
791
+ (id) => `const slot_${id.replace(/[^a-zA-Z0-9]/g, "_")} = getSharedSlot('${id}');`
792
+ ).join("\n ")}
793
+
794
+ KrnlAgentBridge.register('${fid}', {
795
+ trigger: async (overrideInput?: Record<string, any>, ticketId?: string) => {
796
+ return await executeRequest(overrideInput, ticketId);
797
+ }
798
+ });
799
+
800
+ const handleInputChange = (field: string, val: any) => {
801
+ formData.val = { ...formData.val, [field]: val };
802
+ KrnlAgentBridge.setState('${fid}', {
803
+ fid: '${fid}',
804
+ formData: formData.val,
805
+ response: response.val,
806
+ loading: false,
807
+ error: null,
808
+ lastUpdated: Date.now()
809
+ });
810
+ };
811
+
812
+ const executeRequest = async (overrideData?: Record<string, any>, ticketId?: string) => {
813
+ loading.val = true;
814
+ error.val = null;
815
+ if (ticketId || overrideData) {
816
+ phantomActive.val = true;
817
+ }
818
+ const activeInput = overrideData || formData.val;
819
+ if (overrideData) formData.val = { ...formData.val, ...overrideData };
820
+
821
+ try {
822
+ const res = await fetch('/frontend/api/${fid}', {
823
+ method: 'POST',
824
+ headers: { 'Content-Type': 'application/json' },
825
+ body: JSON.stringify(activeInput),
826
+ });
827
+ const data = await res.json();
828
+ const payload = data.payload !== undefined ? data.payload : data;
829
+ response.val = payload;
830
+
831
+ if (payload && (payload.status === 'error' || payload.message)) {
832
+ if (payload.status === 'error') error.val = payload.message || 'Request failed';
833
+ }
834
+
835
+ ${outputs.filter((o) => o.sharedComponentId).map((o) => {
836
+ const varName = `slot_${o.sharedComponentId.replace(/[^a-zA-Z0-9]/g, "_")}`;
837
+ const fieldKey = o.field;
838
+ const dataPath = o.dataPath;
839
+ return `
840
+ let extracted: any = null;
841
+ if (payload && typeof payload === 'object') {
842
+ ${dataPath ? `extracted = payload['${dataPath}'];` : `extracted = payload['${fieldKey}'] !== undefined ? payload['${fieldKey}'] : payload;`}
843
+ }
844
+ if (extracted && typeof extracted === 'object' && !Array.isArray(extracted)) {
845
+ if (extracted.status === 'success' && extracted.data) extracted = extracted.data;
846
+ else if (extracted.providers && Array.isArray(extracted.providers)) extracted = extracted.providers;
847
+ else if (extracted.models && Array.isArray(extracted.models)) extracted = extracted.models;
848
+ else if (extracted.agents && Array.isArray(extracted.agents)) extracted = extracted.agents;
849
+ else if (extracted.keys && typeof extracted.keys === 'object') {
850
+ extracted = Object.keys(extracted).map(k => ({ id: k, name: k, value: k }));
851
+ }
852
+ }
853
+ if (Array.isArray(extracted)) {
854
+ ${varName}.options.val = extracted;
855
+ }`;
856
+ }).join("\n ")}
857
+
858
+ if (ticketId) KrnlAgentBridge.sendJobComplete(ticketId, '${fid}', payload, undefined);
859
+ return payload;
860
+ } catch (err: any) {
861
+ const errMsg = err.message || 'Request failed';
862
+ error.val = errMsg;
863
+ if (ticketId) KrnlAgentBridge.sendJobComplete(ticketId, '${fid}', null, errMsg);
864
+ } finally {
865
+ loading.val = false;
866
+ if (ticketId || overrideData) {
867
+ setTimeout(() => { phantomActive.val = false; }, 2000);
868
+ }
869
+ }
870
+ };
871
+
872
+ ${autoFetchOutput ? `executeRequest({});` : ""}
873
+ ${sourceEndpointInputs.map(
874
+ (i) => `
875
+ fetch('/frontend/api/${i.sourceEndpointId}', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: '{}' })
876
+ .then((r) => r.json())
877
+ .then((d) => {
878
+ const payload = d.payload !== undefined ? d.payload : d;
879
+ let list = payload.${i.dataPath || i.field} || payload.data || payload.items || payload.providers || payload.models || payload.agents || payload;
880
+ if (list && typeof list === 'object' && !Array.isArray(list)) list = Object.keys(list).map(k => ({ id: k, name: k, value: k }));
881
+ if (Array.isArray(list) && ${i.sharedComponentId ? `slot_${i.sharedComponentId.replace(/[^a-zA-Z0-9]/g, "_")}` : "null"}) {
882
+ slot_${i.sharedComponentId.replace(/[^a-zA-Z0-9]/g, "_")}.options.val = list;
883
+ }
884
+ })
885
+ .catch(() => {});`
886
+ ).join("\n ")}
887
+
888
+ const handleSubmit = async (e: Event) => {
889
+ e.preventDefault();
890
+ await executeRequest();
891
+ };
892
+
893
+ return div(
894
+ {
895
+ className: () => \`krnl-endpoint-card \${phantomActive.val ? 'krnl-phantom-active' : ''}\`,
896
+ style: () => \`
897
+ background: var(--krnl-bg-surface);
898
+ padding: 1.5rem;
899
+ border-radius: 12px;
900
+ border: \${phantomActive.val ? '2px solid var(--krnl-accent)' : '1px solid var(--krnl-border-color)'};
901
+ transition: all 0.3s ease;
902
+ position: relative;
903
+ \`
904
+ },
905
+ () => (phantomActive.val ? div({ className: 'krnl-phantom-badge' }, '\u{1F916} Phantom Execution Active') : null),
906
+ h3({ style: 'margin-bottom: 1rem; color: var(--krnl-primary);' }, 'Endpoint: ${fid}'),
907
+ form(
908
+ { onsubmit: handleSubmit },
909
+ ${inputs.map((inSpec) => this.renderVanInputAtom(inSpec)).join(",\n ")}${inputs.length > 0 ? "," : ""}
910
+ KrnlButton({ type: 'submit', loading, disabled: loading, label: 'Execute ${fid}', icon: '\u26A1' })
911
+ ),
912
+ () => (error.val ? div({ style: 'color: var(--krnl-danger); margin-top: 1rem;' }, 'Error: ', error.val) : null),
913
+ () =>
914
+ response.val !== null
915
+ ? div(
916
+ { style: 'margin-top: 1.5rem; border-top: 1px solid var(--krnl-border-color); padding-top: 1rem;' },
917
+ h4({ style: 'margin-bottom: 0.5rem; color: var(--krnl-accent);' }, 'Output Response'),
918
+ ${outputs.length > 0 ? outputs.map((outSpec) => this.renderVanOutputAtom(outSpec)).join(",\n ") : `KrnlJsonViewer({ data: response })`}
919
+ )
920
+ : null
921
+ );
922
+ }
923
+ `;
924
+ }
925
+ static renderVanInputAtom(spec) {
926
+ const label = spec.label || spec.field;
927
+ if (spec.sharedComponentId) {
928
+ const varName = `slot_${spec.sharedComponentId.replace(/[^a-zA-Z0-9]/g, "_")}`;
929
+ const bindMode = spec.bindMode || "selected";
930
+ return `KrnlDropdown({
931
+ label: "${label}",
932
+ value: van.derive(() => formData.val['${spec.field}'] !== undefined ? formData.val['${spec.field}'] : (${varName}.value.val || '')),
933
+ options: ${varName}.options,
934
+ onChange: (v) => {
935
+ ${varName}.value.val = v;
936
+ handleInputChange('${spec.field}', ${bindMode === "all" ? `${varName}.options.val` : "v"});
937
+ }
938
+ })`;
939
+ }
940
+ switch (spec.component) {
941
+ case "KrnlTextInput":
942
+ return `KrnlTextInput({ label: "${label}", value: van.derive(() => formData.val['${spec.field}'] || ''), onChange: (v) => handleInputChange('${spec.field}', v), placeholder: "${spec.placeholder || ""}" })`;
943
+ case "KrnlTextArea":
944
+ return `KrnlTextArea({ label: "${label}", value: van.derive(() => formData.val['${spec.field}'] || ''), onChange: (v) => handleInputChange('${spec.field}', v), placeholder: "${spec.placeholder || ""}" })`;
945
+ case "KrnlDropdown":
946
+ const opts = JSON.stringify(spec.options || []);
947
+ return `KrnlDropdown({ label: "${label}", value: van.derive(() => formData.val['${spec.field}'] || ''), options: ${opts}, onChange: (v) => handleInputChange('${spec.field}', v) })`;
948
+ default:
949
+ return `KrnlTextInput({ label: "${label}", value: van.derive(() => formData.val['${spec.field}'] || ''), onChange: (v) => handleInputChange('${spec.field}', v) })`;
950
+ }
951
+ }
952
+ static renderVanOutputAtom(spec) {
953
+ const label = spec.label || spec.field;
954
+ if (spec.sharedComponentId) {
955
+ const varName = `slot_${spec.sharedComponentId.replace(/[^a-zA-Z0-9]/g, "_")}`;
956
+ return `KrnlJsonViewer({ label: "${label}", data: ${varName}.options })`;
957
+ }
958
+ switch (spec.component) {
959
+ case "KrnlTile":
960
+ return `KrnlTile({ label: "${label}", value: van.derive(() => response.val ? (response.val['${spec.field}'] !== undefined ? response.val['${spec.field}'] : response.val) : '') })`;
961
+ default:
962
+ return `KrnlJsonViewer({ label: "${label}", data: van.derive(() => response.val ? (response.val['${spec.field}'] !== undefined ? response.val['${spec.field}'] : response.val) : response.val) })`;
963
+ }
964
+ }
965
+ /**
966
+ * Generates root index.ts aggregator using VanJS
967
+ */
968
+ static generateIndexSource(appName, uiEndpoints, subAppPrefixes) {
969
+ const imports = uiEndpoints.map(
970
+ (ep) => `import Endpoint_${ep.id.replace(/[^a-zA-Z0-9]/g, "_")} from './components/${ep.id}/index.js';`
971
+ ).join("\n");
972
+ const initialTab = uiEndpoints.length > 0 ? uiEndpoints[0].id : subAppPrefixes.length > 0 ? subAppPrefixes[0] : "welcome";
973
+ return `import van from 'vanjs-core';
974
+ ${imports}
975
+
976
+ const { div, header, span, h1, button, main, iframe, h2, p } = van.tags;
977
+
978
+ function AppShell() {
979
+ const activeTab = van.state<string>('${initialTab}');
980
+
981
+ return div(
982
+ { className: 'krnl-app-shell' },
983
+ header(
984
+ { className: 'krnl-header' },
985
+ div(
986
+ { style: 'display: flex; align-items: center; gap: 0.65rem;' },
987
+ span({ style: 'font-size: 1.2rem; line-height: 1;' }, '\u2B21'),
988
+ h1('${appName}')
989
+ )
990
+ ),
991
+ div(
992
+ { className: 'krnl-nav-tabs' },
993
+ ${uiEndpoints.map(
994
+ (ep) => `button({ className: () => \`krnl-tab-btn \${activeTab.val === '${ep.id}' ? 'active' : ''}\`, onclick: () => activeTab.val = '${ep.id}', title: '${ep.id}' }, span('\u2B21'), span({ className: 'krnl-tab-label' }, '${ep.id}'))`
995
+ ).join(",\n ")}${uiEndpoints.length > 0 && subAppPrefixes.length > 0 ? "," : ""}
996
+ ${subAppPrefixes.map(
997
+ (sub) => `button({ className: () => \`krnl-tab-btn \${activeTab.val === '${sub}' ? 'active' : ''}\`, onclick: () => activeTab.val = '${sub}', title: '${sub}' }, span('\u{1F4E6}'), span({ className: 'krnl-tab-label' }, '${sub}'))`
998
+ ).join(",\n ")}
999
+ ),
1000
+ main(
1001
+ { className: 'krnl-main-body' },
1002
+ ${uiEndpoints.map(
1003
+ (ep) => `div({ style: () => \`display: \${activeTab.val === '${ep.id}' ? 'block' : 'none'}\` }, Endpoint_${ep.id.replace(/[^a-zA-Z0-9]/g, "_")}())`
1004
+ ).join(",\n ")}${uiEndpoints.length > 0 && subAppPrefixes.length > 0 ? "," : ""}
1005
+ ${subAppPrefixes.map(
1006
+ (sub) => `() => activeTab.val === '${sub}' ? iframe({ src: '/${sub}/frontend/', title: '${sub}', style: 'width: 100%; height: calc(100vh - 160px); border: none; border-radius: 8px;' }) : null`
1007
+ ).join(",\n ")}
1008
+ )
1009
+ );
1010
+ }
1011
+
1012
+ const rootElement = document.getElementById('root');
1013
+ if (rootElement) {
1014
+ van.add(rootElement, AppShell());
1015
+ }
1016
+ `;
1017
+ }
1018
+ static generateAtoms(frontendDir) {
1019
+ const atomsDir = path.join(frontendDir, "src", "atoms");
1020
+ fs.mkdirSync(atomsDir, { recursive: true });
1021
+ }
1022
+ };
1023
+ }
1024
+ });
1025
+
1026
+ export {
1027
+ KrnlFrontendGenerator,
1028
+ K_LIB_Krnl_Frontend_Generator_exports,
1029
+ init_K_LIB_Krnl_Frontend_Generator
1030
+ };