superacli 1.1.22 → 1.1.26

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 (257) hide show
  1. package/.beads/.br_history/{issues.20260427_104638_898729442.jsonl → issues.20260427_114912_061702372.jsonl} +65 -65
  2. package/.beads/.br_history/{issues.20260427_104638_209211116.jsonl → issues.20260427_115013_063113906.jsonl} +66 -66
  3. package/.beads/.br_history/{issues.20260427_104637_752744777.jsonl → issues.20260427_115114_208497343.jsonl} +67 -67
  4. package/.beads/.br_history/{issues.20260427_104615_669652069.jsonl → issues.20260427_115215_176080241.jsonl} +68 -68
  5. package/.beads/.br_history/issues.20260427_115316_481573328.jsonl +261 -0
  6. package/.beads/.br_history/issues.20260427_115335_866978652.jsonl +261 -0
  7. package/.beads/.br_history/issues.20260427_115436_866087391.jsonl +261 -0
  8. package/.beads/.br_history/issues.20260427_115538_016094860.jsonl +261 -0
  9. package/.beads/.br_history/issues.20260427_115638_932947449.jsonl +261 -0
  10. package/.beads/.br_history/issues.20260427_115739_982932603.jsonl +261 -0
  11. package/.beads/.br_history/issues.20260427_115840_963681039.jsonl +261 -0
  12. package/.beads/.br_history/issues.20260427_115941_912431509.jsonl +261 -0
  13. package/.beads/.br_history/issues.20260427_120043_043356448.jsonl +261 -0
  14. package/.beads/.br_history/issues.20260427_120144_955596968.jsonl +261 -0
  15. package/.beads/.br_history/issues.20260427_120246_060733977.jsonl +261 -0
  16. package/.beads/.br_history/issues.20260427_120307_717985017.jsonl +261 -0
  17. package/.beads/.br_history/issues.20260427_120408_871846736.jsonl +261 -0
  18. package/.beads/.br_history/issues.20260427_121003_963750732.jsonl +261 -0
  19. package/.beads/.br_history/issues.20260427_121006_018509431.jsonl +261 -0
  20. package/.beads/.br_history/issues.20260427_121019_617062931.jsonl +261 -0
  21. package/.beads/.br_history/issues.20260427_121021_689536311.jsonl +261 -0
  22. package/.beads/.br_history/issues.20260428_172005_240631447.jsonl +261 -0
  23. package/.beads/.br_history/issues.20260430_073641_144807989.jsonl +262 -0
  24. package/.beads/.br_history/issues.20260430_073642_807075699.jsonl +263 -0
  25. package/.beads/.br_history/issues.20260430_073645_625754223.jsonl +264 -0
  26. package/.beads/.br_history/issues.20260430_090639_154646058.jsonl +265 -0
  27. package/.beads/.br_history/issues.20260430_090640_350560318.jsonl +266 -0
  28. package/.beads/.br_history/issues.20260430_090641_509319001.jsonl +267 -0
  29. package/.beads/.br_history/issues.20260430_095140_396749398.jsonl +268 -0
  30. package/.beads/.br_history/issues.20260501_230041_334056077.jsonl +269 -0
  31. package/.beads/.br_recovery/beads.db-wal.20260427_121006_010373120.rebuild-failed +0 -0
  32. package/.beads/.br_recovery/beads.db.20260427_121006_010373120.rebuild-failed +0 -0
  33. package/.beads/beads.db.corrupted +0 -0
  34. package/.beads/issues.jsonl +46 -37
  35. package/.supercli/adapters/test-cli-adapter.js +19 -0
  36. package/cli/adapters/mcp.js +89 -3
  37. package/cli/adapters/openapi.js +71 -2
  38. package/cli/config.js +69 -1
  39. package/cli/daemon.js +349 -0
  40. package/cli/plugins-manager.js +41 -1
  41. package/cli/server-command.js +174 -4
  42. package/cli/supercli.js +35 -15
  43. package/docs/changelog-2026-04.html +6 -0
  44. package/docs/index.html +95 -8
  45. package/docs/plugins.html +30 -0
  46. package/docs/server.md +80 -0
  47. package/graphify-out/GRAPH_REPORT.md +140 -128
  48. package/graphify-out/cache/036077d325d5aa25fc6ad7f01ca32721b02e3f981231f67fb22f59380063572b.json +1 -0
  49. package/graphify-out/cache/11938479cfbdb8e52bf5f0f3c132d56204245ebb19aefe89bb0394feb36dbd5e.json +1 -0
  50. package/graphify-out/cache/141362b25039dee7a4959ef475befea0a25671aae66f3d14917e711e597ffbf6.json +1 -0
  51. package/graphify-out/cache/362adb1b6b483f56d09feaf652f4122366e568aaede752f48383b76d61f5d9d6.json +1 -0
  52. package/graphify-out/cache/42bc8f64d69d2cd9ecf64e92e38f89f7ea479f69c947de5984919e2fd5a716ee.json +1 -0
  53. package/graphify-out/cache/4b5cffce31d9193b7d0a8f15e8a41c66f6733a6490ebaeff2c8ca19701c72c0e.json +1 -0
  54. package/graphify-out/cache/5262fb95a8855fc90efbfff767eb3aacca82c4da5799605a969554580a255404.json +1 -0
  55. package/graphify-out/cache/5ef4e31750d9e7bc4ff4a7b535f247d9ab5ab9627d72e20794779ec4be424c66.json +1 -0
  56. package/graphify-out/cache/68e2b11c95ede4f05b88d001040008cb3ac72d7bd27f351497c87e128fb3b221.json +1 -0
  57. package/graphify-out/cache/6d5023ecffa52382d31152c8c06e86e286789228a3d4f7fde72087f3eafa11bc.json +1 -0
  58. package/graphify-out/cache/6e5776e123142a78f50fa0bfd5ae2d289282051a9b392ae507de7aa9017f95d4.json +1 -0
  59. package/graphify-out/cache/75982dc60fa7353600ca48a3b33f5ba497eea9d5743daac6e61e9cf4f64774be.json +1 -0
  60. package/graphify-out/cache/7796066b51d3a99071debdfb5f40c854400b5bf3aa7c1e83dd6ae88eddca0370.json +1 -0
  61. package/graphify-out/cache/8308aa47b396b929d5b896051ca91422afdde4861108d6f18f5094901093df79.json +1 -0
  62. package/graphify-out/cache/8ccd3b557a1ea98005fba84433f160ccd4a951fbdb6a4c9b7d7ba894f905b06b.json +1 -0
  63. package/graphify-out/cache/90ab53b8f1f3306e7c3a1f92ec628717ea90f4e684db764f7d7aa8e884fe2cae.json +1 -0
  64. package/graphify-out/cache/9832101a7622924f58b9e7d7752b41518049447171f7955e4c50dfd386574e12.json +1 -0
  65. package/graphify-out/cache/9fa6604cc820f3857c55c753ca0024788e4cbb61c4e12ba7ce0e4a1ce4ee0655.json +1 -0
  66. package/graphify-out/cache/a1e24a07beede2876e688cdb6c14e2d24abb22064862a6420cac321efabd3efd.json +1 -0
  67. package/graphify-out/cache/a418312f2805259c33007a18b36b6ae3b6a4da376ccae6006a3b9999262495db.json +1 -0
  68. package/graphify-out/cache/a6d9c029bd31a99b67357291305e27b01c5ef388925f7f2a6e985fec4efa8024.json +1 -0
  69. package/graphify-out/cache/a6ed52b709dd79f98adf72c483e8dfb6098203d7327470b82feb5e11390a5cf4.json +1 -0
  70. package/graphify-out/cache/b045ae4443d972ee8f47b91a0d61c984a190195b38ae1d331430b4c51c43acfd.json +1 -0
  71. package/graphify-out/cache/b849fb88344c2f88f3d22e42b67516e071c7d1e65f77f4ca23f25af6edfca3b3.json +1 -0
  72. package/graphify-out/cache/bc514f4074f89d2f7e7834670602e50bba0f24b8cd8c9d21ff4d361d3fd4d558.json +1 -0
  73. package/graphify-out/cache/be3fc87936f9f24d2d4f4b22b9d2a16f33f538cef1a181663120e23ecf16b81a.json +1 -0
  74. package/graphify-out/cache/c5a4822edbe3a95a56f9f943eed1567e5a5c913d59b1fa3df4ad54ffd9b02697.json +1 -0
  75. package/graphify-out/cache/cf411f1ddde50ee97c1c501f59ed7985bcdc022134574816bcce37ac20848e64.json +1 -0
  76. package/graphify-out/cache/d88625b5813742ca941ce8155cb77afb02425632002f621aca34f217440a392d.json +1 -0
  77. package/graphify-out/cache/da97fdc8af2a3306c292921110f84648bba2b0e85d27473b460b0e8a862ff225.json +1 -0
  78. package/graphify-out/cache/e88de9f1ae117f26fd217c3717eff7e525a284cc942b5f4d26ef2c4d65db59bd.json +1 -0
  79. package/graphify-out/cache/ebf6bd96ef2d8d396389ac7199e6ba818ee2d0c5dfc131fab7cdece9fb710f11.json +1 -0
  80. package/graphify-out/cache/f2a302550a6fe0649d36b8ef0b04b77673bca9f115732af8ae89751944dc9cc6.json +1 -0
  81. package/graphify-out/cache/f9ee11f4395cb41b6f6900079cb7e94297c89b1b0b4798398d2f7a2e244c683b.json +1 -0
  82. package/graphify-out/cache/fb46bd3ef530273918fc8915842453f21b418d536653d3b705b75b417255c246.json +1 -0
  83. package/graphify-out/cache/fcfdab94fe91af1f21fcefdba5a96674b9a16bb768288f3573fe68765163c765.json +1 -0
  84. package/graphify-out/graph.json +2420 -1157
  85. package/package.json +1 -1
  86. package/plugins/bluebuild/install-guidance.json +11 -0
  87. package/plugins/bluebuild/meta.json +5 -0
  88. package/plugins/bluebuild/plugin.json +99 -0
  89. package/plugins/bluebuild/skills/quickstart/SKILL.md +47 -0
  90. package/plugins/cargo-public-api/install-guidance.json +11 -0
  91. package/plugins/cargo-public-api/meta.json +5 -0
  92. package/plugins/cargo-public-api/plugin.json +88 -0
  93. package/plugins/cargo-public-api/skills/quickstart/SKILL.md +51 -0
  94. package/plugins/context-mode/plugin.json +10 -0
  95. package/plugins/context-mode/scripts/post-install.js +1 -1
  96. package/plugins/csview/install-guidance.json +11 -0
  97. package/plugins/csview/meta.json +5 -0
  98. package/plugins/csview/plugin.json +67 -0
  99. package/plugins/csview/skills/quickstart/SKILL.md +51 -0
  100. package/plugins/cymbal/install-guidance.json +11 -0
  101. package/plugins/cymbal/meta.json +5 -0
  102. package/plugins/cymbal/plugin.json +121 -0
  103. package/plugins/cymbal/skills/quickstart/SKILL.md +54 -0
  104. package/plugins/difftastic/install-guidance.json +11 -0
  105. package/plugins/difftastic/meta.json +5 -0
  106. package/plugins/difftastic/plugin.json +87 -0
  107. package/plugins/difftastic/skills/quickstart/SKILL.md +53 -0
  108. package/plugins/dlm/install-guidance.json +11 -0
  109. package/plugins/dlm/meta.json +5 -0
  110. package/plugins/dlm/plugin.json +85 -0
  111. package/plugins/dlm/skills/quickstart/SKILL.md +49 -0
  112. package/plugins/dofigen/install-guidance.json +11 -0
  113. package/plugins/dofigen/meta.json +5 -0
  114. package/plugins/dofigen/plugin.json +84 -0
  115. package/plugins/dofigen/skills/quickstart/SKILL.md +50 -0
  116. package/plugins/example-server-resources/meta.json +11 -0
  117. package/plugins/example-server-resources/plugin.json +46 -0
  118. package/plugins/http-nu/install-guidance.json +11 -0
  119. package/plugins/http-nu/meta.json +5 -0
  120. package/plugins/http-nu/plugin.json +84 -0
  121. package/plugins/http-nu/skills/quickstart/SKILL.md +52 -0
  122. package/plugins/hwatch/install-guidance.json +11 -0
  123. package/plugins/hwatch/meta.json +5 -0
  124. package/plugins/hwatch/plugin.json +100 -0
  125. package/plugins/hwatch/skills/quickstart/SKILL.md +54 -0
  126. package/plugins/hyperfine/install-guidance.json +3 -3
  127. package/plugins/hyperfine/meta.json +2 -2
  128. package/plugins/hyperfine/plugin.json +39 -16
  129. package/plugins/hyperfine/skills/quickstart/SKILL.md +33 -61
  130. package/plugins/json5/install-guidance.json +12 -0
  131. package/plugins/json5/meta.json +5 -0
  132. package/plugins/json5/plugin.json +88 -0
  133. package/plugins/json5/skills/quickstart/SKILL.md +55 -0
  134. package/plugins/mdsf/install-guidance.json +11 -0
  135. package/plugins/mdsf/meta.json +5 -0
  136. package/plugins/mdsf/plugin.json +101 -0
  137. package/plugins/mdsf/skills/quickstart/SKILL.md +55 -0
  138. package/plugins/oha/install-guidance.json +11 -0
  139. package/plugins/oha/meta.json +5 -0
  140. package/plugins/oha/plugin.json +67 -0
  141. package/plugins/oha/skills/quickstart/SKILL.md +44 -0
  142. package/plugins/pv-migrate/install-guidance.json +11 -0
  143. package/plugins/pv-migrate/meta.json +5 -0
  144. package/plugins/pv-migrate/plugin.json +102 -0
  145. package/plugins/pv-migrate/skills/quickstart/SKILL.md +58 -0
  146. package/plugins/rash/install-guidance.json +11 -0
  147. package/plugins/rash/meta.json +5 -0
  148. package/plugins/rash/plugin.json +85 -0
  149. package/plugins/rash/skills/quickstart/SKILL.md +46 -0
  150. package/plugins/rsonpath/install-guidance.json +11 -0
  151. package/plugins/rsonpath/meta.json +5 -0
  152. package/plugins/rsonpath/plugin.json +87 -0
  153. package/plugins/rsonpath/skills/quickstart/SKILL.md +44 -0
  154. package/plugins/rsql/install-guidance.json +11 -0
  155. package/plugins/rsql/meta.json +5 -0
  156. package/plugins/rsql/plugin.json +84 -0
  157. package/plugins/rsql/skills/quickstart/SKILL.md +52 -0
  158. package/plugins/temporal/install-guidance.json +11 -0
  159. package/plugins/temporal/meta.json +5 -0
  160. package/plugins/temporal/plugin.json +95 -0
  161. package/plugins/temporal/skills/quickstart/SKILL.md +49 -0
  162. package/plugins/tkn/install-guidance.json +11 -0
  163. package/plugins/tkn/meta.json +5 -0
  164. package/plugins/tkn/plugin.json +98 -0
  165. package/plugins/tkn/skills/quickstart/SKILL.md +50 -0
  166. package/plugins/toml2json/install-guidance.json +11 -0
  167. package/plugins/toml2json/meta.json +5 -0
  168. package/plugins/toml2json/plugin.json +85 -0
  169. package/plugins/toml2json/skills/quickstart/SKILL.md +47 -0
  170. package/plugins/worktrunk/install-guidance.json +11 -0
  171. package/plugins/worktrunk/meta.json +5 -0
  172. package/plugins/worktrunk/plugin.json +116 -0
  173. package/plugins/worktrunk/skills/quickstart/SKILL.md +62 -0
  174. package/server/app.js +46 -13
  175. package/server/middleware/auth.js +70 -0
  176. package/server/routes/adapters.js +3 -11
  177. package/server/routes/clients.js +46 -0
  178. package/server/routes/docs.js +10 -0
  179. package/server/routes/jobs.js +3 -2
  180. package/server/routes/mcp.js +1 -0
  181. package/server/routes/plugins.js +109 -26
  182. package/server/routes/settings.js +93 -0
  183. package/server/routes/specs.js +8 -3
  184. package/server/services/adaptersService.js +1 -0
  185. package/server/services/pluginResourceService.js +262 -0
  186. package/server/services/pluginsService.js +24 -0
  187. package/server/uploads/superbackend-openapi.json +47 -0
  188. package/server/uploads/test-auth-openapi.json +74 -0
  189. package/server/uploads/test-auth-spec.json +36 -0
  190. package/server/views/adapters.ejs +1 -0
  191. package/server/views/api-keys.ejs +147 -0
  192. package/server/views/clients.ejs +82 -0
  193. package/server/views/docs.ejs +7 -0
  194. package/server/views/jobs.ejs +26 -2
  195. package/server/views/layout.ejs +35 -21
  196. package/server/views/mcp.ejs +278 -21
  197. package/server/views/partials/head.ejs +4 -0
  198. package/server/views/plugins.ejs +33 -32
  199. package/server/views/settings.ejs +99 -0
  200. package/server/views/specs.ejs +217 -22
  201. package/.beads/.br_history/issues.20260427_103259_082222986.jsonl +0 -261
  202. package/.beads/.br_history/issues.20260427_103400_358332602.jsonl +0 -261
  203. package/.beads/.br_history/issues.20260427_103419_681839669.jsonl +0 -261
  204. package/.beads/.br_history/issues.20260427_103522_226669683.jsonl +0 -261
  205. package/.beads/.br_history/issues.20260427_103623_568379011.jsonl +0 -261
  206. package/.beads/.br_history/issues.20260427_103724_772051154.jsonl +0 -261
  207. package/.beads/.br_history/issues.20260427_103825_935228905.jsonl +0 -261
  208. package/.beads/.br_history/issues.20260427_103927_159626780.jsonl +0 -261
  209. package/.beads/.br_history/issues.20260427_104028_436388960.jsonl +0 -261
  210. package/.beads/.br_history/issues.20260427_104129_670562810.jsonl +0 -261
  211. package/.beads/.br_history/issues.20260427_104230_885162615.jsonl +0 -261
  212. package/.beads/.br_history/issues.20260427_104332_071213892.jsonl +0 -261
  213. package/.beads/.br_history/issues.20260427_104349_051129644.jsonl +0 -261
  214. package/.beads/.br_history/issues.20260427_104450_358444507.jsonl +0 -261
  215. package/.beads/.br_history/issues.20260427_104555_654123963.jsonl +0 -261
  216. package/.beads/.br_history/issues.20260427_104559_805009051.jsonl +0 -261
  217. package/.beads/.br_history/issues.20260427_104600_255068030.jsonl +0 -261
  218. package/.beads/.br_history/issues.20260427_104600_717042012.jsonl +0 -261
  219. package/.beads/.br_history/issues.20260427_104601_215845416.jsonl +0 -261
  220. package/.beads/.br_history/issues.20260427_104601_899140331.jsonl +0 -261
  221. package/.beads/.br_history/issues.20260427_104606_172300551.jsonl +0 -261
  222. package/.beads/.br_history/issues.20260427_104606_658098709.jsonl +0 -261
  223. package/.beads/.br_history/issues.20260427_104607_085798670.jsonl +0 -261
  224. package/.beads/.br_history/issues.20260427_104607_627598880.jsonl +0 -261
  225. package/.beads/.br_history/issues.20260427_104608_071298223.jsonl +0 -261
  226. package/.beads/.br_history/issues.20260427_104615_198124329.jsonl +0 -261
  227. package/scripts/post-tweet-issues.js +0 -161
  228. /package/.beads/.br_history/{issues.20260427_103259_082222986.jsonl.meta.json → issues.20260427_114912_061702372.jsonl.meta.json} +0 -0
  229. /package/.beads/.br_history/{issues.20260427_103400_358332602.jsonl.meta.json → issues.20260427_115013_063113906.jsonl.meta.json} +0 -0
  230. /package/.beads/.br_history/{issues.20260427_103419_681839669.jsonl.meta.json → issues.20260427_115114_208497343.jsonl.meta.json} +0 -0
  231. /package/.beads/.br_history/{issues.20260427_103522_226669683.jsonl.meta.json → issues.20260427_115215_176080241.jsonl.meta.json} +0 -0
  232. /package/.beads/.br_history/{issues.20260427_103623_568379011.jsonl.meta.json → issues.20260427_115316_481573328.jsonl.meta.json} +0 -0
  233. /package/.beads/.br_history/{issues.20260427_103724_772051154.jsonl.meta.json → issues.20260427_115335_866978652.jsonl.meta.json} +0 -0
  234. /package/.beads/.br_history/{issues.20260427_103825_935228905.jsonl.meta.json → issues.20260427_115436_866087391.jsonl.meta.json} +0 -0
  235. /package/.beads/.br_history/{issues.20260427_103927_159626780.jsonl.meta.json → issues.20260427_115538_016094860.jsonl.meta.json} +0 -0
  236. /package/.beads/.br_history/{issues.20260427_104028_436388960.jsonl.meta.json → issues.20260427_115638_932947449.jsonl.meta.json} +0 -0
  237. /package/.beads/.br_history/{issues.20260427_104129_670562810.jsonl.meta.json → issues.20260427_115739_982932603.jsonl.meta.json} +0 -0
  238. /package/.beads/.br_history/{issues.20260427_104230_885162615.jsonl.meta.json → issues.20260427_115840_963681039.jsonl.meta.json} +0 -0
  239. /package/.beads/.br_history/{issues.20260427_104332_071213892.jsonl.meta.json → issues.20260427_115941_912431509.jsonl.meta.json} +0 -0
  240. /package/.beads/.br_history/{issues.20260427_104349_051129644.jsonl.meta.json → issues.20260427_120043_043356448.jsonl.meta.json} +0 -0
  241. /package/.beads/.br_history/{issues.20260427_104450_358444507.jsonl.meta.json → issues.20260427_120144_955596968.jsonl.meta.json} +0 -0
  242. /package/.beads/.br_history/{issues.20260427_104555_654123963.jsonl.meta.json → issues.20260427_120246_060733977.jsonl.meta.json} +0 -0
  243. /package/.beads/.br_history/{issues.20260427_104559_805009051.jsonl.meta.json → issues.20260427_120307_717985017.jsonl.meta.json} +0 -0
  244. /package/.beads/.br_history/{issues.20260427_104600_255068030.jsonl.meta.json → issues.20260427_120408_871846736.jsonl.meta.json} +0 -0
  245. /package/.beads/.br_history/{issues.20260427_104600_717042012.jsonl.meta.json → issues.20260427_121003_963750732.jsonl.meta.json} +0 -0
  246. /package/.beads/.br_history/{issues.20260427_104601_215845416.jsonl.meta.json → issues.20260427_121006_018509431.jsonl.meta.json} +0 -0
  247. /package/.beads/.br_history/{issues.20260427_104601_899140331.jsonl.meta.json → issues.20260427_121019_617062931.jsonl.meta.json} +0 -0
  248. /package/.beads/.br_history/{issues.20260427_104606_172300551.jsonl.meta.json → issues.20260427_121021_689536311.jsonl.meta.json} +0 -0
  249. /package/.beads/.br_history/{issues.20260427_104606_658098709.jsonl.meta.json → issues.20260428_172005_240631447.jsonl.meta.json} +0 -0
  250. /package/.beads/.br_history/{issues.20260427_104607_085798670.jsonl.meta.json → issues.20260430_073641_144807989.jsonl.meta.json} +0 -0
  251. /package/.beads/.br_history/{issues.20260427_104607_627598880.jsonl.meta.json → issues.20260430_073642_807075699.jsonl.meta.json} +0 -0
  252. /package/.beads/.br_history/{issues.20260427_104608_071298223.jsonl.meta.json → issues.20260430_073645_625754223.jsonl.meta.json} +0 -0
  253. /package/.beads/.br_history/{issues.20260427_104615_198124329.jsonl.meta.json → issues.20260430_090639_154646058.jsonl.meta.json} +0 -0
  254. /package/.beads/.br_history/{issues.20260427_104615_669652069.jsonl.meta.json → issues.20260430_090640_350560318.jsonl.meta.json} +0 -0
  255. /package/.beads/.br_history/{issues.20260427_104637_752744777.jsonl.meta.json → issues.20260430_090641_509319001.jsonl.meta.json} +0 -0
  256. /package/.beads/.br_history/{issues.20260427_104638_209211116.jsonl.meta.json → issues.20260430_095140_396749398.jsonl.meta.json} +0 -0
  257. /package/.beads/.br_history/{issues.20260427_104638_898729442.jsonl.meta.json → issues.20260501_230041_334056077.jsonl.meta.json} +0 -0
