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
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @name test-cli-adapter
|
|
3
|
+
* @description Test adapter for CLI
|
|
4
|
+
* @context cli
|
|
5
|
+
* @timeout 30000
|
|
6
|
+
* @memory 128
|
|
7
|
+
* @network false
|
|
8
|
+
* @updated 2026-04-27T14:40:52.449Z
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
async function execute(cmd, flags, context) {
|
|
12
|
+
return {
|
|
13
|
+
ok: true,
|
|
14
|
+
message: "Hello from test adapter",
|
|
15
|
+
command: cmd.command
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
module.exports = { execute }
|
package/cli/adapters/mcp.js
CHANGED
|
@@ -220,7 +220,7 @@ async function execute(cmd, flags, context) {
|
|
|
220
220
|
|
|
221
221
|
const input = {};
|
|
222
222
|
for (const [k, v] of Object.entries(flags)) {
|
|
223
|
-
if (!["human", "json", "compact"].includes(k)) {
|
|
223
|
+
if (!["human", "json", "compact", "stream"].includes(k)) {
|
|
224
224
|
input[k] = v;
|
|
225
225
|
}
|
|
226
226
|
}
|
|
@@ -271,11 +271,35 @@ async function execute(cmd, flags, context) {
|
|
|
271
271
|
);
|
|
272
272
|
}
|
|
273
273
|
|
|
274
|
+
const result = await callHttpMcpTool(config, toolName, input, flags.stream === true);
|
|
275
|
+
return result;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
async function callHttpMcpTool(config, toolName, input, streamMode = false) {
|
|
274
279
|
const toolUrl = config.url.replace(/\/+$/, "");
|
|
280
|
+
|
|
281
|
+
// Try SSE first, then fallback to simple HTTP
|
|
282
|
+
const headers = {
|
|
283
|
+
"Content-Type": "application/json",
|
|
284
|
+
"Accept": "text/event-stream, application/json",
|
|
285
|
+
...(config.headers || {}),
|
|
286
|
+
};
|
|
287
|
+
|
|
288
|
+
// Use JSON-RPC format for MCP protocol compatibility
|
|
289
|
+
const body = JSON.stringify({
|
|
290
|
+
jsonrpc: "2.0",
|
|
291
|
+
method: "tools/call",
|
|
292
|
+
params: {
|
|
293
|
+
name: toolName,
|
|
294
|
+
arguments: input,
|
|
295
|
+
},
|
|
296
|
+
id: 1,
|
|
297
|
+
});
|
|
298
|
+
|
|
275
299
|
const tr = await fetch(`${toolUrl}/tool`, {
|
|
276
300
|
method: "POST",
|
|
277
|
-
headers
|
|
278
|
-
body
|
|
301
|
+
headers,
|
|
302
|
+
body,
|
|
279
303
|
});
|
|
280
304
|
|
|
281
305
|
if (!tr.ok) {
|
|
@@ -290,7 +314,69 @@ async function execute(cmd, flags, context) {
|
|
|
290
314
|
);
|
|
291
315
|
}
|
|
292
316
|
|
|
317
|
+
const contentType = tr.headers.get("content-type") || "";
|
|
318
|
+
|
|
319
|
+
// Check if response is SSE
|
|
320
|
+
if (contentType.includes("text/event-stream")) {
|
|
321
|
+
return parseSseResponse(tr, streamMode);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// Standard JSON response
|
|
293
325
|
return tr.json();
|
|
294
326
|
}
|
|
295
327
|
|
|
328
|
+
async function parseSseResponse(response, streamMode) {
|
|
329
|
+
const body = await response.text();
|
|
330
|
+
const lines = body.split("\n");
|
|
331
|
+
const events = [];
|
|
332
|
+
let currentEvent = null;
|
|
333
|
+
|
|
334
|
+
for (const line of lines) {
|
|
335
|
+
if (line.startsWith("event: ")) {
|
|
336
|
+
currentEvent = { event: line.substring(7), data: "" };
|
|
337
|
+
} else if (line.startsWith("data: ")) {
|
|
338
|
+
if (currentEvent) {
|
|
339
|
+
currentEvent.data += line.substring(6);
|
|
340
|
+
}
|
|
341
|
+
} else if (line === "" && currentEvent) {
|
|
342
|
+
// End of event
|
|
343
|
+
try {
|
|
344
|
+
currentEvent.parsed = JSON.parse(currentEvent.data);
|
|
345
|
+
events.push(currentEvent);
|
|
346
|
+
|
|
347
|
+
// In stream mode, we could yield here
|
|
348
|
+
if (streamMode) {
|
|
349
|
+
// For now, just accumulate - streaming support would need generator
|
|
350
|
+
}
|
|
351
|
+
} catch {
|
|
352
|
+
// Invalid JSON, skip
|
|
353
|
+
}
|
|
354
|
+
currentEvent = null;
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
// Handle final event if not terminated properly
|
|
359
|
+
if (currentEvent && currentEvent.data) {
|
|
360
|
+
try {
|
|
361
|
+
currentEvent.parsed = JSON.parse(currentEvent.data);
|
|
362
|
+
events.push(currentEvent);
|
|
363
|
+
} catch {
|
|
364
|
+
// Invalid JSON, skip
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
// Find the result event (usually the last one with content)
|
|
369
|
+
const resultEvent = events.find(e => e.event === "message" || e.parsed?.content);
|
|
370
|
+
|
|
371
|
+
if (resultEvent && resultEvent.parsed) {
|
|
372
|
+
return resultEvent.parsed;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
// Fallback: return all parsed events
|
|
376
|
+
return {
|
|
377
|
+
events: events.map(e => e.parsed || e.data),
|
|
378
|
+
stream: streamMode,
|
|
379
|
+
};
|
|
380
|
+
}
|
|
381
|
+
|
|
296
382
|
module.exports = { execute };
|
package/cli/adapters/openapi.js
CHANGED
|
@@ -98,6 +98,65 @@ function buildUrl(baseUrl, pathStr, method, operation, flags) {
|
|
|
98
98
|
return { url: fullUrl, method };
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
+
function resolveAuthValue(authConfig, flags) {
|
|
102
|
+
if (!authConfig) return null;
|
|
103
|
+
|
|
104
|
+
const { argName, envVar } = authConfig;
|
|
105
|
+
|
|
106
|
+
// Check command arguments first (highest priority)
|
|
107
|
+
if (argName && flags[argName] !== undefined) {
|
|
108
|
+
return flags[argName];
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Check environment variable
|
|
112
|
+
if (envVar && process.env[envVar] !== undefined) {
|
|
113
|
+
return process.env[envVar];
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function applyAuth(fetchOpts, authConfig, authValue) {
|
|
120
|
+
if (!authConfig || !authValue) return;
|
|
121
|
+
|
|
122
|
+
const { type, bearer, headerName, queryName, bodyName } = authConfig;
|
|
123
|
+
|
|
124
|
+
switch (type) {
|
|
125
|
+
case "bearer":
|
|
126
|
+
fetchOpts.headers["Authorization"] = `Bearer ${authValue}`;
|
|
127
|
+
break;
|
|
128
|
+
case "basic": {
|
|
129
|
+
const base64 = Buffer.from(authValue).toString("base64");
|
|
130
|
+
fetchOpts.headers["Authorization"] = `Basic ${base64}`;
|
|
131
|
+
break;
|
|
132
|
+
}
|
|
133
|
+
case "api-key":
|
|
134
|
+
if (headerName) {
|
|
135
|
+
fetchOpts.headers[headerName] = authValue;
|
|
136
|
+
} else if (queryName) {
|
|
137
|
+
// Add to URL query params
|
|
138
|
+
const url = new URL(fetchOpts.url || "");
|
|
139
|
+
url.searchParams.set(queryName, authValue);
|
|
140
|
+
fetchOpts.url = url.toString();
|
|
141
|
+
} else if (bodyName) {
|
|
142
|
+
// Add to body for POST/PUT/PATCH
|
|
143
|
+
if (fetchOpts.body) {
|
|
144
|
+
try {
|
|
145
|
+
const bodyObj = JSON.parse(fetchOpts.body);
|
|
146
|
+
bodyObj[bodyName] = authValue;
|
|
147
|
+
fetchOpts.body = JSON.stringify(bodyObj);
|
|
148
|
+
} catch {
|
|
149
|
+
// If body isn't JSON, can't add auth field
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
break;
|
|
154
|
+
default:
|
|
155
|
+
// Unknown auth type, ignore
|
|
156
|
+
break;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
101
160
|
async function execute(cmd, flags, context) {
|
|
102
161
|
const config = cmd.adapterConfig || {};
|
|
103
162
|
const specName = config.spec;
|
|
@@ -123,7 +182,7 @@ async function execute(cmd, flags, context) {
|
|
|
123
182
|
flags,
|
|
124
183
|
);
|
|
125
184
|
|
|
126
|
-
const fetchOpts = { method: httpMethod, headers: {} };
|
|
185
|
+
const fetchOpts = { method: httpMethod, headers: {}, url };
|
|
127
186
|
|
|
128
187
|
// Handle request body for POST/PUT/PATCH
|
|
129
188
|
if (["POST", "PUT", "PATCH"].includes(httpMethod)) {
|
|
@@ -137,7 +196,17 @@ async function execute(cmd, flags, context) {
|
|
|
137
196
|
fetchOpts.headers["Content-Type"] = "application/json";
|
|
138
197
|
}
|
|
139
198
|
|
|
140
|
-
|
|
199
|
+
// Apply auth if configured
|
|
200
|
+
const authConfig = config.auth;
|
|
201
|
+
const authValue = resolveAuthValue(authConfig, flags);
|
|
202
|
+
if (authConfig && authValue) {
|
|
203
|
+
applyAuth(fetchOpts, authConfig, authValue);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const finalUrl = fetchOpts.url || url;
|
|
207
|
+
delete fetchOpts.url;
|
|
208
|
+
|
|
209
|
+
const r = await fetch(finalUrl, fetchOpts);
|
|
141
210
|
if (!r.ok) {
|
|
142
211
|
const text = await r.text().catch(() => "");
|
|
143
212
|
throw Object.assign(
|
package/cli/config.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const fs = require("fs")
|
|
2
2
|
const path = require("path")
|
|
3
3
|
const os = require("os")
|
|
4
|
+
const crypto = require("crypto")
|
|
4
5
|
const {
|
|
5
6
|
getEffectivePluginCommands,
|
|
6
7
|
listInstalledPlugins,
|
|
@@ -13,6 +14,22 @@ const { spawnSync } = require("child_process")
|
|
|
13
14
|
const CACHE_DIR = path.join(os.homedir(), ".supercli")
|
|
14
15
|
const CACHE_FILE = path.join(CACHE_DIR, "config.json")
|
|
15
16
|
|
|
17
|
+
function getClientId() {
|
|
18
|
+
// Allow ENV override for custom identification
|
|
19
|
+
if (process.env.SUPERCLI_CLIENT_ID) {
|
|
20
|
+
return String(process.env.SUPERCLI_CLIENT_ID).trim()
|
|
21
|
+
}
|
|
22
|
+
// Minimal machine fingerprint
|
|
23
|
+
const pieces = [
|
|
24
|
+
os.hostname(),
|
|
25
|
+
os.type(),
|
|
26
|
+
os.release(),
|
|
27
|
+
os.arch(),
|
|
28
|
+
os.userInfo().username,
|
|
29
|
+
]
|
|
30
|
+
return crypto.createHash('sha256').update(pieces.join('|')).digest('hex')
|
|
31
|
+
}
|
|
32
|
+
|
|
16
33
|
function ensureCacheDir() {
|
|
17
34
|
if (!fs.existsSync(CACHE_DIR)) {
|
|
18
35
|
fs.mkdirSync(CACHE_DIR, { recursive: true })
|
|
@@ -331,6 +348,48 @@ async function syncServerPlugins(server) {
|
|
|
331
348
|
return diagnostics
|
|
332
349
|
}
|
|
333
350
|
|
|
351
|
+
async function syncClientPluginResources(server) {
|
|
352
|
+
const { readPluginsLock } = require("./plugins-store")
|
|
353
|
+
const lock = readPluginsLock()
|
|
354
|
+
const installed = lock.installed || {}
|
|
355
|
+
const clientId = getClientId()
|
|
356
|
+
|
|
357
|
+
// Filter plugins with server_resources
|
|
358
|
+
const pluginsWithResources = []
|
|
359
|
+
for (const [name, plugin] of Object.entries(installed)) {
|
|
360
|
+
if (plugin.server_resources && (plugin.server_resources.mcp || plugin.server_resources.specs)) {
|
|
361
|
+
pluginsWithResources.push({
|
|
362
|
+
name: name,
|
|
363
|
+
server_resources: plugin.server_resources
|
|
364
|
+
})
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
if (pluginsWithResources.length === 0) {
|
|
369
|
+
return { synced: 0, errors: [] }
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
try {
|
|
373
|
+
const r = await fetch(`${server}/api/plugins/client-resources`, {
|
|
374
|
+
method: "POST",
|
|
375
|
+
headers: { "Content-Type": "application/json" },
|
|
376
|
+
body: JSON.stringify({
|
|
377
|
+
client_id: clientId,
|
|
378
|
+
plugins: pluginsWithResources
|
|
379
|
+
})
|
|
380
|
+
})
|
|
381
|
+
|
|
382
|
+
if (!r.ok) {
|
|
383
|
+
throw new Error(`Failed to sync plugin resources: ${r.status} ${r.statusText}`)
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
const result = await r.json()
|
|
387
|
+
return { synced: pluginsWithResources.length, result }
|
|
388
|
+
} catch (err) {
|
|
389
|
+
return { synced: 0, errors: [err.message] }
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
|
|
334
393
|
async function syncConfig(server) {
|
|
335
394
|
const config = await fetchRemoteConfig(server)
|
|
336
395
|
|
|
@@ -370,6 +429,14 @@ async function syncConfig(server) {
|
|
|
370
429
|
}
|
|
371
430
|
}
|
|
372
431
|
|
|
432
|
+
// Sync client plugin resources
|
|
433
|
+
let clientResources = { synced: 0, errors: [] }
|
|
434
|
+
try {
|
|
435
|
+
clientResources = await syncClientPluginResources(server)
|
|
436
|
+
} catch (err) {
|
|
437
|
+
clientResources = { synced: 0, errors: [err.message] }
|
|
438
|
+
}
|
|
439
|
+
|
|
373
440
|
// Sync CLI-context adapters
|
|
374
441
|
let cliAdapters = { total: 0, synced: 0, failed: 0 }
|
|
375
442
|
try {
|
|
@@ -382,6 +449,7 @@ async function syncConfig(server) {
|
|
|
382
449
|
return {
|
|
383
450
|
...written,
|
|
384
451
|
server_plugins: serverPlugins,
|
|
452
|
+
client_resources: clientResources,
|
|
385
453
|
cli_adapters: cliAdapters,
|
|
386
454
|
}
|
|
387
455
|
}
|
|
@@ -549,4 +617,4 @@ async function showConfig() {
|
|
|
549
617
|
}
|
|
550
618
|
}
|
|
551
619
|
|
|
552
|
-
module.exports = { loadConfig, syncConfig, showConfig, setMcpServer, removeMcpServer, listMcpServers, upsertCommand, removeCommandsByNamespace }
|
|
620
|
+
module.exports = { loadConfig, syncConfig, showConfig, setMcpServer, removeMcpServer, listMcpServers, upsertCommand, removeCommandsByNamespace, syncClientPluginResources, getClientId }
|
package/cli/daemon.js
ADDED
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
const fs = require("fs");
|
|
2
|
+
const path = require("path");
|
|
3
|
+
const os = require("os");
|
|
4
|
+
const { spawn } = require("child_process");
|
|
5
|
+
|
|
6
|
+
const SUPERCLI_DIR = path.join(os.homedir(), ".supercli");
|
|
7
|
+
const LOGS_DIR = path.join(SUPERCLI_DIR, "logs");
|
|
8
|
+
const PID_FILE = path.join(SUPERCLI_DIR, "daemon.pid");
|
|
9
|
+
const HEARTBEAT_FILE = path.join(SUPERCLI_DIR, "daemon.heartbeat");
|
|
10
|
+
const CONFIG_FILE = path.join(SUPERCLI_DIR, "daemon.json");
|
|
11
|
+
|
|
12
|
+
// Default daemon configuration
|
|
13
|
+
const DEFAULT_CONFIG = {
|
|
14
|
+
enabled: true,
|
|
15
|
+
sync_interval_seconds: 60,
|
|
16
|
+
log_retention_hours: 24,
|
|
17
|
+
server_url: "http://localhost:3000",
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
// Ensure directories exist
|
|
21
|
+
function ensureDirectories() {
|
|
22
|
+
if (!fs.existsSync(SUPERCLI_DIR)) {
|
|
23
|
+
fs.mkdirSync(SUPERCLI_DIR, { recursive: true });
|
|
24
|
+
}
|
|
25
|
+
if (!fs.existsSync(LOGS_DIR)) {
|
|
26
|
+
fs.mkdirSync(LOGS_DIR, { recursive: true });
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Load daemon configuration
|
|
31
|
+
function loadConfig() {
|
|
32
|
+
ensureDirectories();
|
|
33
|
+
if (!fs.existsSync(CONFIG_FILE)) {
|
|
34
|
+
fs.writeFileSync(CONFIG_FILE, JSON.stringify(DEFAULT_CONFIG, null, 2));
|
|
35
|
+
return DEFAULT_CONFIG;
|
|
36
|
+
}
|
|
37
|
+
try {
|
|
38
|
+
return { ...DEFAULT_CONFIG, ...JSON.parse(fs.readFileSync(CONFIG_FILE, "utf8")) };
|
|
39
|
+
} catch (err) {
|
|
40
|
+
return DEFAULT_CONFIG;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Get daily log file path
|
|
45
|
+
function getLogFilePath() {
|
|
46
|
+
const date = new Date().toISOString().split("T")[0];
|
|
47
|
+
return path.join(LOGS_DIR, `jobs-${date}.jsonl`);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Write log entry to daily log file
|
|
51
|
+
function writeLog(logEntry) {
|
|
52
|
+
ensureDirectories();
|
|
53
|
+
const logFile = getLogFilePath();
|
|
54
|
+
const entry = { ...logEntry, synced: false };
|
|
55
|
+
fs.appendFileSync(logFile, JSON.stringify(entry) + "\n");
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Check if daemon is running
|
|
59
|
+
function isDaemonRunning() {
|
|
60
|
+
if (!fs.existsSync(PID_FILE)) {
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
try {
|
|
64
|
+
const pid = parseInt(fs.readFileSync(PID_FILE, "utf8").trim());
|
|
65
|
+
// Check if process is running
|
|
66
|
+
try {
|
|
67
|
+
process.kill(pid, 0);
|
|
68
|
+
return true;
|
|
69
|
+
} catch (e) {
|
|
70
|
+
// Process not found, clean up stale PID file
|
|
71
|
+
fs.unlinkSync(PID_FILE);
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
} catch (err) {
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Write heartbeat timestamp
|
|
80
|
+
function writeHeartbeat() {
|
|
81
|
+
ensureDirectories();
|
|
82
|
+
fs.writeFileSync(HEARTBEAT_FILE, new Date().toISOString());
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Get heartbeat timestamp
|
|
86
|
+
function getHeartbeat() {
|
|
87
|
+
if (!fs.existsSync(HEARTBEAT_FILE)) {
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
try {
|
|
91
|
+
return new Date(fs.readFileSync(HEARTBEAT_FILE, "utf8").trim());
|
|
92
|
+
} catch (err) {
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Start daemon process
|
|
98
|
+
function startDaemon() {
|
|
99
|
+
ensureDirectories();
|
|
100
|
+
if (isDaemonRunning()) {
|
|
101
|
+
return { ok: true, message: "Daemon already running" };
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const config = loadConfig();
|
|
105
|
+
if (!config.enabled) {
|
|
106
|
+
return { ok: false, message: "Daemon is disabled in config" };
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Spawn daemon as independent process
|
|
110
|
+
const daemonScript = path.join(__dirname, "daemon.js");
|
|
111
|
+
const nodePath = process.execPath;
|
|
112
|
+
|
|
113
|
+
const child = spawn(nodePath, [daemonScript, "--daemon"], {
|
|
114
|
+
detached: true,
|
|
115
|
+
stdio: "ignore",
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
child.unref(); // Allow parent to exit without waiting for child
|
|
119
|
+
|
|
120
|
+
return { ok: true, message: "Daemon started" };
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Stop daemon process
|
|
124
|
+
function stopDaemon() {
|
|
125
|
+
if (!isDaemonRunning()) {
|
|
126
|
+
return { ok: true, message: "Daemon not running" };
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
try {
|
|
130
|
+
const pid = parseInt(fs.readFileSync(PID_FILE, "utf8").trim());
|
|
131
|
+
process.kill(pid, "SIGTERM");
|
|
132
|
+
fs.unlinkSync(PID_FILE);
|
|
133
|
+
return { ok: true, message: "Daemon stopped" };
|
|
134
|
+
} catch (err) {
|
|
135
|
+
return { ok: false, message: `Failed to stop daemon: ${err.message}` };
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Get daemon status
|
|
140
|
+
function getDaemonStatus() {
|
|
141
|
+
const running = isDaemonRunning();
|
|
142
|
+
const heartbeat = getHeartbeat();
|
|
143
|
+
const config = loadConfig();
|
|
144
|
+
|
|
145
|
+
return {
|
|
146
|
+
running,
|
|
147
|
+
enabled: config.enabled,
|
|
148
|
+
heartbeat: heartbeat ? heartbeat.toISOString() : null,
|
|
149
|
+
pid: running ? parseInt(fs.readFileSync(PID_FILE, "utf8").trim()) : null,
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Sync logs to server
|
|
154
|
+
async function syncLogs(config) {
|
|
155
|
+
const logFile = getLogFilePath();
|
|
156
|
+
if (!fs.existsSync(logFile)) {
|
|
157
|
+
return { synced: 0, errors: 0 };
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const lines = fs.readFileSync(logFile, "utf8").split("\n").filter(Boolean);
|
|
161
|
+
const unsyncedLogs = [];
|
|
162
|
+
|
|
163
|
+
for (const line of lines) {
|
|
164
|
+
try {
|
|
165
|
+
const log = JSON.parse(line);
|
|
166
|
+
if (!log.synced) {
|
|
167
|
+
unsyncedLogs.push(log);
|
|
168
|
+
}
|
|
169
|
+
} catch (err) {
|
|
170
|
+
// Skip invalid lines
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (unsyncedLogs.length === 0) {
|
|
175
|
+
return { synced: 0, errors: 0 };
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
let synced = 0;
|
|
179
|
+
let errors = 0;
|
|
180
|
+
|
|
181
|
+
// Upload logs one at a time (server expects single job object)
|
|
182
|
+
for (const log of unsyncedLogs) {
|
|
183
|
+
try {
|
|
184
|
+
const headers = { "Content-Type": "application/json" };
|
|
185
|
+
const apiKey = process.env.SUPERCLI_API_KEY;
|
|
186
|
+
if (apiKey) {
|
|
187
|
+
headers["X-API-Key"] = apiKey;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const response = await fetch(`${config.server_url}/api/jobs`, {
|
|
191
|
+
method: "POST",
|
|
192
|
+
headers,
|
|
193
|
+
body: JSON.stringify({
|
|
194
|
+
command: log.command,
|
|
195
|
+
args: log.args,
|
|
196
|
+
status: log.status,
|
|
197
|
+
duration_ms: log.duration_ms,
|
|
198
|
+
timestamp: log.timestamp,
|
|
199
|
+
client_id: log.client_id,
|
|
200
|
+
}),
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
if (response.ok) {
|
|
204
|
+
synced++;
|
|
205
|
+
// Mark log as synced
|
|
206
|
+
updateSyncStatus(logFile, [log.timestamp]);
|
|
207
|
+
} else {
|
|
208
|
+
errors++;
|
|
209
|
+
}
|
|
210
|
+
} catch (err) {
|
|
211
|
+
errors++;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
return { synced, errors };
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// Update sync status in log file
|
|
219
|
+
function updateSyncStatus(logFile, timestamps) {
|
|
220
|
+
const lines = fs.readFileSync(logFile, "utf8").split("\n").filter(Boolean);
|
|
221
|
+
const timestampSet = new Set(timestamps);
|
|
222
|
+
const updatedLines = lines.map((line) => {
|
|
223
|
+
try {
|
|
224
|
+
const log = JSON.parse(line);
|
|
225
|
+
if (timestampSet.has(log.timestamp)) {
|
|
226
|
+
log.synced = true;
|
|
227
|
+
return JSON.stringify(log);
|
|
228
|
+
}
|
|
229
|
+
return line;
|
|
230
|
+
} catch (err) {
|
|
231
|
+
return line;
|
|
232
|
+
}
|
|
233
|
+
});
|
|
234
|
+
fs.writeFileSync(logFile, updatedLines.join("\n") + "\n");
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// Prune old logs
|
|
238
|
+
function pruneLogs(config) {
|
|
239
|
+
ensureDirectories();
|
|
240
|
+
const retentionMs = config.log_retention_hours * 60 * 60 * 1000;
|
|
241
|
+
const cutoff = Date.now() - retentionMs;
|
|
242
|
+
|
|
243
|
+
const files = fs.readdirSync(LOGS_DIR);
|
|
244
|
+
let pruned = 0;
|
|
245
|
+
|
|
246
|
+
for (const file of files) {
|
|
247
|
+
if (!file.startsWith("jobs-") || !file.endsWith(".jsonl")) {
|
|
248
|
+
continue;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
const filePath = path.join(LOGS_DIR, file);
|
|
252
|
+
const lines = fs.readFileSync(filePath, "utf8").split("\n").filter(Boolean);
|
|
253
|
+
const keptLines = [];
|
|
254
|
+
|
|
255
|
+
for (const line of lines) {
|
|
256
|
+
try {
|
|
257
|
+
const log = JSON.parse(line);
|
|
258
|
+
const logTime = new Date(log.timestamp).getTime();
|
|
259
|
+
// Keep if: not synced OR within retention period
|
|
260
|
+
if (!log.synced || logTime > cutoff) {
|
|
261
|
+
keptLines.push(line);
|
|
262
|
+
} else {
|
|
263
|
+
pruned++;
|
|
264
|
+
}
|
|
265
|
+
} catch (err) {
|
|
266
|
+
// Keep invalid lines
|
|
267
|
+
keptLines.push(line);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
if (keptLines.length === 0) {
|
|
272
|
+
// Delete empty file
|
|
273
|
+
fs.unlinkSync(filePath);
|
|
274
|
+
} else {
|
|
275
|
+
fs.writeFileSync(filePath, keptLines.join("\n") + "\n");
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
return { pruned };
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// Daemon main loop
|
|
283
|
+
async function daemonMain() {
|
|
284
|
+
const config = loadConfig();
|
|
285
|
+
console.log("Daemon started, syncing every", config.sync_interval_seconds, "seconds");
|
|
286
|
+
|
|
287
|
+
while (true) {
|
|
288
|
+
try {
|
|
289
|
+
writeHeartbeat();
|
|
290
|
+
const syncResult = await syncLogs(config);
|
|
291
|
+
const pruneResult = pruneLogs(config);
|
|
292
|
+
|
|
293
|
+
if (syncResult.synced > 0 || pruneResult.pruned > 0) {
|
|
294
|
+
console.log(`Synced ${syncResult.synced} logs, pruned ${pruneResult.pruned} logs`);
|
|
295
|
+
}
|
|
296
|
+
} catch (err) {
|
|
297
|
+
console.error("Daemon error:", err);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
await new Promise((resolve) => setTimeout(resolve, config.sync_interval_seconds * 1000));
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// CLI entry point
|
|
305
|
+
if (require.main === module) {
|
|
306
|
+
const args = process.argv.slice(2);
|
|
307
|
+
|
|
308
|
+
if (args.includes("--daemon")) {
|
|
309
|
+
// Run as daemon process
|
|
310
|
+
const pid = process.pid;
|
|
311
|
+
ensureDirectories();
|
|
312
|
+
fs.writeFileSync(PID_FILE, pid.toString());
|
|
313
|
+
daemonMain().catch((err) => {
|
|
314
|
+
console.error("Daemon fatal error:", err);
|
|
315
|
+
process.exit(1);
|
|
316
|
+
});
|
|
317
|
+
} else {
|
|
318
|
+
// CLI commands
|
|
319
|
+
const command = args[0];
|
|
320
|
+
|
|
321
|
+
switch (command) {
|
|
322
|
+
case "start":
|
|
323
|
+
console.log(JSON.stringify(startDaemon(), null, 2));
|
|
324
|
+
break;
|
|
325
|
+
case "stop":
|
|
326
|
+
console.log(JSON.stringify(stopDaemon(), null, 2));
|
|
327
|
+
break;
|
|
328
|
+
case "status":
|
|
329
|
+
console.log(JSON.stringify(getDaemonStatus(), null, 2));
|
|
330
|
+
break;
|
|
331
|
+
default:
|
|
332
|
+
console.log("Usage: node daemon.js {start|stop|status}");
|
|
333
|
+
process.exit(1);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
module.exports = {
|
|
339
|
+
startDaemon,
|
|
340
|
+
stopDaemon,
|
|
341
|
+
isDaemonRunning,
|
|
342
|
+
writeLog,
|
|
343
|
+
syncLogs,
|
|
344
|
+
pruneLogs,
|
|
345
|
+
writeHeartbeat,
|
|
346
|
+
getHeartbeat,
|
|
347
|
+
getDaemonStatus,
|
|
348
|
+
loadConfig,
|
|
349
|
+
};
|