diff --git a/README.md b/README.md index bb6410e..ebaaeb0 100644 --- a/README.md +++ b/README.md @@ -76,10 +76,10 @@ Oder manuell: ```bash # .txz von Gitea laden -curl -O http://192.168.1.2:4000/michael/MUA-Mikes-Unraid-Agent/raw/branch/main/dist/mua-2026.08.24.r017-x86_64-1.txz +curl -O http://192.168.1.2:4000/michael/MUA-Mikes-Unraid-Agent/raw/branch/main/dist/mua-2026.08.24.r018-x86_64-1.txz # Installieren -upgradepkg --install-new mua-2026.08.24.r017-x86_64-1.txz +upgradepkg --install-new mua-2026.08.24.r018-x86_64-1.txz ``` ### 2. Service starten @@ -94,7 +94,7 @@ Der Service startet automatisch bei jedem Boot (SysVinit). ```bash curl http://192.168.1.2:3002/health -# → {"status":"ok","version":"2026.08.24.r017","auth":"required"} +# → {"status":"ok","version":"2026.08.24.r018","auth":"required"} ``` --- diff --git a/dist/mua-2026.08.24.r018-x86_64-1.txz b/dist/mua-2026.08.24.r018-x86_64-1.txz new file mode 100644 index 0000000..6e6cb82 Binary files /dev/null and b/dist/mua-2026.08.24.r018-x86_64-1.txz differ diff --git a/package.json b/package.json index 3132ccf..a419878 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "mua", - "version": "2026.08.24.r017", + "version": "2026.08.24.r018", "description": "Mikes Unraid Agent - MCP over HTTP (Streamable HTTP) for Unraid", "type": "module", "main": "src/index.ts", @@ -8,7 +8,7 @@ "build": "bun build ./src/index.ts --compile --target=bun-linux-x64 --outfile dist/mua", "build:mac": "bun build ./src/index.ts --compile --target=bun-darwin-arm64 --outfile dist/mua-mac", "start": "bun run src/index.ts", - "selftest": "bun run src/selftest.ts" + "selftest": "bun test src/security.test.ts" }, "devDependencies": { "@types/bun": "latest" diff --git a/plugin/mua.plg b/plugin/mua.plg index b92319c..ac3f47d 100644 --- a/plugin/mua.plg +++ b/plugin/mua.plg @@ -2,13 +2,13 @@ - + - - + + ]> +### 2026.08.24.r018 +- Added focused, batched container-log analysis via `focus_terms` to avoid repeated shell/grep loops. +- Repaired the repository self-test command. + ### 2026.08.24.r017 - Community-Applications-Suche akzeptiert bis zu fünf mit `|` getrennte Namens-, Schreib- oder Zweckvarianten in einem einzigen begrenzten Feed-Durchlauf. - Treffer nennen die tatsächlich passenden Varianten; kleine Agenten müssen Synonyme nicht mehr in einzelnen Werkzeugrunden abfragen. @@ -122,7 +126,7 @@ Das .txz enthält: install/doinst.sh (läuft nach Installation) =========================================== --> - + &txzURL; &txzSHA256; diff --git a/plugin/plugin.json b/plugin/plugin.json index b351711..a4ebc5e 100644 --- a/plugin/plugin.json +++ b/plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "mua", "author": "Michael", - "version": "2026.08.24.r017", + "version": "2026.08.24.r018", "minver": "7.0.0", "pluginDirectory": "/usr/local/emhttp/plugins/mua", "configDirectory": "/boot/config/plugins/mua", diff --git a/src/helpers.ts b/src/helpers.ts index 7a667f2..75076aa 100644 --- a/src/helpers.ts +++ b/src/helpers.ts @@ -14,7 +14,7 @@ import { createHash, randomUUID } from "node:crypto"; // ── Konstanten ────────────────────────────────────────────────────────── export const MUA_SERVER_NAME = "mua"; -export const MUA_VERSION = "2026.08.24.r017"; +export const MUA_VERSION = "2026.08.24.r018"; export const MUA_PROTOCOL_VERSION = "2025-03-26"; export const PHP_HELPER = "/usr/local/bin/unraid-docker-mcp-helper.php"; export const STATUS_HELPER = "/usr/local/bin/unraid-mcp-status-helper.php"; @@ -545,6 +545,7 @@ export async function analyzeContainerLogs( since: string, scanTail: number, maxResults: number, + focusTerms: string[] = [], ): Promise { const severityLevels: Record = { error: ["error", "fatal", "panic", "exception", "traceback", "critical"], @@ -553,6 +554,10 @@ export async function analyzeContainerLogs( }; const patterns = severityLevels[severity] ?? severityLevels["error"]; const regex = new RegExp(`(${patterns.join("|")})`, "i"); + const normalisedFocusTerms = focusTerms + .map((term) => term.trim().toLowerCase()) + .filter((term) => term.length > 0) + .slice(0, 12); // Ziel-Container: einzelner Container oder alle laufenden let targets: string[]; @@ -575,6 +580,12 @@ export async function analyzeContainerLogs( const raw = await dockerExec(logCmd, 120); let containerMatches = 0; for (const line of raw.split("\n")) { + if ( + normalisedFocusTerms.length > 0 + && !normalisedFocusTerms.some((term) => line.toLowerCase().includes(term)) + ) { + continue; + } const m = line.match(regex); if (m) { const key = m[1].toLowerCase(); @@ -594,6 +605,7 @@ export async function analyzeContainerLogs( container, since, scan_tail: scanTail, + focus_terms: normalisedFocusTerms, total_matches: Object.values(counts).reduce((a, b) => a + b, 0), pattern_counts: counts, containers_with_matches: containersWithMatches, diff --git a/src/tools.ts b/src/tools.ts index caa483d..5432b7a 100644 --- a/src/tools.ts +++ b/src/tools.ts @@ -131,7 +131,7 @@ export const TOOLS: ToolDef[] = [ { name: "unraid_docker_analyze_logs", description: - "Analyze container logs server-side for errors/warnings. Returns pattern counts and sample matches.", + "Analyze container logs server-side for errors/warnings in one bounded call. Use optional focus_terms to restrict results to components such as hass_mcp, yaml or zigbee instead of issuing multiple grep/shell calls. Returns pattern counts and sample matches.", inputSchema: { type: "object", properties: { @@ -144,6 +144,13 @@ export const TOOLS: ToolDef[] = [ since: str("Time filter (default 24h)"), scan_tail: int("Max lines to scan (default 1000)"), max_results: int("Max sample matches (default 50)"), + focus_terms: { + type: "array", + items: { type: "string", minLength: 1, maxLength: 80 }, + maxItems: 12, + description: + "Optional literal component/topic terms. A log line must contain at least one term, case-insensitively. Bundle related terms in one call.", + }, }, required: ["severity"], }, @@ -156,7 +163,19 @@ export const TOOLS: ToolDef[] = [ const since = (a["since"] as string) ?? "24h"; const scanTail = Number(a["scan_tail"] ?? 1000); const maxResults = Number(a["max_results"] ?? 50); - return analyzeContainerLogs(severity, container ?? null, since, scanTail, maxResults); + const focusTerms = Array.isArray(a["focus_terms"]) + ? (a["focus_terms"] as unknown[]).filter( + (item): item is string => typeof item === "string", + ) + : []; + return analyzeContainerLogs( + severity, + container ?? null, + since, + scanTail, + maxResults, + focusTerms, + ); }, }, {