@@ -9,30 +9,62 @@
9
9
  <button @click="showForm=!showForm" class="btn btn-primary">+ Add Server</button>
10
10
  </div>
11
11
 
12
- <!-- Add form -->
12
+ <!-- Add/Edit form -->
13
13
  <div v-if="showForm" class="bento-card p-6 mb-6">
14
- <form @submit.prevent="create">
14
+ <form @submit.prevent="editing ? update() : create()">
15
15
  <div class="flex flex-col md:flex-row gap-4 items-end mb-4">
16
16
  <div class="flex-1 w-full">
17
17
  <label class="block text-xs font-semibold text-[#787774] uppercase tracking-wider mb-2">Name</label>
18
18
  <input v-model="form.name" class="w-full px-4 py-3 bg-[#F7F6F3] border border-[#EAEAEA] rounded-md text-[#111111] focus:outline-none focus:border-[#111111] transition-colors" required placeholder="e.g. context-mode">
19
19
  </div>
20
- <div class="flex-1 w-full">
21
- <label class="block text-xs font-semibold text-[#787774] uppercase tracking-wider mb-2">URL (HTTP MCP)</label>
20
+ </div>
21
+
22
+ <div class="mb-4">
23
+ <label class="block text-xs font-semibold text-[#787774] uppercase tracking-wider mb-3">Transport Mode</label>
24
+ <div class="flex gap-6">
25
+ <label class="flex items-center gap-2 cursor-pointer">
26
+ <input type="radio" v-model="form.transport" value="http" class="w-4 h-4 accent-[#111111]">
27
+ <span class="text-sm text-[#2F3437]">SSE/HTTP</span>
28
+ </label>
29
+ <label class="flex items-center gap-2 cursor-pointer">
30
+ <input type="radio" v-model="form.transport" value="stdio" class="w-4 h-4 accent-[#111111]">
31
+ <span class="text-sm text-[#2F3437]">STDIO</span>
32
+ </label>
33
+ </div>
34
+ </div>
35
+
36
+ <!-- SSE/HTTP fields -->
37
+ <div v-if="form.transport === 'http'" class="space-y-4">
38
+ <div>
39
+ <label class="block text-xs font-semibold text-[#787774] uppercase tracking-wider mb-2">URL</label>
22
40
  <input v-model="form.url" class="w-full px-4 py-3 bg-[#F7F6F3] border border-[#EAEAEA] rounded-md text-[#111111] focus:outline-none focus:border-[#111111] transition-colors" placeholder="https://mcp.example.com">
