superacli 1.1.2 → 1.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +10 -1
  3. package/__tests__/blogwatcher-plugin.test.js +157 -0
  4. package/__tests__/clix-plugin.test.js +143 -0
  5. package/__tests__/config.test.js +46 -1
  6. package/__tests__/himalaya-plugin.test.js +121 -0
  7. package/__tests__/mcp-adapter.test.js +79 -0
  8. package/__tests__/mcp-local.test.js +43 -1
  9. package/__tests__/mongosh-plugin.test.js +106 -0
  10. package/__tests__/mysql-plugin.test.js +94 -0
  11. package/__tests__/plugin-blogwatcher.test.js +55 -0
  12. package/__tests__/plugin-clix.test.js +51 -0
  13. package/__tests__/plugin-xurl.test.js +51 -0
  14. package/__tests__/server-config-service.test.js +8 -1
  15. package/__tests__/skills.test.js +26 -0
  16. package/__tests__/wacli-plugin.test.js +132 -0
  17. package/__tests__/xurl-plugin.test.js +176 -0
  18. package/cli/adapter-schema.js +7 -0
  19. package/cli/adapters/mcp.js +82 -20
  20. package/cli/config.js +65 -8
  21. package/cli/mcp-local.js +50 -4
  22. package/cli/plugin-install-guidance.js +100 -0
  23. package/cli/skills.js +55 -0
  24. package/cli/supercli.js +1 -1
  25. package/docs/features/adapters.md +6 -2
  26. package/docs/initial/mcp-local-mode.md +3 -0
  27. package/docs/skills-catalog.md +50 -0
  28. package/docs/supported-harnesses.md +20 -0
  29. package/package.json +2 -1
  30. package/plugins/blogwatcher/README.md +52 -0
  31. package/plugins/blogwatcher/plugin.json +195 -0
  32. package/plugins/blogwatcher/scripts/post-install.js +66 -0
  33. package/plugins/blogwatcher/scripts/post-uninstall.js +25 -0
  34. package/plugins/clix/README.md +44 -0
  35. package/plugins/clix/plugin.json +126 -0
  36. package/plugins/clix/scripts/post-install.js +66 -0
  37. package/plugins/clix/scripts/post-uninstall.js +25 -0
  38. package/plugins/himalaya/README.md +48 -0
  39. package/plugins/himalaya/plugin.json +157 -0
  40. package/plugins/mongosh/README.md +56 -0
  41. package/plugins/mongosh/plugin.json +88 -0
  42. package/plugins/mysql/README.md +48 -0
  43. package/plugins/mysql/plugin.json +64 -0
  44. package/plugins/plugins.json +63 -0
  45. package/plugins/wacli/README.md +52 -0
  46. package/plugins/wacli/plugin.json +260 -0
  47. package/plugins/xurl/README.md +52 -0
  48. package/plugins/xurl/plugin.json +239 -0
  49. package/plugins/xurl/scripts/post-install.js +66 -0
  50. package/plugins/xurl/scripts/post-uninstall.js +25 -0
  51. package/server/routes/mcp.js +30 -4
  52. package/server/services/configService.js +9 -1
  53. package/tests/test-blogwatcher-smoke.sh +48 -0
  54. package/tests/test-clix-smoke.sh +44 -0
  55. package/tests/test-himalaya-smoke.sh +47 -0
  56. package/tests/test-mcp-browser-use-smoke.sh +141 -0
  57. package/tests/test-mongosh-smoke.sh +40 -0
  58. package/tests/test-mysql-smoke.sh +37 -0
  59. package/tests/test-plugins-registry.js +35 -0
  60. package/tests/test-wacli-smoke.sh +46 -0
  61. package/tests/test-xurl-smoke.sh +46 -0
