superacli 1.1.20 → 1.1.21
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/cli/adapter-schema.js +1 -6
- package/cli/config.js +95 -0
- package/cli/executor.js +90 -11
- package/cli/server-command.js +395 -0
- package/cli/supercli.js +82 -1
- package/docs/adapters.md +159 -0
- package/docs/changelog-2026-04.html +15 -0
- package/docs/index.html +314 -98
- package/docs/meta-plugins.json +24424 -1247
- package/docs/plugins-examples.md +8 -8
- package/docs/plugins-how-to.md +0 -13
- package/docs/plugins.html +165 -61
- package/docs/server.md +50 -0
- package/graphify-out/GRAPH_REPORT.md +141 -136
- package/graphify-out/cache/1821936911d6eb3130499b6b45b8e6d760d6c05328131e115ac9825a5e3061cd.json +1 -0
- package/graphify-out/cache/20b0e1261b41286d639e7b039c8143682001981f607e26eb2a492a06f12c233a.json +1 -0
- package/graphify-out/cache/21dc011c08a0813ac33c0520c9cf131d8922e6007df7d9b51df740974fee5fb3.json +1 -0
- package/graphify-out/cache/22d3d43c24fdff310ab8445bd442fbcf4714872c6950f15b90777e5a8358dcd9.json +1 -0
- package/graphify-out/cache/2b8a3fbc23afe7bbc1a5ecb60d4074f39afdf6cb42120922176c280f4cc4ab4b.json +1 -0
- package/graphify-out/cache/37418b8b152d9c436d75361969c39bc74a76260ab7209ceabf455f80452f1654.json +1 -0
- package/graphify-out/cache/3b9aae57e4326f5862f8153c13b7d809a34c422996993e7b491c5ea3a66b5e04.json +1 -0
- package/graphify-out/cache/448f8e0c5e77cfb15fa625ed40ee0dc1622d9814d24905fe4c12c4d175131dc9.json +1 -0
- package/graphify-out/cache/47dd42009ceac28b7a7fec5586e565e7bc850c80be96b8329e1ed7c49efbe34f.json +1 -0
- package/graphify-out/cache/492712949d38efbe312c303fe717e5c93e1ecc8888e1ceb855750afc48caffdb.json +1 -0
- package/graphify-out/cache/494fd6af80b6a1bb7a2a5641c6ad11e2fbe2a67b5a2b3ff57fd04bb49ccad1c4.json +1 -0
- package/graphify-out/cache/4b31b0de656deff083119780df64d17205c45e72233987e1c495f2121c4923fd.json +1 -0
- package/graphify-out/cache/5a16fa8cdd687a39661321adb39186ce8cf924a79ef936e42b365380b5e3543e.json +1 -0
- package/graphify-out/cache/644fcb129e595cfdb4e3a9757ed9658230347f5ed0652be45a894528a2b6585b.json +1 -0
- package/graphify-out/cache/6b8762db2c5f51408cf49d915f3e44bcc25c7ebb32abc635307fdf2a672b370c.json +1 -0
- package/graphify-out/cache/6db88050060880e56dd26ec58f29e3b56a409ace48e31bc75f54cab7dc74e409.json +1 -0
- package/graphify-out/cache/7932d1b6a9751b2cb2f4dc6b4e5f9fd7dcc2cee82d7866ec3d655482010533c0.json +1 -0
- package/graphify-out/cache/8140a76ff5b70dfdd0d020aeffff27f87af39da5b237c35baa938fcc4b288ba5.json +1 -0
- package/graphify-out/cache/8876638878758733a412bd4ca4cbd22863322689fbba9f037d92abfa877ea3ab.json +1 -0
- package/graphify-out/cache/90445359c448f8135207ddcd378c8391b9274460064cf998f1ea2ad6f2173703.json +1 -0
- package/graphify-out/cache/b01f675bb3942d2e8f359d11c38d52f28487544f54cdef518c60b7d8dfd2b383.json +1 -0
- package/graphify-out/cache/bc353b40b348d54c730d8498af675f57998b14ae796b4f952cdc200147008f13.json +1 -0
- package/graphify-out/cache/c38b2e784b1c441121b422db0c79b4911fb525d2a3cfad10d3fd561a07900bd5.json +1 -0
- package/graphify-out/cache/ce9df9bb2d36ecf310a53fb34526168bdf25773edf0826c1f55accebfa3c012d.json +1 -0
- package/graphify-out/cache/ceaeb0f99851d313b29cb1440240db1cf835de425b9b0c1e58fa3035355c2b2b.json +1 -0
- package/graphify-out/cache/d8f3e78931a41cd0f367fdab1d11b634bbffb2c90280c75a80a1a53a6b2d6bc7.json +1 -0
- package/graphify-out/cache/f574e4ce3bcb1250ca88410519fb4fd26c5b6cd786039410880fbd6e606ab23f.json +1 -0
- package/graphify-out/cache/faa65636d5f779b8b1c790dde11cc6470b48125bf42a12a77651827fd1653fd7.json +1 -0
- package/graphify-out/graph.json +4173 -2115
- package/ideal-plugins.csv +124 -0
- package/package.json +3 -2
- package/plugin-scores.csv +999 -0
- package/plugins/beads/plugin.json +2 -2
- package/plugins/biome/install-guidance.json +11 -0
- package/plugins/biome/meta.json +13 -0
- package/plugins/biome/plugin.json +83 -0
- package/plugins/biome/skills/quickstart/SKILL.md +25 -0
- package/plugins/bore/install-guidance.json +11 -0
- package/plugins/bore/meta.json +11 -0
- package/plugins/bore/plugin.json +65 -0
- package/plugins/bore/skills/quickstart/SKILL.md +25 -0
- package/plugins/ccf-deadlines/install-guidance.json +11 -0
- package/plugins/ccf-deadlines/meta.json +11 -0
- package/plugins/ccf-deadlines/plugin.json +28 -0
- package/plugins/ccf-deadlines/skills/quickstart/SKILL.md +25 -0
- package/plugins/code2prompt/install-guidance.json +11 -0
- package/plugins/code2prompt/meta.json +12 -0
- package/plugins/code2prompt/plugin.json +46 -0
- package/plugins/code2prompt/skills/quickstart/SKILL.md +25 -0
- package/plugins/commiat/install-guidance.json +10 -0
- package/plugins/commiat/plugin.json +2 -2
- package/plugins/dua-cli/install-guidance.json +11 -0
- package/plugins/dua-cli/meta.json +11 -0
- package/plugins/dua-cli/plugin.json +57 -0
- package/plugins/dua-cli/skills/quickstart/SKILL.md +25 -0
- package/plugins/forever/install-guidance.json +11 -0
- package/plugins/forever/meta.json +11 -0
- package/plugins/forever/plugin.json +75 -0
- package/plugins/forever/skills/quickstart/SKILL.md +25 -0
- package/plugins/gitmoji-cli/install-guidance.json +11 -0
- package/plugins/gitmoji-cli/meta.json +11 -0
- package/plugins/gitmoji-cli/plugin.json +49 -0
- package/plugins/gitmoji-cli/skills/quickstart/SKILL.md +25 -0
- package/plugins/google-maps-scraper/plugin.json +1 -1
- package/plugins/gwc/install-guidance.json +10 -0
- package/plugins/gwc/plugin.json +2 -2
- package/plugins/quarto/install-guidance.json +11 -0
- package/plugins/quarto/meta.json +11 -0
- package/plugins/quarto/plugin.json +61 -0
- package/plugins/quarto/skills/quickstart/SKILL.md +25 -0
- package/plugins/readme-md-generator/install-guidance.json +11 -0
- package/plugins/readme-md-generator/meta.json +11 -0
- package/plugins/readme-md-generator/plugin.json +28 -0
- package/plugins/readme-md-generator/skills/quickstart/SKILL.md +25 -0
- package/plugins/twarc/install-guidance.json +11 -0
- package/plugins/twarc/meta.json +13 -0
- package/plugins/twarc/plugin.json +165 -0
- package/plugins/twarc/skills/quickstart/SKILL.md +61 -0
- package/plugins/vale/install-guidance.json +11 -0
- package/plugins/vale/meta.json +12 -0
- package/plugins/vale/plugin.json +50 -0
- package/plugins/vale/skills/quickstart/SKILL.md +25 -0
- package/plugins/volta/install-guidance.json +11 -0
- package/plugins/volta/meta.json +12 -0
- package/plugins/volta/plugin.json +75 -0
- package/plugins/volta/skills/quickstart/SKILL.md +25 -0
- package/plugins/wgcf/install-guidance.json +11 -0
- package/plugins/wgcf/meta.json +12 -0
- package/plugins/wgcf/plugin.json +45 -0
- package/plugins/wgcf/skills/quickstart/SKILL.md +25 -0
- package/scripts/analyze-plugins.js +130 -0
- package/scripts/enrich-meta-plugins.js +67 -0
- package/server/app.js +23 -1
- package/server/routes/adapters.js +356 -0
- package/server/routes/dashboard.js +113 -0
- package/server/routes/jobs.js +26 -0
- package/server/services/adaptersService.js +284 -0
- package/server/services/pluginsService.js +4 -0
- package/server/views/adapter-edit.ejs +226 -0
- package/server/views/adapter-packages.ejs +191 -0
- package/server/views/adapters.ejs +112 -0
- package/server/views/command-edit.ejs +48 -21
- package/server/views/commands.ejs +25 -22
- package/server/views/dashboard.ejs +196 -0
- package/server/views/layout.ejs +94 -14
- package/server/views/mcp.ejs +38 -35
- package/server/views/partials/head.ejs +88 -12
- package/server/views/plugins.ejs +9 -0
- package/server/views/specs.ejs +33 -30
- package/cli/adapters/builtin.js +0 -43
- package/index.html +0 -384
package/cli/adapter-schema.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const SUPPORTED_ADAPTERS = ["http", "openapi", "mcp", "process", "
|
|
1
|
+
const SUPPORTED_ADAPTERS = ["http", "openapi", "mcp", "process", "shell"]
|
|
2
2
|
|
|
3
3
|
function asInvalid(message, suggestions = []) {
|
|
4
4
|
return Object.assign(new Error(message), {
|
|
@@ -94,11 +94,6 @@ function validateAdapterConfig(cmd) {
|
|
|
94
94
|
return
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
-
if (adapterName === "builtin") {
|
|
98
|
-
if (!config.builtin || typeof config.builtin !== "string") throw asInvalid("Builtin adapter requires adapterConfig.builtin")
|
|
99
|
-
return
|
|
100
|
-
}
|
|
101
|
-
|
|
102
97
|
if (adapterName === "shell") {
|
|
103
98
|
if (config.unsafe !== true) {
|
|
104
99
|
throw asInvalid("Shell adapter requires adapterConfig.unsafe=true")
|
package/cli/config.js
CHANGED
|
@@ -369,10 +369,105 @@ async function syncConfig(server) {
|
|
|
369
369
|
reason: err.message,
|
|
370
370
|
}
|
|
371
371
|
}
|
|
372
|
+
|
|
373
|
+
// Sync CLI-context adapters
|
|
374
|
+
let cliAdapters = { total: 0, synced: 0, failed: 0 }
|
|
375
|
+
try {
|
|
376
|
+
cliAdapters = await syncCliAdapters(server)
|
|
377
|
+
} catch (err) {
|
|
378
|
+
cliAdapters = { total: 0, synced: 0, failed: 0, error: err.message }
|
|
379
|
+
}
|
|
380
|
+
|
|
372
381
|
const written = writeCache(normalizeConfig(config))
|
|
373
382
|
return {
|
|
374
383
|
...written,
|
|
375
384
|
server_plugins: serverPlugins,
|
|
385
|
+
cli_adapters: cliAdapters,
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
async function syncCliAdapters(server) {
|
|
390
|
+
const fs = require("fs")
|
|
391
|
+
const path = require("path")
|
|
392
|
+
|
|
393
|
+
// Fetch all adapters from server
|
|
394
|
+
const res = await fetch(`${server}/api/adapters`, {
|
|
395
|
+
headers: { Accept: "application/json" },
|
|
396
|
+
})
|
|
397
|
+
|
|
398
|
+
if (!res.ok) {
|
|
399
|
+
throw new Error(`Failed to fetch adapters: ${res.status}`)
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
const data = await res.json()
|
|
403
|
+
const adapters = data.adapters || []
|
|
404
|
+
|
|
405
|
+
// Filter CLI-context adapters
|
|
406
|
+
const cliAdapters = adapters.filter(a => a.execution_context === "cli")
|
|
407
|
+
const cliAdapterNames = new Set(cliAdapters.map(a => a.name))
|
|
408
|
+
|
|
409
|
+
// Ensure adapters directory exists
|
|
410
|
+
const adaptersDir = path.join(process.cwd(), ".supercli", "adapters")
|
|
411
|
+
fs.mkdirSync(adaptersDir, { recursive: true })
|
|
412
|
+
|
|
413
|
+
// Remove local adapter files that are no longer CLI-context
|
|
414
|
+
if (fs.existsSync(adaptersDir)) {
|
|
415
|
+
const localFiles = fs.readdirSync(adaptersDir)
|
|
416
|
+
for (const file of localFiles) {
|
|
417
|
+
if (file.endsWith('.js')) {
|
|
418
|
+
const adapterName = file.slice(0, -3) // Remove .js extension
|
|
419
|
+
if (!cliAdapterNames.has(adapterName)) {
|
|
420
|
+
// Remove local file since adapter is no longer CLI-context
|
|
421
|
+
const filePath = path.join(adaptersDir, file)
|
|
422
|
+
fs.unlinkSync(filePath)
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
let synced = 0
|
|
429
|
+
let failed = 0
|
|
430
|
+
|
|
431
|
+
for (const adapter of cliAdapters) {
|
|
432
|
+
try {
|
|
433
|
+
// Fetch adapter source
|
|
434
|
+
const sourceRes = await fetch(`${server}/api/adapters/${adapter.name}/source`, {
|
|
435
|
+
headers: { Accept: "application/json" },
|
|
436
|
+
})
|
|
437
|
+
|
|
438
|
+
if (!sourceRes.ok) {
|
|
439
|
+
throw new Error(`Failed to fetch source for ${adapter.name}`)
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
const sourceData = await sourceRes.json()
|
|
443
|
+
const source = sourceData.source || ""
|
|
444
|
+
|
|
445
|
+
// Write adapter file with metadata header
|
|
446
|
+
const adapterPath = path.join(adaptersDir, `${adapter.name}.js`)
|
|
447
|
+
const metadataHeader = `/**
|
|
448
|
+
* @name ${adapter.name}
|
|
449
|
+
* @description ${adapter.description || ""}
|
|
450
|
+
* @context cli
|
|
451
|
+
* @timeout ${adapter.timeout_ms || 30000}
|
|
452
|
+
* @memory ${adapter.memory_limit_mb || 128}
|
|
453
|
+
* @network ${adapter.allow_network || false}
|
|
454
|
+
* @updated ${adapter.updated_at}
|
|
455
|
+
*/
|
|
456
|
+
|
|
457
|
+
`
|
|
458
|
+
|
|
459
|
+
fs.writeFileSync(adapterPath, metadataHeader + source, "utf-8")
|
|
460
|
+
synced++
|
|
461
|
+
} catch (err) {
|
|
462
|
+
console.error(`Failed to sync adapter ${adapter.name}:`, err.message)
|
|
463
|
+
failed++
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
return {
|
|
468
|
+
total: cliAdapters.length,
|
|
469
|
+
synced,
|
|
470
|
+
failed,
|
|
376
471
|
}
|
|
377
472
|
}
|
|
378
473
|
|
package/cli/executor.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
const path = require("path")
|
|
2
|
+
const fs = require("fs")
|
|
3
|
+
const { NodeVM } = require("vm2")
|
|
2
4
|
const { validateAdapterConfig } = require("./adapter-schema")
|
|
3
5
|
|
|
4
6
|
// Adapter registry — lazy-loaded
|
|
@@ -7,7 +9,6 @@ const ADAPTERS = {
|
|
|
7
9
|
mcp: () => require("./adapters/mcp"),
|
|
8
10
|
http: () => require("./adapters/http"),
|
|
9
11
|
process: () => require("./adapters/process"),
|
|
10
|
-
builtin: () => require("./adapters/builtin"),
|
|
11
12
|
shell: () => require("./adapters/shell")
|
|
12
13
|
}
|
|
13
14
|
|
|
@@ -22,16 +23,7 @@ async function execute(cmd, flags, context) {
|
|
|
22
23
|
validateAdapterConfig(cmd)
|
|
23
24
|
|
|
24
25
|
if (!ADAPTERS[adapterName]) {
|
|
25
|
-
|
|
26
|
-
const custom = require(path.resolve("adapters", adapterName))
|
|
27
|
-
return custom.execute(cmd, flags, context)
|
|
28
|
-
} catch (e) {
|
|
29
|
-
throw Object.assign(new Error(`Unknown adapter: ${adapterName}`), {
|
|
30
|
-
code: 110,
|
|
31
|
-
type: "internal_error",
|
|
32
|
-
recoverable: false
|
|
33
|
-
})
|
|
34
|
-
}
|
|
26
|
+
return executeCustomAdapter(adapterName, cmd, flags, context)
|
|
35
27
|
}
|
|
36
28
|
|
|
37
29
|
const adapter = ADAPTERS[adapterName]()
|
|
@@ -119,4 +111,91 @@ async function executeWorkflow(workflow, flags, context, steps) {
|
|
|
119
111
|
return { workflow: workflow.namespace + "." + workflow.resource + "." + workflow.action, steps: results }
|
|
120
112
|
}
|
|
121
113
|
|
|
114
|
+
async function executeCustomAdapter(adapterName, cmd, flags, context) {
|
|
115
|
+
// Check if adapter exists in local .supercli/adapters/ directory
|
|
116
|
+
const localAdapterPath = path.join(process.cwd(), ".supercli", "adapters", `${adapterName}.js`)
|
|
117
|
+
|
|
118
|
+
if (fs.existsSync(localAdapterPath)) {
|
|
119
|
+
// Execute locally using vm2
|
|
120
|
+
return executeLocalAdapter(localAdapterPath, cmd, flags, context)
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// If server is available, delegate to server
|
|
124
|
+
if (context.server) {
|
|
125
|
+
const res = await fetch(`${context.server}/api/adapters/execute`, {
|
|
126
|
+
method: "POST",
|
|
127
|
+
headers: { "Content-Type": "application/json" },
|
|
128
|
+
body: JSON.stringify({ cmd, flags })
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
if (!res.ok) {
|
|
132
|
+
const err = await res.json()
|
|
133
|
+
const errorMessage = err.error?.message || err.error || `Adapter '${adapterName}' execution failed`
|
|
134
|
+
throw Object.assign(new Error(errorMessage), {
|
|
135
|
+
code: err.error?.code || 110,
|
|
136
|
+
type: err.error?.type || "internal_error",
|
|
137
|
+
recoverable: err.error?.recoverable || false
|
|
138
|
+
})
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return res.json()
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
throw Object.assign(new Error(`Adapter '${adapterName}' not found locally and server is unavailable`), {
|
|
145
|
+
code: 110,
|
|
146
|
+
type: "internal_error",
|
|
147
|
+
recoverable: false
|
|
148
|
+
})
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
async function executeLocalAdapter(adapterPath, cmd, flags, context) {
|
|
152
|
+
const source = fs.readFileSync(adapterPath, "utf-8")
|
|
153
|
+
|
|
154
|
+
// Extract metadata from adapter (first comment block if exists)
|
|
155
|
+
const metadataMatch = source.match(/\/\*\*?\s*\n([\s\S]*?)\n\s*\*\//)
|
|
156
|
+
const metadata = {}
|
|
157
|
+
if (metadataMatch) {
|
|
158
|
+
const metaText = metadataMatch[1]
|
|
159
|
+
const timeoutMatch = metaText.match(/@timeout\s+(\d+)/)
|
|
160
|
+
const memoryMatch = metaText.match(/@memory\s+(\d+)/)
|
|
161
|
+
const networkMatch = metaText.match(/@network\s+(true|false)/)
|
|
162
|
+
if (timeoutMatch) metadata.timeout = parseInt(timeoutMatch[1])
|
|
163
|
+
if (memoryMatch) metadata.memory = parseInt(memoryMatch[1])
|
|
164
|
+
if (networkMatch) metadata.network = networkMatch[1] === "true"
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const vm = new NodeVM({
|
|
168
|
+
timeout: metadata.timeout || 30000,
|
|
169
|
+
sandbox: {
|
|
170
|
+
console: {
|
|
171
|
+
log: (...args) => {},
|
|
172
|
+
error: (...args) => {},
|
|
173
|
+
warn: (...args) => {},
|
|
174
|
+
},
|
|
175
|
+
},
|
|
176
|
+
require: {
|
|
177
|
+
external: metadata.network !== false,
|
|
178
|
+
root: path.dirname(adapterPath),
|
|
179
|
+
},
|
|
180
|
+
memoryLimit: (metadata.memory || 128) * 1024 * 1024,
|
|
181
|
+
})
|
|
182
|
+
|
|
183
|
+
const script = `
|
|
184
|
+
${source}
|
|
185
|
+
module.exports = { execute }
|
|
186
|
+
`
|
|
187
|
+
|
|
188
|
+
const fn = vm.run(script, adapterPath)
|
|
189
|
+
|
|
190
|
+
if (typeof fn.execute !== "function") {
|
|
191
|
+
throw Object.assign(new Error(`Adapter must export an 'execute' function`), {
|
|
192
|
+
code: 85,
|
|
193
|
+
type: "invalid_argument",
|
|
194
|
+
recoverable: false
|
|
195
|
+
})
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
return fn.execute(cmd, flags, context)
|
|
199
|
+
}
|
|
200
|
+
|
|
122
201
|
module.exports = { execute }
|
|
@@ -0,0 +1,395 @@
|
|
|
1
|
+
const fs = require("fs")
|
|
2
|
+
const path = require("path")
|
|
3
|
+
|
|
4
|
+
const SERVER = process.env.SUPERCLI_SERVER
|
|
5
|
+
|
|
6
|
+
function getServerUrl() {
|
|
7
|
+
return SERVER ? SERVER.replace(/\/$/, "") : null
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
async function serverFetch(endpoint, options = {}) {
|
|
11
|
+
const base = getServerUrl()
|
|
12
|
+
if (!base) {
|
|
13
|
+
throw new Error("SUPERCLI_SERVER not set. Set it to your server URL.")
|
|
14
|
+
}
|
|
15
|
+
const url = `${base}${endpoint}`
|
|
16
|
+
const res = await fetch(url, {
|
|
17
|
+
...options,
|
|
18
|
+
headers: {
|
|
19
|
+
"Content-Type": "application/json",
|
|
20
|
+
...options.headers,
|
|
21
|
+
},
|
|
22
|
+
})
|
|
23
|
+
if (!res.ok) {
|
|
24
|
+
const text = await res.text()
|
|
25
|
+
throw new Error(`Server error ${res.status}: ${text}`)
|
|
26
|
+
}
|
|
27
|
+
return res.json()
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function readJsonFile(filePath) {
|
|
31
|
+
const fullPath = path.resolve(filePath)
|
|
32
|
+
if (!fs.existsSync(fullPath)) {
|
|
33
|
+
throw new Error(`File not found: ${filePath}`)
|
|
34
|
+
}
|
|
35
|
+
const content = fs.readFileSync(fullPath, "utf-8")
|
|
36
|
+
return JSON.parse(content)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function readFileBuffer(filePath) {
|
|
40
|
+
const fullPath = path.resolve(filePath)
|
|
41
|
+
if (!fs.existsSync(fullPath)) {
|
|
42
|
+
throw new Error(`File not found: ${filePath}`)
|
|
43
|
+
}
|
|
44
|
+
return fs.readFileSync(fullPath)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async function handleServerCommand(options) {
|
|
48
|
+
const { positional, flags, humanMode, output, outputHumanTable, outputError } = options
|
|
49
|
+
const resource = positional[1]
|
|
50
|
+
const action = positional[2]
|
|
51
|
+
const arg = positional[3]
|
|
52
|
+
|
|
53
|
+
if (!resource) {
|
|
54
|
+
outputError({ code: 85, type: "invalid_argument", message: "Usage: supercli server <resource> <action> [args]", recoverable: false })
|
|
55
|
+
return true
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (!getServerUrl()) {
|
|
59
|
+
outputError({ code: 85, type: "invalid_argument", message: "SUPERCLI_SERVER not set", recoverable: false })
|
|
60
|
+
return true
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
try {
|
|
64
|
+
switch (resource) {
|
|
65
|
+
case "status":
|
|
66
|
+
return await handleStatus({ humanMode, output })
|
|
67
|
+
case "plugins":
|
|
68
|
+
return await handlePlugins({ action, arg, flags, humanMode, output, outputHumanTable })
|
|
69
|
+
case "mcp":
|
|
70
|
+
return await handleMcp({ action, arg, flags, humanMode, output, outputHumanTable })
|
|
71
|
+
case "commands":
|
|
72
|
+
return await handleCommands({ action, arg, flags, humanMode, output, outputHumanTable })
|
|
73
|
+
case "specs":
|
|
74
|
+
return await handleSpecs({ action, arg, flags, humanMode, output, outputHumanTable })
|
|
75
|
+
case "openapi":
|
|
76
|
+
return await handleOpenapi({ action, arg, flags, humanMode, output })
|
|
77
|
+
case "jobs":
|
|
78
|
+
return await handleJobs({ action, flags, humanMode, output, outputHumanTable })
|
|
79
|
+
default:
|
|
80
|
+
outputError({ code: 85, type: "invalid_argument", message: `Unknown server resource: ${resource}`, recoverable: false })
|
|
81
|
+
return true
|
|
82
|
+
}
|
|
83
|
+
} catch (err) {
|
|
84
|
+
outputError({ code: 500, type: "server_error", message: err.message, recoverable: false })
|
|
85
|
+
return true
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
async function handleStatus({ humanMode, output }) {
|
|
90
|
+
const settings = await serverFetch("/api/plugins/settings")
|
|
91
|
+
const result = {
|
|
92
|
+
admin_mode: settings.admin_mode_enabled === true,
|
|
93
|
+
server: getServerUrl(),
|
|
94
|
+
max_zip_mb: settings.max_zip_mb,
|
|
95
|
+
default_hooks_policy: settings.default_hooks_policy,
|
|
96
|
+
}
|
|
97
|
+
if (humanMode) {
|
|
98
|
+
console.log(`\n ⚡ Server Status\n`)
|
|
99
|
+
console.log(` Server: ${result.server}`)
|
|
100
|
+
console.log(` Admin Mode: ${result.admin_mode ? "ENABLED (⚠️ CLI modifications allowed)" : "disabled"}`)
|
|
101
|
+
console.log(` Max ZIP Size: ${result.max_zip_mb}MB`)
|
|
102
|
+
console.log(` Hooks Policy: ${result.default_hooks_policy}`)
|
|
103
|
+
console.log("")
|
|
104
|
+
} else {
|
|
105
|
+
output(result)
|
|
106
|
+
}
|
|
107
|
+
return true
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
async function handlePlugins({ action, arg, flags, humanMode, output, outputHumanTable }) {
|
|
111
|
+
if (action === "list") {
|
|
112
|
+
const data = await serverFetch("/api/plugins?format=json")
|
|
113
|
+
if (humanMode) {
|
|
114
|
+
console.log("\n ⚡ Server Plugins\n")
|
|
115
|
+
outputHumanTable(data.plugins, [
|
|
116
|
+
{ key: "name", label: "Name" },
|
|
117
|
+
{ key: "source_type", label: "Type" },
|
|
118
|
+
{ key: "enabled", label: "Enabled" },
|
|
119
|
+
{ key: "version", label: "Version" },
|
|
120
|
+
])
|
|
121
|
+
console.log("")
|
|
122
|
+
} else {
|
|
123
|
+
output(data)
|
|
124
|
+
}
|
|
125
|
+
return true
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (action === "add") {
|
|
129
|
+
let payload
|
|
130
|
+
const zipPath = flags["from-zip"]
|
|
131
|
+
const jsonPath = flags["from-json"]
|
|
132
|
+
|
|
133
|
+
if (zipPath) {
|
|
134
|
+
const buffer = readFileBuffer(zipPath)
|
|
135
|
+
const base64 = buffer.toString("base64")
|
|
136
|
+
const manifest = flags.manifest ? JSON.parse(flags.manifest) : {}
|
|
137
|
+
payload = {
|
|
138
|
+
source_type: "zip",
|
|
139
|
+
name: manifest.name || path.basename(zipPath, ".zip"),
|
|
140
|
+
version: manifest.version || "0.1.0",
|
|
141
|
+
manifest,
|
|
142
|
+
archive_base64: base64,
|
|
143
|
+
}
|
|
144
|
+
} else if (jsonPath) {
|
|
145
|
+
payload = readJsonFile(jsonPath)
|
|
146
|
+
payload.source_type = "json"
|
|
147
|
+
} else if (arg) {
|
|
148
|
+
payload = JSON.parse(arg)
|
|
149
|
+
payload.source_type = payload.source_type || "json"
|
|
150
|
+
} else {
|
|
151
|
+
throw new Error("Usage: supercli server plugins add '{json}' or --from-json <path> or --from-zip <path> --manifest '{json}'")
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const result = await serverFetch("/api/plugins", {
|
|
155
|
+
method: "POST",
|
|
156
|
+
body: JSON.stringify(payload),
|
|
157
|
+
})
|
|
158
|
+
output(result)
|
|
159
|
+
return true
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (action === "remove") {
|
|
163
|
+
if (!arg) {
|
|
164
|
+
throw new Error("Usage: supercli server plugins remove <name>")
|
|
165
|
+
}
|
|
166
|
+
const result = await serverFetch(`/api/plugins/${encodeURIComponent(arg)}`, {
|
|
167
|
+
method: "DELETE",
|
|
168
|
+
})
|
|
169
|
+
output(result)
|
|
170
|
+
return true
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
throw new Error(`Unknown plugins action: ${action}. Use: list, add, remove`)
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
async function handleMcp({ action, arg, flags, humanMode, output, outputHumanTable }) {
|
|
177
|
+
if (action === "list") {
|
|
178
|
+
const servers = await serverFetch("/api/mcp?format=json")
|
|
179
|
+
if (humanMode) {
|
|
180
|
+
console.log("\n ⚡ MCP Servers\n")
|
|
181
|
+
outputHumanTable(servers, [
|
|
182
|
+
{ key: "name", label: "Name" },
|
|
183
|
+
{ key: "url", label: "URL" },
|
|
184
|
+
{ key: "command", label: "Command" },
|
|
185
|
+
])
|
|
186
|
+
console.log("")
|
|
187
|
+
} else {
|
|
188
|
+
output({ servers })
|
|
189
|
+
}
|
|
190
|
+
return true
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
if (action === "add") {
|
|
194
|
+
let payload
|
|
195
|
+
if (flags["from-json"]) {
|
|
196
|
+
payload = readJsonFile(flags["from-json"])
|
|
197
|
+
} else if (arg) {
|
|
198
|
+
payload = JSON.parse(arg)
|
|
199
|
+
} else {
|
|
200
|
+
throw new Error("Usage: supercli server mcp add '{json}' or --from-json <path>")
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
const result = await serverFetch("/api/mcp", {
|
|
204
|
+
method: "POST",
|
|
205
|
+
body: JSON.stringify(payload),
|
|
206
|
+
})
|
|
207
|
+
output(result)
|
|
208
|
+
return true
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
if (action === "remove") {
|
|
212
|
+
if (!arg) {
|
|
213
|
+
throw new Error("Usage: supercli server mcp remove <name>")
|
|
214
|
+
}
|
|
215
|
+
const result = await serverFetch(`/api/mcp/${encodeURIComponent(arg)}`, {
|
|
216
|
+
method: "DELETE",
|
|
217
|
+
})
|
|
218
|
+
output(result)
|
|
219
|
+
return true
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
throw new Error(`Unknown mcp action: ${action}. Use: list, add, remove`)
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
async function handleCommands({ action, arg, flags, humanMode, output, outputHumanTable }) {
|
|
226
|
+
if (action === "list") {
|
|
227
|
+
const commands = await serverFetch("/api/commands?format=json")
|
|
228
|
+
if (humanMode) {
|
|
229
|
+
console.log("\n ⚡ Server Commands\n")
|
|
230
|
+
outputHumanTable(commands.map(c => ({
|
|
231
|
+
id: c._id,
|
|
232
|
+
namespace: c.namespace,
|
|
233
|
+
resource: c.resource,
|
|
234
|
+
action: c.action,
|
|
235
|
+
adapter: c.adapter,
|
|
236
|
+
})), [
|
|
237
|
+
{ key: "id", label: "ID" },
|
|
238
|
+
{ key: "namespace", label: "NS" },
|
|
239
|
+
{ key: "resource", label: "Resource" },
|
|
240
|
+
{ key: "action", label: "Action" },
|
|
241
|
+
{ key: "adapter", label: "Adapter" },
|
|
242
|
+
])
|
|
243
|
+
console.log("")
|
|
244
|
+
} else {
|
|
245
|
+
output({ commands })
|
|
246
|
+
}
|
|
247
|
+
return true
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
if (action === "add") {
|
|
251
|
+
let payload
|
|
252
|
+
if (flags["from-json"]) {
|
|
253
|
+
payload = readJsonFile(flags["from-json"])
|
|
254
|
+
} else if (arg) {
|
|
255
|
+
payload = JSON.parse(arg)
|
|
256
|
+
} else {
|
|
257
|
+
throw new Error("Usage: supercli server commands add '{json}' or --from-json <path>")
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
const result = await serverFetch("/api/commands", {
|
|
261
|
+
method: "POST",
|
|
262
|
+
body: JSON.stringify(payload),
|
|
263
|
+
})
|
|
264
|
+
output(result)
|
|
265
|
+
return true
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
if (action === "remove") {
|
|
269
|
+
if (!arg) {
|
|
270
|
+
throw new Error("Usage: supercli server commands remove <namespace.resource.action>")
|
|
271
|
+
}
|
|
272
|
+
const id = arg.startsWith("command:") ? arg : `command:${arg}`
|
|
273
|
+
const result = await serverFetch(`/api/commands/${encodeURIComponent(id)}`, {
|
|
274
|
+
method: "DELETE",
|
|
275
|
+
})
|
|
276
|
+
output(result)
|
|
277
|
+
return true
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
throw new Error(`Unknown commands action: ${action}. Use: list, add, remove`)
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
async function handleSpecs({ action, arg, flags, humanMode, output, outputHumanTable }) {
|
|
284
|
+
if (action === "list") {
|
|
285
|
+
const specs = await serverFetch("/api/specs?format=json")
|
|
286
|
+
if (humanMode) {
|
|
287
|
+
console.log("\n ⚡ OpenAPI Specs\n")
|
|
288
|
+
outputHumanTable(specs, [
|
|
289
|
+
{ key: "name", label: "Name" },
|
|
290
|
+
{ key: "url", label: "URL" },
|
|
291
|
+
{ key: "auth", label: "Auth" },
|
|
292
|
+
])
|
|
293
|
+
console.log("")
|
|
294
|
+
} else {
|
|
295
|
+
output({ specs })
|
|
296
|
+
}
|
|
297
|
+
return true
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
if (action === "add") {
|
|
301
|
+
let payload
|
|
302
|
+
if (flags["from-json"]) {
|
|
303
|
+
payload = readJsonFile(flags["from-json"])
|
|
304
|
+
} else if (arg) {
|
|
305
|
+
payload = JSON.parse(arg)
|
|
306
|
+
} else {
|
|
307
|
+
throw new Error("Usage: supercli server specs add '{json}' or --from-json <path>")
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
const result = await serverFetch("/api/specs", {
|
|
311
|
+
method: "POST",
|
|
312
|
+
body: JSON.stringify(payload),
|
|
313
|
+
})
|
|
314
|
+
output(result)
|
|
315
|
+
return true
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
if (action === "remove") {
|
|
319
|
+
if (!arg) {
|
|
320
|
+
throw new Error("Usage: supercli server specs remove <name>")
|
|
321
|
+
}
|
|
322
|
+
const id = arg.startsWith("spec:") ? arg : `spec:${arg}`
|
|
323
|
+
const result = await serverFetch(`/api/specs/${encodeURIComponent(id)}`, {
|
|
324
|
+
method: "DELETE",
|
|
325
|
+
})
|
|
326
|
+
output(result)
|
|
327
|
+
return true
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
throw new Error(`Unknown specs action: ${action}. Use: list, add, remove`)
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
async function handleOpenapi({ action, arg, flags, humanMode, output }) {
|
|
334
|
+
// OpenAPI specs are managed via specs endpoint
|
|
335
|
+
if (action === "add") {
|
|
336
|
+
let payload
|
|
337
|
+
if (flags["from-json"]) {
|
|
338
|
+
payload = readJsonFile(flags["from-json"])
|
|
339
|
+
} else if (arg) {
|
|
340
|
+
payload = JSON.parse(arg)
|
|
341
|
+
} else {
|
|
342
|
+
throw new Error("Usage: supercli server openapi add '{json}' or --from-json <path>")
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
const result = await serverFetch("/api/specs", {
|
|
346
|
+
method: "POST",
|
|
347
|
+
body: JSON.stringify(payload),
|
|
348
|
+
})
|
|
349
|
+
output(result)
|
|
350
|
+
return true
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
throw new Error(`Unknown openapi action: ${action}. Use: add`)
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
async function handleJobs({ action, flags, humanMode, output, outputHumanTable }) {
|
|
357
|
+
if (action === "list") {
|
|
358
|
+
const jobs = await serverFetch("/api/jobs?format=json")
|
|
359
|
+
if (humanMode) {
|
|
360
|
+
console.log("\n ⚡ Jobs\n")
|
|
361
|
+
outputHumanTable(jobs.slice(0, 20).map(j => ({
|
|
362
|
+
id: j._id,
|
|
363
|
+
command: j.command,
|
|
364
|
+
status: j.status,
|
|
365
|
+
timestamp: j.timestamp,
|
|
366
|
+
})), [
|
|
367
|
+
{ key: "id", label: "ID" },
|
|
368
|
+
{ key: "command", label: "Command" },
|
|
369
|
+
{ key: "status", label: "Status" },
|
|
370
|
+
{ key: "timestamp", label: "Timestamp" },
|
|
371
|
+
])
|
|
372
|
+
console.log("")
|
|
373
|
+
} else {
|
|
374
|
+
output({ jobs })
|
|
375
|
+
}
|
|
376
|
+
return true
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
if (action === "prune") {
|
|
380
|
+
const days = parseInt(flags["older-than"]) || 7
|
|
381
|
+
const result = await serverFetch(`/api/jobs?older_than=${days}`, {
|
|
382
|
+
method: "DELETE",
|
|
383
|
+
})
|
|
384
|
+
if (humanMode) {
|
|
385
|
+
console.log(`\n Pruned ${result.pruned} jobs older than ${result.older_than_days} days\n`)
|
|
386
|
+
} else {
|
|
387
|
+
output(result)
|
|
388
|
+
}
|
|
389
|
+
return true
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
throw new Error(`Unknown jobs action: ${action}. Use: list, prune`)
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
module.exports = { handleServerCommand }
|