41
+ <p class="text-xs text-[#787774] mt-1">Uses SSE with HTTP fallback</p>
23
42
  </div>
24
- <div class="flex-1 w-full">
25
- <label class="block text-xs font-semibold text-[#787774] uppercase tracking-wider mb-2">Command (stdio MCP)</label>
26
- <input v-model="form.command" class="w-full px-4 py-3 bg-[#F7F6F3] border border-[#EAEAEA] rounded-md text-[#111111] focus:outline-none focus:border-[#111111] transition-colors" placeholder="e.g. context-mode">
43
+ <div>
44
+ <label class="block text-xs font-semibold text-[#787774] uppercase tracking-wider mb-2">Timeout (ms)</label>
45
+ <input v-model="form.timeout_ms" type="number" class="w-full px-4 py-3 bg-[#F7F6F3] border border-[#EAEAEA] rounded-md text-[#111111] focus:outline-none focus:border-[#111111] transition-colors" placeholder="30000">
46
+ <p class="text-xs text-[#787774] mt-1">Default: 30000ms (30 seconds)</p>
27
47
  </div>
28
48
  </div>
29
- <div class="flex gap-4 items-center">
49
+
50
+ <!-- STDIO fields -->
51
+ <div v-if="form.transport === 'stdio'" class="space-y-4">
52
+ <div>
53
+ <label class="block text-xs font-semibold text-[#787774] uppercase tracking-wider mb-2">Command</label>
54
+ <input v-model="form.command" class="w-full px-4 py-3 bg-[#F7F6F3] border border-[#EAEAEA] rounded-md text-[#111111] focus:outline-none focus:border-[#111111] transition-colors" placeholder="e.g. context-mode">
55
+ </div>
30
56
  <label class="flex items-center gap-2 cursor-pointer">