@@ -0,0 +1,66 @@
1
+ const { addProvider, syncCatalog } = require("../../../cli/skills-catalog")
2
+
3
+ const OWNER = "xdevplatform"
4
+ const REPO = "xurl"
5
+ const REF = "main"
6
+ const SOURCE_REPO = `https://github.com/${OWNER}/${REPO}`
7
+ const RAW_BASE_URL = `https://raw.githubusercontent.com/${OWNER}/${REPO}/${REF}`
8
+
9
+ const CATALOG_FILES = [
10
+ {
11
+ id: "root.skill",
12
+ name: "xurl Agent Skill",
13
+ path: "SKILL.md",
14
+ description: "Agent-oriented guidance for safe xurl usage, shortcut commands, and raw X API access.",
15
+ tags: ["agents", "x", "api"]
16
+ },
17
+ {
18
+ id: "root.readme",
19
+ name: "xurl Overview",
20
+ path: "README.md",
21
+ description: "Project overview, auth model, installation, and command examples for xurl.",
22
+ tags: ["overview", "x", "oauth"]
23
+ }
24
+ ]
25
+
26
+ function buildRemoteEntries() {
27
+ return CATALOG_FILES.map(file => ({
28
+ ...file,
29
+ source_url: `${RAW_BASE_URL}/${file.path}`
30
+ }))
31
+ }
32
+
33
+ function run() {
34
+ const entries = buildRemoteEntries()
35
+ addProvider({
36
+ name: "xurl",
37
+ type: "remote_static",
38
+ enabled: true,
39
+ source_repo: SOURCE_REPO,
40
+ ref: REF,
41
+ entries
42
+ })
43
+
44
+ const index = syncCatalog()
45
+ return {
46
+ provider: "xurl",
47
+ entries: entries.length,
48
+ synced_skills: Array.isArray(index.skills) ? index.skills.length : 0
49
+ }
50
+ }
51
+
52
+ if (require.main === module) {
53
+ try {
54
+ const result = run()
55
+ process.stdout.write(JSON.stringify(result))
56
+ } catch (err) {
57
+ process.stderr.write(err.message)
58
+ process.exit(1)
59
+ }
60
+ }
61
+
62
+ module.exports = {
63
+ CATALOG_FILES,
64
+ buildRemoteEntries,
65
+ run
66
+ }
@@ -0,0 +1,25 @@
1
+ const { removeProvider, syncCatalog } = require("../../../cli/skills-catalog")
2
+
3
+ function run() {
4
+ const removed = removeProvider("xurl")
5
+ const index = syncCatalog()
6
+ return {
7
+ provider: "xurl",
8
+ removed,
9
+ synced_skills: Array.isArray(index.skills) ? index.skills.length : 0
10
+ }
11
+ }
12
+
13
+ if (require.main === module) {
14
+ try {
15
+ const result = run()
16
+ process.stdout.write(JSON.stringify(result))
17
+ } catch (err) {
18
+ process.stderr.write(err.message)
19
+ process.exit(1)
20
+ }
21
+ }
22
+
23
+ module.exports = {
24
+ run
25
+ }
@@ -4,6 +4,23 @@ const { bumpVersion } = require("../services/configService")
4
4
 
5
5
  const router = Router()
6
6
 
