unraidclaw 0.1.5 → 0.1.6

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/dist/index.d.ts CHANGED
@@ -1,3 +1,21 @@
1
+ interface ClientConfig {
2
+ serverUrl: string;
3
+ apiKey: string;
4
+ tlsSkipVerify?: boolean;
5
+ }
6
+ declare class UnraidClient {
7
+ private configResolver;
8
+ private insecureAgent;
9
+ constructor(configResolver: () => ClientConfig);
10
+ private getConfig;
11
+ get<T>(path: string, query?: Record<string, string>): Promise<T>;
12
+ post<T>(path: string, body?: unknown): Promise<T>;
13
+ patch<T>(path: string, body?: unknown): Promise<T>;
14
+ delete<T>(path: string): Promise<T>;
15
+ private doRequest;
16
+ }
17
+
18
+ type ClientResolver = (serverName?: string) => UnraidClient;
1
19
  declare function register(api: any): void;
2
20
 
3
- export { register as default };
21
+ export { type ClientResolver, register as default };
package/dist/index.js CHANGED
@@ -115,14 +115,19 @@ function errorResult(err) {
115
115
  }
116
116
 
117
117
  // src/tools/health.ts
118
- function registerHealthTools(api, client) {
118
+ function registerHealthTools(api, getClient) {
119
119
  api.registerTool({
120
120
  name: "unraid_health_check",
121
121
  description: "Check the health status of the Unraid server connection, including API and GraphQL reachability.",
122
- parameters: { type: "object", properties: {} },
123
- execute: async () => {
122
+ parameters: {
123
+ type: "object",
124
+ properties: {
125
+ server: { type: "string", description: "Target server name (optional, uses default server)" }
126
+ }
127
+ },
128
+ execute: async (_id, params) => {
124
129
  try {
125
- return textResult(await client.get("/api/health"));
130
+ return textResult(await getClient(params.server).get("/api/health"));
126
131
  } catch (err) {
127
132
  return errorResult(err);
128
133
  }
@@ -131,14 +136,19 @@ function registerHealthTools(api, client) {
131
136
  }
132
137
 
133
138
  // src/tools/docker.ts
134
- function registerDockerTools(api, client) {
139
+ function registerDockerTools(api, getClient) {
135
140
  api.registerTool({
136
141
  name: "unraid_docker_list",
137
142
  description: "List all Docker containers on the Unraid server with their current state, image, and status.",
138
- parameters: { type: "object", properties: {} },
139
- execute: async () => {
143
+ parameters: {
144
+ type: "object",
145
+ properties: {
146
+ server: { type: "string", description: "Target server name (optional, uses default server)" }
147
+ }
148
+ },
149
+ execute: async (_id, params) => {
140
150
  try {
141
- return textResult(await client.get("/api/docker/containers"));
151
+ return textResult(await getClient(params.server).get("/api/docker/containers"));
142
152
  } catch (err) {
143
153
  return errorResult(err);
144
154
  }
@@ -150,13 +160,14 @@ function registerDockerTools(api, client) {
150
160
  parameters: {
151
161
  type: "object",
152
162
  properties: {
153
- id: { type: "string", description: "Container ID or name" }
163
+ id: { type: "string", description: "Container ID or name" },
164
+ server: { type: "string", description: "Target server name (optional, uses default server)" }
154
165
  },
155
166
  required: ["id"]
156
167
  },
157
168
  execute: async (_id, params) => {
158
169
  try {
159
- return textResult(await client.get(`/api/docker/containers/${params.id}`));
170
+ return textResult(await getClient(params.server).get(`/api/docker/containers/${params.id}`));
160
171
  } catch (err) {
161
172
  return errorResult(err);
162
173
  }
@@ -170,7 +181,8 @@ function registerDockerTools(api, client) {
170
181
  properties: {
171
182
  id: { type: "string", description: "Container ID or name" },
172
183
  tail: { type: "number", description: "Number of lines from the end (default: 100)" },
173
- since: { type: "string", description: "Show logs since timestamp (e.g., 2024-01-01T00:00:00Z)" }
184
+ since: { type: "string", description: "Show logs since timestamp (e.g., 2024-01-01T00:00:00Z)" },
185
+ server: { type: "string", description: "Target server name (optional, uses default server)" }
174
186
  },
175
187
  required: ["id"]
176
188
  },
@@ -179,102 +191,39 @@ function registerDockerTools(api, client) {
179
191
  const query = {};
180
192
  if (params.tail) query.tail = String(params.tail);
181
193
  if (params.since) query.since = String(params.since);
182
- return textResult(await client.get(`/api/docker/containers/${params.id}/logs`, query));
183
- } catch (err) {
184
- return errorResult(err);
185
- }
186
- }
187
- });
188
- api.registerTool({
189
- name: "unraid_docker_start",
190
- description: "Start a stopped Docker container.",
191
- parameters: {
192
- type: "object",
193
- properties: {
194
- id: { type: "string", description: "Container ID or name" }
195
- },
196
- required: ["id"]
197
- },
198
- execute: async (_id, params) => {
199
- try {
200
- return textResult(await client.post(`/api/docker/containers/${params.id}/start`));
201
- } catch (err) {
202
- return errorResult(err);
203
- }
204
- }
205
- });
206
- api.registerTool({
207
- name: "unraid_docker_stop",
208
- description: "Stop a running Docker container.",
209
- parameters: {
210
- type: "object",
211
- properties: {
212
- id: { type: "string", description: "Container ID or name" }
213
- },
214
- required: ["id"]
215
- },
216
- execute: async (_id, params) => {
217
- try {
218
- return textResult(await client.post(`/api/docker/containers/${params.id}/stop`));
194
+ return textResult(await getClient(params.server).get(`/api/docker/containers/${params.id}/logs`, query));
219
195
  } catch (err) {
220
196
  return errorResult(err);
221
197
  }
222
198
  }
223
199
  });
224
- api.registerTool({
225
- name: "unraid_docker_restart",
226
- description: "Restart a Docker container.",
227
- parameters: {
228
- type: "object",
229
- properties: {
230
- id: { type: "string", description: "Container ID or name" }
231
- },
232
- required: ["id"]
233
- },
234
- execute: async (_id, params) => {
235
- try {
236
- return textResult(await client.post(`/api/docker/containers/${params.id}/restart`));
237
- } catch (err) {
238
- return errorResult(err);
239
- }
240
- }
241
- });
242
- api.registerTool({
243
- name: "unraid_docker_pause",
244
- description: "Pause a running Docker container (freeze all processes).",
245
- parameters: {
246
- type: "object",
247
- properties: {
248
- id: { type: "string", description: "Container ID or name" }
249
- },
250
- required: ["id"]
251
- },
252
- execute: async (_id, params) => {
253
- try {
254
- return textResult(await client.post(`/api/docker/containers/${params.id}/pause`));
255
- } catch (err) {
256
- return errorResult(err);
257
- }
258
- }
259
- });
260
- api.registerTool({
261
- name: "unraid_docker_unpause",
262
- description: "Unpause a paused Docker container.",
263
- parameters: {
264
- type: "object",
265
- properties: {
266
- id: { type: "string", description: "Container ID or name" }
200
+ for (const [toolName, desc, action] of [
201
+ ["unraid_docker_start", "Start a stopped Docker container.", "start"],
202
+ ["unraid_docker_stop", "Stop a running Docker container.", "stop"],
203
+ ["unraid_docker_restart", "Restart a Docker container.", "restart"],
204
+ ["unraid_docker_pause", "Pause a running Docker container (freeze all processes).", "pause"],
205
+ ["unraid_docker_unpause", "Unpause a paused Docker container.", "unpause"]
206
+ ]) {
207
+ api.registerTool({
208
+ name: toolName,
209
+ description: desc,
210
+ parameters: {
211
+ type: "object",
212
+ properties: {
213
+ id: { type: "string", description: "Container ID or name" },
214
+ server: { type: "string", description: "Target server name (optional, uses default server)" }
215
+ },
216
+ required: ["id"]
267
217
  },
268
- required: ["id"]
269
- },
270
- execute: async (_id, params) => {
271
- try {
272
- return textResult(await client.post(`/api/docker/containers/${params.id}/unpause`));
273
- } catch (err) {
274
- return errorResult(err);
218
+ execute: async (_id, params) => {
219
+ try {
220
+ return textResult(await getClient(params.server).post(`/api/docker/containers/${params.id}/${action}`));
221
+ } catch (err) {
222
+ return errorResult(err);
223
+ }
275
224
  }
276
- }
277
- });
225
+ });
226
+ }
278
227
  api.registerTool({
279
228
  name: "unraid_docker_remove",
280
229
  description: "Remove a Docker container. Pass force=true to stop and remove in one step. This is a destructive operation that cannot be undone.",
@@ -282,14 +231,15 @@ function registerDockerTools(api, client) {
282
231
  type: "object",
283
232
  properties: {
284
233
  id: { type: "string", description: "Container ID or name" },
285
- force: { type: "boolean", description: "Stop the container before removing (default: false)" }
234
+ force: { type: "boolean", description: "Stop the container before removing (default: false)" },
235
+ server: { type: "string", description: "Target server name (optional, uses default server)" }
286
236
  },
287
237
  required: ["id"]
288
238
  },
289
239
  execute: async (_id, params) => {
290
240
  try {
291
241
  const query = params.force ? "?force=true" : "";
292
- return textResult(await client.delete(`/api/docker/containers/${params.id}${query}`));
242
+ return textResult(await getClient(params.server).delete(`/api/docker/containers/${params.id}${query}`));
293
243
  } catch (err) {
294
244
  return errorResult(err);
295
245
  }
@@ -323,13 +273,15 @@ function registerDockerTools(api, client) {
323
273
  enum: ["no", "always", "unless-stopped", "on-failure"],
324
274
  description: "Restart policy (default: unless-stopped)"
325
275
  },
326
- network: { type: "string", description: "Network to attach the container to" }
276
+ network: { type: "string", description: "Network to attach the container to" },
277
+ server: { type: "string", description: "Target server name (optional, uses default server)" }
327
278
  },
328
279
  required: ["image"]
329
280
  },
330
281
  execute: async (_id, params) => {
331
282
  try {
332
- return textResult(await client.post("/api/docker/containers", params));
283
+ const { server, ...body } = params;
284
+ return textResult(await getClient(server).post("/api/docker/containers", body));
333
285
  } catch (err) {
334
286
  return errorResult(err);
335
287
  }
@@ -338,194 +290,127 @@ function registerDockerTools(api, client) {
338
290
  }
339
291
 
340
292
  // src/tools/vms.ts
341
- function registerVMTools(api, client) {
293
+ function registerVMTools(api, getClient) {
342
294
  api.registerTool({
343
295
  name: "unraid_vm_list",
344
296
  description: "List all virtual machines on the Unraid server with their current state.",
345
- parameters: { type: "object", properties: {} },
346
- execute: async () => {
347
- try {
348
- return textResult(await client.get("/api/vms"));
349
- } catch (err) {
350
- return errorResult(err);
351
- }
352
- }
353
- });
354
- api.registerTool({
355
- name: "unraid_vm_inspect",
356
- description: "Get detailed information about a specific virtual machine.",
357
297
  parameters: {
358
298
  type: "object",
359
299
  properties: {
360
- id: { type: "string", description: "VM ID or name" }
361
- },
362
- required: ["id"]
363
- },
364
- execute: async (_id, params) => {
365
- try {
366
- return textResult(await client.get(`/api/vms/${params.id}`));
367
- } catch (err) {
368
- return errorResult(err);
300
+ server: { type: "string", description: "Target server name (optional, uses default server)" }
369
301
  }
370
- }
371
- });
372
- api.registerTool({
373
- name: "unraid_vm_start",
374
- description: "Start a stopped virtual machine.",
375
- parameters: {
376
- type: "object",
377
- properties: {
378
- id: { type: "string", description: "VM ID or name" }
379
- },
380
- required: ["id"]
381
302
  },
382
303
  execute: async (_id, params) => {
383
304
  try {
384
- return textResult(await client.post(`/api/vms/${params.id}/start`));
305
+ return textResult(await getClient(params.server).get("/api/vms"));
385
306
  } catch (err) {
386
307
  return errorResult(err);
387
308
  }
388
309
  }
389
310
  });
311
+ for (const [name, desc, action] of [
312
+ ["unraid_vm_inspect", "Get detailed information about a specific virtual machine.", null],
313
+ ["unraid_vm_start", "Start a stopped virtual machine.", "start"],
314
+ ["unraid_vm_stop", "Gracefully stop a running virtual machine (ACPI shutdown).", "stop"],
315
+ ["unraid_vm_pause", "Pause a running virtual machine (suspend to RAM).", "pause"],
316
+ ["unraid_vm_resume", "Resume a paused virtual machine.", "resume"],
317
+ ["unraid_vm_force_stop", "Force stop a virtual machine (equivalent to pulling the power plug). This is destructive and may cause data loss.", "force-stop"],
318
+ ["unraid_vm_reboot", "Reboot a running virtual machine (ACPI reboot).", "reboot"]
319
+ ]) {
320
+ api.registerTool(
321
+ {
322
+ name,
323
+ description: desc,
324
+ parameters: {
325
+ type: "object",
326
+ properties: {
327
+ id: { type: "string", description: "VM ID or name" },
328
+ server: { type: "string", description: "Target server name (optional, uses default server)" }
329
+ },
330
+ required: ["id"]
331
+ },
332
+ execute: async (_id, params) => {
333
+ try {
334
+ const client = getClient(params.server);
335
+ if (action) {
336
+ return textResult(await client.post(`/api/vms/${params.id}/${action}`));
337
+ }
338
+ return textResult(await client.get(`/api/vms/${params.id}`));
339
+ } catch (err) {
340
+ return errorResult(err);
341
+ }
342
+ }
343
+ },
344
+ ...name === "unraid_vm_force_stop" ? [{ optional: true }] : []
345
+ );
346
+ }
347
+ }
348
+
349
+ // src/tools/array.ts
350
+ function registerArrayTools(api, getClient) {
390
351
  api.registerTool({
391
- name: "unraid_vm_stop",
392
- description: "Gracefully stop a running virtual machine (ACPI shutdown).",
352
+ name: "unraid_array_status",
353
+ description: "Get the current status of the Unraid array including state, capacity, disks, and parities. Capacity is in kilobytes (KiB). Disk 'size' fields are in kilobytes (KiB).",
393
354
  parameters: {
394
355
  type: "object",
395
356
  properties: {
396
- id: { type: "string", description: "VM ID or name" }
397
- },
398
- required: ["id"]
357
+ server: { type: "string", description: "Target server name (optional, uses default server)" }
358
+ }
399
359
  },
400
360
  execute: async (_id, params) => {
401
361
  try {
402
- return textResult(await client.post(`/api/vms/${params.id}/stop`));
362
+ return textResult(await getClient(params.server).get("/api/array/status"));
403
363
  } catch (err) {
404
364
  return errorResult(err);
405
365
  }
406
366
  }
407
367
  });
408
368
  api.registerTool({
409
- name: "unraid_vm_pause",
410
- description: "Pause a running virtual machine (suspend to RAM).",
369
+ name: "unraid_array_start",
370
+ description: "Start the Unraid array. This will mount all disks and start Docker/VMs if configured.",
411
371
  parameters: {
412
372
  type: "object",
413
373
  properties: {
414
- id: { type: "string", description: "VM ID or name" }
415
- },
416
- required: ["id"]
374
+ server: { type: "string", description: "Target server name (optional, uses default server)" }
375
+ }
417
376
  },
418
377
  execute: async (_id, params) => {
419
378
  try {
420
- return textResult(await client.post(`/api/vms/${params.id}/pause`));
379
+ return textResult(await getClient(params.server).post("/api/array/start"));
421
380
  } catch (err) {
422
381
  return errorResult(err);
423
382
  }
424
383
  }
425
384
  });
426
385
  api.registerTool({
427
- name: "unraid_vm_resume",
428
- description: "Resume a paused virtual machine.",
386
+ name: "unraid_array_stop",
387
+ description: "Stop the Unraid array. This will stop all Docker containers and VMs, then unmount all disks.",
429
388
  parameters: {
430
389
  type: "object",
431
390
  properties: {
432
- id: { type: "string", description: "VM ID or name" }
433
- },
434
- required: ["id"]
391
+ server: { type: "string", description: "Target server name (optional, uses default server)" }
392
+ }
435
393
  },
436
394
  execute: async (_id, params) => {
437
395
  try {
438
- return textResult(await client.post(`/api/vms/${params.id}/resume`));
396
+ return textResult(await getClient(params.server).post("/api/array/stop"));
439
397
  } catch (err) {
440
398
  return errorResult(err);
441
399
  }
442
400
  }
443
401
  });
444
- api.registerTool(
445
- {
446
- name: "unraid_vm_force_stop",
447
- description: "Force stop a virtual machine (equivalent to pulling the power plug). This is destructive and may cause data loss.",
448
- parameters: {
449
- type: "object",
450
- properties: {
451
- id: { type: "string", description: "VM ID or name" }
452
- },
453
- required: ["id"]
454
- },
455
- execute: async (_id, params) => {
456
- try {
457
- return textResult(await client.post(`/api/vms/${params.id}/force-stop`));
458
- } catch (err) {
459
- return errorResult(err);
460
- }
461
- }
462
- }
463
- );
464
402
  api.registerTool({
465
- name: "unraid_vm_reboot",
466
- description: "Reboot a running virtual machine (ACPI reboot).",
403
+ name: "unraid_parity_status",
404
+ description: "Get the current parity check status (running, progress, speed, errors).",
467
405
  parameters: {
468
406
  type: "object",
469
407
  properties: {
470
- id: { type: "string", description: "VM ID or name" }
471
- },
472
- required: ["id"]
408
+ server: { type: "string", description: "Target server name (optional, uses default server)" }
409
+ }
473
410
  },
474
411
  execute: async (_id, params) => {
475
412
  try {
476
- return textResult(await client.post(`/api/vms/${params.id}/reboot`));
477
- } catch (err) {
478
- return errorResult(err);
479
- }
480
- }
481
- });
482
- }
483
-
484
- // src/tools/array.ts
485
- function registerArrayTools(api, client) {
486
- api.registerTool({
487
- name: "unraid_array_status",
488
- description: "Get the current status of the Unraid array including state, capacity, disks, and parities. Capacity is in kilobytes (KiB). Disk 'size' fields are in kilobytes (KiB).",
489
- parameters: { type: "object", properties: {} },
490
- execute: async () => {
491
- try {
492
- return textResult(await client.get("/api/array/status"));
493
- } catch (err) {
494
- return errorResult(err);
495
- }
496
- }
497
- });
498
- api.registerTool({
499
- name: "unraid_array_start",
500
- description: "Start the Unraid array. This will mount all disks and start Docker/VMs if configured.",
501
- parameters: { type: "object", properties: {} },
502
- execute: async () => {
503
- try {
504
- return textResult(await client.post("/api/array/start"));
505
- } catch (err) {
506
- return errorResult(err);
507
- }
508
- }
509
- });
510
- api.registerTool({
511
- name: "unraid_array_stop",
512
- description: "Stop the Unraid array. This will stop all Docker containers and VMs, then unmount all disks.",
513
- parameters: { type: "object", properties: {} },
514
- execute: async () => {
515
- try {
516
- return textResult(await client.post("/api/array/stop"));
517
- } catch (err) {
518
- return errorResult(err);
519
- }
520
- }
521
- });
522
- api.registerTool({
523
- name: "unraid_parity_status",
524
- description: "Get the current parity check status (running, progress, speed, errors).",
525
- parameters: { type: "object", properties: {} },
526
- execute: async () => {
527
- try {
528
- return textResult(await client.get("/api/array/parity/status"));
413
+ return textResult(await getClient(params.server).get("/api/array/parity/status"));
529
414
  } catch (err) {
530
415
  return errorResult(err);
531
416
  }
@@ -540,12 +425,13 @@ function registerArrayTools(api, client) {
540
425
  correct: {
541
426
  type: "boolean",
542
427
  description: "If true, run a correcting parity check. Defaults to false (non-correcting)."
543
- }
428
+ },
429
+ server: { type: "string", description: "Target server name (optional, uses default server)" }
544
430
  }
545
431
  },
546
432
  execute: async (_id, params) => {
547
433
  try {
548
- return textResult(await client.post("/api/array/parity/start", { correct: params.correct ?? false }));
434
+ return textResult(await getClient(params.server).post("/api/array/parity/start", { correct: params.correct ?? false }));
549
435
  } catch (err) {
550
436
  return errorResult(err);
551
437
  }
@@ -554,10 +440,15 @@ function registerArrayTools(api, client) {
554
440
  api.registerTool({
555
441
  name: "unraid_parity_pause",
556
442
  description: "Pause a running parity check.",
557
- parameters: { type: "object", properties: {} },
558
- execute: async () => {
443
+ parameters: {
444
+ type: "object",
445
+ properties: {
446
+ server: { type: "string", description: "Target server name (optional, uses default server)" }
447
+ }
448
+ },
449
+ execute: async (_id, params) => {
559
450
  try {
560
- return textResult(await client.post("/api/array/parity/pause"));
451
+ return textResult(await getClient(params.server).post("/api/array/parity/pause"));
561
452
  } catch (err) {
562
453
  return errorResult(err);
563
454
  }
@@ -566,10 +457,15 @@ function registerArrayTools(api, client) {
566
457
  api.registerTool({
567
458
  name: "unraid_parity_resume",
568
459
  description: "Resume a paused parity check.",
569
- parameters: { type: "object", properties: {} },
570
- execute: async () => {
460
+ parameters: {
461
+ type: "object",
462
+ properties: {
463
+ server: { type: "string", description: "Target server name (optional, uses default server)" }
464
+ }
465
+ },
466
+ execute: async (_id, params) => {
571
467
  try {
572
- return textResult(await client.post("/api/array/parity/resume"));
468
+ return textResult(await getClient(params.server).post("/api/array/parity/resume"));
573
469
  } catch (err) {
574
470
  return errorResult(err);
575
471
  }
@@ -578,10 +474,15 @@ function registerArrayTools(api, client) {
578
474
  api.registerTool({
579
475
  name: "unraid_parity_cancel",
580
476
  description: "Cancel a running or paused parity check.",
581
- parameters: { type: "object", properties: {} },
582
- execute: async () => {
477
+ parameters: {
478
+ type: "object",
479
+ properties: {
480
+ server: { type: "string", description: "Target server name (optional, uses default server)" }
481
+ }
482
+ },
483
+ execute: async (_id, params) => {
583
484
  try {
584
- return textResult(await client.post("/api/array/parity/cancel"));
485
+ return textResult(await getClient(params.server).post("/api/array/parity/cancel"));
585
486
  } catch (err) {
586
487
  return errorResult(err);
587
488
  }
@@ -590,14 +491,19 @@ function registerArrayTools(api, client) {
590
491
  }
591
492
 
592
493
  // src/tools/disks.ts
593
- function registerDiskTools(api, client) {
494
+ function registerDiskTools(api, getClient) {
594
495
  api.registerTool({
595
496
  name: "unraid_disk_list",
596
497
  description: "List all disks (data + parity) with name, size, used, free, usedPercent, temp, status, and fsType.",
597
- parameters: { type: "object", properties: {} },
598
- execute: async () => {
498
+ parameters: {
499
+ type: "object",
500
+ properties: {
501
+ server: { type: "string", description: "Target server name (optional, uses default server)" }
502
+ }
503
+ },
504
+ execute: async (_id, params) => {
599
505
  try {
600
- return textResult(await client.get("/api/disks"));
506
+ return textResult(await getClient(params.server).get("/api/disks"));
601
507
  } catch (err) {
602
508
  return errorResult(err);
603
509
  }
@@ -609,13 +515,14 @@ function registerDiskTools(api, client) {
609
515
  parameters: {
610
516
  type: "object",
611
517
  properties: {
612
- id: { type: "string", description: "Disk ID (e.g., 'disk1', 'parity')" }
518
+ id: { type: "string", description: "Disk ID (e.g., 'disk1', 'parity')" },
519
+ server: { type: "string", description: "Target server name (optional, uses default server)" }
613
520
  },
614
521
  required: ["id"]
615
522
  },
616
523
  execute: async (_id, params) => {
617
524
  try {
618
- return textResult(await client.get(`/api/disks/${params.id}`));
525
+ return textResult(await getClient(params.server).get(`/api/disks/${params.id}`));
619
526
  } catch (err) {
620
527
  return errorResult(err);
621
528
  }
@@ -624,14 +531,19 @@ function registerDiskTools(api, client) {
624
531
  }
625
532
 
626
533
  // src/tools/shares.ts
627
- function registerShareTools(api, client) {
534
+ function registerShareTools(api, getClient) {
628
535
  api.registerTool({
629
536
  name: "unraid_share_list",
630
537
  description: "List all user shares on the Unraid server with their settings and usage. The 'free' and 'size' fields are in kilobytes (KiB).",
631
- parameters: { type: "object", properties: {} },
632
- execute: async () => {
538
+ parameters: {
539
+ type: "object",
540
+ properties: {
541
+ server: { type: "string", description: "Target server name (optional, uses default server)" }
542
+ }
543
+ },
544
+ execute: async (_id, params) => {
633
545
  try {
634
- return textResult(await client.get("/api/shares"));
546
+ return textResult(await getClient(params.server).get("/api/shares"));
635
547
  } catch (err) {
636
548
  return errorResult(err);
637
549
  }
@@ -643,13 +555,14 @@ function registerShareTools(api, client) {
643
555
  parameters: {
644
556
  type: "object",
645
557
  properties: {
646
- name: { type: "string", description: "Share name" }
558
+ name: { type: "string", description: "Share name" },
559
+ server: { type: "string", description: "Target server name (optional, uses default server)" }
647
560
  },
648
561
  required: ["name"]
649
562
  },
650
563
  execute: async (_id, params) => {
651
564
  try {
652
- return textResult(await client.get(`/api/shares/${params.name}`));
565
+ return textResult(await getClient(params.server).get(`/api/shares/${params.name}`));
653
566
  } catch (err) {
654
567
  return errorResult(err);
655
568
  }
@@ -657,7 +570,7 @@ function registerShareTools(api, client) {
657
570
  });
658
571
  api.registerTool({
659
572
  name: "unraid_share_update",
660
- description: "Update safe settings for a user share. Only affects metadata and future write behavior \u2014 does not move existing data.",
573
+ description: "Update safe settings for a user share. Only affects metadata and future write behavior -- does not move existing data.",
661
574
  parameters: {
662
575
  type: "object",
663
576
  properties: {
@@ -665,14 +578,15 @@ function registerShareTools(api, client) {
665
578
  comment: { type: "string", description: "Share description/comment" },
666
579
  allocator: { type: "string", description: "Disk allocation method: highwater, fill, or most-free" },
667
580
  floor: { type: "string", description: "Minimum free space per disk (e.g. '0' or '50000')" },
668
- splitLevel: { type: "string", description: "Split level for distributing files across disks" }
581
+ splitLevel: { type: "string", description: "Split level for distributing files across disks" },
582
+ server: { type: "string", description: "Target server name (optional, uses default server)" }
669
583
  },
670
584
  required: ["name"]
671
585
  },
672
586
  execute: async (_id, params) => {
673
587
  try {
674
- const { name, ...updates } = params;
675
- return textResult(await client.patch(`/api/shares/${name}`, updates));
588
+ const { name, server, ...updates } = params;
589
+ return textResult(await getClient(server).patch(`/api/shares/${name}`, updates));
676
590
  } catch (err) {
677
591
  return errorResult(err);
678
592
  }
@@ -681,14 +595,19 @@ function registerShareTools(api, client) {
681
595
  }
682
596
 
683
597
  // src/tools/system.ts
684
- function registerSystemTools(api, client) {
598
+ function registerSystemTools(api, getClient) {
685
599
  api.registerTool({
686
600
  name: "unraid_system_info",
687
601
  description: "Get system information: OS (platform, hostname, uptime), CPU (model, cores, threads), memory (total, used, free, percent), and CPU load averages (1m, 5m, 15m).",
688
- parameters: { type: "object", properties: {} },
689
- execute: async () => {
602
+ parameters: {
603
+ type: "object",
604
+ properties: {
605
+ server: { type: "string", description: "Target server name (optional, uses default server)" }
606
+ }
607
+ },
608
+ execute: async (_id, params) => {
690
609
  try {
691
- return textResult(await client.get("/api/system/info"));
610
+ return textResult(await getClient(params.server).get("/api/system/info"));
692
611
  } catch (err) {
693
612
  return errorResult(err);
694
613
  }
@@ -697,10 +616,15 @@ function registerSystemTools(api, client) {
697
616
  api.registerTool({
698
617
  name: "unraid_system_metrics",
699
618
  description: "Get live system metrics: memory (total, used, free bytes, percent) and CPU load averages (1m, 5m, 15m). Lightweight endpoint for polling.",
700
- parameters: { type: "object", properties: {} },
701
- execute: async () => {
619
+ parameters: {
620
+ type: "object",
621
+ properties: {
622
+ server: { type: "string", description: "Target server name (optional, uses default server)" }
623
+ }
624
+ },
625
+ execute: async (_id, params) => {
702
626
  try {
703
- return textResult(await client.get("/api/system/metrics"));
627
+ return textResult(await getClient(params.server).get("/api/system/metrics"));
704
628
  } catch (err) {
705
629
  return errorResult(err);
706
630
  }
@@ -709,10 +633,15 @@ function registerSystemTools(api, client) {
709
633
  api.registerTool({
710
634
  name: "unraid_service_list",
711
635
  description: "List system services and their current state.",
712
- parameters: { type: "object", properties: {} },
713
- execute: async () => {
636
+ parameters: {
637
+ type: "object",
638
+ properties: {
639
+ server: { type: "string", description: "Target server name (optional, uses default server)" }
640
+ }
641
+ },
642
+ execute: async (_id, params) => {
714
643
  try {
715
- return textResult(await client.get("/api/system/services"));
644
+ return textResult(await getClient(params.server).get("/api/system/services"));
716
645
  } catch (err) {
717
646
  return errorResult(err);
718
647
  }
@@ -721,10 +650,15 @@ function registerSystemTools(api, client) {
721
650
  api.registerTool({
722
651
  name: "unraid_system_reboot",
723
652
  description: "Reboot the Unraid server. This is a destructive operation that will interrupt all running services, VMs, and containers.",
724
- parameters: { type: "object", properties: {} },
725
- execute: async () => {
653
+ parameters: {
654
+ type: "object",
655
+ properties: {
656
+ server: { type: "string", description: "Target server name (optional, uses default server)" }
657
+ }
658
+ },
659
+ execute: async (_id, params) => {
726
660
  try {
727
- return textResult(await client.post("/api/system/reboot"));
661
+ return textResult(await getClient(params.server).post("/api/system/reboot"));
728
662
  } catch (err) {
729
663
  return errorResult(err);
730
664
  }
@@ -733,10 +667,15 @@ function registerSystemTools(api, client) {
733
667
  api.registerTool({
734
668
  name: "unraid_system_shutdown",
735
669
  description: "Shut down the Unraid server. This is a destructive operation that will power off the server.",
736
- parameters: { type: "object", properties: {} },
737
- execute: async () => {
670
+ parameters: {
671
+ type: "object",
672
+ properties: {
673
+ server: { type: "string", description: "Target server name (optional, uses default server)" }
674
+ }
675
+ },
676
+ execute: async (_id, params) => {
738
677
  try {
739
- return textResult(await client.post("/api/system/shutdown"));
678
+ return textResult(await getClient(params.server).post("/api/system/shutdown"));
740
679
  } catch (err) {
741
680
  return errorResult(err);
742
681
  }
@@ -745,14 +684,19 @@ function registerSystemTools(api, client) {
745
684
  }
746
685
 
747
686
  // src/tools/notifications.ts
748
- function registerNotificationTools(api, client) {
687
+ function registerNotificationTools(api, getClient) {
749
688
  api.registerTool({
750
689
  name: "unraid_notification_list",
751
690
  description: "List all system notifications with their importance level and archive status.",
752
- parameters: { type: "object", properties: {} },
753
- execute: async () => {
691
+ parameters: {
692
+ type: "object",
693
+ properties: {
694
+ server: { type: "string", description: "Target server name (optional, uses default server)" }
695
+ }
696
+ },
697
+ execute: async (_id, params) => {
754
698
  try {
755
- return textResult(await client.get("/api/notifications"));
699
+ return textResult(await getClient(params.server).get("/api/notifications"));
756
700
  } catch (err) {
757
701
  return errorResult(err);
758
702
  }
@@ -767,7 +711,8 @@ function registerNotificationTools(api, client) {
767
711
  title: { type: "string", description: "Notification title" },
768
712
  subject: { type: "string", description: "Notification subject" },
769
713
  description: { type: "string", description: "Notification body text" },
770
- importance: { type: "string", description: "Importance level: alert, warning, or normal" }
714
+ importance: { type: "string", description: "Importance level: alert, warning, or normal" },
715
+ server: { type: "string", description: "Target server name (optional, uses default server)" }
771
716
  },
772
717
  required: ["title", "subject", "description"]
773
718
  },
@@ -779,7 +724,7 @@ function registerNotificationTools(api, client) {
779
724
  description: params.description
780
725
  };
781
726
  if (params.importance) body.importance = params.importance;
782
- return textResult(await client.post("/api/notifications", body));
727
+ return textResult(await getClient(params.server).post("/api/notifications", body));
783
728
  } catch (err) {
784
729
  return errorResult(err);
785
730
  }
@@ -791,13 +736,14 @@ function registerNotificationTools(api, client) {
791
736
  parameters: {
792
737
  type: "object",
793
738
  properties: {
794
- id: { type: "string", description: "Notification ID" }
739
+ id: { type: "string", description: "Notification ID" },
740
+ server: { type: "string", description: "Target server name (optional, uses default server)" }
795
741
  },
796
742
  required: ["id"]
797
743
  },
798
744
  execute: async (_id, params) => {
799
745
  try {
800
- return textResult(await client.post(`/api/notifications/${params.id}/archive`));
746
+ return textResult(await getClient(params.server).post(`/api/notifications/${params.id}/archive`));
801
747
  } catch (err) {
802
748
  return errorResult(err);
803
749
  }
@@ -809,13 +755,14 @@ function registerNotificationTools(api, client) {
809
755
  parameters: {
810
756
  type: "object",
811
757
  properties: {
812
- id: { type: "string", description: "Notification ID" }
758
+ id: { type: "string", description: "Notification ID" },
759
+ server: { type: "string", description: "Target server name (optional, uses default server)" }
813
760
  },
814
761
  required: ["id"]
815
762
  },
816
763
  execute: async (_id, params) => {
817
764
  try {
818
- return textResult(await client.delete(`/api/notifications/${params.id}`));
765
+ return textResult(await getClient(params.server).delete(`/api/notifications/${params.id}`));
819
766
  } catch (err) {
820
767
  return errorResult(err);
821
768
  }
@@ -824,14 +771,19 @@ function registerNotificationTools(api, client) {
824
771
  }
825
772
 
826
773
  // src/tools/network.ts
827
- function registerNetworkTools(api, client) {
774
+ function registerNetworkTools(api, getClient) {
828
775
  api.registerTool({
829
776
  name: "unraid_network_info",
830
777
  description: "Get network information including hostname, gateway, DNS servers, and all network interfaces.",
831
- parameters: { type: "object", properties: {} },
832
- execute: async () => {
778
+ parameters: {
779
+ type: "object",
780
+ properties: {
781
+ server: { type: "string", description: "Target server name (optional, uses default server)" }
782
+ }
783
+ },
784
+ execute: async (_id, params) => {
833
785
  try {
834
- return textResult(await client.get("/api/network"));
786
+ return textResult(await getClient(params.server).get("/api/network"));
835
787
  } catch (err) {
836
788
  return errorResult(err);
837
789
  }
@@ -840,14 +792,19 @@ function registerNetworkTools(api, client) {
840
792
  }
841
793
 
842
794
  // src/tools/users.ts
843
- function registerUserTools(api, client) {
795
+ function registerUserTools(api, getClient) {
844
796
  api.registerTool({
845
797
  name: "unraid_user_me",
846
798
  description: "Get information about the current authenticated user.",
847
- parameters: { type: "object", properties: {} },
848
- execute: async () => {
799
+ parameters: {
800
+ type: "object",
801
+ properties: {
802
+ server: { type: "string", description: "Target server name (optional, uses default server)" }
803
+ }
804
+ },
805
+ execute: async (_id, params) => {
849
806
  try {
850
- return textResult(await client.get("/api/users/me"));
807
+ return textResult(await getClient(params.server).get("/api/users/me"));
851
808
  } catch (err) {
852
809
  return errorResult(err);
853
810
  }
@@ -856,7 +813,7 @@ function registerUserTools(api, client) {
856
813
  }
857
814
 
858
815
  // src/tools/logs.ts
859
- function registerLogTools(api, client) {
816
+ function registerLogTools(api, getClient) {
860
817
  api.registerTool({
861
818
  name: "unraid_syslog",
862
819
  description: "Get recent syslog entries from the Unraid server. Returns the most recent log lines.",
@@ -866,14 +823,15 @@ function registerLogTools(api, client) {
866
823
  lines: {
867
824
  type: "number",
868
825
  description: "Number of log lines to retrieve (1-1000, default 50)."
869
- }
826
+ },
827
+ server: { type: "string", description: "Target server name (optional, uses default server)" }
870
828
  }
871
829
  },
872
830
  execute: async (_id, params) => {
873
831
  try {
874
832
  const query = {};
875
833
  if (params.lines) query.lines = String(params.lines);
876
- return textResult(await client.get("/api/logs/syslog", query));
834
+ return textResult(await getClient(params.server).get("/api/logs/syslog", query));
877
835
  } catch (err) {
878
836
  return errorResult(err);
879
837
  }
@@ -882,29 +840,53 @@ function registerLogTools(api, client) {
882
840
  }
883
841
 
884
842
  // src/index.ts
885
- function resolveConfig(api) {
886
- if (api.config?.serverUrl) return api.config;
887
- if (api.pluginConfig?.serverUrl) return api.pluginConfig;
888
- const nested = api.config?.plugins?.entries?.unraidclaw?.config;
889
- if (nested?.serverUrl) return nested;
890
- return { serverUrl: "", apiKey: "" };
843
+ function resolveServers(api) {
844
+ const raw = api.config?.servers ?? api.pluginConfig?.servers ?? api.config?.plugins?.entries?.unraidclaw?.config?.servers;
845
+ if (Array.isArray(raw) && raw.length > 0) return raw;
846
+ const single = api.config?.serverUrl ? api.config : api.pluginConfig?.serverUrl ? api.pluginConfig : api.config?.plugins?.entries?.unraidclaw?.config;
847
+ if (single?.serverUrl) {
848
+ return [{ name: "default", serverUrl: single.serverUrl, apiKey: single.apiKey, tlsSkipVerify: single.tlsSkipVerify, default: true }];
849
+ }
850
+ return [];
891
851
  }
892
852
  function register(api) {
893
853
  const log = api.logger || console;
894
- const client = new UnraidClient(() => resolveConfig(api));
895
- registerHealthTools(api, client);
896
- registerDockerTools(api, client);
897
- registerVMTools(api, client);
898
- registerArrayTools(api, client);
899
- registerDiskTools(api, client);
900
- registerShareTools(api, client);
901
- registerSystemTools(api, client);
902
- registerNotificationTools(api, client);
903
- registerNetworkTools(api, client);
904
- registerUserTools(api, client);
905
- registerLogTools(api, client);
906
- const cfg = resolveConfig(api);
907
- log.info(`UnraidClaw: registered 40 tools${cfg.serverUrl ? ", server: " + cfg.serverUrl : " (config will resolve at runtime)"}`);
854
+ const clients = /* @__PURE__ */ new Map();
855
+ function getClient(serverName) {
856
+ const servers2 = resolveServers(api);
857
+ if (servers2.length === 0) {
858
+ return new UnraidClient(() => ({ serverUrl: "", apiKey: "" }));
859
+ }
860
+ const target = serverName ? servers2.find((s) => s.name === serverName) : servers2.find((s) => s.default) ?? servers2[0];
861
+ if (!target) {
862
+ throw new Error(`Server "${serverName}" not found. Available: ${servers2.map((s) => s.name).join(", ")}`);
863
+ }
864
+ let client = clients.get(target.name);
865
+ if (!client) {
866
+ client = new UnraidClient(() => ({ serverUrl: target.serverUrl, apiKey: target.apiKey, tlsSkipVerify: target.tlsSkipVerify }));
867
+ clients.set(target.name, client);
868
+ }
869
+ return client;
870
+ }
871
+ registerHealthTools(api, getClient);
872
+ registerDockerTools(api, getClient);
873
+ registerVMTools(api, getClient);
874
+ registerArrayTools(api, getClient);
875
+ registerDiskTools(api, getClient);
876
+ registerShareTools(api, getClient);
877
+ registerSystemTools(api, getClient);
878
+ registerNotificationTools(api, getClient);
879
+ registerNetworkTools(api, getClient);
880
+ registerUserTools(api, getClient);
881
+ registerLogTools(api, getClient);
882
+ const servers = resolveServers(api);
883
+ if (servers.length > 1) {
884
+ log.info(`UnraidClaw: registered tools for ${servers.length} servers: ${servers.map((s) => s.name).join(", ")}`);
885
+ } else if (servers.length === 1) {
886
+ log.info(`UnraidClaw: registered tools, server: ${servers[0].serverUrl}`);
887
+ } else {
888
+ log.info(`UnraidClaw: registered tools (config will resolve at runtime)`);
889
+ }
908
890
  }
909
891
  export {
910
892
  register as default
@@ -6,19 +6,49 @@
6
6
  "properties": {
7
7
  "serverUrl": {
8
8
  "type": "string",
9
- "description": "URL of the UnraidClaw server (e.g., http://192.168.1.100:9876)"
9
+ "description": "URL of the UnraidClaw server (single-server mode)"
10
10
  },
11
11
  "apiKey": {
12
12
  "type": "string",
13
- "description": "API key for authenticating with UnraidClaw"
13
+ "description": "API key for authenticating with UnraidClaw (single-server mode)"
14
14
  },
15
15
  "tlsSkipVerify": {
16
16
  "type": "boolean",
17
17
  "description": "Skip TLS certificate verification (for self-signed certs)",
18
18
  "default": false
19
+ },
20
+ "servers": {
21
+ "type": "array",
22
+ "description": "Multiple UnraidClaw servers (multi-server mode)",
23
+ "items": {
24
+ "type": "object",
25
+ "properties": {
26
+ "name": {
27
+ "type": "string",
28
+ "description": "Server name used to target this server in tool calls"
29
+ },
30
+ "serverUrl": {
31
+ "type": "string",
32
+ "description": "URL of the UnraidClaw server"
33
+ },
34
+ "apiKey": {
35
+ "type": "string",
36
+ "description": "API key for authenticating with this server"
37
+ },
38
+ "tlsSkipVerify": {
39
+ "type": "boolean",
40
+ "description": "Skip TLS certificate verification",
41
+ "default": false
42
+ },
43
+ "default": {
44
+ "type": "boolean",
45
+ "description": "Use this server when no server name is specified"
46
+ }
47
+ },
48
+ "required": ["name", "serverUrl", "apiKey"]
49
+ }
19
50
  }
20
- },
21
- "required": ["serverUrl", "apiKey"]
51
+ }
22
52
  },
23
53
  "uiHints": {
24
54
  "serverUrl": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "unraidclaw",
3
- "version": "0.1.5",
3
+ "version": "0.1.6",
4
4
  "description": "OpenClaw plugin to manage your Unraid server — Docker, VMs, array, shares, system, and more.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",