31
57
  <input type="checkbox" v-model="form.stateful" class="w-4 h-4 accent-[#111111]">
32
- <span class="text-sm text-[#2F3437]">Stateful (requires daemon)</span>
58
+ <div class="flex flex-col">
59
+ <span class="text-sm text-[#2F3437]">Stateful (requires daemon)</span>
60
+ <span class="text-xs text-[#787774]">Daemon runs on CLI side, starts when command is executed via CLI (not server)</span>
61
+ </div>
33
62
  </label>
34
- <button type="submit" class="btn btn-primary">Save</button>
35
- <button type="button" @click="showForm=false" class="btn">Cancel</button>
63
+ </div>
64
+
65
+ <div class="flex gap-4 items-center mt-4">
66
+ <button type="submit" class="btn btn-primary">{{ editing ? 'Update' : 'Save' }}</button>
67
+ <button type="button" @click="cancelEdit" class="btn">Cancel</button>
36
68
  </div>
37
69
  </form>
38
70
  </div>
@@ -42,21 +74,45 @@
42
74
  <thead>
43
75
  <tr class="border-b border-[#EAEAEA] bg-[#F7F6F3]">
44
76
  <th class="px-6 py-3 text-left text-xs font-semibold text-[#787774] uppercase tracking-wider">Name</th>
