From 69c5e3ddb92f4d23925fe1f137d1709fa36cb3a5 Mon Sep 17 00:00:00 2001 From: CyberSecurityUP Date: Tue, 11 Aug 2026 23:47:12 -0300 Subject: [PATCH] feat(3.6.9): OpenCode Zen + Nous Research (Hermes) providers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add two new model providers, both usable via API key or --subscription (local CLI login, no key): - opencode: OpenCode Zen gateway (OPENCODE_API_KEY, opencode.ai/zen/v1). Subscription mode drives the `opencode` CLI (`opencode run --auto`). Supports the Playwright MCP (--mcp): our .mcp.json is converted to OpenCode's own config schema and injected via OPENCODE_CONFIG. - nous: Nous Research / Hermes models (NOUS_API_KEY, inference-api.nousresearch.com/v1). Subscription mode drives the `hermes` CLI (NousResearch/hermes-agent) on the user's Nous Portal OAuth login (`hermes setup --portal`), via `hermes chat -q`. No CLI-level MCP hook โ€” falls back to Hermes's own built-in toolsets (web/terminal/computer-use). Both wired into cli_binary_for, installed_cli_backends, cli_login_status (prompt passed as argv, not stdin โ€” neither CLI reads stdin for this). Bump version 3.6.8 -> 3.6.9 across Cargo.toml, README, TUTORIAL, setup.sh, install.ps1, and in-binary version strings. README/.env.example updated with the new provider rows and subscription-login table. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01HHFAVCHMvRkTy9Wgw7SayG --- .env.example | 10 ++ README.md | 14 ++- TUTORIAL.md | 4 +- install.ps1 | 4 +- neurosploit-rs/Cargo.lock | 4 +- neurosploit-rs/Cargo.toml | 2 +- neurosploit-rs/app/src/main.rs | 12 +- neurosploit-rs/app/src/repl.rs | 4 +- neurosploit-rs/app/src/tui.rs | 2 +- neurosploit-rs/crates/harness/src/models.rs | 121 ++++++++++++++++++-- setup.sh | 4 +- 11 files changed, 152 insertions(+), 29 deletions(-) diff --git a/.env.example b/.env.example index fd8774e..14688f2 100755 --- a/.env.example +++ b/.env.example @@ -51,6 +51,16 @@ TOGETHER_API_KEY= # openrouter: https://openrouter.ai/keys OPENROUTER_API_KEY= +# opencode: https://opencode.ai/auth (OpenCode Zen gateway) +# Or skip the key entirely and use --subscription with the +# `opencode` CLI logged into your own Zen/plan account. +OPENCODE_API_KEY= + +# nous: Nous Portal (https://portal.nousresearch.com) โ€” Hermes models. +# Or skip the key entirely and use --subscription with the +# `hermes` CLI (`hermes setup --portal` for OAuth login). +NOUS_API_KEY= + # ollama: local, no key needed. Override the endpoint if not default: #OLLAMA_BASE_URL=http://localhost:11434/v1 diff --git a/README.md b/README.md index 5d7b419..4bdf6c5 100755 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -

๐Ÿง  NeuroSploit v3.6.8

+

๐Ÿง  NeuroSploit v3.6.9

JoasASantos%2FNeuroSploit | Trendshift @@ -12,7 +12,7 @@

