Add security hardening, Ghidra, recreate skill, checkpoints, shells, web search and themes
- Sandbox (bubblewrap) for non-admin shell/git, MCP per-user rights, hard budgets and quotas, login rate limiting, audit log and backups - Ghidra headless decompilation (ARM64 natives built from source), binary_info tool, clean room / deep workflow per session - Recreate skill and commands for analysing and re-building old software - File checkpoints with rewind/restore, background shells (bash_output, kill_shell) - web_search tool (DuckDuckGo, SearXNG, Brave, Tavily) - In-app dialogs and toasts instead of browser dialogs - 15 base themes (trilium-themes palettes plus Nord, VS Code, GitHub, Catppuccin, One, Dracula, Rose Pine, Everforest, Kanagawa) with light/dark/auto switcher and license notes Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
fab9add5d5
commit
5b569b6835
108 files changed
+6785
-279
No files matched your search
+14
-5
@@ -504,11 +504,14 @@ impl McpManager {
|
||||
|
||||
/// All MCP tools, split into tools given to the model directly and tools
|
||||
/// that are only reachable through mcp_search / mcp_call.
|
||||
pub fn tools(&self, cfg: &crate::config::Config) -> (Vec<Arc<dyn Tool>>, Vec<Arc<McpTool>>) {
|
||||
pub fn tools(&self, cfg: &crate::config::Config, user: Option<&crate::store::User>) -> (Vec<Arc<dyn Tool>>, Vec<Arc<McpTool>>) {
|
||||
let servers = self.servers.lock().unwrap();
|
||||
let mut direct: Vec<Arc<dyn Tool>> = Vec::new();
|
||||
let mut deferred = Vec::new();
|
||||
for (sname, server) in servers.iter() {
|
||||
if !cfg.mcp_allowed(sname, user) {
|
||||
continue;
|
||||
}
|
||||
let tools = server.tools.lock().unwrap();
|
||||
let load = cfg.mcp.get(sname).and_then(|m| m.load.clone()).unwrap_or_else(|| "auto".into());
|
||||
let on_demand = match load.as_str() {
|
||||
@@ -529,8 +532,8 @@ impl McpManager {
|
||||
}
|
||||
|
||||
/// Short overview of on-demand servers for the system prompt.
|
||||
pub fn deferred_summary(&self, cfg: &crate::config::Config) -> Vec<(String, usize, Vec<String>)> {
|
||||
let (_, deferred) = self.tools(cfg);
|
||||
pub fn deferred_summary(&self, cfg: &crate::config::Config, user: Option<&crate::store::User>) -> Vec<(String, usize, Vec<String>)> {
|
||||
let (_, deferred) = self.tools(cfg, user);
|
||||
let mut by_server: BTreeMap<String, Vec<String>> = BTreeMap::new();
|
||||
for t in deferred {
|
||||
by_server.entry(t.server.name.clone()).or_default().push(t.info.name.clone());
|
||||
@@ -579,7 +582,7 @@ impl Tool for McpSearch {
|
||||
let query = input["query"].as_str().unwrap_or("").to_lowercase();
|
||||
let server = input["server"].as_str();
|
||||
let words: Vec<&str> = query.split_whitespace().collect();
|
||||
let (_, deferred) = ctx.app.mcp.tools(&ctx.app.config());
|
||||
let (_, deferred) = ctx.app.mcp.tools(&ctx.app.config(), owner(ctx).as_ref());
|
||||
let mut scored: Vec<(usize, &Arc<McpTool>)> = deferred
|
||||
.iter()
|
||||
.filter(|t| server.is_none_or(|s| t.server.name == s))
|
||||
@@ -635,10 +638,16 @@ impl Tool for McpCall {
|
||||
}
|
||||
}
|
||||
|
||||
/// The session owner (MCP access is decided per user).
|
||||
fn owner(ctx: &ToolCtx) -> Option<crate::store::User> {
|
||||
let s = ctx.app.session(&ctx.session_id).ok()?;
|
||||
ctx.app.store.user(s.owner_id.as_deref()?).ok().flatten()
|
||||
}
|
||||
|
||||
/// Find the MCP tool an mcp_call refers to.
|
||||
pub fn resolve_call_target(ctx: &ToolCtx, input: &Value) -> Result<Arc<McpTool>> {
|
||||
let name = input["tool"].as_str().unwrap_or("");
|
||||
let (_, deferred) = ctx.app.mcp.tools(&ctx.app.config());
|
||||
let (_, deferred) = ctx.app.mcp.tools(&ctx.app.config(), owner(ctx).as_ref());
|
||||
deferred
|
||||
.into_iter()
|
||||
.find(|t| t.full_name == name)
|
||||
|
||||
Reference in new issue
Block a user