77
+ <th class="px-6 py-3 text-left text-xs font-semibold text-[#787774] uppercase tracking-wider">Plugin</th>
45
78
  <th class="px-6 py-3 text-left text-xs font-semibold text-[#787774] uppercase tracking-wider">Source</th>
46
79
  <th class="px-6 py-3 text-left text-xs font-semibold text-[#787774] uppercase tracking-wider">Type</th>
47
80
  <th class="px-6 py-3 text-left text-xs font-semibold text-[#787774] uppercase tracking-wider">Actions</th>
48
81
  </tr>
49
82
  </thead>
50
83
  <tbody>
51
- <tr v-if="servers.length===0"><td colspan="4" class="px-6 py-12 text-center text-[#787774]">No MCP servers registered.</td></tr>
84
+ <tr v-if="servers.length===0"><td colspan="5" class="px-6 py-12 text-center text-[#787774]">No MCP servers registered.</td></tr>
52
85
  <tr v-for="s in servers" :key="s._id" class="border-b border-[#EAEAEA] hover:bg-[#F7F6F3] transition-colors">
53
- <td class="px-6 py-4"><code class="mono-text text-sm text-[#1F6C9F]">{{ s.name }}</code></td>
86
+ <td class="px-6 py-4">
87
+ <code class="mono-text text-sm text-[#1F6C9F]">{{ s.displayName || s.name }}</code>
88
+ <div v-if="getUsedCommands(s).length > 0" class="mt-1 text-xs text-[#787774]">
89
+ used by: {{ getUsedCommands(s).map(c => `${c.namespace}.${c.resource}.${c.action}`).join(', ') }}
90
+ </div>
91
+ <div v-if="s.clientId" class="mt-1 text-xs text-[#787774]">
92
+ synced by: {{ s.clientId.substring(0, 8) }}...
93
+ </div>
94
+ </td>
95
+ <td class="px-6 py-4">
96
+ <span v-if="s.origin" class="px-2 py-1 rounded-full text-xs font-semibold" :class="{
97
+ 'bg-blue-100 text-blue-700': s.origin === 'cli',
98
+ 'bg-green-100 text-green-700': s.origin === 'server',
99
+ 'bg-gray-100 text-gray-700': s.origin === 'manual'
100
+ }">{{ s.origin }}</span>
101
+ <span v-else class="text-xs text-[#787774]">manual</span>
102
+ </td>
54
103
  <td class="px-6 py-4 max-w-xs truncate text-[#2F3437]">{{ s.url || s.command || '' }}</td>
