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.
- package/.beads/.br_history/{issues.20260427_104638_898729442.jsonl → issues.20260427_114912_061702372.jsonl} +65 -65
- package/.beads/.br_history/{issues.20260427_104638_209211116.jsonl → issues.20260427_115013_063113906.jsonl} +66 -66
- package/.beads/.br_history/{issues.20260427_104637_752744777.jsonl → issues.20260427_115114_208497343.jsonl} +67 -67
- package/.beads/.br_history/{issues.20260427_104615_669652069.jsonl → issues.20260427_115215_176080241.jsonl} +68 -68
- package/.beads/.br_history/issues.20260427_115316_481573328.jsonl +261 -0
- package/.beads/.br_history/issues.20260427_115335_866978652.jsonl +261 -0
- package/.beads/.br_history/issues.20260427_115436_866087391.jsonl +261 -0
- package/.beads/.br_history/issues.20260427_115538_016094860.jsonl +261 -0
- package/.beads/.br_history/issues.20260427_115638_932947449.jsonl +261 -0
- package/.beads/.br_history/issues.20260427_115739_982932603.jsonl +261 -0
- package/.beads/.br_history/issues.20260427_115840_963681039.jsonl +261 -0
- package/.beads/.br_history/issues.20260427_115941_912431509.jsonl +261 -0
- package/.beads/.br_history/issues.20260427_120043_043356448.jsonl +261 -0
- package/.beads/.br_history/issues.20260427_120144_955596968.jsonl +261 -0
- package/.beads/.br_history/issues.20260427_120246_060733977.jsonl +261 -0
- package/.beads/.br_history/issues.20260427_120307_717985017.jsonl +261 -0
- package/.beads/.br_history/issues.20260427_120408_871846736.jsonl +261 -0
- package/.beads/.br_history/issues.20260427_121003_963750732.jsonl +261 -0
- package/.beads/.br_history/issues.20260427_121006_018509431.jsonl +261 -0
- package/.beads/.br_history/issues.20260427_121019_617062931.jsonl +261 -0
- package/.beads/.br_history/issues.20260427_121021_689536311.jsonl +261 -0
- package/.beads/.br_history/issues.20260428_172005_240631447.jsonl +261 -0
- package/.beads/.br_history/issues.20260430_073641_144807989.jsonl +262 -0
- package/.beads/.br_history/issues.20260430_073642_807075699.jsonl +263 -0
- package/.beads/.br_history/issues.20260430_073645_625754223.jsonl +264 -0
- package/.beads/.br_history/issues.20260430_090639_154646058.jsonl +265 -0
- package/.beads/.br_history/issues.20260430_090640_350560318.jsonl +266 -0
- package/.beads/.br_history/issues.20260430_090641_509319001.jsonl +267 -0
- package/.beads/.br_history/issues.20260430_095140_396749398.jsonl +268 -0
- package/.beads/.br_history/issues.20260501_230041_334056077.jsonl +269 -0
- package/.beads/.br_recovery/beads.db-wal.20260427_121006_010373120.rebuild-failed +0 -0
- package/.beads/.br_recovery/beads.db.20260427_121006_010373120.rebuild-failed +0 -0
- package/.beads/beads.db.corrupted +0 -0
- package/.beads/issues.jsonl +46 -37
- package/.supercli/adapters/test-cli-adapter.js +19 -0
- package/cli/adapters/mcp.js +89 -3
- package/cli/adapters/openapi.js +71 -2
- package/cli/config.js +69 -1
- package/cli/daemon.js +349 -0
- package/cli/plugins-manager.js +41 -1
- package/cli/server-command.js +174 -4
- package/cli/supercli.js +35 -15
- package/docs/changelog-2026-04.html +6 -0
- package/docs/index.html +95 -8
- package/docs/plugins.html +30 -0
- package/docs/server.md +80 -0
- package/graphify-out/GRAPH_REPORT.md +140 -128
- package/graphify-out/cache/036077d325d5aa25fc6ad7f01ca32721b02e3f981231f67fb22f59380063572b.json +1 -0
- package/graphify-out/cache/11938479cfbdb8e52bf5f0f3c132d56204245ebb19aefe89bb0394feb36dbd5e.json +1 -0
- package/graphify-out/cache/141362b25039dee7a4959ef475befea0a25671aae66f3d14917e711e597ffbf6.json +1 -0
- package/graphify-out/cache/362adb1b6b483f56d09feaf652f4122366e568aaede752f48383b76d61f5d9d6.json +1 -0
- package/graphify-out/cache/42bc8f64d69d2cd9ecf64e92e38f89f7ea479f69c947de5984919e2fd5a716ee.json +1 -0
- package/graphify-out/cache/4b5cffce31d9193b7d0a8f15e8a41c66f6733a6490ebaeff2c8ca19701c72c0e.json +1 -0
- package/graphify-out/cache/5262fb95a8855fc90efbfff767eb3aacca82c4da5799605a969554580a255404.json +1 -0
- package/graphify-out/cache/5ef4e31750d9e7bc4ff4a7b535f247d9ab5ab9627d72e20794779ec4be424c66.json +1 -0
- package/graphify-out/cache/68e2b11c95ede4f05b88d001040008cb3ac72d7bd27f351497c87e128fb3b221.json +1 -0
- package/graphify-out/cache/6d5023ecffa52382d31152c8c06e86e286789228a3d4f7fde72087f3eafa11bc.json +1 -0
- package/graphify-out/cache/6e5776e123142a78f50fa0bfd5ae2d289282051a9b392ae507de7aa9017f95d4.json +1 -0
- package/graphify-out/cache/75982dc60fa7353600ca48a3b33f5ba497eea9d5743daac6e61e9cf4f64774be.json +1 -0
- package/graphify-out/cache/7796066b51d3a99071debdfb5f40c854400b5bf3aa7c1e83dd6ae88eddca0370.json +1 -0
- package/graphify-out/cache/8308aa47b396b929d5b896051ca91422afdde4861108d6f18f5094901093df79.json +1 -0
- package/graphify-out/cache/8ccd3b557a1ea98005fba84433f160ccd4a951fbdb6a4c9b7d7ba894f905b06b.json +1 -0
- package/graphify-out/cache/90ab53b8f1f3306e7c3a1f92ec628717ea90f4e684db764f7d7aa8e884fe2cae.json +1 -0
- package/graphify-out/cache/9832101a7622924f58b9e7d7752b41518049447171f7955e4c50dfd386574e12.json +1 -0
- package/graphify-out/cache/9fa6604cc820f3857c55c753ca0024788e4cbb61c4e12ba7ce0e4a1ce4ee0655.json +1 -0
- package/graphify-out/cache/a1e24a07beede2876e688cdb6c14e2d24abb22064862a6420cac321efabd3efd.json +1 -0
- package/graphify-out/cache/a418312f2805259c33007a18b36b6ae3b6a4da376ccae6006a3b9999262495db.json +1 -0
- package/graphify-out/cache/a6d9c029bd31a99b67357291305e27b01c5ef388925f7f2a6e985fec4efa8024.json +1 -0
- package/graphify-out/cache/a6ed52b709dd79f98adf72c483e8dfb6098203d7327470b82feb5e11390a5cf4.json +1 -0
- package/graphify-out/cache/b045ae4443d972ee8f47b91a0d61c984a190195b38ae1d331430b4c51c43acfd.json +1 -0
- package/graphify-out/cache/b849fb88344c2f88f3d22e42b67516e071c7d1e65f77f4ca23f25af6edfca3b3.json +1 -0
- package/graphify-out/cache/bc514f4074f89d2f7e7834670602e50bba0f24b8cd8c9d21ff4d361d3fd4d558.json +1 -0
- package/graphify-out/cache/be3fc87936f9f24d2d4f4b22b9d2a16f33f538cef1a181663120e23ecf16b81a.json +1 -0
- package/graphify-out/cache/c5a4822edbe3a95a56f9f943eed1567e5a5c913d59b1fa3df4ad54ffd9b02697.json +1 -0
- package/graphify-out/cache/cf411f1ddde50ee97c1c501f59ed7985bcdc022134574816bcce37ac20848e64.json +1 -0
- package/graphify-out/cache/d88625b5813742ca941ce8155cb77afb02425632002f621aca34f217440a392d.json +1 -0
- package/graphify-out/cache/da97fdc8af2a3306c292921110f84648bba2b0e85d27473b460b0e8a862ff225.json +1 -0
- package/graphify-out/cache/e88de9f1ae117f26fd217c3717eff7e525a284cc942b5f4d26ef2c4d65db59bd.json +1 -0
- package/graphify-out/cache/ebf6bd96ef2d8d396389ac7199e6ba818ee2d0c5dfc131fab7cdece9fb710f11.json +1 -0
- package/graphify-out/cache/f2a302550a6fe0649d36b8ef0b04b77673bca9f115732af8ae89751944dc9cc6.json +1 -0
- package/graphify-out/cache/f9ee11f4395cb41b6f6900079cb7e94297c89b1b0b4798398d2f7a2e244c683b.json +1 -0
- package/graphify-out/cache/fb46bd3ef530273918fc8915842453f21b418d536653d3b705b75b417255c246.json +1 -0
- package/graphify-out/cache/fcfdab94fe91af1f21fcefdba5a96674b9a16bb768288f3573fe68765163c765.json +1 -0
- package/graphify-out/graph.json +2420 -1157
- package/package.json +1 -1
- package/plugins/bluebuild/install-guidance.json +11 -0
- package/plugins/bluebuild/meta.json +5 -0
- package/plugins/bluebuild/plugin.json +99 -0
- package/plugins/bluebuild/skills/quickstart/SKILL.md +47 -0
- package/plugins/cargo-public-api/install-guidance.json +11 -0
- package/plugins/cargo-public-api/meta.json +5 -0
- package/plugins/cargo-public-api/plugin.json +88 -0
- package/plugins/cargo-public-api/skills/quickstart/SKILL.md +51 -0
- package/plugins/context-mode/plugin.json +10 -0
- package/plugins/context-mode/scripts/post-install.js +1 -1
- package/plugins/csview/install-guidance.json +11 -0
- package/plugins/csview/meta.json +5 -0
- package/plugins/csview/plugin.json +67 -0
- package/plugins/csview/skills/quickstart/SKILL.md +51 -0
- package/plugins/cymbal/install-guidance.json +11 -0
- package/plugins/cymbal/meta.json +5 -0
- package/plugins/cymbal/plugin.json +121 -0
- package/plugins/cymbal/skills/quickstart/SKILL.md +54 -0
- package/plugins/difftastic/install-guidance.json +11 -0
- package/plugins/difftastic/meta.json +5 -0
- package/plugins/difftastic/plugin.json +87 -0
- package/plugins/difftastic/skills/quickstart/SKILL.md +53 -0
- package/plugins/dlm/install-guidance.json +11 -0
- package/plugins/dlm/meta.json +5 -0
- package/plugins/dlm/plugin.json +85 -0
- package/plugins/dlm/skills/quickstart/SKILL.md +49 -0
- package/plugins/dofigen/install-guidance.json +11 -0
- package/plugins/dofigen/meta.json +5 -0
- package/plugins/dofigen/plugin.json +84 -0
- package/plugins/dofigen/skills/quickstart/SKILL.md +50 -0
- package/plugins/example-server-resources/meta.json +11 -0
- package/plugins/example-server-resources/plugin.json +46 -0
- package/plugins/http-nu/install-guidance.json +11 -0
- package/plugins/http-nu/meta.json +5 -0
- package/plugins/http-nu/plugin.json +84 -0
- package/plugins/http-nu/skills/quickstart/SKILL.md +52 -0
- package/plugins/hwatch/install-guidance.json +11 -0
- package/plugins/hwatch/meta.json +5 -0
- package/plugins/hwatch/plugin.json +100 -0
- package/plugins/hwatch/skills/quickstart/SKILL.md +54 -0
- package/plugins/hyperfine/install-guidance.json +3 -3
- package/plugins/hyperfine/meta.json +2 -2
- package/plugins/hyperfine/plugin.json +39 -16
- package/plugins/hyperfine/skills/quickstart/SKILL.md +33 -61
- package/plugins/json5/install-guidance.json +12 -0
- package/plugins/json5/meta.json +5 -0
- package/plugins/json5/plugin.json +88 -0
- package/plugins/json5/skills/quickstart/SKILL.md +55 -0
- package/plugins/mdsf/install-guidance.json +11 -0
- package/plugins/mdsf/meta.json +5 -0
- package/plugins/mdsf/plugin.json +101 -0
- package/plugins/mdsf/skills/quickstart/SKILL.md +55 -0
- package/plugins/oha/install-guidance.json +11 -0
- package/plugins/oha/meta.json +5 -0
- package/plugins/oha/plugin.json +67 -0
- package/plugins/oha/skills/quickstart/SKILL.md +44 -0
- package/plugins/pv-migrate/install-guidance.json +11 -0
- package/plugins/pv-migrate/meta.json +5 -0
- package/plugins/pv-migrate/plugin.json +102 -0
- package/plugins/pv-migrate/skills/quickstart/SKILL.md +58 -0
- package/plugins/rash/install-guidance.json +11 -0
- package/plugins/rash/meta.json +5 -0
- package/plugins/rash/plugin.json +85 -0
- package/plugins/rash/skills/quickstart/SKILL.md +46 -0
- package/plugins/rsonpath/install-guidance.json +11 -0
- package/plugins/rsonpath/meta.json +5 -0
- package/plugins/rsonpath/plugin.json +87 -0
- package/plugins/rsonpath/skills/quickstart/SKILL.md +44 -0
- package/plugins/rsql/install-guidance.json +11 -0
- package/plugins/rsql/meta.json +5 -0
- package/plugins/rsql/plugin.json +84 -0
- package/plugins/rsql/skills/quickstart/SKILL.md +52 -0
- package/plugins/temporal/install-guidance.json +11 -0
- package/plugins/temporal/meta.json +5 -0
- package/plugins/temporal/plugin.json +95 -0
- package/plugins/temporal/skills/quickstart/SKILL.md +49 -0
- package/plugins/tkn/install-guidance.json +11 -0
- package/plugins/tkn/meta.json +5 -0
- package/plugins/tkn/plugin.json +98 -0
- package/plugins/tkn/skills/quickstart/SKILL.md +50 -0
- package/plugins/toml2json/install-guidance.json +11 -0
- package/plugins/toml2json/meta.json +5 -0
- package/plugins/toml2json/plugin.json +85 -0
- package/plugins/toml2json/skills/quickstart/SKILL.md +47 -0
- package/plugins/worktrunk/install-guidance.json +11 -0
- package/plugins/worktrunk/meta.json +5 -0
- package/plugins/worktrunk/plugin.json +116 -0
- package/plugins/worktrunk/skills/quickstart/SKILL.md +62 -0
- package/server/app.js +46 -13
- package/server/middleware/auth.js +70 -0
- package/server/routes/adapters.js +3 -11
- package/server/routes/clients.js +46 -0
- package/server/routes/docs.js +10 -0
- package/server/routes/jobs.js +3 -2
- package/server/routes/mcp.js +1 -0
- package/server/routes/plugins.js +109 -26
- package/server/routes/settings.js +93 -0
- package/server/routes/specs.js +8 -3
- package/server/services/adaptersService.js +1 -0
- package/server/services/pluginResourceService.js +262 -0
- package/server/services/pluginsService.js +24 -0
- package/server/uploads/superbackend-openapi.json +47 -0
- package/server/uploads/test-auth-openapi.json +74 -0
- package/server/uploads/test-auth-spec.json +36 -0
- package/server/views/adapters.ejs +1 -0
- package/server/views/api-keys.ejs +147 -0
- package/server/views/clients.ejs +82 -0
- package/server/views/docs.ejs +7 -0
- package/server/views/jobs.ejs +26 -2
- package/server/views/layout.ejs +35 -21
- package/server/views/mcp.ejs +278 -21
- package/server/views/partials/head.ejs +4 -0
- package/server/views/plugins.ejs +33 -32
- package/server/views/settings.ejs +99 -0
- package/server/views/specs.ejs +217 -22
- package/.beads/.br_history/issues.20260427_103259_082222986.jsonl +0 -261
- package/.beads/.br_history/issues.20260427_103400_358332602.jsonl +0 -261
- package/.beads/.br_history/issues.20260427_103419_681839669.jsonl +0 -261
- package/.beads/.br_history/issues.20260427_103522_226669683.jsonl +0 -261
- package/.beads/.br_history/issues.20260427_103623_568379011.jsonl +0 -261
- package/.beads/.br_history/issues.20260427_103724_772051154.jsonl +0 -261
- package/.beads/.br_history/issues.20260427_103825_935228905.jsonl +0 -261
- package/.beads/.br_history/issues.20260427_103927_159626780.jsonl +0 -261
- package/.beads/.br_history/issues.20260427_104028_436388960.jsonl +0 -261
- package/.beads/.br_history/issues.20260427_104129_670562810.jsonl +0 -261
- package/.beads/.br_history/issues.20260427_104230_885162615.jsonl +0 -261
- package/.beads/.br_history/issues.20260427_104332_071213892.jsonl +0 -261
- package/.beads/.br_history/issues.20260427_104349_051129644.jsonl +0 -261
- package/.beads/.br_history/issues.20260427_104450_358444507.jsonl +0 -261
- package/.beads/.br_history/issues.20260427_104555_654123963.jsonl +0 -261
- package/.beads/.br_history/issues.20260427_104559_805009051.jsonl +0 -261
- package/.beads/.br_history/issues.20260427_104600_255068030.jsonl +0 -261
- package/.beads/.br_history/issues.20260427_104600_717042012.jsonl +0 -261
- package/.beads/.br_history/issues.20260427_104601_215845416.jsonl +0 -261
- package/.beads/.br_history/issues.20260427_104601_899140331.jsonl +0 -261
- package/.beads/.br_history/issues.20260427_104606_172300551.jsonl +0 -261
- package/.beads/.br_history/issues.20260427_104606_658098709.jsonl +0 -261
- package/.beads/.br_history/issues.20260427_104607_085798670.jsonl +0 -261
- package/.beads/.br_history/issues.20260427_104607_627598880.jsonl +0 -261
- package/.beads/.br_history/issues.20260427_104608_071298223.jsonl +0 -261
- package/.beads/.br_history/issues.20260427_104615_198124329.jsonl +0 -261
- package/scripts/post-tweet-issues.js +0 -161
- /package/.beads/.br_history/{issues.20260427_103259_082222986.jsonl.meta.json → issues.20260427_114912_061702372.jsonl.meta.json} +0 -0
- /package/.beads/.br_history/{issues.20260427_103400_358332602.jsonl.meta.json → issues.20260427_115013_063113906.jsonl.meta.json} +0 -0
- /package/.beads/.br_history/{issues.20260427_103419_681839669.jsonl.meta.json → issues.20260427_115114_208497343.jsonl.meta.json} +0 -0
- /package/.beads/.br_history/{issues.20260427_103522_226669683.jsonl.meta.json → issues.20260427_115215_176080241.jsonl.meta.json} +0 -0
- /package/.beads/.br_history/{issues.20260427_103623_568379011.jsonl.meta.json → issues.20260427_115316_481573328.jsonl.meta.json} +0 -0
- /package/.beads/.br_history/{issues.20260427_103724_772051154.jsonl.meta.json → issues.20260427_115335_866978652.jsonl.meta.json} +0 -0
- /package/.beads/.br_history/{issues.20260427_103825_935228905.jsonl.meta.json → issues.20260427_115436_866087391.jsonl.meta.json} +0 -0
- /package/.beads/.br_history/{issues.20260427_103927_159626780.jsonl.meta.json → issues.20260427_115538_016094860.jsonl.meta.json} +0 -0
- /package/.beads/.br_history/{issues.20260427_104028_436388960.jsonl.meta.json → issues.20260427_115638_932947449.jsonl.meta.json} +0 -0
- /package/.beads/.br_history/{issues.20260427_104129_670562810.jsonl.meta.json → issues.20260427_115739_982932603.jsonl.meta.json} +0 -0
- /package/.beads/.br_history/{issues.20260427_104230_885162615.jsonl.meta.json → issues.20260427_115840_963681039.jsonl.meta.json} +0 -0
- /package/.beads/.br_history/{issues.20260427_104332_071213892.jsonl.meta.json → issues.20260427_115941_912431509.jsonl.meta.json} +0 -0
- /package/.beads/.br_history/{issues.20260427_104349_051129644.jsonl.meta.json → issues.20260427_120043_043356448.jsonl.meta.json} +0 -0
- /package/.beads/.br_history/{issues.20260427_104450_358444507.jsonl.meta.json → issues.20260427_120144_955596968.jsonl.meta.json} +0 -0
- /package/.beads/.br_history/{issues.20260427_104555_654123963.jsonl.meta.json → issues.20260427_120246_060733977.jsonl.meta.json} +0 -0
- /package/.beads/.br_history/{issues.20260427_104559_805009051.jsonl.meta.json → issues.20260427_120307_717985017.jsonl.meta.json} +0 -0
- /package/.beads/.br_history/{issues.20260427_104600_255068030.jsonl.meta.json → issues.20260427_120408_871846736.jsonl.meta.json} +0 -0
- /package/.beads/.br_history/{issues.20260427_104600_717042012.jsonl.meta.json → issues.20260427_121003_963750732.jsonl.meta.json} +0 -0
- /package/.beads/.br_history/{issues.20260427_104601_215845416.jsonl.meta.json → issues.20260427_121006_018509431.jsonl.meta.json} +0 -0
- /package/.beads/.br_history/{issues.20260427_104601_899140331.jsonl.meta.json → issues.20260427_121019_617062931.jsonl.meta.json} +0 -0
- /package/.beads/.br_history/{issues.20260427_104606_172300551.jsonl.meta.json → issues.20260427_121021_689536311.jsonl.meta.json} +0 -0
- /package/.beads/.br_history/{issues.20260427_104606_658098709.jsonl.meta.json → issues.20260428_172005_240631447.jsonl.meta.json} +0 -0
- /package/.beads/.br_history/{issues.20260427_104607_085798670.jsonl.meta.json → issues.20260430_073641_144807989.jsonl.meta.json} +0 -0
- /package/.beads/.br_history/{issues.20260427_104607_627598880.jsonl.meta.json → issues.20260430_073642_807075699.jsonl.meta.json} +0 -0
- /package/.beads/.br_history/{issues.20260427_104608_071298223.jsonl.meta.json → issues.20260430_073645_625754223.jsonl.meta.json} +0 -0
- /package/.beads/.br_history/{issues.20260427_104615_198124329.jsonl.meta.json → issues.20260430_090639_154646058.jsonl.meta.json} +0 -0
- /package/.beads/.br_history/{issues.20260427_104615_669652069.jsonl.meta.json → issues.20260430_090640_350560318.jsonl.meta.json} +0 -0
- /package/.beads/.br_history/{issues.20260427_104637_752744777.jsonl.meta.json → issues.20260430_090641_509319001.jsonl.meta.json} +0 -0
- /package/.beads/.br_history/{issues.20260427_104638_209211116.jsonl.meta.json → issues.20260430_095140_396749398.jsonl.meta.json} +0 -0
- /package/.beads/.br_history/{issues.20260427_104638_898729442.jsonl.meta.json → issues.20260501_230041_334056077.jsonl.meta.json} +0 -0
package/server/routes/plugins.js
CHANGED
|
@@ -10,6 +10,10 @@ const {
|
|
|
10
10
|
removeServerPlugin,
|
|
11
11
|
getPluginArchiveBuffer,
|
|
12
12
|
} = require("../services/pluginsService")
|
|
13
|
+
const { getStorage } = require("../storage/adapter")
|
|
14
|
+
const { registerPluginResources, unregisterPluginResources } = require("../services/pluginResourceService")
|
|
15
|
+
const { bumpVersion } = require("../services/configService")
|
|
16
|
+
const { requireAuth } = require("../middleware/auth")
|
|
13
17
|
|
|
14
18
|
const router = Router()
|
|
15
19
|
|
|
@@ -147,7 +151,7 @@ async function parseMultipartFormData(req) {
|
|
|
147
151
|
}
|
|
148
152
|
}
|
|
149
153
|
|
|
150
|
-
router.get("/", async (req, res) => {
|
|
154
|
+
router.get("/", requireAuth, async (req, res) => {
|
|
151
155
|
try {
|
|
152
156
|
const plugins = await listServerPlugins()
|
|
153
157
|
const settings = await getSettings()
|
|
@@ -160,25 +164,7 @@ router.get("/", async (req, res) => {
|
|
|
160
164
|
}
|
|
161
165
|
})
|
|
162
166
|
|
|
163
|
-
router.
|
|
164
|
-
try {
|
|
165
|
-
const settings = await getSettings()
|
|
166
|
-
res.json(settings)
|
|
167
|
-
} catch (err) {
|
|
168
|
-
handleError(res, err)
|
|
169
|
-
}
|
|
170
|
-
})
|
|
171
|
-
|
|
172
|
-
router.put("/settings", async (req, res) => {
|
|
173
|
-
try {
|
|
174
|
-
const settings = await updateSettings(req.body || {})
|
|
175
|
-
res.json({ ok: true, settings })
|
|
176
|
-
} catch (err) {
|
|
177
|
-
handleError(res, err)
|
|
178
|
-
}
|
|
179
|
-
})
|
|
180
|
-
|
|
181
|
-
router.post("/", async (req, res) => {
|
|
167
|
+
router.post("/", requireAuth, async (req, res) => {
|
|
182
168
|
try {
|
|
183
169
|
const sourceType = String((req.body && req.body.source_type) || "json").trim().toLowerCase()
|
|
184
170
|
const plugin = sourceType === "zip"
|
|
@@ -190,7 +176,7 @@ router.post("/", async (req, res) => {
|
|
|
190
176
|
}
|
|
191
177
|
})
|
|
192
178
|
|
|
193
|
-
router.post("/upload", async (req, res) => {
|
|
179
|
+
router.post("/upload", requireAuth, async (req, res) => {
|
|
194
180
|
try {
|
|
195
181
|
const contentType = String(req.headers["content-type"] || "")
|
|
196
182
|
const payload = contentType.includes("multipart/form-data")
|
|
@@ -203,7 +189,7 @@ router.post("/upload", async (req, res) => {
|
|
|
203
189
|
}
|
|
204
190
|
})
|
|
205
191
|
|
|
206
|
-
router.get("/:name", async (req, res) => {
|
|
192
|
+
router.get("/:name", requireAuth, async (req, res) => {
|
|
207
193
|
try {
|
|
208
194
|
const plugin = await getServerPlugin(req.params.name)
|
|
209
195
|
if (!plugin) {
|
|
@@ -215,7 +201,7 @@ router.get("/:name", async (req, res) => {
|
|
|
215
201
|
}
|
|
216
202
|
})
|
|
217
203
|
|
|
218
|
-
router.patch("/:name", async (req, res) => {
|
|
204
|
+
router.patch("/:name", requireAuth, async (req, res) => {
|
|
219
205
|
try {
|
|
220
206
|
const plugin = await updatePluginMetadata(req.params.name, req.body || {})
|
|
221
207
|
res.json({ ok: true, plugin })
|
|
@@ -224,7 +210,7 @@ router.patch("/:name", async (req, res) => {
|
|
|
224
210
|
}
|
|
225
211
|
})
|
|
226
212
|
|
|
227
|
-
router.delete("/:name", async (req, res) => {
|
|
213
|
+
router.delete("/:name", requireAuth, async (req, res) => {
|
|
228
214
|
try {
|
|
229
215
|
const removed = await removeServerPlugin(req.params.name)
|
|
230
216
|
res.json({ ok: true, removed })
|
|
@@ -233,7 +219,7 @@ router.delete("/:name", async (req, res) => {
|
|
|
233
219
|
}
|
|
234
220
|
})
|
|
235
221
|
|
|
236
|
-
router.get("/:name/manifest", async (req, res) => {
|
|
222
|
+
router.get("/:name/manifest", requireAuth, async (req, res) => {
|
|
237
223
|
try {
|
|
238
224
|
const plugin = await getServerPlugin(req.params.name)
|
|
239
225
|
if (!plugin) {
|
|
@@ -245,7 +231,7 @@ router.get("/:name/manifest", async (req, res) => {
|
|
|
245
231
|
}
|
|
246
232
|
})
|
|
247
233
|
|
|
248
|
-
router.get("/:name/archive", async (req, res) => {
|
|
234
|
+
router.get("/:name/archive", requireAuth, async (req, res) => {
|
|
249
235
|
try {
|
|
250
236
|
const archive = await getPluginArchiveBuffer(req.params.name)
|
|
251
237
|
if (!archive) {
|
|
@@ -259,4 +245,101 @@ router.get("/:name/archive", async (req, res) => {
|
|
|
259
245
|
}
|
|
260
246
|
})
|
|
261
247
|
|
|
248
|
+
router.post("/client-resources", requireAuth, async (req, res) => {
|
|
249
|
+
try {
|
|
250
|
+
const storage = getStorage()
|
|
251
|
+
const { client_id, plugins } = req.body || {}
|
|
252
|
+
|
|
253
|
+
if (!Array.isArray(plugins)) {
|
|
254
|
+
return res.status(400).json({ error: "plugins must be an array", type: "invalid_argument" })
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
if (!client_id || typeof client_id !== "string") {
|
|
258
|
+
return res.status(400).json({ error: "client_id is required", type: "invalid_argument" })
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// Update client last_seen
|
|
262
|
+
await storage.set(`client:${client_id}`, {
|
|
263
|
+
client_id: client_id,
|
|
264
|
+
last_seen: new Date(),
|
|
265
|
+
plugin_count: plugins.length
|
|
266
|
+
})
|
|
267
|
+
|
|
268
|
+
// Remove existing cli resources from this specific client only
|
|
269
|
+
const allMcpKeys = await storage.listKeys("mcp:")
|
|
270
|
+
const allSpecKeys = await storage.listKeys("spec:")
|
|
271
|
+
const clientMcpKeys = allMcpKeys.filter(k => k.startsWith(`mcp:cli:${client_id}:`))
|
|
272
|
+
const clientSpecKeys = allSpecKeys.filter(k => k.startsWith(`spec:cli:${client_id}:`))
|
|
273
|
+
|
|
274
|
+
// Remove this client's existing resources
|
|
275
|
+
for (const key of clientMcpKeys) {
|
|
276
|
+
await storage.delete(key)
|
|
277
|
+
}
|
|
278
|
+
for (const key of clientSpecKeys) {
|
|
279
|
+
await storage.delete(key)
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// Store plugin metadata and register resources
|
|
283
|
+
const results = { synced: plugins.length, registered_mcp: 0, registered_specs: 0, errors: [] }
|
|
284
|
+
|
|
285
|
+
for (const plugin of plugins) {
|
|
286
|
+
if (!plugin.name || !plugin.server_resources) continue
|
|
287
|
+
|
|
288
|
+
try {
|
|
289
|
+
// Store plugin metadata with client_id
|
|
290
|
+
await storage.set(`plugin_client:${client_id}:${plugin.name}`, {
|
|
291
|
+
name: plugin.name,
|
|
292
|
+
server_resources: plugin.server_resources,
|
|
293
|
+
client_id: client_id,
|
|
294
|
+
synced_at: new Date()
|
|
295
|
+
})
|
|
296
|
+
|
|
297
|
+
// Register resources with client_id in key
|
|
298
|
+
const regResults = await registerPluginResources(plugin.name, plugin.server_resources, "cli", client_id)
|
|
299
|
+
results.registered_mcp += regResults.mcp.length
|
|
300
|
+
results.registered_specs += regResults.specs.length
|
|
301
|
+
results.errors.push(...regResults.errors)
|
|
302
|
+
} catch (err) {
|
|
303
|
+
results.errors.push(`Failed to sync plugin ${plugin.name}: ${err.message}`)
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
await bumpVersion()
|
|
308
|
+
res.json({ ok: true, ...results })
|
|
309
|
+
} catch (err) {
|
|
310
|
+
handleError(res, err)
|
|
311
|
+
}
|
|
312
|
+
})
|
|
313
|
+
|
|
314
|
+
// List clients endpoint
|
|
315
|
+
router.get("/clients", async (req, res) => {
|
|
316
|
+
try {
|
|
317
|
+
const storage = getStorage()
|
|
318
|
+
const clientKeys = await storage.listKeys("client:")
|
|
319
|
+
const clients = []
|
|
320
|
+
|
|
321
|
+
for (const key of clientKeys) {
|
|
322
|
+
try {
|
|
323
|
+
const client = await storage.get(key)
|
|
324
|
+
if (client && client.client_id) {
|
|
325
|
+
clients.push(client)
|
|
326
|
+
}
|
|
327
|
+
} catch (err) {
|
|
328
|
+
// Skip if client read fails
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// Sort by last_seen descending
|
|
333
|
+
clients.sort((a, b) => new Date(b.last_seen) - new Date(a.last_seen))
|
|
334
|
+
|
|
335
|
+
if (req.query.format !== "json" && req.accepts("html") && !req.xhr && !req.headers["x-requested-with"]) {
|
|
336
|
+
return res.render("clients", { clients })
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
res.json({ clients })
|
|
340
|
+
} catch (err) {
|
|
341
|
+
handleError(res, err)
|
|
342
|
+
}
|
|
343
|
+
})
|
|
344
|
+
|
|
262
345
|
module.exports = router
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
const { Router } = require("express");
|
|
2
|
+
const { getSettings, updateSettings } = require("../services/pluginsService");
|
|
3
|
+
|
|
4
|
+
const router = Router();
|
|
5
|
+
|
|
6
|
+
// Helper function to handle errors
|
|
7
|
+
function handleError(res, err) {
|
|
8
|
+
console.error(err);
|
|
9
|
+
const status = err.status || 500;
|
|
10
|
+
const error = err.error || { code: 500, type: "internal_error", message: err.message || "Internal server error" };
|
|
11
|
+
res.status(status).json({ error });
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// GET /settings - Render settings view
|
|
15
|
+
router.get("/settings", async (req, res) => {
|
|
16
|
+
try {
|
|
17
|
+
const settings = await getSettings()
|
|
18
|
+
if (req.query.format !== "json" && req.accepts("html") && !req.xhr && !req.headers["x-requested-with"]) {
|
|
19
|
+
return res.render("settings", { settings })
|
|
20
|
+
}
|
|
21
|
+
res.json(settings)
|
|
22
|
+
} catch (err) {
|
|
23
|
+
handleError(res, err)
|
|
24
|
+
}
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
// PUT /settings - Update settings
|
|
28
|
+
router.put("/settings", async (req, res) => {
|
|
29
|
+
try {
|
|
30
|
+
const settings = await updateSettings(req.body || {})
|
|
31
|
+
res.json({ ok: true, settings })
|
|
32
|
+
} catch (err) {
|
|
33
|
+
handleError(res, err)
|
|
34
|
+
}
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
// GET /api-keys - Render API keys view
|
|
38
|
+
router.get("/api-keys", async (req, res) => {
|
|
39
|
+
try {
|
|
40
|
+
const settings = await getSettings()
|
|
41
|
+
if (req.query.format !== "json" && req.accepts("html") && !req.xhr && !req.headers["x-requested-with"]) {
|
|
42
|
+
return res.render("api-keys", { settings })
|
|
43
|
+
}
|
|
44
|
+
res.json({ api_keys: settings.api_keys || [] })
|
|
45
|
+
} catch (err) {
|
|
46
|
+
handleError(res, err)
|
|
47
|
+
}
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
// POST /api-keys - Create API key
|
|
51
|
+
router.post("/api-keys", async (req, res) => {
|
|
52
|
+
try {
|
|
53
|
+
const { name } = req.body || {}
|
|
54
|
+
if (!name || typeof name !== "string") {
|
|
55
|
+
return res.status(400).json({ error: "name is required", type: "invalid_argument" })
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const settings = await getSettings()
|
|
59
|
+
const crypto = require("crypto")
|
|
60
|
+
const key = crypto.randomBytes(32).toString("hex")
|
|
61
|
+
|
|
62
|
+
const newApiKey = {
|
|
63
|
+
name: String(name).trim(),
|
|
64
|
+
key,
|
|
65
|
+
created_at: new Date().toISOString()
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const apiKeys = Array.isArray(settings.api_keys) ? settings.api_keys : []
|
|
69
|
+
apiKeys.push(newApiKey)
|
|
70
|
+
|
|
71
|
+
await updateSettings({ api_keys: apiKeys })
|
|
72
|
+
res.status(201).json(newApiKey)
|
|
73
|
+
} catch (err) {
|
|
74
|
+
handleError(res, err)
|
|
75
|
+
}
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
// DELETE /api-keys/:key - Delete API key
|
|
79
|
+
router.delete("/api-keys/:key", async (req, res) => {
|
|
80
|
+
try {
|
|
81
|
+
const keyToDelete = req.params.key
|
|
82
|
+
const settings = await getSettings()
|
|
83
|
+
const apiKeys = Array.isArray(settings.api_keys) ? settings.api_keys : []
|
|
84
|
+
const filtered = apiKeys.filter(k => k.key !== keyToDelete)
|
|
85
|
+
|
|
86
|
+
await updateSettings({ api_keys: filtered })
|
|
87
|
+
res.json({ ok: true })
|
|
88
|
+
} catch (err) {
|
|
89
|
+
handleError(res, err)
|
|
90
|
+
}
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
module.exports = router;
|
package/server/routes/specs.js
CHANGED
|
@@ -28,9 +28,12 @@ router.get("/", async (req, res) => {
|
|
|
28
28
|
router.post("/", async (req, res) => {
|
|
29
29
|
try {
|
|
30
30
|
const storage = getStorage()
|
|
31
|
-
const { name, url, auth } = req.body
|
|
31
|
+
const { name, url, auth, pluginSource } = req.body
|
|
32
32
|
const key = `spec:${name}`
|
|
33
|
-
|
|
33
|
+
// Support both simple string auth and object auth
|
|
34
|
+
const authValue = auth || "none"
|
|
35
|
+
const doc = { _id: key, name, url, auth: authValue, createdAt: new Date() }
|
|
36
|
+
if (typeof pluginSource === "string") doc.pluginSource = pluginSource
|
|
34
37
|
await storage.set(key, doc)
|
|
35
38
|
await bumpVersion()
|
|
36
39
|
if (req.headers["content-type"]?.includes("urlencoded")) {
|
|
@@ -54,7 +57,9 @@ router.put("/:id", async (req, res) => {
|
|
|
54
57
|
await storage.delete(id)
|
|
55
58
|
}
|
|
56
59
|
|
|
57
|
-
|
|
60
|
+
// Support both simple string auth and object auth
|
|
61
|
+
const authValue = auth || "none"
|
|
62
|
+
const doc = { _id: newKey, name, url, auth: authValue }
|
|
58
63
|
await storage.set(newKey, doc)
|
|
59
64
|
await bumpVersion()
|
|
60
65
|
res.json({ ok: true })
|
|
@@ -65,6 +65,7 @@ async function listAdapters() {
|
|
|
65
65
|
const records = await Promise.all(keys.map(k => storage.get(k)))
|
|
66
66
|
return records
|
|
67
67
|
.filter(Boolean)
|
|
68
|
+
.filter(record => record.name) // Filter out records without name (e.g., package records)
|
|
68
69
|
.sort((a, b) => String(a.name || "").localeCompare(String(b.name || "")))
|
|
69
70
|
.map(toPublicAdapter)
|
|
70
71
|
}
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plugin Resource Service
|
|
3
|
+
* Handles registration and cleanup of server resources (MCP/OpenAPI) from plugins
|
|
4
|
+
*/
|
|
5
|
+
const { getStorage } = require("../storage/adapter")
|
|
6
|
+
|
|
7
|
+
async function registerPluginResources(pluginName, serverResources, origin = "cli", clientId = null) {
|
|
8
|
+
const storage = getStorage()
|
|
9
|
+
const results = { mcp: [], specs: [], errors: [] }
|
|
10
|
+
|
|
11
|
+
try {
|
|
12
|
+
// Register MCP resources
|
|
13
|
+
if (serverResources.mcp && Array.isArray(serverResources.mcp)) {
|
|
14
|
+
for (const mcpResource of serverResources.mcp) {
|
|
15
|
+
try {
|
|
16
|
+
if (!mcpResource.name || (!mcpResource.url && !mcpResource.command)) {
|
|
17
|
+
results.errors.push(`Invalid MCP resource: missing name or url/command`)
|
|
18
|
+
continue
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Build key with client_id if origin is cli
|
|
22
|
+
const key = origin === "cli" && clientId
|
|
23
|
+
? `mcp:${origin}:${clientId}:${pluginName}:${mcpResource.name}`
|
|
24
|
+
: `mcp:${origin}:${pluginName}:${mcpResource.name}`
|
|
25
|
+
|
|
26
|
+
const doc = {
|
|
27
|
+
_id: key,
|
|
28
|
+
name: key,
|
|
29
|
+
displayName: mcpResource.name,
|
|
30
|
+
pluginSource: pluginName,
|
|
31
|
+
origin: origin,
|
|
32
|
+
clientId: clientId || null,
|
|
33
|
+
createdAt: new Date()
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Copy MCP properties
|
|
37
|
+
if (mcpResource.url) doc.url = mcpResource.url
|
|
38
|
+
if (mcpResource.command) doc.command = mcpResource.command
|
|
39
|
+
if (mcpResource.args) doc.args = mcpResource.args
|
|
40
|
+
if (mcpResource.headers) doc.headers = mcpResource.headers
|
|
41
|
+
if (mcpResource.env) doc.env = mcpResource.env
|
|
42
|
+
if (mcpResource.timeout_ms) doc.timeout_ms = mcpResource.timeout_ms
|
|
43
|
+
if (mcpResource.stateful) doc.stateful = true
|
|
44
|
+
|
|
45
|
+
await storage.set(key, doc)
|
|
46
|
+
results.mcp.push(key)
|
|
47
|
+
} catch (err) {
|
|
48
|
+
results.errors.push(`Failed to register MCP ${mcpResource.name}: ${err.message}`)
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Register OpenAPI spec resources
|
|
54
|
+
if (serverResources.specs && Array.isArray(serverResources.specs)) {
|
|
55
|
+
for (const specResource of serverResources.specs) {
|
|
56
|
+
try {
|
|
57
|
+
if (!specResource.name || !specResource.url) {
|
|
58
|
+
results.errors.push(`Invalid spec resource: missing name or url`)
|
|
59
|
+
continue
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Build key with client_id if origin is cli
|
|
63
|
+
const key = origin === "cli" && clientId
|
|
64
|
+
? `spec:${origin}:${clientId}:${pluginName}:${specResource.name}`
|
|
65
|
+
: `spec:${origin}:${pluginName}:${specResource.name}`
|
|
66
|
+
|
|
67
|
+
const doc = {
|
|
68
|
+
_id: key,
|
|
69
|
+
name: key,
|
|
70
|
+
displayName: specResource.name,
|
|
71
|
+
url: specResource.url,
|
|
72
|
+
auth: specResource.auth || "none",
|
|
73
|
+
pluginSource: pluginName,
|
|
74
|
+
origin: origin,
|
|
75
|
+
clientId: clientId || null,
|
|
76
|
+
createdAt: new Date()
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
await storage.set(key, doc)
|
|
80
|
+
results.specs.push(key)
|
|
81
|
+
} catch (err) {
|
|
82
|
+
results.errors.push(`Failed to register spec ${specResource.name}: ${err.message}`)
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
} catch (err) {
|
|
87
|
+
results.errors.push(`Resource registration failed: ${err.message}`)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return results
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
async function unregisterPluginResources(pluginName, origin = "cli") {
|
|
94
|
+
const storage = getStorage()
|
|
95
|
+
const results = { mcp: [], specs: [], errors: [] }
|
|
96
|
+
|
|
97
|
+
try {
|
|
98
|
+
// Remove MCP resources by origin prefix
|
|
99
|
+
const allMcpKeys = await storage.listKeys("mcp:")
|
|
100
|
+
const targetMcpKeys = allMcpKeys.filter(k => k.startsWith(`mcp:${origin}:${pluginName}:`))
|
|
101
|
+
for (const key of targetMcpKeys) {
|
|
102
|
+
try {
|
|
103
|
+
await storage.delete(key)
|
|
104
|
+
results.mcp.push(key)
|
|
105
|
+
} catch (err) {
|
|
106
|
+
results.errors.push(`Failed to remove MCP ${key}: ${err.message}`)
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Remove spec resources by origin prefix
|
|
111
|
+
const allSpecKeys = await storage.listKeys("spec:")
|
|
112
|
+
const targetSpecKeys = allSpecKeys.filter(k => k.startsWith(`spec:${origin}:${pluginName}:`))
|
|
113
|
+
for (const key of targetSpecKeys) {
|
|
114
|
+
try {
|
|
115
|
+
await storage.delete(key)
|
|
116
|
+
results.specs.push(key)
|
|
117
|
+
} catch (err) {
|
|
118
|
+
results.errors.push(`Failed to remove spec ${key}: ${err.message}`)
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
} catch (err) {
|
|
122
|
+
results.errors.push(`Resource cleanup failed: ${err.message}`)
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return results
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
async function registerAllPluginResources() {
|
|
129
|
+
const storage = getStorage()
|
|
130
|
+
const results = { plugins: {}, totalErrors: 0, registeredMcp: 0, registeredSpecs: 0 }
|
|
131
|
+
|
|
132
|
+
// Register resources from client plugins (plugin_client:{client-id}:*)
|
|
133
|
+
const clientPluginKeys = await storage.listKeys("plugin_client:")
|
|
134
|
+
for (const key of clientPluginKeys) {
|
|
135
|
+
try {
|
|
136
|
+
const plugin = await storage.get(key)
|
|
137
|
+
if (plugin && plugin.server_resources && plugin.client_id) {
|
|
138
|
+
const pluginResults = await registerPluginResources(plugin.name, plugin.server_resources, "cli", plugin.client_id)
|
|
139
|
+
results.plugins[plugin.name] = pluginResults
|
|
140
|
+
results.totalErrors += pluginResults.errors.length
|
|
141
|
+
results.registeredMcp += pluginResults.mcp.length
|
|
142
|
+
results.registeredSpecs += pluginResults.specs.length
|
|
143
|
+
}
|
|
144
|
+
} catch (err) {
|
|
145
|
+
results.totalErrors++
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Register resources from server plugins (plugin_server:*)
|
|
150
|
+
const serverPluginKeys = await storage.listKeys("plugin_server:")
|
|
151
|
+
for (const key of serverPluginKeys) {
|
|
152
|
+
try {
|
|
153
|
+
const plugin = await storage.get(key)
|
|
154
|
+
if (plugin && plugin.manifest && plugin.manifest.server_resources) {
|
|
155
|
+
const pluginResults = await registerPluginResources(plugin.name, plugin.manifest.server_resources, "server")
|
|
156
|
+
results.plugins[plugin.name] = pluginResults
|
|
157
|
+
results.totalErrors += pluginResults.errors.length
|
|
158
|
+
results.registeredMcp += pluginResults.mcp.length
|
|
159
|
+
results.registeredSpecs += pluginResults.specs.length
|
|
160
|
+
}
|
|
161
|
+
} catch (err) {
|
|
162
|
+
results.totalErrors++
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
return results
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
async function syncPluginResources() {
|
|
170
|
+
const storage = getStorage()
|
|
171
|
+
|
|
172
|
+
// Get all currently registered plugin resources
|
|
173
|
+
const allMcpKeys = await storage.listKeys("mcp:")
|
|
174
|
+
const allSpecKeys = await storage.listKeys("spec:")
|
|
175
|
+
|
|
176
|
+
// Filter for plugin resources (contain origin prefix)
|
|
177
|
+
const pluginMcpKeys = allMcpKeys.filter(k => k.split(':').length > 2)
|
|
178
|
+
const pluginSpecKeys = allSpecKeys.filter(k => k.split(':').length > 2)
|
|
179
|
+
|
|
180
|
+
// Build set of expected resources from server storage (plugin_client:* and plugin_server:*)
|
|
181
|
+
const expectedMcpKeys = new Set()
|
|
182
|
+
const expectedSpecKeys = new Set()
|
|
183
|
+
|
|
184
|
+
const clientPluginKeys = await storage.listKeys("plugin_client:")
|
|
185
|
+
const serverPluginKeys = await storage.listKeys("plugin_server:")
|
|
186
|
+
|
|
187
|
+
// Read client plugins
|
|
188
|
+
for (const key of clientPluginKeys) {
|
|
189
|
+
try {
|
|
190
|
+
const plugin = await storage.get(key)
|
|
191
|
+
if (plugin && plugin.server_resources && plugin.client_id) {
|
|
192
|
+
if (plugin.server_resources.mcp) {
|
|
193
|
+
for (const mcp of plugin.server_resources.mcp) {
|
|
194
|
+
expectedMcpKeys.add(`mcp:cli:${plugin.client_id}:${plugin.name}:${mcp.name}`)
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
if (plugin.server_resources.specs) {
|
|
198
|
+
for (const spec of plugin.server_resources.specs) {
|
|
199
|
+
expectedSpecKeys.add(`spec:cli:${plugin.client_id}:${plugin.name}:${spec.name}`)
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
} catch (err) {
|
|
204
|
+
// Skip if plugin read fails
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// Read server plugins
|
|
209
|
+
for (const key of serverPluginKeys) {
|
|
210
|
+
try {
|
|
211
|
+
const plugin = await storage.get(key)
|
|
212
|
+
if (plugin && plugin.manifest && plugin.manifest.server_resources) {
|
|
213
|
+
if (plugin.manifest.server_resources.mcp) {
|
|
214
|
+
for (const mcp of plugin.manifest.server_resources.mcp) {
|
|
215
|
+
expectedMcpKeys.add(`mcp:server:${plugin.name}:${mcp.name}`)
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
if (plugin.manifest.server_resources.specs) {
|
|
219
|
+
for (const spec of plugin.manifest.server_resources.specs) {
|
|
220
|
+
expectedSpecKeys.add(`spec:server:${plugin.name}:${spec.name}`)
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
} catch (err) {
|
|
225
|
+
// Skip if plugin read fails
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// Remove orphaned resources (no longer in any installed plugin)
|
|
230
|
+
const cleanupResults = { removedMcp: [], removedSpecs: [], errors: [] }
|
|
231
|
+
|
|
232
|
+
for (const key of pluginMcpKeys) {
|
|
233
|
+
if (!expectedMcpKeys.has(key)) {
|
|
234
|
+
try {
|
|
235
|
+
await storage.delete(key)
|
|
236
|
+
cleanupResults.removedMcp.push(key)
|
|
237
|
+
} catch (err) {
|
|
238
|
+
cleanupResults.errors.push(`Failed to remove orphaned MCP ${key}: ${err.message}`)
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
for (const key of pluginSpecKeys) {
|
|
244
|
+
if (!expectedSpecKeys.has(key)) {
|
|
245
|
+
try {
|
|
246
|
+
await storage.delete(key)
|
|
247
|
+
cleanupResults.removedSpecs.push(key)
|
|
248
|
+
} catch (err) {
|
|
249
|
+
cleanupResults.errors.push(`Failed to remove orphaned spec ${key}: ${err.message}`)
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
return cleanupResults
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
module.exports = {
|
|
258
|
+
registerPluginResources,
|
|
259
|
+
unregisterPluginResources,
|
|
260
|
+
registerAllPluginResources,
|
|
261
|
+
syncPluginResources
|
|
262
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const crypto = require("crypto")
|
|
2
2
|
const { getStorage } = require("../storage/adapter")
|
|
3
3
|
const { bumpVersion } = require("./configService")
|
|
4
|
+
const { registerPluginResources, unregisterPluginResources } = require("./pluginResourceService")
|
|
4
5
|
|
|
5
6
|
const PLUGIN_PREFIX = "plugin_server:"
|
|
6
7
|
const SETTINGS_KEY = "settings:plugins"
|
|
@@ -18,6 +19,8 @@ function defaultSettings() {
|
|
|
18
19
|
max_zip_mb: 10,
|
|
19
20
|
default_hooks_policy: "deny",
|
|
20
21
|
admin_mode_enabled: false,
|
|
22
|
+
public_access: true,
|
|
23
|
+
api_keys: [],
|
|
21
24
|
}
|
|
22
25
|
}
|
|
23
26
|
|
|
@@ -119,6 +122,15 @@ async function updateSettings(patch = {}) {
|
|
|
119
122
|
if (patch.admin_mode_enabled !== undefined) {
|
|
120
123
|
next.admin_mode_enabled = patch.admin_mode_enabled === true || patch.admin_mode_enabled === "true"
|
|
121
124
|
}
|
|
125
|
+
if (patch.public_access !== undefined) {
|
|
126
|
+
next.public_access = patch.public_access === true || patch.public_access === "true"
|
|
127
|
+
}
|
|
128
|
+
if (patch.api_keys !== undefined) {
|
|
129
|
+
if (!Array.isArray(patch.api_keys)) {
|
|
130
|
+
throw invalid("api_keys must be an array")
|
|
131
|
+
}
|
|
132
|
+
next.api_keys = patch.api_keys
|
|
133
|
+
}
|
|
122
134
|
const storage = getStorage()
|
|
123
135
|
await storage.set(SETTINGS_KEY, next)
|
|
124
136
|
return next
|
|
@@ -194,6 +206,12 @@ async function upsertJsonPlugin(payload) {
|
|
|
194
206
|
}
|
|
195
207
|
const storage = getStorage()
|
|
196
208
|
await storage.set(pluginKey(common.name), next)
|
|
209
|
+
|
|
210
|
+
// Register server resources if present
|
|
211
|
+
if (manifest.server_resources) {
|
|
212
|
+
await registerPluginResources(common.name, manifest.server_resources, "server")
|
|
213
|
+
}
|
|
214
|
+
|
|
197
215
|
await bumpVersion()
|
|
198
216
|
return toPublicPlugin(next)
|
|
199
217
|
}
|
|
@@ -233,6 +251,12 @@ async function upsertZipPlugin(payload) {
|
|
|
233
251
|
}
|
|
234
252
|
const storage = getStorage()
|
|
235
253
|
await storage.set(pluginKey(common.name), next)
|
|
254
|
+
|
|
255
|
+
// Register server resources if present
|
|
256
|
+
if (manifest.server_resources) {
|
|
257
|
+
await registerPluginResources(common.name, manifest.server_resources, "server")
|
|
258
|
+
}
|
|
259
|
+
|
|
236
260
|
await bumpVersion()
|
|
237
261
|
return toPublicPlugin(next)
|
|
238
262
|
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"openapi": "3.0.0",
|
|
3
|
+
"info": {
|
|
4
|
+
"title": "Superbackend API",
|
|
5
|
+
"version": "1.0.0"
|
|
6
|
+
},
|
|
7
|
+
"servers": [
|
|
8
|
+
{
|
|
9
|
+
"url": "https://superbackend.coolify.intrane.fr"
|
|
10
|
+
}
|
|
11
|
+
],
|
|
12
|
+
"paths": {
|
|
13
|
+
"/health": {
|
|
14
|
+
"get": {
|
|
15
|
+
"operationId": "getHealth",
|
|
16
|
+
"summary": "Health check endpoint",
|
|
17
|
+
"description": "Returns the health status of the backend service",
|
|
18
|
+
"responses": {
|
|
19
|
+
"200": {
|
|
20
|
+
"description": "Health status",
|
|
21
|
+
"content": {
|
|
22
|
+
"application/json": {
|
|
23
|
+
"schema": {
|
|
24
|
+
"type": "object",
|
|
25
|
+
"properties": {
|
|
26
|
+
"status": {
|
|
27
|
+
"type": "string",
|
|
28
|
+
"example": "ok"
|
|
29
|
+
},
|
|
30
|
+
"mode": {
|
|
31
|
+
"type": "string",
|
|
32
|
+
"example": "middleware"
|
|
33
|
+
},
|
|
34
|
+
"database": {
|
|
35
|
+
"type": "string",
|
|
36
|
+
"example": "connected"
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|