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:
Richard ZinkandClaude Opus 5.5 committed 2026-11-08 19:12:30 +01:00
1 parent fab9add5d5
commit 5b569b6835
108 files changed
+6785 -279

No files matched your search

+3 -2
View File
@@ -2,6 +2,7 @@ import { del, post } from "../api";
import { useT } from "../i18n";
import { useStore } from "../store";
import type { QueueItem } from "../types";
import { confirmDialog, promptDialog, toast } from "../dialogs";
const EMPTY: QueueItem[] = [];
@@ -15,7 +16,7 @@ export function QueueList({ sessionId, busy }: { sessionId: string; busy: boolea
<div className="queue-head">
<span>{busy ? t("queue.pending", { n: items.length }) : t("queue.stopped", { n: items.length })}</span>
{!busy && (
<button className="small primary" onClick={() => post(`/sessions/${sessionId}/queue/flush`).catch((e) => alert(e.message))}>
<button className="small primary" onClick={() => post(`/sessions/${sessionId}/queue/flush`).catch((e) => toast(e.message))}>
{t("queue.sendNow")}
</button>
)}
@@ -35,7 +36,7 @@ export function QueueList({ sessionId, busy }: { sessionId: string; busy: boolea
className="small ghost"
aria-label={t("queue.remove")}
title={t("queue.remove")}
onClick={() => del(`/sessions/${sessionId}/queue/${it.id}`).catch((e) => alert(e.message))}
onClick={() => del(`/sessions/${sessionId}/queue/${it.id}`).catch((e) => toast(e.message))}
>
×
</button>