release: r019 idempotent batch container updates
This commit is contained in:
@@ -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.r018-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.r019-x86_64-1.txz
|
||||||
|
|
||||||
# Installieren
|
# Installieren
|
||||||
upgradepkg --install-new mua-2026.08.24.r018-x86_64-1.txz
|
upgradepkg --install-new mua-2026.08.24.r019-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.r018","auth":"required"}
|
# → {"status":"ok","version":"2026.08.24.r019","auth":"required"}
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -212,7 +212,7 @@ Zusätzlich kann jedes Werkzeug einzeln nach Risikostufe freigegeben werden:
|
|||||||
|
|
||||||
| Gruppe | Tools |
|
| Gruppe | Tools |
|
||||||
|--------|-------|
|
|--------|-------|
|
||||||
| **Docker (15)** | `unraid_docker_list`, `unraid_docker_inspect`, `unraid_docker_logs`, `unraid_docker_analyze_logs`, `unraid_docker_processes`, `unraid_docker_stats`, `unraid_docker_info`, `unraid_docker_update_status`, `unraid_docker_start`, `unraid_docker_stop`, `unraid_docker_restart`, `unraid_docker_create`, `unraid_docker_modify`, `unraid_docker_update`, `unraid_docker_rebuild` |
|
| **Docker (16)** | `unraid_docker_list`, `unraid_docker_inspect`, `unraid_docker_logs`, `unraid_docker_analyze_logs`, `unraid_docker_processes`, `unraid_docker_stats`, `unraid_docker_info`, `unraid_docker_update_status`, `unraid_docker_start`, `unraid_docker_stop`, `unraid_docker_restart`, `unraid_docker_create`, `unraid_docker_modify`, `unraid_docker_update`, `unraid_docker_update_verified_batch`, `unraid_docker_rebuild` |
|
||||||
| **Community Applications (3)** | `unraid_ca_search`, `unraid_ca_install_preview`, `unraid_ca_install` |
|
| **Community Applications (3)** | `unraid_ca_search`, `unraid_ca_install_preview`, `unraid_ca_install` |
|
||||||
| **Netzwerk (6)** | `unraid_network_inventory`, `unraid_network_list`, `unraid_network_inspect`, `unraid_network_host_state`, `unraid_network_audit_tcp`, `unraid_network_lan_probe` |
|
| **Netzwerk (6)** | `unraid_network_inventory`, `unraid_network_list`, `unraid_network_inspect`, `unraid_network_host_state`, `unraid_network_audit_tcp`, `unraid_network_lan_probe` |
|
||||||
| **System (9)** | `unraid_system_health`, `unraid_storage_status`, `unraid_disk_health`, `unraid_notifications_list`, `unraid_shares_list`, `unraid_share_inspect`, `unraid_system_connection_test`, `unraid_system_shell_readonly`, `unraid_system_shell` |
|
| **System (9)** | `unraid_system_health`, `unraid_storage_status`, `unraid_disk_health`, `unraid_notifications_list`, `unraid_shares_list`, `unraid_share_inspect`, `unraid_system_connection_test`, `unraid_system_shell_readonly`, `unraid_system_shell` |
|
||||||
|
|||||||
BIN
Binary file not shown.
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "mua",
|
"name": "mua",
|
||||||
"version": "2026.08.24.r018",
|
"version": "2026.08.24.r019",
|
||||||
"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",
|
||||||
|
|||||||
+9
-4
@@ -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.r018">
|
<!ENTITY version "2026.08.24.r019">
|
||||||
<!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.r018-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.r019-x86_64-1.txz">
|
||||||
<!ENTITY txzSHA256 "174603af715b3f875a875563b6a67045c6af6b8166b6943780712d8074bc2071">
|
<!ENTITY txzSHA256 "382435dbb98ae4818c4aced563f4247c6fca6b552d4a9de5695f9e4e98764d6c">
|
||||||
]>
|
]>
|
||||||
|
|
||||||
<PLUGIN name="&name;"
|
<PLUGIN name="&name;"
|
||||||
@@ -23,6 +23,11 @@
|
|||||||
>
|
>
|
||||||
|
|
||||||
<CHANGES>
|
<CHANGES>
|
||||||
|
### 2026.08.24.r019
|
||||||
|
- Ein gebündelter Docker-Updateablauf prüft echte Image-IDs, überspringt bereits aktuelle Images trotz veraltetem Unraid-Cache und erhält den ursprünglichen Laufzustand.
|
||||||
|
- Bis zu 25 ausdrücklich benannte Container werden in einem Werkzeugaufruf aktualisiert und anschließend kompakt auf Container-ID, Zustand, RestartCount und Health geprüft.
|
||||||
|
- Eine bestehende Freigabe des bisherigen Einzel-Update-Werkzeugs schaltet automatisch dessen sicherere Batch-Variante frei.
|
||||||
|
|
||||||
### 2026.08.24.r018
|
### 2026.08.24.r018
|
||||||
- Added focused, batched container-log analysis via `focus_terms` to avoid repeated shell/grep loops.
|
- Added focused, batched container-log analysis via `focus_terms` to avoid repeated shell/grep loops.
|
||||||
- Repaired the repository self-test command.
|
- Repaired the repository self-test command.
|
||||||
@@ -126,7 +131,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.r018-x86_64-1.txz" Run="upgradepkg --install-new" Mode="755" Min="7.0.0">
|
<FILE Name="/boot/config/plugins/&name;/mua-2026.08.24.r019-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
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "mua",
|
"name": "mua",
|
||||||
"author": "Michael",
|
"author": "Michael",
|
||||||
"version": "2026.08.24.r018",
|
"version": "2026.08.24.r019",
|
||||||
"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",
|
||||||
|
|||||||
+1
-1
@@ -104,7 +104,7 @@ cat > "${BUILD_DIR}/install/slack-desc" << DESCEOF
|
|||||||
|
|
|
|
||||||
|${PKG_NAME} - Mikes Unraid Agent
|
|${PKG_NAME} - Mikes Unraid Agent
|
||||||
|MCP over HTTP (Streamable HTTP) Server für Unraid.
|
|MCP over HTTP (Streamable HTTP) Server für Unraid.
|
||||||
|33 Tools: Docker, Community Applications, Netzwerk und Unraid-System.
|
|34 Tools: Docker, Community Applications, Netzwerk und Unraid-System.
|
||||||
|Port: 3002, Endpunkt: /mcp
|
|Port: 3002, Endpunkt: /mcp
|
||||||
|
|
|
|
||||||
|Runtime: TypeScript (Bun Runtime, kompiliertes Binary)
|
|Runtime: TypeScript (Bun Runtime, kompiliertes Binary)
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
* create <template_name>
|
* create <template_name>
|
||||||
* modify <container> <field> <value>
|
* modify <container> <field> <value>
|
||||||
* update <container>
|
* update <container>
|
||||||
|
* update-verified-batch <container1|container2|...>
|
||||||
* rebuild <container>
|
* rebuild <container>
|
||||||
* ca-install <template_url> <container_name> <overrides_json> <start:true|false>
|
* ca-install <template_url> <container_name> <overrides_json> <start:true|false>
|
||||||
*
|
*
|
||||||
@@ -116,6 +117,109 @@ function rebuild_container(string $name, bool $pull_image = false, bool $force_s
|
|||||||
out("Done: $Name");
|
out("Done: $Name");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function docker_inspect_value(string $format, string $name): string {
|
||||||
|
return docker_exec('inspect --format ' . escapeshellarg($format) . ' ' . escapeshellarg($name));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pull and update several exact containers without trusting Unraid's cached
|
||||||
|
* update flag. The immutable image ID is the authority. A stale cache can
|
||||||
|
* therefore never cause a needless container recreation.
|
||||||
|
*/
|
||||||
|
function update_verified_batch(string $raw_names): void {
|
||||||
|
$names = array_values(array_unique(array_filter(array_map('trim', explode('|', $raw_names)))));
|
||||||
|
if (count($names) < 1 || count($names) > 25) {
|
||||||
|
fail('Expected 1-25 container names separated by |');
|
||||||
|
}
|
||||||
|
|
||||||
|
$results = [];
|
||||||
|
foreach ($names as $name) {
|
||||||
|
if (!preg_match('/^[a-zA-Z0-9._-]{1,128}$/', $name)) {
|
||||||
|
$results[] = ['container' => $name, 'result' => 'error', 'error' => 'invalid container name'];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$tmpl = get_template_path($name);
|
||||||
|
[, $template_name, $repository] = xmlToCommand($tmpl);
|
||||||
|
if ($template_name !== $name) {
|
||||||
|
throw new RuntimeException("Template name mismatch: $template_name");
|
||||||
|
}
|
||||||
|
|
||||||
|
$before_id = docker_inspect_value('{{.Id}}', $name);
|
||||||
|
$before_image_id = docker_inspect_value('{{.Image}}', $name);
|
||||||
|
$before_state = docker_inspect_value('{{.State.Status}}', $name);
|
||||||
|
$was_running = docker_inspect_value('{{.State.Running}}', $name) === 'true';
|
||||||
|
|
||||||
|
$pull_output = docker_exec('pull ' . escapeshellarg($repository));
|
||||||
|
if (preg_match('/(?:^|\n)(?:Error response|error:|failed)/i', $pull_output)) {
|
||||||
|
throw new RuntimeException('Image pull failed: ' . substr($pull_output, 0, 500));
|
||||||
|
}
|
||||||
|
$pulled_image_id = docker_exec('image inspect --format ' . escapeshellarg('{{.Id}}') . ' ' . escapeshellarg($repository));
|
||||||
|
if ($pulled_image_id === '') {
|
||||||
|
throw new RuntimeException('Pulled image ID could not be resolved');
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($before_image_id === $pulled_image_id) {
|
||||||
|
$results[] = [
|
||||||
|
'container' => $name,
|
||||||
|
'result' => 'already-current',
|
||||||
|
'recreated' => false,
|
||||||
|
'original_state' => $before_state,
|
||||||
|
'final_state' => $before_state,
|
||||||
|
'container_id_changed' => false,
|
||||||
|
'image_id_changed' => false,
|
||||||
|
];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ob_start();
|
||||||
|
rebuild_container($name, false, false);
|
||||||
|
ob_end_clean();
|
||||||
|
|
||||||
|
$after_id = docker_inspect_value('{{.Id}}', $name);
|
||||||
|
$after_image_id = docker_inspect_value('{{.Image}}', $name);
|
||||||
|
$after_state = docker_inspect_value('{{.State.Status}}', $name);
|
||||||
|
$restart_count = (int)docker_inspect_value('{{.RestartCount}}', $name);
|
||||||
|
$health = docker_inspect_value('{{if .State.Health}}{{.State.Health.Status}}{{else}}none{{end}}', $name);
|
||||||
|
$state_preserved = $was_running ? $after_state === 'running' : $after_state !== 'running';
|
||||||
|
|
||||||
|
$results[] = [
|
||||||
|
'container' => $name,
|
||||||
|
'result' => ($after_image_id === $pulled_image_id && $state_preserved) ? 'updated' : 'verification-failed',
|
||||||
|
'recreated' => true,
|
||||||
|
'original_state' => $before_state,
|
||||||
|
'final_state' => $after_state,
|
||||||
|
'state_preserved' => $state_preserved,
|
||||||
|
'container_id_changed' => $before_id !== $after_id,
|
||||||
|
'image_id_changed' => $before_image_id !== $after_image_id,
|
||||||
|
'restart_count' => $restart_count,
|
||||||
|
'health' => $health,
|
||||||
|
];
|
||||||
|
} catch (Throwable $error) {
|
||||||
|
if (ob_get_level() > 0) ob_end_clean();
|
||||||
|
$results[] = [
|
||||||
|
'container' => $name,
|
||||||
|
'result' => 'error',
|
||||||
|
'error' => substr($error->getMessage(), 0, 600),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$counts = [];
|
||||||
|
foreach ($results as $result) {
|
||||||
|
$key = (string)$result['result'];
|
||||||
|
$counts[$key] = ($counts[$key] ?? 0) + 1;
|
||||||
|
}
|
||||||
|
echo json_encode([
|
||||||
|
'schema_version' => '1.0',
|
||||||
|
'requested_count' => count($names),
|
||||||
|
'result_counts' => $counts,
|
||||||
|
'all_verified' => !isset($counts['error']) && !isset($counts['verification-failed']),
|
||||||
|
'containers' => $results,
|
||||||
|
], JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) . "\n";
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the text content of a DOM element.
|
* Set the text content of a DOM element.
|
||||||
* This is the actual value Unraid uses (not the Default attribute).
|
* This is the actual value Unraid uses (not the Default attribute).
|
||||||
@@ -354,6 +458,11 @@ switch ($action) {
|
|||||||
rebuild_container($arg1, true);
|
rebuild_container($arg1, true);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'update-verified-batch':
|
||||||
|
if (!$arg1) fail('Missing container names');
|
||||||
|
update_verified_batch($arg1);
|
||||||
|
break;
|
||||||
|
|
||||||
case 'rebuild':
|
case 'rebuild':
|
||||||
if (!$arg1) fail("Missing container name");
|
if (!$arg1) fail("Missing container name");
|
||||||
out("Rebuilding container: $arg1");
|
out("Rebuilding container: $arg1");
|
||||||
@@ -366,5 +475,5 @@ switch ($action) {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
fail("Unknown action: $action. Usage: create|modify|update|rebuild|ca-install");
|
fail("Unknown action: $action. Usage: create|modify|update|update-verified-batch|rebuild|ca-install");
|
||||||
}
|
}
|
||||||
|
|||||||
+11
@@ -93,6 +93,17 @@ export function parseConfig(content: string): MUAConfig {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// r019 replaces repeated single-container update calls with an idempotent
|
||||||
|
// batch variant. If an administrator already enabled the older update
|
||||||
|
// capability, expose the safer equivalent automatically; this does not
|
||||||
|
// grant a new class of operation.
|
||||||
|
if (
|
||||||
|
!cfg.allToolsEnabled
|
||||||
|
&& cfg.enabledTools.includes("unraid_docker_update")
|
||||||
|
&& !cfg.enabledTools.includes("unraid_docker_update_verified_batch")
|
||||||
|
) {
|
||||||
|
cfg.enabledTools.push("unraid_docker_update_verified_batch");
|
||||||
|
}
|
||||||
return cfg;
|
return cfg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -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.r018";
|
export const MUA_VERSION = "2026.08.24.r019";
|
||||||
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";
|
||||||
|
|||||||
@@ -22,6 +22,14 @@ describe("secure tool configuration", () => {
|
|||||||
expect(cfg.allToolsEnabled).toBe(true);
|
expect(cfg.allToolsEnabled).toBe(true);
|
||||||
expect(cfg.enabledTools).toEqual([]);
|
expect(cfg.enabledTools).toEqual([]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("an enabled legacy update also exposes the safer batch update", () => {
|
||||||
|
const cfg = parseConfig(
|
||||||
|
"MUA_API_KEY=test\nMUA_ENABLED_TOOLS=unraid_docker_update\n",
|
||||||
|
);
|
||||||
|
expect(cfg.enabledTools).toContain("unraid_docker_update");
|
||||||
|
expect(cfg.enabledTools).toContain("unraid_docker_update_verified_batch");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("secret handling", () => {
|
describe("secret handling", () => {
|
||||||
@@ -40,6 +48,7 @@ describe("risk classification", () => {
|
|||||||
test("classifies root shell and container changes as critical", () => {
|
test("classifies root shell and container changes as critical", () => {
|
||||||
expect(getToolRisk("unraid_system_shell")).toBe("critical");
|
expect(getToolRisk("unraid_system_shell")).toBe("critical");
|
||||||
expect(getToolRisk("unraid_docker_modify")).toBe("critical");
|
expect(getToolRisk("unraid_docker_modify")).toBe("critical");
|
||||||
|
expect(getToolRisk("unraid_docker_update_verified_batch")).toBe("critical");
|
||||||
expect(getToolRisk("unraid_docker_restart")).toBe("write");
|
expect(getToolRisk("unraid_docker_restart")).toBe("write");
|
||||||
expect(getToolRisk("unraid_network_lan_probe")).toBe("active");
|
expect(getToolRisk("unraid_network_lan_probe")).toBe("active");
|
||||||
expect(getToolRisk("unraid_docker_list")).toBe("read");
|
expect(getToolRisk("unraid_docker_list")).toBe("read");
|
||||||
|
|||||||
+27
-1
@@ -40,6 +40,7 @@ const CRITICAL_TOOLS = new Set([
|
|||||||
"unraid_docker_create",
|
"unraid_docker_create",
|
||||||
"unraid_docker_modify",
|
"unraid_docker_modify",
|
||||||
"unraid_docker_update",
|
"unraid_docker_update",
|
||||||
|
"unraid_docker_update_verified_batch",
|
||||||
"unraid_docker_rebuild",
|
"unraid_docker_rebuild",
|
||||||
"unraid_system_shell",
|
"unraid_system_shell",
|
||||||
"unraid_ca_install",
|
"unraid_ca_install",
|
||||||
@@ -281,7 +282,8 @@ export const TOOLS: ToolDef[] = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "unraid_docker_update",
|
name: "unraid_docker_update",
|
||||||
description: "Update a container (pull latest image, rebuild).",
|
description:
|
||||||
|
"Update one explicitly named container (pull latest image and rebuild). For two or more containers, or when update status may be stale, use unraid_docker_update_verified_batch instead.",
|
||||||
inputSchema: {
|
inputSchema: {
|
||||||
type: "object",
|
type: "object",
|
||||||
properties: { container: str("Container/template name") },
|
properties: { container: str("Container/template name") },
|
||||||
@@ -289,6 +291,30 @@ export const TOOLS: ToolDef[] = [
|
|||||||
},
|
},
|
||||||
handler: (a) => runPhpHelper("update", validateName(a["container"], "container")),
|
handler: (a) => runPhpHelper("update", validateName(a["container"], "container")),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "unraid_docker_update_verified_batch",
|
||||||
|
description:
|
||||||
|
"Authoritative one-call Docker image update workflow for 1-25 explicitly named Unraid containers. Pulls each configured image, compares immutable image IDs, skips already-current containers even if Unraid's cached status is stale, rebuilds only when the image actually changed, preserves every original running/stopped state, and returns compact post-verification (container ID change, state, restart count and health). Use this after read-only update discovery when the user explicitly requested the updates.",
|
||||||
|
inputSchema: {
|
||||||
|
type: "object",
|
||||||
|
properties: {
|
||||||
|
containers: str(
|
||||||
|
"One to 25 exact container names separated by |, as returned by Unraid inventory/update status",
|
||||||
|
),
|
||||||
|
},
|
||||||
|
required: ["containers"],
|
||||||
|
additionalProperties: false,
|
||||||
|
},
|
||||||
|
handler: (a) => {
|
||||||
|
const raw = String(a["containers"] ?? "");
|
||||||
|
const containers = raw.split("|").map((name) => name.trim()).filter(Boolean);
|
||||||
|
if (containers.length < 1 || containers.length > 25) {
|
||||||
|
throw new Error("containers must contain 1-25 names separated by |");
|
||||||
|
}
|
||||||
|
const unique = [...new Set(containers.map((name) => validateName(name, "container")))];
|
||||||
|
return runPhpHelper("update-verified-batch", unique.join("|"));
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "unraid_docker_rebuild",
|
name: "unraid_docker_rebuild",
|
||||||
description:
|
description:
|
||||||
|
|||||||
Reference in New Issue
Block a user