55
104
  <td class="px-6 py-4">
56
105
  <span v-if="s.stateful" class="badge bg-[#111111] text-white">stateful</span>
57
- <span v-else class="badge">{{ s.url ? 'http' : 'stdio' }}</span>
106
+ <span v-else class="badge">{{ s.url ? 'sse/http' : 'stdio' }}</span>
107
+ <span v-if="s.url && s.status" class="ml-2 text-xs" :class="{'text-green-600': s.status === 'alive', 'text-red-600': s.status === 'error'}">
108
+ {{ s.status === 'alive' ? '✓' : '✗' }} {{ s.lastCheck }}
109
+ </span>
58
110
  </td>
59
111
  <td class="px-6 py-4">
112
+ <button v-if="s.url" @click="check(s)" :disabled="s.checking" class="btn text-xs border-green-200 text-green-600 hover:bg-green-50 hover:border-green-300 mr-2" :class="{'opacity-50': s.checking}">
113
+ {{ s.checking ? 'Checking...' : 'Check' }}
114
+ </button>
115
+ <button @click="edit(s)" class="btn text-xs border-blue-200 text-blue-600 hover:bg-blue-50 hover:border-blue-300 mr-2">Edit</button>
60
116
  <button @click="remove(s._id)" class="btn text-xs border-red-200 text-red-600 hover:bg-red-50 hover:border-red-300">Delete</button>
61
117
  </td>
62
118
  </tr>
@@ -72,16 +128,84 @@
72
128
  data() {
73
129
  return {
74
130
  servers: initialServers,
131
+ commands: [],
75
132
  showForm: false,
76
- form: { name: '', url: '', command: '', stateful: false }
133
+ editing: false,
134
+ editId: null,
135
+ form: { name: '', transport: 'http', url: '', timeout_ms: 30000, command: '', stateful: false },
136
+ autoCheckInterval: null
137
+ }
138
+ },
139
+ mounted() {
140
+ // Start auto-check every minute for HTTP/SSE servers
141
+ this.autoCheckInterval = setInterval(() => {
142
+ this.autoCheck()
143
+ }, 60000)
144
+
145
+ // Fetch commands
146
+ this.fetchCommands()
147
+
148
+ // Refresh commands every 30 seconds
149
+ setInterval(() => {
150
+ this.fetchCommands()
151
+ }, 30000)
152
+ },
153
+ beforeUnmount() {
154
+ if (this.autoCheckInterval) {
155
+ clearInterval(this.autoCheckInterval)
77
156
  }
78
157
  },
