release: r018 focused log analysis

This commit is contained in:
Mikei386 committed 2026-08-24 10:42:43 +02:00
1 parent 02deec8dc9
commit 47c27b06c4
7 files changed
+48 -13

No files matched your search

+3 -3
View File
@@ -76,10 +76,10 @@ Oder manuell:
```bash ```bash
# .txz von Gitea laden # .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 # 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 ### 2. Service starten
@@ -94,7 +94,7 @@ Der Service startet automatisch bei jedem Boot (SysVinit).
```bash ```bash
curl http://192.168.1.2:3002/health 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"}
``` ```
--- ---
Binary file not shown.
+2 -2
View File
@@ -1,6 +1,6 @@
{ {
"name": "mua", "name": "mua",
"version": "2026.08.24.r017", "version": "2026.08.24.r018",
"description": "Mikes Unraid Agent - MCP over HTTP (Streamable HTTP) for Unraid", "description": "Mikes Unraid Agent - MCP over HTTP (Streamable HTTP) for Unraid",
"type": "module", "type": "module",
"main": "src/index.ts", "main": "src/index.ts",
@@ -8,7 +8,7 @@
"build": "bun build ./src/index.ts --compile --target=bun-linux-x64 --outfile dist/mua", "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", "build:mac": "bun build ./src/index.ts --compile --target=bun-darwin-arm64 --outfile dist/mua-mac",
"start": "bun run src/index.ts", "start": "bun run src/index.ts",
"selftest": "bun run src/selftest.ts" "selftest": "bun test src/security.test.ts"
}, },
"devDependencies": { "devDependencies": {
"@types/bun": "latest" "@types/bun": "latest"
+8 -4
View File
@@ -2,13 +2,13 @@
<!DOCTYPE PLUGIN [ <!DOCTYPE PLUGIN [
<!ENTITY name "mua"> <!ENTITY name "mua">
<!ENTITY author "Michael"> <!ENTITY author "Michael">
<!ENTITY version "2026.08.24.r017"> <!ENTITY version "2026.08.24.r018">
<!ENTITY launch "Settings/mua"> <!ENTITY launch "Settings/mua">
<!ENTITY pluginURL "http://192.168.1.2:4000/michael/MUA-Mikes-Unraid-Agent/raw/branch/main/plugin/mua.plg"> <!ENTITY pluginURL "http://192.168.1.2:4000/michael/MUA-Mikes-Unraid-Agent/raw/branch/main/plugin/mua.plg">
<!ENTITY pluginLOC "/boot/config/plugins/&name;"> <!ENTITY pluginLOC "/boot/config/plugins/&name;">
<!ENTITY emhttpLOC "/usr/local/emhttp/plugins/&name;"> <!ENTITY emhttpLOC "/usr/local/emhttp/plugins/&name;">
<!ENTITY txzURL "http://192.168.1.2:4000/michael/MUA-Mikes-Unraid-Agent/raw/branch/main/dist/mua-2026.08.24.r017-x86_64-1.txz"> <!ENTITY txzURL "http://192.168.1.2:4000/michael/MUA-Mikes-Unraid-Agent/raw/branch/main/dist/mua-2026.08.24.r018-x86_64-1.txz">
<!ENTITY txzSHA256 "dd95d28fdcc875ff6d1f8ae317735b724b6d192dd23e7dfd3d866fd07395c7f8"> <!ENTITY txzSHA256 "174603af715b3f875a875563b6a67045c6af6b8166b6943780712d8074bc2071">
]> ]>
<PLUGIN name="&name;" <PLUGIN name="&name;"
@@ -23,6 +23,10 @@
> >
<CHANGES> <CHANGES>
### 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 ### 2026.08.24.r017
- Community-Applications-Suche akzeptiert bis zu fünf mit `|` getrennte Namens-, Schreib- oder Zweckvarianten in einem einzigen begrenzten Feed-Durchlauf. - 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. - 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) install/doinst.sh (läuft nach Installation)
=========================================== ===========================================
--> -->
<FILE Name="/boot/config/plugins/&name;/mua-2026.08.24.r017-x86_64-1.txz" Run="upgradepkg --install-new" Mode="755" Min="7.0.0"> <FILE Name="/boot/config/plugins/&name;/mua-2026.08.24.r018-x86_64-1.txz" Run="upgradepkg --install-new" Mode="755" Min="7.0.0">
<URL>&txzURL;</URL> <URL>&txzURL;</URL>
<SHA256>&txzSHA256;</SHA256> <SHA256>&txzSHA256;</SHA256>
</FILE> </FILE>
+1 -1
View File
@@ -1,7 +1,7 @@
{ {
"name": "mua", "name": "mua",
"author": "Michael", "author": "Michael",
"version": "2026.08.24.r017", "version": "2026.08.24.r018",
"minver": "7.0.0", "minver": "7.0.0",
"pluginDirectory": "/usr/local/emhttp/plugins/mua", "pluginDirectory": "/usr/local/emhttp/plugins/mua",
"configDirectory": "/boot/config/plugins/mua", "configDirectory": "/boot/config/plugins/mua",
+13 -1
View File
@@ -14,7 +14,7 @@ import { createHash, randomUUID } from "node:crypto";
// ── Konstanten ────────────────────────────────────────────────────────── // ── Konstanten ──────────────────────────────────────────────────────────
export const MUA_SERVER_NAME = "mua"; 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 MUA_PROTOCOL_VERSION = "2025-03-26";
export const PHP_HELPER = "/usr/local/bin/unraid-docker-mcp-helper.php"; export const PHP_HELPER = "/usr/local/bin/unraid-docker-mcp-helper.php";
export const STATUS_HELPER = "/usr/local/bin/unraid-mcp-status-helper.php"; export const STATUS_HELPER = "/usr/local/bin/unraid-mcp-status-helper.php";
@@ -545,6 +545,7 @@ export async function analyzeContainerLogs(
since: string, since: string,
scanTail: number, scanTail: number,
maxResults: number, maxResults: number,
focusTerms: string[] = [],
): Promise<string> { ): Promise<string> {
const severityLevels: Record<string, string[]> = { const severityLevels: Record<string, string[]> = {
error: ["error", "fatal", "panic", "exception", "traceback", "critical"], error: ["error", "fatal", "panic", "exception", "traceback", "critical"],
@@ -553,6 +554,10 @@ export async function analyzeContainerLogs(
}; };
const patterns = severityLevels[severity] ?? severityLevels["error"]; const patterns = severityLevels[severity] ?? severityLevels["error"];
const regex = new RegExp(`(${patterns.join("|")})`, "i"); 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 // Ziel-Container: einzelner Container oder alle laufenden
let targets: string[]; let targets: string[];
@@ -575,6 +580,12 @@ export async function analyzeContainerLogs(
const raw = await dockerExec(logCmd, 120); const raw = await dockerExec(logCmd, 120);
let containerMatches = 0; let containerMatches = 0;
for (const line of raw.split("\n")) { for (const line of raw.split("\n")) {
if (
normalisedFocusTerms.length > 0
&& !normalisedFocusTerms.some((term) => line.toLowerCase().includes(term))
) {
continue;
}
const m = line.match(regex); const m = line.match(regex);
if (m) { if (m) {
const key = m[1].toLowerCase(); const key = m[1].toLowerCase();
@@ -594,6 +605,7 @@ export async function analyzeContainerLogs(
container, container,
since, since,
scan_tail: scanTail, scan_tail: scanTail,
focus_terms: normalisedFocusTerms,
total_matches: Object.values(counts).reduce((a, b) => a + b, 0), total_matches: Object.values(counts).reduce((a, b) => a + b, 0),
pattern_counts: counts, pattern_counts: counts,
containers_with_matches: containersWithMatches, containers_with_matches: containersWithMatches,
+21 -2
View File
@@ -131,7 +131,7 @@ export const TOOLS: ToolDef[] = [
{ {
name: "unraid_docker_analyze_logs", name: "unraid_docker_analyze_logs",
description: 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: { inputSchema: {
type: "object", type: "object",
properties: { properties: {
@@ -144,6 +144,13 @@ export const TOOLS: ToolDef[] = [
since: str("Time filter (default 24h)"), since: str("Time filter (default 24h)"),
scan_tail: int("Max lines to scan (default 1000)"), scan_tail: int("Max lines to scan (default 1000)"),
max_results: int("Max sample matches (default 50)"), 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"], required: ["severity"],
}, },
@@ -156,7 +163,19 @@ export const TOOLS: ToolDef[] = [
const since = (a["since"] as string) ?? "24h"; const since = (a["since"] as string) ?? "24h";
const scanTail = Number(a["scan_tail"] ?? 1000); const scanTail = Number(a["scan_tail"] ?? 1000);
const maxResults = Number(a["max_results"] ?? 50); 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,
);
}, },
}, },
{ {