- + @@ -433,6 +433,8 @@ export GROQ_API_KEY=... # groq:* export TOGETHER_API_KEY=... # together:* export MOONSHOT_API_KEY=... # moonshot:* (Kimi K3/K2) export OPENROUTER_API_KEY=... # openrouter:* +export OPENCODE_API_KEY=... # opencode:* (OpenCode Zen gateway) +export NOUS_API_KEY=... # nous:* (Nous Portal โ€” Hermes) # ollama / llamacpp need no key (local) # then run via API (note: NO --subscription) @@ -462,6 +464,8 @@ Or put the keys in a `.env` and source it (`cp .env.example .env`; edit; `set -a | `together:` | `TOGETHER_API_KEY` | api.together.xyz | | `moonshot:` | `MOONSHOT_API_KEY` | api.moonshot.ai | | `openrouter:` | `OPENROUTER_API_KEY` | openrouter.ai | +| `opencode:` | `OPENCODE_API_KEY` | opencode.ai/zen (OpenCode Zen gateway) | +| `nous:` | `NOUS_API_KEY` | inference-api.nousresearch.com (Hermes 4) | | `ollama:` | _(none)_ | localhost:11434 | | `llamacpp:` | _(none)_ | localhost:8080 | @@ -484,6 +488,12 @@ install and log into one of the CLIs first: | `openai:` | `codex` | `codex` login | | `gemini:` | `gemini` | `gemini` login | | `xai:` | `grok` | `grok` login | +| `opencode:` | `opencode` | `opencode auth login` (or `/connect` in the TUI) โ€” Zen/plan account | +| `nous:` | `hermes` | `hermes setup --portal` โ€” Nous Portal OAuth | + +`opencode:` also gets the Playwright MCP (`--mcp`) like anthropic/openai do. +`nous:` relies on Hermes's own built-in toolsets (web/terminal/computer-use) +instead โ€” it has no CLI-level MCP hook. ```bash ./target/release/neurosploit run http://testphp.vulnweb.com/ \ diff --git a/TUTORIAL.md b/TUTORIAL.md index ef36222..37dad79 100644 --- a/TUTORIAL.md +++ b/TUTORIAL.md @@ -1,4 +1,4 @@ -# NeuroSploit โ€” Tutorial & User Guide (v3.6.8) +# NeuroSploit โ€” Tutorial & User Guide (v3.6.9) A complete, hands-on guide to installing, configuring and running NeuroSploit โ€” the autonomous, multi-model penetration-testing harness. @@ -98,7 +98,7 @@ Agents **degrade gracefully**: if `rustscan` is absent they use `nmap`; if neith ### Verify ```bash -neurosploit --version # neurosploit 3.6.8 +neurosploit --version # neurosploit 3.6.9 neurosploit agents # {"vulns":241,...,"ai":30,...,"total":430} neurosploit models # all providers & models ``` diff --git a/install.ps1 b/install.ps1 index 4342dcb..98da84b 100644 --- a/install.ps1 +++ b/install.ps1 @@ -14,7 +14,7 @@ function Ok ($m) { Write-Host " + $m" -ForegroundColor Green } function Warn($m){ Write-Host " ! $m" -ForegroundColor Yellow } Write-Host "" -Write-Host " NeuroSploit installer (Windows) โ€” v3.6.8" -ForegroundColor Cyan +Write-Host " NeuroSploit installer (Windows) โ€” v3.6.9" -ForegroundColor Cyan # arch โ†’ asset arch (only x64 prebuilt today; arm64 falls back to source) $rawArch = $env:PROCESSOR_ARCHITECTURE @@ -29,7 +29,7 @@ $ref = $env:NEUROSPLOIT_REF if (-not $ref) { try { $ref = (Invoke-RestMethod "https://api.github.com/repos/$slug/releases/latest").tag_name } catch { } } -if (-not $ref) { $ref = "v3.6.8" } +if (-not $ref) { $ref = "v3.6.9" } Say "Release: $ref" New-Item -ItemType Directory -Force -Path $dir | Out-Null diff --git a/neurosploit-rs/Cargo.lock b/neurosploit-rs/Cargo.lock index 7f000d8..0a25272 100644 --- a/neurosploit-rs/Cargo.lock +++ b/neurosploit-rs/Cargo.lock @@ -871,7 +871,7 @@ dependencies = [ [[package]] name = "neurosploit" -version = "3.6.8" +version = "3.6.9" dependencies = [ "anyhow", "clap", @@ -888,7 +888,7 @@ dependencies = [ [[package]] name = "neurosploit-harness" -version = "3.6.8" +version = "3.6.9" dependencies = [ "anyhow", "futures", diff --git a/neurosploit-rs/Cargo.toml b/neurosploit-rs/Cargo.toml index fcb863b..0005c05 100644 --- a/neurosploit-rs/Cargo.toml +++ b/neurosploit-rs/Cargo.toml @@ -3,7 +3,7 @@ members = ["crates/harness", "app"] resolver = "2" [workspace.package] -version = "3.6.8" +version = "3.6.9" edition = "2021" license = "MIT" repository = "https://github.com/JoasASantos/NeuroSploit" diff --git a/neurosploit-rs/app/src/main.rs b/neurosploit-rs/app/src/main.rs index 6bd45ce..fe3a493 100644 --- a/neurosploit-rs/app/src/main.rs +++ b/neurosploit-rs/app/src/main.rs @@ -1,4 +1,4 @@ -//! NeuroSploit v3.6.8 โ€” interactive harness + CLI (`run` / `whitebox` / `agents` / `models`). +//! NeuroSploit v3.6.9 โ€” interactive harness + CLI (`run` / `whitebox` / `agents` / `models`). mod repl; mod tui; @@ -11,9 +11,9 @@ use std::path::{Path, PathBuf}; #[command( name = "neurosploit", version, - about = "NeuroSploit v3.6.8 โ€” multi-model autonomous pentest harness", - long_about = "NeuroSploit v3.6.8 โ€” a Rust multi-model harness that drives a pool of LLMs \ -(API key or local subscription: Claude/Codex/Gemini/Grok) to autonomously test a target. \ + about = "NeuroSploit v3.6.9 โ€” multi-model autonomous pentest harness", + long_about = "NeuroSploit v3.6.9 โ€” a Rust multi-model harness that drives a pool of LLMs \ +(API key or local subscription: Claude/Codex/Gemini/Grok/OpenCode/Hermes) to autonomously test a target. \ After recon it INTELLIGENTLY selects only the agents matching the discovered surface, runs \ them in parallel, then validates every finding by cross-model voting before reporting.\n\n\ Run with NO arguments for an interactive wizard.\n\n\ @@ -54,7 +54,7 @@ enum Cmd { recon: usize, #[arg(long)] offline: bool, - /// Use local agentic CLI subscription (Claude/Codex/Gemini/Grok login). + /// Use local agentic CLI subscription (Claude/Codex/Gemini/Grok/OpenCode/Hermes login). #[arg(long)] subscription: bool, /// Enable Playwright MCP (auto-installed if missing; backends that don't @@ -765,7 +765,7 @@ pub(crate) fn spawn_engagement(base: &Path, mut cfg: RunConfig, mcp: bool, mode: println!(" โ”‚ ua : {ua}"); write_status(&workdir, "running", &format!("\"target\":{:?}", cfg.target)); - println!(" โ”Œโ”€ NeuroSploit v3.6.8 ยท by Joas A Santos & Red Team Leaders"); + println!(" โ”Œโ”€ NeuroSploit v3.6.9 ยท by Joas A Santos & Red Team Leaders"); println!(" โ”‚ run id : {run_id}"); println!(" โ”‚ target : {}", cfg.target); println!(" โ”‚ models : {}", cfg.models.join(", ")); diff --git a/neurosploit-rs/app/src/repl.rs b/neurosploit-rs/app/src/repl.rs index f3d5841..1b1272e 100644 --- a/neurosploit-rs/app/src/repl.rs +++ b/neurosploit-rs/app/src/repl.rs @@ -1,4 +1,4 @@ -//! NeuroSploit v3.6.8 โ€” interactive session (Claude-Code / Codex / Cursor-CLI style). +//! NeuroSploit v3.6.9 โ€” interactive session (Claude-Code / Codex / Cursor-CLI style). //! //! Launched when `neurosploit` runs with no subcommand. A persistent REPL with //! real line editing (arrow-key history recall, Ctrl-A/E/K, paste), model @@ -371,7 +371,7 @@ pub async fn repl(base: &Path) -> anyhow::Result<()> { let backends = harness::installed_cli_backends(); println!("\x1b[1m"); println!(" โ–ˆโ–ˆโ–ˆโ•— โ–ˆโ–ˆโ•—โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•—โ–ˆโ–ˆโ•— โ–ˆโ–ˆโ•—โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•— โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•—"); - println!(" โ–ˆโ–ˆโ–ˆโ–ˆโ•— โ–ˆโ–ˆโ•‘โ–ˆโ–ˆโ•”โ•โ•โ•โ•โ•โ–ˆโ–ˆโ•‘ โ–ˆโ–ˆโ•‘โ–ˆโ–ˆโ•”โ•โ•โ–ˆโ–ˆโ•—โ–ˆโ–ˆโ•”โ•โ•โ•โ–ˆโ–ˆโ•— NeuroSploit v3.6.8"); + println!(" โ–ˆโ–ˆโ–ˆโ–ˆโ•— โ–ˆโ–ˆโ•‘โ–ˆโ–ˆโ•”โ•โ•โ•โ•โ•โ–ˆโ–ˆโ•‘ โ–ˆโ–ˆโ•‘โ–ˆโ–ˆโ•”โ•โ•โ–ˆโ–ˆโ•—โ–ˆโ–ˆโ•”โ•โ•โ•โ–ˆโ–ˆโ•— NeuroSploit v3.6.9"); println!(" โ–ˆโ–ˆโ•”โ–ˆโ–ˆโ•— โ–ˆโ–ˆโ•‘โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•— โ–ˆโ–ˆโ•‘ โ–ˆโ–ˆโ•‘โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•”โ•โ–ˆโ–ˆโ•‘ โ–ˆโ–ˆโ•‘ interactive harness"); println!(" โ–ˆโ–ˆโ•‘โ•šโ–ˆโ–ˆโ•—โ–ˆโ–ˆโ•‘โ–ˆโ–ˆโ•”โ•โ•โ• โ–ˆโ–ˆโ•‘ โ–ˆโ–ˆโ•‘โ–ˆโ–ˆโ•”โ•โ•โ–ˆโ–ˆโ•—โ–ˆโ–ˆโ•‘ โ–ˆโ–ˆโ•‘ by Joas A Santos"); println!(" โ–ˆโ–ˆโ•‘ โ•šโ–ˆโ–ˆโ–ˆโ–ˆโ•‘โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•—โ•šโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•”โ•โ–ˆโ–ˆโ•‘ โ–ˆโ–ˆโ•‘โ•šโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•”โ• & Red Team Leaders"); diff --git a/neurosploit-rs/app/src/tui.rs b/neurosploit-rs/app/src/tui.rs index 0d615d1..68fe71d 100644 --- a/neurosploit-rs/app/src/tui.rs +++ b/neurosploit-rs/app/src/tui.rs @@ -1,4 +1,4 @@ -//! NeuroSploit v3.6.8 โ€” TUI "Mission Control" mode. +//! NeuroSploit v3.6.9 โ€” TUI "Mission Control" mode. //! //! Concurrent panels that update live while the engagement runs in the //! background, with a composer input that stays active during execution: diff --git a/neurosploit-rs/crates/harness/src/models.rs b/neurosploit-rs/crates/harness/src/models.rs index ef7dc04..e4bb108 100644 --- a/neurosploit-rs/crates/harness/src/models.rs +++ b/neurosploit-rs/crates/harness/src/models.rs @@ -52,6 +52,21 @@ pub fn providers() -> Vec { models: vec!["gpt-4o", "claude-3-7-sonnet", "gemini/gemini-2.5-pro"] }, Provider { key: "openrouter", label: "OpenRouter", base_url: "https://openrouter.ai/api/v1", env_key: "OPENROUTER_API_KEY", kind: "api", models: vec!["anthropic/claude-opus-4-8", "qwen/qwen-2.5-coder-32b-instruct", "deepseek/deepseek-r1", "meta-llama/llama-3.3-70b-instruct"] }, + // OpenCode Zen โ€” the curated OpenAI-compatible gateway behind the + // `opencode` CLI (https://opencode.ai/zen). Works two ways, like + // anthropic/openai/xai/gemini above: as a plain API-key provider here, + // or (with --subscription) driven through the locally-installed + // `opencode` agentic CLI on the user's own Zen/plan login โ€” no key + // needed in that mode. `kind: "cli"` reflects the latter. + Provider { key: "opencode", label: "OpenCode Zen", base_url: "https://opencode.ai/zen/v1", env_key: "OPENCODE_API_KEY", kind: "cli", + models: vec!["claude-opus-5", "claude-sonnet-5", "gpt-5.6-sol", "gpt-5.5", "gemini-3-pro", "grok-4.5", "deepseek-v4-pro", "qwen3.7-max", "kimi-k3"] }, + // Nous Research โ€” Hermes models via the Nous Portal. As an API-key + // provider here (OpenAI-compatible `inference-api.nousresearch.com`), + // or (with --subscription) driven through the `hermes` CLI + // (NousResearch/hermes-agent) on the user's OAuth Portal login + // (`hermes setup --portal`) โ€” 300+ routed frontier models, no key. + Provider { key: "nous", label: "Nous Research (Hermes)", base_url: "https://inference-api.nousresearch.com/v1", env_key: "NOUS_API_KEY", kind: "cli", + models: vec!["Hermes-4-405B", "Hermes-4-70B", "DeepHermes-3-Mistral-24B-Preview"] }, // Azure OpenAI (OpenAI-compatible). Set AZURE_OPENAI_ENDPOINT (e.g. // https://.openai.azure.com), optionally AZURE_OPENAI_API_VERSION // (default 2024-10-21), and use `azure:` as the model. @@ -226,6 +241,11 @@ impl ChatClient { } let mut cmd = Command::new(bin); + // Most agentic CLIs here take the prompt on stdin; opencode and hermes + // take it as a trailing positional argument instead โ€” track which so we + // don't also pipe it into stdin below (that would just hang the child + // waiting on a request it already got as an argv value). + let mut prompt_via_stdin = true; match bin { // Codex non-interactive exec (uses the ChatGPT/Codex login), prompt on stdin. "codex" => { @@ -250,13 +270,46 @@ impl ChatClient { "grok" => { cmd.arg("--model").arg(model); } + // OpenCode CLI (`opencode run`) โ€” non-interactive one-shot, prompt + // as a positional arg, not stdin. `--auto` auto-approves anything + // not explicitly denied (our equivalent of --dangerously-skip-permissions). + // MCP (Playwright) is injected via a generated opencode.json pointed + // at through OPENCODE_CONFIG rather than a CLI flag (opencode has none). + "opencode" => { + prompt_via_stdin = false; + cmd.arg("run").arg("--model").arg(model).arg("--auto"); + if let Some(mcp) = mcp_config { + match write_opencode_mcp_config(mcp) { + Ok(cfg) => { cmd.env("OPENCODE_CONFIG", cfg); } + Err(e) => eprintln!(" [!] opencode MCP config failed: {e}"), + } + } + cmd.arg(&prompt); + } + // Hermes Agent CLI (NousResearch/hermes-agent) โ€” single-query mode. + // `-q` is the prompt-supplying flag (not a stdin read); `--provider + // nous` pins the Nous Portal OAuth login; `-Q` quiets banner/spinner + // for programmatic use; `--yolo` bypasses dangerous-command prompts. + // No CLI-level MCP hook โ€” Hermes falls back to its own built-in + // toolsets (web/terminal/computer-use) rather than our Playwright MCP. + "hermes" => { + prompt_via_stdin = false; + cmd.arg("chat").arg("-m").arg(model).arg("--provider").arg("nous") + .arg("-Q").arg("--yolo").arg("-q").arg(&prompt); + } _ => {} } cmd.stdin(Stdio::piped()).stdout(Stdio::piped()).stderr(Stdio::piped()).kill_on_drop(true); let mut child = cmd.spawn().map_err(|e| anyhow!("spawn {} failed: {}", bin, e))?; - if let Some(mut stdin) = child.stdin.take() { - stdin.write_all(prompt.as_bytes()).await?; - // Drop closes stdin so the CLI processes the prompt and exits. + if prompt_via_stdin { + if let Some(mut stdin) = child.stdin.take() { + stdin.write_all(prompt.as_bytes()).await?; + // Drop closes stdin so the CLI processes the prompt and exits. + } + } else { + // Prompt went in as an argv value; close stdin immediately so + // nothing lingers waiting on it (opencode/hermes never read it). + drop(child.stdin.take()); } // Cap a single agentic CLI turn so a stuck tool-loop can't hang the run. let out = match tokio::time::timeout(Duration::from_secs(600), child.wait_with_output()).await { @@ -577,6 +630,8 @@ pub fn cli_binary_for(provider: &str) -> Option<&'static str> { "openai" => Some("codex"), "xai" => Some("grok"), "gemini" => Some("gemini"), + "opencode" => Some("opencode"), + "nous" => Some("hermes"), _ => None, } } @@ -590,7 +645,7 @@ pub fn binary_in_path(name: &str) -> bool { /// Which subscription CLI backends are installed locally. pub fn installed_cli_backends() -> Vec<&'static str> { - ["claude", "codex", "grok", "gemini"].into_iter().filter(|b| binary_in_path(b)).collect() + ["claude", "codex", "grok", "gemini", "opencode", "hermes"].into_iter().filter(|b| binary_in_path(b)).collect() } /// Login state of a subscription CLI backend. @@ -610,15 +665,23 @@ pub async fn cli_login_status(provider: &str) -> LoginStatus { let Some(bin) = cli_binary_for(provider) else { return LoginStatus::NotInstalled }; if !binary_in_path(bin) { return LoginStatus::NotInstalled; } let mut cmd = Command::new(bin); + // opencode/hermes take the probe prompt as an argv value, not stdin. + let prompt_via_stdin = !matches!(bin, "opencode" | "hermes"); match bin { "claude" => { cmd.arg("-p").arg("--output-format").arg("text").arg("--dangerously-skip-permissions"); } "codex" => { cmd.arg("exec").arg("--dangerously-bypass-approvals-and-sandbox").arg("-"); } + "opencode" => { cmd.arg("run").arg("--auto").arg("Reply with exactly: OK"); } + "hermes" => { cmd.arg("chat").arg("--provider").arg("nous").arg("-Q").arg("--yolo").arg("-q").arg("Reply with exactly: OK"); } _ => { cmd.arg("-p"); } // grok / gemini: prompt on stdin } cmd.stdin(Stdio::piped()).stdout(Stdio::piped()).stderr(Stdio::piped()).kill_on_drop(true); let mut child = match cmd.spawn() { Ok(c) => c, Err(_) => return LoginStatus::Unknown }; - if let Some(mut stdin) = child.stdin.take() { - let _ = stdin.write_all(b"Reply with exactly: OK").await; + if prompt_via_stdin { + if let Some(mut stdin) = child.stdin.take() { + let _ = stdin.write_all(b"Reply with exactly: OK").await; + } + } else { + drop(child.stdin.take()); } let out = match tokio::time::timeout(Duration::from_secs(45), child.wait_with_output()).await { Ok(Ok(o)) => o, @@ -642,10 +705,50 @@ pub async fn cli_login_status(provider: &str) -> LoginStatus { } /// Does this provider's agentic CLI accept a Playwright MCP config? -/// Claude Code and Codex do; Gemini/Grok CLIs don't take an MCP-config flag, so -/// they fall back to their own built-in tools. +/// Claude Code, Codex, and OpenCode do (OpenCode via a generated +/// `opencode.json` + `OPENCODE_CONFIG`, see `write_opencode_mcp_config`). +/// Gemini/Grok/Hermes have no CLI-level MCP hook, so they fall back to their +/// own built-in tools (Hermes ships web/terminal/computer-use natively). pub fn mcp_supported(provider: &str) -> bool { - matches!(provider, "anthropic" | "openai") + matches!(provider, "anthropic" | "openai" | "opencode") +} + +/// Convert our `.mcp.json` (`{"mcpServers": {name: {command, args}}}`) into +/// OpenCode's own config schema (`{"mcp": {name: {"type":"local","command": +/// [command, ...args], "enabled": true}}}`) and write it next to the source +/// file. OpenCode has no `--mcp-config` flag; it's pointed at a config file +/// via the `OPENCODE_CONFIG` env var instead (set by the `opencode` arm of +/// `chat_cli`), so this doesn't touch the user's own `opencode.json`. +fn write_opencode_mcp_config(mcp_json_path: &str) -> Result { + let txt = std::fs::read_to_string(mcp_json_path) + .map_err(|e| anyhow!("read {mcp_json_path}: {e}"))?; + let v: serde_json::Value = serde_json::from_str(&txt) + .map_err(|e| anyhow!("parse {mcp_json_path}: {e}"))?; + let servers = v.get("mcpServers").cloned().unwrap_or(v); + let mut mcp = serde_json::Map::new(); + if let Some(obj) = servers.as_object() { + for (name, s) in obj { + let command = s.get("command").and_then(|c| c.as_str()).unwrap_or("").to_string(); + if command.is_empty() { continue; } + let mut argv = vec![serde_json::Value::String(command)]; + if let Some(args) = s.get("args").and_then(|a| a.as_array()) { + argv.extend(args.iter().cloned()); + } + mcp.insert(name.clone(), serde_json::json!({ + "type": "local", + "command": argv, + "enabled": true + })); + } + } + let cfg = serde_json::json!({ + "$schema": "https://opencode.ai/config.json", + "mcp": mcp + }); + let path = std::path::Path::new(mcp_json_path).with_file_name("opencode.json"); + std::fs::write(&path, serde_json::to_string_pretty(&cfg).unwrap_or_default()) + .map_err(|e| anyhow!("write {}: {e}", path.display()))?; + Ok(path) } /// Best-effort ensure the Playwright MCP server is available locally. Requires diff --git a/setup.sh b/setup.sh index 2df24e3..e61f3c2 100755 --- a/setup.sh +++ b/setup.sh @@ -28,7 +28,7 @@ cat <<'BANNER' โ–ˆโ–ˆโ–ˆโ•— โ–ˆโ–ˆโ•—โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•—โ–ˆโ–ˆโ•— โ–ˆโ–ˆโ•—โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•— โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•— โ–ˆโ–ˆโ–ˆโ–ˆโ•— โ–ˆโ–ˆโ•‘โ–ˆโ–ˆโ•”โ•โ•โ•โ•โ•โ–ˆโ–ˆโ•‘ โ–ˆโ–ˆโ•‘โ–ˆโ–ˆโ•”โ•โ•โ–ˆโ–ˆโ•—โ–ˆโ–ˆโ•”โ•โ•โ•โ–ˆโ–ˆโ•— NeuroSploit installer - โ–ˆโ–ˆโ•”โ–ˆโ–ˆโ•— โ–ˆโ–ˆโ•‘โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•— โ–ˆโ–ˆโ•‘ โ–ˆโ–ˆโ•‘โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•”โ•โ–ˆโ–ˆโ•‘ โ–ˆโ–ˆโ•‘ v3.6.8 โ€” Rust harness + โ–ˆโ–ˆโ•”โ–ˆโ–ˆโ•— โ–ˆโ–ˆโ•‘โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•— โ–ˆโ–ˆโ•‘ โ–ˆโ–ˆโ•‘โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•”โ•โ–ˆโ–ˆโ•‘ โ–ˆโ–ˆโ•‘ v3.6.9 โ€” Rust harness โ–ˆโ–ˆโ•‘โ•šโ–ˆโ–ˆโ•—โ–ˆโ–ˆโ•‘โ–ˆโ–ˆโ•”โ•โ•โ• โ–ˆโ–ˆโ•‘ โ–ˆโ–ˆโ•‘โ–ˆโ–ˆโ•”โ•โ•โ–ˆโ–ˆโ•—โ–ˆโ–ˆโ•‘ โ–ˆโ–ˆโ•‘ by Joas A Santos โ–ˆโ–ˆโ•‘ โ•šโ–ˆโ–ˆโ–ˆโ–ˆโ•‘โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•—โ•šโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•”โ•โ–ˆโ–ˆโ•‘ โ–ˆโ–ˆโ•‘โ•šโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•”โ• & Red Team Leaders โ•šโ•โ• โ•šโ•โ•โ•โ•โ•šโ•โ•โ•โ•โ•โ•โ• โ•šโ•โ•โ•โ•โ•โ• โ•šโ•โ• โ•šโ•โ• โ•šโ•โ•โ•โ•โ•โ• @@ -63,7 +63,7 @@ if [ -z "$REF" ]; then REF="$(dl "https://api.github.com/repos/${REPO_SLUG}/releases/latest" /dev/stdout 2>/dev/null \ | grep -m1 '"tag_name"' | sed -E 's/.*"tag_name" *: *"([^"]+)".*/\1/' || true)" fi -[ -z "$REF" ] && REF="v3.6.8" +[ -z "$REF" ] && REF="v3.6.9" say "Release: $REF" installed=0