7
+ function sanitizeMcpPayload(payload, fallbackName) {
8
+ const name = typeof payload.name === "string" ? payload.name : fallbackName
9
+ if (!name) return null
10
+ const out = { name }
11
+ if (typeof payload.url === "string") out.url = payload.url
12
+ if (typeof payload.command === "string") out.command = payload.command
13
+ if (Array.isArray(payload.args)) out.args = payload.args.filter(v => typeof v === "string")
14
+ if (payload.headers && typeof payload.headers === "object" && !Array.isArray(payload.headers)) {
15
+ out.headers = Object.fromEntries(Object.entries(payload.headers).filter(([k, v]) => typeof k === "string" && typeof v === "string"))
16
+ }
17
+ if (payload.env && typeof payload.env === "object" && !Array.isArray(payload.env)) {
18
+ out.env = Object.fromEntries(Object.entries(payload.env).filter(([k, v]) => typeof k === "string" && typeof v === "string"))
19
+ }
20
+ if (typeof payload.timeout_ms === "number" && payload.timeout_ms > 0) out.timeout_ms = payload.timeout_ms
21
+ return out
22
+ }
23
+
7
24
  async function getAllMCPs() {
8
25
  const storage = getStorage()
9
26
  const keys = await storage.listKeys("mcp:")
@@ -28,9 +45,13 @@ router.get("/", async (req, res) => {
28
45
  router.post("/", async (req, res) => {
29
46
  try {
30
47
  const storage = getStorage()
31
- const { name, url } = req.body
48
+ const sanitized = sanitizeMcpPayload(req.body || {})
49
+ if (!sanitized || (!sanitized.url && !sanitized.command)) {
50
+ return res.status(400).json({ error: "MCP server requires name and one of: url or command" })
51
+ }
52
+ const { name } = sanitized
32
53
  const key = `mcp:${name}`
33
- const doc = { _id: key, name, url, createdAt: new Date() }
54
+ const doc = { _id: key, ...sanitized, createdAt: new Date() }
34
55
  await storage.set(key, doc)
35
56
  await bumpVersion()
36
57
  if (req.headers["content-type"]?.includes("urlencoded")) {
@@ -47,14 +68,19 @@ router.put("/:id", async (req, res) => {
47
68
  try {
48
69
  const storage = getStorage()
49
70
  const id = decodeURIComponent(req.params.id)
50
- const { name, url } = req.body
71
+ const currentName = id.startsWith("mcp:") ? id.slice(4) : undefined
72
+ const sanitized = sanitizeMcpPayload(req.body || {}, currentName)
73
+ if (!sanitized || (!sanitized.url && !sanitized.command)) {
74
+ return res.status(400).json({ error: "MCP server requires name and one of: url or command" })
75
+ }
76
+ const { name } = sanitized
51
77
 
52
78
  const newKey = `mcp:${name}`
53
79
  if (newKey !== id) {
54
80
  await storage.delete(id)
55
81
  }
56
82
 
57
- const doc = { _id: newKey, name, url }
83
+ const doc = { _id: newKey, ...sanitized }
58
84
  await storage.set(newKey, doc)
59
85
  await bumpVersion()
60
86
  res.json({ ok: true })
@@ -15,7 +15,15 @@ async function getCLIConfig() {
15
15
  ttl: 3600,
16
16
  mcp_servers: mcpServers
17
17
  .filter(Boolean)
18
- .map(s => ({ name: s.name, url: s.url })),
18
+ .map(s => ({
19
+ name: s.name,
20
+ url: s.url,
21
+ command: s.command,
22
+ args: Array.isArray(s.args) ? s.args : undefined,
23
+ headers: s.headers && typeof s.headers === "object" ? s.headers : undefined,
24
+ env: s.env && typeof s.env === "object" ? s.env : undefined,
25
+ timeout_ms: typeof s.timeout_ms === "number" ? s.timeout_ms : undefined
26
+ })),
19
27
  specs: specs
20
28
  .filter(Boolean)
21
29
  .map(s => ({ name: s.name, url: s.url, auth: s.auth || "none" })),
@@ -0,0 +1,48 @@
1
+ #!/usr/bin/env bash
2
+
3
+ set -euo pipefail
4
+
5
+ ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
6
+ CLI="${ROOT_DIR}/cli/supercli.js"
7
+
8
+ echo "== blogwatcher smoke test =="
9
+
10
+ if ! command -v blogwatcher >/dev/null 2>&1; then
11
+ echo "blogwatcher not found in PATH."
12
+ echo "Install it first and verify with: blogwatcher --version"
13
+ exit 1
14
+ fi
15
+
16
+ if ! command -v curl >/dev/null 2>&1; then
17
+ echo "curl not found in PATH"
18
+ exit 1
19
+ fi
20
+
21
+ if ! command -v node >/dev/null 2>&1; then
22
+ echo "Node.js not found in PATH"
23
+ exit 1
24
+ fi
25
+
26
+ TEMP_HOME="$(mktemp -d)"
27
+ trap 'rm -rf "${TEMP_HOME}"' EXIT
28
+ export HOME="${TEMP_HOME}"
29
+
30
+ echo "Installing/refreshing blogwatcher plugin..."
31
+ node "${CLI}" plugins install blogwatcher --on-conflict replace --json >/dev/null
32
+
33
+ echo "Checking indexed skills..."
34
+ node "${CLI}" skills list --catalog --provider blogwatcher --json
35
+ node "${CLI}" skills get blogwatcher:root.skill >/dev/null
36
+
37
+ echo "Running wrapped version command..."
38
+ node "${CLI}" blogwatcher cli version --json
39
+
40
+ echo "Running wrapped add/list/remove flow..."
41
+ node "${CLI}" blogwatcher blogs add --name "Example" --url "https://example.com/blog" --feed-url "https://example.com/feed.xml" --json
42
+ node "${CLI}" blogwatcher blogs list --json
43
+ node "${CLI}" blogwatcher blogs remove --name "Example" --json
44
+
45
+ echo "Running passthrough smoke test..."
46
+ node "${CLI}" blogwatcher --version
47
+
48
+ echo "Smoke test completed."
@@ -0,0 +1,44 @@
1
+ #!/usr/bin/env bash
2
+
3
+ set -euo pipefail
4
+
5
+ ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
6
+ CLI="${ROOT_DIR}/cli/supercli.js"
7
+
8
+ echo "== clix smoke test =="
9
+
10
+ if ! command -v clix >/dev/null 2>&1; then
11
+ echo "clix not found in PATH."
12
+ echo "Install it first and verify with: clix auth status --json"
13
+ exit 1
14
+ fi
15
+
16
+ if ! command -v curl >/dev/null 2>&1; then
17
+ echo "curl not found in PATH"
18
+ exit 1
19
+ fi
20
+
21
+ if ! command -v node >/dev/null 2>&1; then
22
+ echo "Node.js not found in PATH"
23
+ exit 1
24
+ fi
25
+
26
+ echo "Installing/refreshing clix plugin..."
27
+ node "${CLI}" plugins install clix --on-conflict replace --json >/dev/null
28
+
29
+ echo "Checking indexed skills..."
30
+ node "${CLI}" skills list --catalog --provider clix --json
31
+ node "${CLI}" skills get clix:root.skill >/dev/null
32
+
33
+ echo "Running safe wrapped commands..."
34
+ node "${CLI}" clix auth status --json
35
+
36
+ if [[ "${CLIX_LIVE_READONLY:-}" == "1" ]]; then
37
+ echo "Running optional live read-only commands..."
38
+ node "${CLI}" clix timeline list --count 10 --json
39
+ node "${CLI}" clix posts search --query "from:openai" --count 10 --json
40
+ else
41
+ echo "Skipping live read-only calls; set CLIX_LIVE_READONLY=1 to enable them."
42
+ fi
43
+
44
+ echo "Smoke test completed."
@@ -0,0 +1,47 @@
1
+ #!/usr/bin/env bash
2
+
3
+ set -euo pipefail
4
+
5
+ ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
6
+ CLI="${ROOT_DIR}/cli/supercli.js"
7
+
8
+ echo "== himalaya smoke test =="
9
+
10
+ if ! command -v himalaya >/dev/null 2>&1; then
11
+ echo "himalaya not found in PATH."
12
+ echo "Install it first and verify with: himalaya --version"
13
+ exit 1
14
+ fi
15
+
16
+ if ! command -v node >/dev/null 2>&1; then
17
+ echo "Node.js not found in PATH"
18
+ exit 1
19
+ fi
20
+
21
+ echo "Installing/refreshing himalaya plugin..."
22
+ node "${CLI}" plugins install himalaya --on-conflict replace --json >/dev/null
23
+
24
+ echo "Running wrapped version command..."
25
+ node "${CLI}" himalaya cli version --json
26
+
27
+ echo "Running wrapped account list command..."
28
+ node "${CLI}" himalaya account list --json
29
+
30
+ if [[ -n "${HIMALAYA_ACCOUNT:-}" ]]; then
31
+ echo "Running wrapped folder list command..."
32
+ node "${CLI}" himalaya folder list --account "${HIMALAYA_ACCOUNT}" --json
33
+
34
+ if [[ -n "${HIMALAYA_FOLDER:-}" ]]; then
35
+ echo "Running wrapped envelope list command..."
36
+ node "${CLI}" himalaya envelope list --account "${HIMALAYA_ACCOUNT}" --folder "${HIMALAYA_FOLDER}" --page 1 --json
37
+ else
38
+ echo "Skipping envelope list test; set HIMALAYA_FOLDER to enable it."
39
+ fi
40
+ else
41
+ echo "Skipping account-scoped tests; set HIMALAYA_ACCOUNT to enable them."
42
+ fi
43
+
44
+ echo "Running passthrough smoke test..."
45
+ node "${CLI}" himalaya --help
46
+
47
+ echo "Smoke test completed."
@@ -0,0 +1,141 @@
1
+ #!/usr/bin/env bash
2
+
3
+ set -euo pipefail
4
+
5
+ ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
6
+ CLI="${ROOT_DIR}/cli/supercli.js"
7
+
8
+ API_KEY=""
9
+ if [[ "${1:-}" == "--api-key" ]]; then
10
+ API_KEY="${2:-}"
11
+ fi
12
+
13
+ if ! command -v node >/dev/null 2>&1; then
14
+ echo "Node.js not found in PATH"
15
+ exit 1
16
+ fi
17
+
18
+ if ! command -v python3 >/dev/null 2>&1; then
19
+ echo "python3 not found in PATH"
20
+ exit 1
21
+ fi
22
+
23
+ TMP_HOME="$(mktemp -d)"
24
+ cleanup() {
25
+ rm -rf "${TMP_HOME}"
26
+ }
27
+ trap cleanup EXIT
28
+
29
+ echo "== MCP browser-use smoke test =="
30
+ echo "Using isolated HOME: ${TMP_HOME}"
31
+
32
+ export HOME="${TMP_HOME}"
33
+
34
+ echo "[1/3] Add local stdio MCP server with server-side args/env..."
35
+ node "${CLI}" mcp add mock-bridge \
36
+ --command python3 \
37
+ --args-json '["-c","import json, os, sys; payload=json.load(sys.stdin); out={\"argv\": sys.argv[1:], \"env\": {\"BROWSER_USE_API_KEY\": os.environ.get(\"BROWSER_USE_API_KEY\"), \"SERVER_ONLY\": os.environ.get(\"SERVER_ONLY\"), \"CMD_ONLY\": os.environ.get(\"CMD_ONLY\")}, \"payload\": payload}; print(json.dumps(out))","--server-arg"]' \
38
+ --env-json '{"BROWSER_USE_API_KEY":"server-key","SERVER_ONLY":"1"}' \
39
+ --headers-json '{"X-Server":"1"}' \
40
+ --json >/dev/null
41
+
42
+ echo "[2/3] Inject command using named MCP server + client-side args/env..."
43
+ python3 - "${HOME}/.supercli/config.json" <<'PY'
44
+ import json
45
+ import sys
46
+
47
+ path = sys.argv[1]
48
+ with open(path, "r", encoding="utf-8") as f:
49
+ cfg = json.load(f)
50
+
51
+ cmd = {
52
+ "_id": "command:ai.browser.probe",
53
+ "namespace": "ai",
54
+ "resource": "browser",
55
+ "action": "probe",
56
+ "description": "Smoke: MCP merge wiring",
57
+ "adapter": "mcp",
58
+ "adapterConfig": {
59
+ "server": "mock-bridge",
60
+ "tool": "probe",
61
+ "args": ["--cmd-arg"],
62
+ "env": {
63
+ "BROWSER_USE_API_KEY": "cmd-key",
64
+ "CMD_ONLY": "1"
65
+ },
66
+ "headers": {
67
+ "X-Cmd": "1"
68
+ }
69
+ },
70
+ "args": []
71
+ }
72
+
73
+ commands = cfg.get("commands") or []
74
+ found = False
75
+ for i, existing in enumerate(commands):
76
+ if (
77
+ isinstance(existing, dict)
78
+ and existing.get("namespace") == "ai"
79
+ and existing.get("resource") == "browser"
80
+ and existing.get("action") == "probe"
81
+ ):
82
+ commands[i] = cmd
83
+ found = True
84
+ break
85
+
86
+ if not found:
87
+ commands.append(cmd)
88
+
89
+ cfg["commands"] = commands
90
+ with open(path, "w", encoding="utf-8") as f:
91
+ json.dump(cfg, f, indent=2)
92
+ PY
93
+
94
+ echo "[3/3] Execute command and assert merged args/env reached process..."
95
+ RESULT="$(node "${CLI}" ai browser probe --json)"
96
+ python3 - "${RESULT}" <<'PY'
97
+ import json
98
+ import sys
99
+
100
+ envelope = json.loads(sys.argv[1])
101
+ data = envelope.get("data") or {}
102
+ argv = data.get("argv") or []
103
+ env = data.get("env") or {}
104
+ payload = data.get("payload") or {}
105
+
106
+ assert "--server-arg" in argv, "missing server-side arg"
107
+ assert "--cmd-arg" in argv, "missing client-side arg"
108
+ assert env.get("SERVER_ONLY") == "1", "missing server env"
109
+ assert env.get("CMD_ONLY") == "1", "missing client env"
110
+ assert env.get("BROWSER_USE_API_KEY") == "cmd-key", "client env should override server env"
111
+ assert payload.get("tool") == "probe", "tool mismatch"
112
+ print("Merged stdio args/env execution checks passed.")
113
+ PY
114
+
115
+ if [[ -n "${API_KEY}" ]]; then
116
+ echo "Optional browser-use config check with provided API key..."
117
+ node "${CLI}" mcp add browser-use \
118
+ --command npx \
119
+ --args-json "[\"mcp-remote\",\"https://api.browser-use.com/mcp\",\"--header\",\"X-Browser-Use-API-Key: ${API_KEY}\"]" \
120
+ --env-json "{\"BROWSER_USE_API_KEY\":\"${API_KEY}\"}" \
121
+ --json >/dev/null
122
+
123
+ LIST_JSON="$(node "${CLI}" mcp list --json)"
124
+ python3 - "${LIST_JSON}" <<'PY'
125
+ import json
126
+ import sys
127
+
128
+ rows = (json.loads(sys.argv[1]) or {}).get("mcp_servers") or []
129
+ browser = next((r for r in rows if isinstance(r, dict) and r.get("name") == "browser-use"), None)
130
+ assert browser is not None, "browser-use server not found"
131
+ assert browser.get("command") == "npx", "browser-use command mismatch"
132
+ args = browser.get("args") or []
133
+ assert len(args) >= 2 and args[0] == "mcp-remote", "browser-use args missing mcp-remote"
134
+ assert args[1] == "https://api.browser-use.com/mcp", "browser-use URL mismatch"
135
+ print("browser-use registration checks passed.")
136
+ PY
137
+ else
138
+ echo "Skipping browser-use API-key registration check (pass --api-key <key> to enable)."
139
+ fi
140
+
141
+ echo "Smoke test completed."
@@ -0,0 +1,40 @@
1
+ #!/usr/bin/env bash
2
+
3
+ set -euo pipefail
4
+
5
+ ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
6
+ CLI="${ROOT_DIR}/cli/supercli.js"
7
+
8
+ echo "== mongosh smoke test =="
9
+
10
+ if ! command -v mongosh >/dev/null 2>&1; then
11
+ echo "mongosh not found in PATH."
12
+ echo "Install it first and verify with: mongosh --version"
13
+ exit 1
14
+ fi
15
+
16
+ if ! command -v node >/dev/null 2>&1; then
17
+ echo "Node.js not found in PATH"
18
+ exit 1
19
+ fi
20
+
21
+ echo "Installing/refreshing mongosh plugin..."
22
+ node "${CLI}" plugins install mongosh --on-conflict replace --json >/dev/null
23
+
24
+ echo "Running wrapped version command..."
25
+ node "${CLI}" mongosh cli version --json
26
+
27
+ if [[ -n "${MONGOSH_HOST:-}" ]]; then
28
+ echo "Running wrapped server ping command..."
29
+ node "${CLI}" mongosh server ping --host "${MONGOSH_HOST}" --json
30
+ elif [[ -n "${MONGODB_URI:-}" ]]; then
31
+ echo "Running passthrough ping command with MONGODB_URI..."
32
+ node "${CLI}" mongosh "${MONGODB_URI}" --quiet --json=relaxed --eval "db.adminCommand({ ping: 1 })" --json
33
+ else
34
+ echo "Skipping live ping test; set MONGOSH_HOST or MONGODB_URI to enable it."
35
+ fi
36
+
37
+ echo "Running passthrough smoke test..."
38
+ node "${CLI}" mongosh --help
39
+
40
+ echo "Smoke test completed."
@@ -0,0 +1,37 @@
1
+ #!/usr/bin/env bash
2
+
3
+ set -euo pipefail
4
+
5
+ ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
6
+ CLI="${ROOT_DIR}/cli/supercli.js"
7
+
8
+ echo "== mysql smoke test =="
9
+
10
+ if ! command -v mysql >/dev/null 2>&1; then
11
+ echo "mysql not found in PATH."
12
+ echo "Install it first and verify with: mysql --version"
13
+ exit 1
14
+ fi
15
+
16
+ if ! command -v node >/dev/null 2>&1; then
17
+ echo "Node.js not found in PATH"
18
+ exit 1
19
+ fi
20
+
21
+ echo "Installing/refreshing mysql plugin..."
22
+ node "${CLI}" plugins install mysql --on-conflict replace --json >/dev/null
23
+
24
+ echo "Running wrapped version command..."
25
+ node "${CLI}" mysql cli version --json
26
+
27
+ if [[ -n "${MYSQL_HOST:-}" && -n "${MYSQL_USER:-}" && -n "${MYSQL_DATABASE:-}" ]]; then
28
+ echo "Running wrapped query command..."
29
+ node "${CLI}" mysql query execute --execute "select 1" --host "${MYSQL_HOST}" --user "${MYSQL_USER}" --database "${MYSQL_DATABASE}" --json
30
+ else
31
+ echo "Skipping live query test; set MYSQL_HOST, MYSQL_USER, and MYSQL_DATABASE to enable it."
32
+ fi
33
+
34
+ echo "Running passthrough smoke test..."
35
+ node "${CLI}" mysql --help
36
+
37
+ echo "Smoke test completed."
@@ -178,6 +178,41 @@ assert(nextestExplore.ok, "nextest explore should succeed")
178
178
  const nextestExploreData = JSON.parse(nextestExplore.output)
179
179
  assert(nextestExploreData.plugins.some(p => p.name === "nextest"), "explore filters should find nextest")
180
180
 
181
+ const mysqlExplore = runNoServer("plugins explore --name mysql --tags sql --json")
182
+ assert(mysqlExplore.ok, "mysql explore should succeed")
183
+ const mysqlExploreData = JSON.parse(mysqlExplore.output)
184
+ assert(mysqlExploreData.plugins.some(p => p.name === "mysql"), "explore filters should find mysql")
185
+
186
+ const mongoshExplore = runNoServer("plugins explore --name mongosh --tags mongodb --json")
187
+ assert(mongoshExplore.ok, "mongosh explore should succeed")
188
+ const mongoshExploreData = JSON.parse(mongoshExplore.output)
189
+ assert(mongoshExploreData.plugins.some(p => p.name === "mongosh"), "explore filters should find mongosh")
190
+
191
+ const blogwatcherExplore = runNoServer("plugins explore --name blogwatcher --tags rss --json")
192
+ assert(blogwatcherExplore.ok, "blogwatcher explore should succeed")
193
+ const blogwatcherExploreData = JSON.parse(blogwatcherExplore.output)
194
+ assert(blogwatcherExploreData.plugins.some(p => p.name === "blogwatcher"), "explore filters should find blogwatcher")
195
+
196
+ const himalayaExplore = runNoServer("plugins explore --name himalaya --tags email --json")
197
+ assert(himalayaExplore.ok, "himalaya explore should succeed")
198
+ const himalayaExploreData = JSON.parse(himalayaExplore.output)
199
+ assert(himalayaExploreData.plugins.some(p => p.name === "himalaya"), "explore filters should find himalaya")
200
+
201
+ const wacliExplore = runNoServer("plugins explore --name wacli --tags whatsapp --json")
202
+ assert(wacliExplore.ok, "wacli explore should succeed")
203
+ const wacliExploreData = JSON.parse(wacliExplore.output)
204
+ assert(wacliExploreData.plugins.some(p => p.name === "wacli"), "explore filters should find wacli")
205
+
206
+ const xurlExplore = runNoServer("plugins explore --name xurl --tags twitter --json")
207
+ assert(xurlExplore.ok, "xurl explore should succeed")
208
+ const xurlExploreData = JSON.parse(xurlExplore.output)
209
+ assert(xurlExploreData.plugins.some(p => p.name === "xurl"), "explore filters should find xurl")
210
+
211
+ const clixExplore = runNoServer("plugins explore --name clix --tags agents --json")
212
+ assert(clixExplore.ok, "clix explore should succeed")
213
+ const clixExploreData = JSON.parse(clixExplore.output)
214
+ assert(clixExploreData.plugins.some(p => p.name === "clix"), "explore filters should find clix")
215
+
181
216
  const clineExplore = runNoServer("plugins explore --name cline --tags streaming --json")
182
217
  assert(clineExplore.ok, "cline explore should succeed")
183
218
  const clineExploreData = JSON.parse(clineExplore.output)
@@ -0,0 +1,46 @@
1
+ #!/usr/bin/env bash
2
+
3
+ set -euo pipefail
4
+
5
+ ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
6
+ CLI="${ROOT_DIR}/cli/supercli.js"
7
+
8
+ echo "== wacli smoke test =="
9
+
10
+ if ! command -v wacli >/dev/null 2>&1; then
11
+ echo "wacli not found in PATH."
12
+ echo "Install it first and verify with: wacli --version"
13
+ exit 1
14
+ fi
15
+
16
+ if ! command -v node >/dev/null 2>&1; then
17
+ echo "Node.js not found in PATH"
18
+ exit 1
19
+ fi
20
+
21
+ echo "Installing/refreshing wacli plugin..."
22
+ node "${CLI}" plugins install wacli --on-conflict replace --json >/dev/null
23
+
24
+ echo "Running wrapped version command..."
25
+ node "${CLI}" wacli cli version --json
26
+
27
+ TEMP_STORE="$(mktemp -d)"
28
+ trap 'rm -rf "${TEMP_STORE}"' EXIT
29
+
30
+ echo "Running safe diagnostics..."
31
+ node "${CLI}" wacli doctor run --store "${TEMP_STORE}" --json
32
+ node "${CLI}" wacli auth status --store "${TEMP_STORE}" --json
33
+
34
+ if [[ -n "${WACLI_STORE:-}" ]]; then
35
+ echo "Running read-only store-backed commands..."
36
+ node "${CLI}" wacli chats list --store "${WACLI_STORE}" --json
37
+ if [[ -n "${WACLI_CHAT_JID:-}" ]]; then
38
+ node "${CLI}" wacli messages list --store "${WACLI_STORE}" --chat "${WACLI_CHAT_JID}" --limit 10 --json
39
+ else
40
+ echo "Skipping messages list test; set WACLI_CHAT_JID to enable it."
41
+ fi
42
+ else
43
+ echo "Skipping store-backed chat/message tests; set WACLI_STORE to enable them."
44
+ fi
45
+
46
+ echo "Smoke test completed."
@@ -0,0 +1,46 @@
1
+ #!/usr/bin/env bash
2
+
3
+ set -euo pipefail
4
+
5
+ ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
6
+ CLI="${ROOT_DIR}/cli/supercli.js"
7
+
8
+ echo "== xurl smoke test =="
9
+
10
+ if ! command -v xurl >/dev/null 2>&1; then
11
+ echo "xurl not found in PATH."
12
+ echo "Install it first and verify with: xurl version"
13
+ exit 1
14
+ fi
15
+
16
+ if ! command -v curl >/dev/null 2>&1; then
17
+ echo "curl not found in PATH"
18
+ exit 1
19
+ fi
20
+
21
+ if ! command -v node >/dev/null 2>&1; then
22
+ echo "Node.js not found in PATH"
23
+ exit 1
24
+ fi
25
+
26
+ echo "Installing/refreshing xurl plugin..."
27
+ node "${CLI}" plugins install xurl --on-conflict replace --json >/dev/null
28
+
29
+ echo "Checking indexed skills..."
30
+ node "${CLI}" skills list --catalog --provider xurl --json
31
+ node "${CLI}" skills get xurl:root.skill >/dev/null
32
+
33
+ echo "Running safe wrapped commands..."
34
+ node "${CLI}" xurl cli version --json
35
+ node "${CLI}" xurl auth status --json
36
+ node "${CLI}" xurl apps list --json
37
+
38
+ if [[ "${XURL_LIVE_READONLY:-}" == "1" ]]; then
39
+ echo "Running optional live read-only commands..."
40
+ node "${CLI}" xurl account whoami --json
41
+ node "${CLI}" xurl posts search --query "from:XDevelopers" --max-results 5 --json
42
+ else
43
+ echo "Skipping live read-only calls; set XURL_LIVE_READONLY=1 to enable them."
44
+ fi
45
+
46
+ echo "Smoke test completed."