79
158
  methods: {
159
+ async fetchCommands() {
160
+ try {
161
+ const res = await fetch('/api/commands?format=json')
162
+ const data = await res.json()
163
+ this.commands = data.commands || data || []
164
+ } catch (err) {
165
+ console.error('Failed to fetch commands:', err)
166
+ }
167
+ },
168
+ getUsedCommands(server) {
169
+ // Find commands that use this MCP server
170
+ return this.commands.filter(cmd => {
171
+ const adapterConfig = cmd.adapterConfig || {}
172
+ // Match by server name (for stdio) or URL (for HTTP)
173
+ if (server.url) {
174
+ return adapterConfig.url === server.url
175
+ } else {
176
+ return adapterConfig.server === server.name
177
+ }
178
+ })
179
+ },
180
+ edit(server) {
181
+ this.editing = true
182
+ this.editId = server._id
183
+ this.form = {
184
+ name: server.name,
185
+ transport: server.url ? 'http' : 'stdio',
186
+ url: server.url || '',
187
+ timeout_ms: server.timeout_ms || 30000,
188
+ command: server.command || '',
189
+ stateful: server.stateful || false
190
+ }
191
+ this.showForm = true
192
+ },
193
+ cancelEdit() {
194
+ this.editing = false
195
+ this.editId = null
196
+ this.form = { name: '', transport: 'http', url: '', timeout_ms: 30000, command: '', stateful: false }
197
+ this.showForm = false
198
+ },
80
199
  async create() {
81
200
  const payload = { name: this.form.name }
82
- if (this.form.url) payload.url = this.form.url
83
- if (this.form.command) payload.command = this.form.command
84
- if (this.form.stateful) payload.stateful = true
201
+ if (this.form.transport === 'http') {
202
+ payload.url = this.form.url
203
+ if (this.form.timeout_ms) payload.timeout_ms = parseInt(this.form.timeout_ms)
204
+ }
205
+ if (this.form.transport === 'stdio') {
206
+ payload.command = this.form.command
207
+ if (this.form.stateful) payload.stateful = true
208
+ }
85
209
  const r = await fetch('/api/mcp', {
86
210
  method: 'POST',
87
211
  headers: { 'Content-Type': 'application/json' },
@@ -89,14 +213,147 @@
89
213
  })
90
214
  const doc = await r.json()
91
215
  this.servers.push(doc)
92
- this.form = { name: '', url: '', command: '', stateful: false }
93
- this.showForm = false
216
+ this.cancelEdit()
217
+ await this.fetchCommands()
218
+ },
219
+ async update() {
220
+ const payload = { name: this.form.name }
221
+ if (this.form.transport === 'http') {
222
+ payload.url = this.form.url
223
+ if (this.form.timeout_ms) payload.timeout_ms = parseInt(this.form.timeout_ms)
224
+ }
225
+ if (this.form.transport === 'stdio') {
226
+ payload.command = this.form.command
227
+ if (this.form.stateful) payload.stateful = true
228
+ }
229
+ const r = await fetch('/api/mcp/' + this.editId, {
230
+ method: 'PUT',
231
+ headers: { 'Content-Type': 'application/json' },
232
+ body: JSON.stringify(payload)
233
+ })
234
+ const doc = await r.json()
235
+ const idx = this.servers.findIndex(s => s._id === this.editId)
236
+ if (idx !== -1) this.servers[idx] = { ...this.servers[idx], ...payload, updatedAt: new Date() }
237
+ this.cancelEdit()
238
+ await this.fetchCommands()
239
+ },
240
+ async check(server) {
241
+ server.checking = true
242
+ let sessionId = null
243
+ try {
244
+ const timeout = server.timeout_ms || 10000
245
+ const controller = new AbortController()
246
+ const timeoutId = setTimeout(() => controller.abort(), timeout)
247
+
248
+ // Try different endpoints
249
+ const endpoints = ['/mcp', '/tool', '/sse']
250
+ let response = null
251
+
252
+ // First try without session (stateless)
253
+ for (const endpoint of endpoints) {
254
+ try {
255
+ response = await fetch(server.url + endpoint, {
256
+ method: 'POST',
257
+ headers: { 'Content-Type': 'application/json', 'Accept': 'application/json, text/event-stream' },
258
+ body: JSON.stringify({ jsonrpc: '2.0', method: 'tools/list', id: 1 }),
259
+ signal: controller.signal
260
+ })
261
+ // Accept any response that returns data (SSE might not be 200)
262
+ if (response.status >= 200 && response.status < 500) break
263
+ } catch (e) {
264
+ // Try next endpoint
265
+ }
266
+ }
267
+
268
+ // If stateless check failed with session error, try with session
269
+ if (!response || response.status >= 500) {
270
+ // Try to create a session
271
+ for (const endpoint of endpoints) {
272
+ try {
273
+ const sessionRes = await fetch(server.url + endpoint, {
274
+ method: 'POST',
275
+ headers: { 'Content-Type': 'application/json', 'Accept': 'application/json, text/event-stream' },
276
+ body: JSON.stringify({ jsonrpc: '2.0', method: 'initialize', id: 1 }),
277
+ signal: controller.signal
278
+ })
279
+ if (sessionRes.status >= 200 && sessionRes.status < 500) {
280
+ const text = await sessionRes.text()
281
+ // Extract session ID from response
282
+ const match = text.match(/id:\s*([^\s_]+)/)
283
+ if (match) {
284
+ sessionId = match[1]
285
+ break
286
+ }
287
+ }
288
+ } catch (e) {
289
+ // Try next endpoint
290
+ }
291
+ }
292
+
293
+ // If we got a session, try the check with it
294
+ if (sessionId) {
295
+ for (const endpoint of endpoints) {
296
+ try {
297
+ response = await fetch(server.url + endpoint, {
298
+ method: 'POST',
299
+ headers: {
300
+ 'Content-Type': 'application/json',
301
+ 'Accept': 'application/json, text/event-stream',
302
+ 'X-MCP-Session-Id': sessionId
303
+ },
304
+ body: JSON.stringify({ jsonrpc: '2.0', method: 'tools/list', id: 1 }),
305
+ signal: controller.signal
306
+ })
307
+ if (response.status >= 200 && response.status < 500) break
308
+ } catch (e) {
309
+ // Try next endpoint
310
+ }
311
+ }
312
+ }
313
+ }
314
+
315
+ clearTimeout(timeoutId)
316
+
317
+ if (response && response.status >= 200 && response.status < 500) {
318
+ server.status = 'alive'
319
+ server.lastCheck = new Date().toLocaleTimeString()
320
+ } else {
321
+ server.status = 'error'
322
+ server.lastCheck = new Date().toLocaleTimeString()
323
+ }
324
+ } catch (err) {
325
+ server.status = 'error'
326
+ server.lastCheck = new Date().toLocaleTimeString()
327
+ } finally {
328
+ server.checking = false
329
+ // Clean up session if created
330
+ if (sessionId) {
331
+ try {
332
+ await fetch(server.url + '/mcp', {
333
+ method: 'POST',
334
+ headers: { 'Content-Type': 'application/json' },
335
+ body: JSON.stringify({ jsonrpc: '2.0', method: 'shutdown', id: 1 })
336
+ })
337
+ } catch (e) {
338
+ // Ignore cleanup errors
339
+ }
340
+ }
341
+ }
94
342
  },
95
343
  async remove(id) {
96
344
  if (!confirm('Delete this server?')) return
97
345
  await fetch('/api/mcp/' + id, { method: 'DELETE' })
98
346
  this.servers = this.servers.filter(s => s._id !== id)
99
- }
347
+ await this.fetchCommands()
348
+ },
349
+ autoCheck() {
350
+ // Check all HTTP/SSE servers that aren't already being checked
351
+ this.servers.forEach(server => {
352
+ if (server.url && !server.checking) {
353
+ this.check(server)
354
+ }
355
+ })
356
+ },
100
357
  }
101
358
  }).mount('#mcp-app')
102
359
  </script>
@@ -98,7 +98,11 @@
98
98
  <a href="/api/specs" class="btn justify-start" id="nav-specs">OpenAPI Specs</a>
99
99
  <a href="/api/mcp" class="btn justify-start" id="nav-mcp">MCP Servers</a>
100
100
  <a href="/api/plugins" class="btn justify-start" id="nav-plugins">Server Plugins</a>
101
+ <a href="/api/settings" class="btn justify-start" id="nav-settings">Settings</a>
102
+ <a href="/api/api-keys" class="btn justify-start" id="nav-api-keys">API Keys</a>
103
+ <a href="/api/clients" class="btn justify-start" id="nav-clients">Clients</a>
101
104
  <a href="/api/jobs" class="btn justify-start" id="nav-jobs">Jobs</a>
105
+ <a href="/api/docs" class="btn justify-start" id="nav-docs">Documentation</a>
102
106
  </aside>
103
107
  <!-- Main content -->
104
108
  <main class="flex-1 p-8 bg-[#FBFBFA]">
@@ -15,30 +15,8 @@
15
15
  <span><strong>Admin Mode Enabled:</strong> CLI clients can modify server state without authentication. Disable when not needed.</span>
16
16
  </div>
17
17
 
18
- <div class="grid grid-cols-1 lg:grid-cols-3 gap-4 mb-6">
18
+ <div class="grid grid-cols-1 lg:grid-cols-2 gap-4 mb-6">
19
19
  <div class="card bg-base-100 border border-base-300">
20
- <div class="card-body p-4">
21
- <h2 class="card-title text-base">Settings</h2>
22
- <label class="form-control mb-2">
23
- <span class="label-text text-xs">Max ZIP Size (MB)</span>
24
- <input v-model.number="settings.max_zip_mb" type="number" min="1" class="input input-bordered input-sm" />
25
- </label>
26
- <label class="form-control mb-3">
27
- <span class="label-text text-xs">Default Hooks Policy</span>
28
- <select v-model="settings.default_hooks_policy" class="select select-bordered select-sm">
29
- <option value="deny">deny</option>
30
- <option value="allow">allow</option>
31
- </select>
32
- </label>
33
- <label class="label cursor-pointer justify-start gap-2 mb-3">
34
- <input type="checkbox" v-model="settings.admin_mode_enabled" class="checkbox checkbox-error checkbox-sm" />
35
- <span class="label-text text-xs text-error font-semibold">Admin Mode (allows CLI modifications)</span>
36
- </label>
37
- <button class="btn btn-sm btn-primary" @click="saveSettings">Save Settings</button>
38
- </div>
39
- </div>
40
-
41
- <div class="card bg-base-100 border border-base-300 lg:col-span-2">
42
20
  <div class="card-body p-4">
