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