unraidclaw 0.1.14 → 0.1.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # unraidclaw
2
2
 
3
- > OpenClaw plugin to manage your Unraid server through AI agents — Docker, VMs, array, shares, system, notifications, and more, with permission control.
3
+ > OpenClaw plugin to manage your Unraid server through AI agents: Docker, VMs, array, shares, system, notifications, and more, with permission control.
4
4
 
5
5
  [![npm](https://img.shields.io/npm/v/unraidclaw)](https://www.npmjs.com/package/unraidclaw)
6
6
 
@@ -8,22 +8,30 @@ This is the [OpenClaw](https://github.com/openclaw/openclaw) plugin for **[Unrai
8
8
 
9
9
  ## Prerequisites
10
10
 
11
- 1. **The UnraidClaw plugin installed on your Unraid server** — install it from the Unraid Community Apps store, or see the [main repo](https://github.com/emaspa/unraidclaw). It runs the gateway on port `9876` (HTTPS).
12
- 2. **An UnraidClaw API key** — generate one on the **Settings → UnraidClaw** page in the Unraid WebGUI.
11
+ 1. **The UnraidClaw plugin installed on your Unraid server.** Install it from the Unraid Community Apps store, or see the [main repo](https://github.com/emaspa/unraidclaw). It runs the gateway on port `9876` (HTTPS) by default.
12
+ 2. **An UnraidClaw API key.** Generate one on the **Settings > UnraidClaw** page in the Unraid WebGUI.
13
13
  3. **OpenClaw** installed (`openclaw --version`).
14
14
 
15
15
  ## Install
16
16
 
17
17
  ```bash
18
- npm pack unraidclaw && openclaw plugins install unraidclaw-*.tgz && rm unraidclaw-*.tgz
18
+ openclaw plugins install clawhub:unraidclaw --accept-capabilities
19
+ ```
20
+
21
+ The same package is on npm. OpenClaw asks you to confirm installs from outside ClawHub, so installing from npm needs `--force`:
22
+
23
+ ```bash
24
+ openclaw plugins install unraidclaw --force --accept-capabilities
19
25
  ```
20
26
 
21
27
  To update to the latest version:
22
28
 
23
29
  ```bash
24
- rm -rf ~/.openclaw/extensions/unraidclaw && npm pack unraidclaw && openclaw plugins install unraidclaw-*.tgz && rm unraidclaw-*.tgz
30
+ openclaw plugins update unraidclaw --accept-capabilities
25
31
  ```
26
32
 
33
+ Then restart the gateway with `openclaw gateway restart` so it loads the new version.
34
+
27
35
  ## Configure
28
36
 
29
37
  Edit `~/.openclaw/openclaw.json`.
@@ -57,8 +65,8 @@ Edit `~/.openclaw/openclaw.json`.
57
65
  "unraidclaw": {
58
66
  "config": {
59
67
  "servers": [
60
- { "name": "home", "serverUrl": "https://192.168.1.100:9876", "apiKey": "...", "tlsSkipVerify": true, "default": true },
61
- { "name": "work", "serverUrl": "https://10.0.0.50:9876", "apiKey": "..." }
68
+ { "name": "home", "serverUrl": "https://<home-server>:9876", "apiKey": "<api-key>", "tlsSkipVerify": true, "default": true },
69
+ { "name": "work", "serverUrl": "https://<work-server>:9876", "apiKey": "<api-key>" }
62
70
  ]
63
71
  }
64
72
  }
@@ -67,21 +75,21 @@ Edit `~/.openclaw/openclaw.json`.
67
75
  }
68
76
  ```
69
77
 
70
- With multi-server config, every tool accepts an optional `server` parameter (e.g. `unraid_docker_list(server: "work")`); the default server is used when it's omitted.
78
+ With multi-server config, every tool accepts an optional `server` parameter (e.g. `unraid_docker_list(server: "work")`); the first server marked `default` is used when it's omitted, or the first configured server if none is marked.
71
79
 
72
- Set `tlsSkipVerify: true` when using UnraidClaw's auto-generated self-signed certificate.
80
+ Set `tlsSkipVerify: true` to accept the gateway's self-signed certificate. The plugin does not verify the certificate in that mode, so regenerating the certificate on the server does not affect it. The repository README's [TLS certificate](https://github.com/emaspa/unraidclaw#tls-certificate) section explains what the certificate contains and how strict clients can trust it.
73
81
 
74
82
  ### Keeping the API key out of the config file
75
83
 
76
84
  You don't have to hard-code the key in `openclaw.json`. Two options:
77
85
 
78
- **Environment variable** — OpenClaw expands `${VAR}` references at config-load time:
86
+ **Environment variable.** OpenClaw expands `${VAR}` references at config-load time:
79
87
 
80
88
  ```json
81
89
  "apiKey": "${UNRAID_API_KEY}"
82
90
  ```
83
91
 
84
- **Provider-backed secret (`SecretRef`)** — point `apiKey` at one of your configured secret providers; OpenClaw resolves it before the plugin loads, so the plugin only ever sees the resolved string:
92
+ **Provider-backed secret (`SecretRef`).** Point `apiKey` at one of your configured secret providers; OpenClaw resolves it before the plugin loads, so the plugin only ever sees the resolved string:
85
93
 
86
94
  ```json
87
95
  "apiKey": { "source": "file", "provider": "default", "id": "/unraidclaw_key" }
@@ -91,7 +99,7 @@ You don't have to hard-code the key in `openclaw.json`. Two options:
91
99
 
92
100
  ## Usage
93
101
 
94
- Once installed and configured, just ask your agent:
102
+ Once installed and configured, ask your agent:
95
103
 
96
104
  - "List all running Docker containers"
97
105
  - "Stop the plex container"
@@ -129,7 +137,7 @@ Once installed and configured, just ask your agent:
129
137
 
130
138
  The six plugin tools manage Unraid `.plg` plugins through Unraid's own plugin manager. Installing one runs vendor code as root, checking for an update downloads a plugin file and stages it, and removing one runs the plugin's removal script, which may take its data with it. All four mutating tools take `dryRun`.
131
139
 
132
- Every tool is gated by a 30-key `resource:action` permission matrix configured from the Unraid WebGUI, so you control exactly what agents can do.
140
+ Tools use the gateway's 30-key `resource:action` permission matrix configured from the Unraid WebGUI. Health requires no permission.
133
141
 
134
142
  ## Links
135
143
 
@@ -137,6 +145,10 @@ Every tool is gated by a 30-key `resource:action` permission matrix configured f
137
145
  - [Issues](https://github.com/emaspa/unraidclaw/issues)
138
146
  - [Unraid Community Apps](https://unraid.net/community/apps)
139
147
 
148
+ ## Gateway MCP mode
149
+
150
+ The gateway has an optional MCP endpoint at `/mcp` that serves the same 55 tools to MCP clients. It is off by default and is switched on with **Enable MCP** in the gateway's Settings tab. This plugin does not use it: OpenClaw keeps calling `/api/*` whether MCP is on or off. The tool definitions in this package are shared with the gateway through the `unraidclaw/tools` export, so OpenClaw, MCP and the standalone CLI use the same tools and the `READ_ONLY` set exported by `src/registry.ts`. The CLI, command `unraidclaw`, is attached to each [GitHub release](https://github.com/emaspa/unraidclaw/releases/latest) as `unraidclaw-cli-<version>.tar.gz`; see the [CLI guide](https://github.com/emaspa/unraidclaw/blob/main/packages/cli/README.md). See the [repository README](https://github.com/emaspa/unraidclaw#mcp) for MCP client setup.
151
+
140
152
  ## License
141
153
 
142
154
  MIT
package/dist/index.js CHANGED
@@ -109,9 +109,12 @@ var UnraidClient = class {
109
109
  function textResult(data) {
110
110
  return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
111
111
  }
112
+ var failures = /* @__PURE__ */ new WeakSet();
112
113
  function errorResult(err) {
113
114
  const message = err instanceof Error ? err.message : String(err);
114
- return { content: [{ type: "text", text: `Error: ${message}` }] };
115
+ const result = { content: [{ type: "text", text: `Error: ${message}` }] };
116
+ failures.add(result);
117
+ return result;
115
118
  }
116
119
  function checkParams(params, allowed) {
117
120
  for (const key of Object.keys(params)) {
@@ -194,7 +197,7 @@ function registerDockerTools(api, getClient) {
194
197
  type: "object",
195
198
  properties: {
196
199
  id: { type: "string", description: "Container ID or name" },
197
- tail: { type: "number", description: "Number of lines from the end (default: 100)" },
200
+ tail: { type: "integer", minimum: 1, maximum: 1e4, description: "Number of lines from the end (default: 100)" },
198
201
  since: { type: "string", description: "Show logs since timestamp (e.g., 2024-01-01T00:00:00Z)" },
199
202
  server: { type: "string", description: "Target server name (optional, uses default server)" }
200
203
  },
@@ -1078,7 +1081,7 @@ function registerNotificationTools(api, getClient) {
1078
1081
  title: { type: "string", description: "Notification title" },
1079
1082
  subject: { type: "string", description: "Notification subject" },
1080
1083
  description: { type: "string", description: "Notification body text" },
1081
- importance: { type: "string", description: "Importance level: alert, warning, or normal" },
1084
+ importance: { type: "string", enum: ["normal", "warning", "alert"], description: "Importance level: alert, warning, or normal" },
1082
1085
  server: { type: "string", description: "Target server name (optional, uses default server)" }
1083
1086
  },
1084
1087
  required: ["title", "subject", "description"]
@@ -1206,6 +1209,23 @@ function registerLogTools(api, getClient) {
1206
1209
  });
1207
1210
  }
1208
1211
 
1212
+ // src/registry.ts
1213
+ function registerTools(api, getClient) {
1214
+ registerHealthTools(api, getClient);
1215
+ registerDockerTools(api, getClient);
1216
+ registerCaTools(api, getClient);
1217
+ registerPluginTools(api, getClient);
1218
+ registerVMTools(api, getClient);
1219
+ registerArrayTools(api, getClient);
1220
+ registerDiskTools(api, getClient);
1221
+ registerShareTools(api, getClient);
1222
+ registerSystemTools(api, getClient);
1223
+ registerNotificationTools(api, getClient);
1224
+ registerNetworkTools(api, getClient);
1225
+ registerUserTools(api, getClient);
1226
+ registerLogTools(api, getClient);
1227
+ }
1228
+
1209
1229
  // src/index.ts
1210
1230
  function resolveServers(api) {
1211
1231
  const raw = api.config?.servers ?? api.pluginConfig?.servers ?? api.config?.plugins?.entries?.unraidclaw?.config?.servers;
@@ -1235,19 +1255,7 @@ function register(api) {
1235
1255
  }
1236
1256
  return client;
1237
1257
  }
1238
- registerHealthTools(api, getClient);
1239
- registerDockerTools(api, getClient);
1240
- registerCaTools(api, getClient);
1241
- registerPluginTools(api, getClient);
1242
- registerVMTools(api, getClient);
1243
- registerArrayTools(api, getClient);
1244
- registerDiskTools(api, getClient);
1245
- registerShareTools(api, getClient);
1246
- registerSystemTools(api, getClient);
1247
- registerNotificationTools(api, getClient);
1248
- registerNetworkTools(api, getClient);
1249
- registerUserTools(api, getClient);
1250
- registerLogTools(api, getClient);
1258
+ registerTools(api, getClient);
1251
1259
  const servers = resolveServers(api);
1252
1260
  if (servers.length > 1) {
1253
1261
  log.info(`UnraidClaw: registered tools for ${servers.length} servers: ${servers.map((s) => s.name).join(", ")}`);
@@ -0,0 +1,38 @@
1
+ interface ToolDefinition {
2
+ name: string;
3
+ description: string;
4
+ parameters: JsonSchema;
5
+ execute: (id: string, params: Record<string, unknown>) => Promise<ToolResult>;
6
+ }
7
+ interface ToolOptions {
8
+ optional?: boolean;
9
+ }
10
+ interface JsonSchema {
11
+ type: string;
12
+ properties?: Record<string, unknown>;
13
+ required?: string[];
14
+ additionalProperties?: boolean;
15
+ }
16
+ interface ToolResult {
17
+ content: Array<{
18
+ type: "text";
19
+ text: string;
20
+ }>;
21
+ }
22
+
23
+ declare function isErrorResult(result: ToolResult): boolean;
24
+
25
+ /** The tools depend only on this transport, not on an HTTP client or host. */
26
+ interface ToolClient {
27
+ get<T>(path: string, query?: Record<string, string>): Promise<T>;
28
+ post<T>(path: string, body?: unknown): Promise<T>;
29
+ patch<T>(path: string, body?: unknown): Promise<T>;
30
+ delete<T>(path: string): Promise<T>;
31
+ }
32
+ type ClientResolver = (serverName?: string) => ToolClient;
33
+ declare function registerTools(api: {
34
+ registerTool(tool: ToolDefinition, options?: ToolOptions): void;
35
+ }, getClient: ClientResolver): void;
36
+ declare const READ_ONLY: Set<string>;
37
+
38
+ export { type ClientResolver, READ_ONLY, type ToolClient, type ToolDefinition, type ToolOptions, type ToolResult, isErrorResult, registerTools };