43
21
  <h2 class="card-title text-base">Create JSON Plugin</h2>
44
22
  <div class="grid grid-cols-2 gap-2 mb-2">
@@ -180,15 +158,6 @@
180
158
  this.plugins = out.plugins || []
181
159
  if (out.settings) this.settings = out.settings
182
160
  },
183
- async saveSettings() {
184
- try {
185
- await this.api('/api/plugins/settings', 'PUT', this.settings)
186
- this.setMessage('success', 'Settings updated')
187
- await this.reloadPlugins()
188
- } catch (err) {
189
- this.setMessage('error', err.message)
190
- }
191
- },
192
161
  async createJsonPlugin() {
193
162
  try {
194
163
  const manifest = JSON.parse(this.jsonForm.manifestStr)
@@ -266,6 +235,38 @@
266
235
  this.setMessage('error', err.message)
267
236
  }
268
237
  },
238
+ async createApiKey() {
239
+ if (!this.newKeyName.trim()) {
240
+ this.setMessage('error', 'API key name is required')
241
+ return
242
+ }
243
+ try {
244
+ const result = await this.api('/api/plugins/api-keys', 'POST', { name: this.newKeyName })
245
+ this.setMessage('success', `Created API key '${result.name}'`)
246
+ this.newKeyName = ''
247
+ await this.reloadPlugins()
248
+ } catch (err) {
249
+ this.setMessage('error', err.message)
250
+ }
251
+ },
252
+ async deleteApiKey(key) {
253
+ if (!confirm('Delete this API key?')) return
254
+ try {
255
+ await this.api(`/api/plugins/api-keys/${encodeURIComponent(key)}`, 'DELETE')
256
+ this.setMessage('success', 'API key deleted')
257
+ await this.reloadPlugins()
258
+ } catch (err) {
259
+ this.setMessage('error', err.message)
260
+ }
261
+ },
262
+ async copyApiKey(key) {
263
+ try {
264
+ await navigator.clipboard.writeText(key)
265
+ this.setMessage('success', 'API key copied to clipboard')
266
+ } catch (err) {
267
+ this.setMessage('error', 'Failed to copy API key')
268
+ }
269
+ },
269
270
  },
270
271
  }).mount('#plugins-app')
271
272
  </script>
@@ -0,0 +1,99 @@
1
+ <%- include("partials/head", { title: "Server Settings" }) %>
2
+
3
+ <div id="settings-app" class="max-w-4xl">
4
+ <div class="flex justify-between items-center mb-6">
5
+ <h1 class="text-2xl font-bold">Server Settings</h1>
6
+ <div class="text-sm opacity-80">Configure server-wide behavior and security.</div>
7
+ </div>
8
+
9
+ <div v-if="message.text" :class="['alert mb-4', message.type === 'error' ? 'alert-error' : 'alert-success']">
10
+ <span>{{ message.text }}</span>
11
+ </div>
12
+
13
+ <div v-if="settings.admin_mode_enabled" class="alert alert-error mb-4">
14
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 shrink-0 stroke-current" fill="none" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" /></svg>
15
+ <span><strong>Admin Mode Enabled:</strong> CLI clients can modify server state without authentication. Disable when not needed.</span>
16
+ </div>
17
+
18
+ <div class="card bg-base-100 border border-base-300">
19
+ <div class="card-body">
20
+ <h2 class="card-title">General Settings</h2>
21
+ <label class="form-control mb-3">
22
+ <span class="label-text">Max ZIP Size (MB)</span>
23
+ <input v-model.number="settings.max_zip_mb" type="number" min="1" class="input input-bordered" />
24
+ </label>
25
+ <label class="form-control mb-3">
26
+ <span class="label-text">Default Hooks Policy</span>
27
+ <select v-model="settings.default_hooks_policy" class="select select-bordered">
28
+ <option value="deny">deny</option>
29
+ <option value="allow">allow</option>
30
+ </select>
31
+ </label>
32
+ </div>
33
+ </div>
34
+
35
+ <div class="card bg-base-100 border border-base-300 mt-4">
36
+ <div class="card-body">
37
+ <h2 class="card-title">Security Settings</h2>
38
+ <label class="label cursor-pointer justify-start gap-2 mb-3">
39
+ <input type="checkbox" v-model="settings.admin_mode_enabled" class="checkbox checkbox-error" />
40
+ <span class="label-text text-error font-semibold">Admin Mode (allows CLI modifications without auth)</span>
41
+ </label>
42
+ <label class="label cursor-pointer justify-start gap-2 mb-3">
43
+ <input type="checkbox" v-model="settings.public_access" class="checkbox" />
44
+ <span class="label-text">Public Access (allow API requests without API key)</span>
45
+ </label>
46
+ </div>
47
+ </div>
48
+
49
+ <div class="mt-6">
50
+ <button class="btn btn-primary" @click="saveSettings">Save Settings</button>
51
+ </div>
52
+ </div>
53
+
54
+ <script>
55
+ const { createApp } = Vue
56
+
57
+ createApp({
58
+ data() {
59
+ return {
60
+ settings: {},
61
+ message: { text: '', type: 'success' }
62
+ }
63
+ },
64
+ async mounted() {
65
+ await this.loadSettings()
66
+ },
67
+ methods: {
68
+ async loadSettings() {
69
+ try {
70
+ const res = await fetch('/api/settings?format=json')
71
+ const data = await res.json()
72
+ this.settings = data
73
+ } catch (err) {
74
+ this.setMessage('error', err.message)
75
+ }
76
+ },
77
+ async saveSettings() {
78
+ try {
79
+ const res = await fetch('/api/settings', {
80
+ method: 'PUT',
81
+ headers: { 'Content-Type': 'application/json' },
82
+ body: JSON.stringify(this.settings)
83
+ })
84
+ const data = await res.json()
85
+ this.settings = data.settings
86
+ this.setMessage('success', 'Settings saved')
87
+ } catch (err) {
88
+ this.setMessage('error', err.message)
89
+ }
90
+ },
91
+ setMessage(type, text) {
92
+ this.message = { type, text }
93
+ setTimeout(() => this.message = { text: '', type: 'success' }, 3000)
94
+ }
95
+ }
96
+ }).mount('#settings-app')
97
+ </script>
98
+
99
+ <%- include("partials/foot") %>