unraidclaw 0.1.0

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.
@@ -0,0 +1,3 @@
1
+ declare function register(api: any): void;
2
+
3
+ export { register as default };
package/dist/index.js ADDED
@@ -0,0 +1,844 @@
1
+ // src/client.ts
2
+ var UnraidApiError = class extends Error {
3
+ constructor(message, statusCode, errorCode) {
4
+ super(message);
5
+ this.statusCode = statusCode;
6
+ this.errorCode = errorCode;
7
+ this.name = "UnraidApiError";
8
+ }
9
+ };
10
+ var UnraidClient = class {
11
+ configResolver;
12
+ tlsConfigured = false;
13
+ constructor(configResolver) {
14
+ this.configResolver = configResolver;
15
+ }
16
+ getConfig() {
17
+ const cfg = this.configResolver();
18
+ if (!cfg.serverUrl) {
19
+ throw new UnraidApiError("UnraidClaw serverUrl not configured", 0, "CONFIG_ERROR");
20
+ }
21
+ if (!this.tlsConfigured && cfg.tlsSkipVerify && cfg.serverUrl.startsWith("https")) {
22
+ process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
23
+ this.tlsConfigured = true;
24
+ }
25
+ return {
26
+ baseUrl: cfg.serverUrl.replace(/\/+$/, ""),
27
+ apiKey: cfg.apiKey || ""
28
+ };
29
+ }
30
+ async get(path, query) {
31
+ const { baseUrl } = this.getConfig();
32
+ let url = `${baseUrl}${path}`;
33
+ if (query) {
34
+ const params = new URLSearchParams(query);
35
+ url += `?${params.toString()}`;
36
+ }
37
+ return this.request("GET", url);
38
+ }
39
+ async post(path, body) {
40
+ const { baseUrl } = this.getConfig();
41
+ return this.request("POST", `${baseUrl}${path}`, body);
42
+ }
43
+ async patch(path, body) {
44
+ const { baseUrl } = this.getConfig();
45
+ return this.request("PATCH", `${baseUrl}${path}`, body);
46
+ }
47
+ async delete(path) {
48
+ const { baseUrl } = this.getConfig();
49
+ return this.request("DELETE", `${baseUrl}${path}`);
50
+ }
51
+ async request(method, url, body) {
52
+ const { apiKey } = this.getConfig();
53
+ const headers = {
54
+ "x-api-key": apiKey
55
+ };
56
+ const init = { method, headers };
57
+ if (body !== void 0) {
58
+ headers["Content-Type"] = "application/json";
59
+ init.body = JSON.stringify(body);
60
+ }
61
+ let response;
62
+ try {
63
+ response = await fetch(url, init);
64
+ } catch (err) {
65
+ throw new UnraidApiError(
66
+ `Connection failed: ${err instanceof Error ? err.message : "Unknown error"}`,
67
+ 0,
68
+ "CONNECTION_ERROR"
69
+ );
70
+ }
71
+ const json = await response.json();
72
+ if (!json.ok) {
73
+ throw new UnraidApiError(
74
+ json.error.message,
75
+ response.status,
76
+ json.error.code
77
+ );
78
+ }
79
+ return json.data;
80
+ }
81
+ };
82
+
83
+ // src/tools/util.ts
84
+ function textResult(data) {
85
+ return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
86
+ }
87
+ function errorResult(err) {
88
+ const message = err instanceof Error ? err.message : String(err);
89
+ return { content: [{ type: "text", text: `Error: ${message}` }] };
90
+ }
91
+
92
+ // src/tools/health.ts
93
+ function registerHealthTools(api, client) {
94
+ api.registerTool({
95
+ name: "unraid_health_check",
96
+ description: "Check the health status of the Unraid server connection, including API and GraphQL reachability.",
97
+ parameters: { type: "object" },
98
+ execute: async () => {
99
+ try {
100
+ return textResult(await client.get("/api/health"));
101
+ } catch (err) {
102
+ return errorResult(err);
103
+ }
104
+ }
105
+ });
106
+ }
107
+
108
+ // src/tools/docker.ts
109
+ function registerDockerTools(api, client) {
110
+ api.registerTool({
111
+ name: "unraid_docker_list",
112
+ description: "List all Docker containers on the Unraid server with their current state, image, and status.",
113
+ parameters: { type: "object" },
114
+ execute: async () => {
115
+ try {
116
+ return textResult(await client.get("/api/docker/containers"));
117
+ } catch (err) {
118
+ return errorResult(err);
119
+ }
120
+ }
121
+ });
122
+ api.registerTool({
123
+ name: "unraid_docker_inspect",
124
+ description: "Get detailed information about a specific Docker container including ports, mounts, and network mode.",
125
+ parameters: {
126
+ type: "object",
127
+ properties: {
128
+ id: { type: "string", description: "Container ID or name" }
129
+ },
130
+ required: ["id"]
131
+ },
132
+ execute: async (_id, params) => {
133
+ try {
134
+ return textResult(await client.get(`/api/docker/containers/${params.id}`));
135
+ } catch (err) {
136
+ return errorResult(err);
137
+ }
138
+ }
139
+ });
140
+ api.registerTool({
141
+ name: "unraid_docker_logs",
142
+ description: "Get logs from a specific Docker container.",
143
+ parameters: {
144
+ type: "object",
145
+ properties: {
146
+ id: { type: "string", description: "Container ID or name" },
147
+ tail: { type: "number", description: "Number of lines from the end (default: 100)" },
148
+ since: { type: "string", description: "Show logs since timestamp (e.g., 2024-01-01T00:00:00Z)" }
149
+ },
150
+ required: ["id"]
151
+ },
152
+ execute: async (_id, params) => {
153
+ try {
154
+ const query = {};
155
+ if (params.tail) query.tail = String(params.tail);
156
+ if (params.since) query.since = String(params.since);
157
+ return textResult(await client.get(`/api/docker/containers/${params.id}/logs`, query));
158
+ } catch (err) {
159
+ return errorResult(err);
160
+ }
161
+ }
162
+ });
163
+ api.registerTool({
164
+ name: "unraid_docker_start",
165
+ description: "Start a stopped Docker container.",
166
+ parameters: {
167
+ type: "object",
168
+ properties: {
169
+ id: { type: "string", description: "Container ID or name" }
170
+ },
171
+ required: ["id"]
172
+ },
173
+ execute: async (_id, params) => {
174
+ try {
175
+ return textResult(await client.post(`/api/docker/containers/${params.id}/start`));
176
+ } catch (err) {
177
+ return errorResult(err);
178
+ }
179
+ }
180
+ });
181
+ api.registerTool({
182
+ name: "unraid_docker_stop",
183
+ description: "Stop a running Docker container.",
184
+ parameters: {
185
+ type: "object",
186
+ properties: {
187
+ id: { type: "string", description: "Container ID or name" }
188
+ },
189
+ required: ["id"]
190
+ },
191
+ execute: async (_id, params) => {
192
+ try {
193
+ return textResult(await client.post(`/api/docker/containers/${params.id}/stop`));
194
+ } catch (err) {
195
+ return errorResult(err);
196
+ }
197
+ }
198
+ });
199
+ api.registerTool({
200
+ name: "unraid_docker_restart",
201
+ description: "Restart a Docker container.",
202
+ parameters: {
203
+ type: "object",
204
+ properties: {
205
+ id: { type: "string", description: "Container ID or name" }
206
+ },
207
+ required: ["id"]
208
+ },
209
+ execute: async (_id, params) => {
210
+ try {
211
+ return textResult(await client.post(`/api/docker/containers/${params.id}/restart`));
212
+ } catch (err) {
213
+ return errorResult(err);
214
+ }
215
+ }
216
+ });
217
+ api.registerTool({
218
+ name: "unraid_docker_pause",
219
+ description: "Pause a running Docker container (freeze all processes).",
220
+ parameters: {
221
+ type: "object",
222
+ properties: {
223
+ id: { type: "string", description: "Container ID or name" }
224
+ },
225
+ required: ["id"]
226
+ },
227
+ execute: async (_id, params) => {
228
+ try {
229
+ return textResult(await client.post(`/api/docker/containers/${params.id}/pause`));
230
+ } catch (err) {
231
+ return errorResult(err);
232
+ }
233
+ }
234
+ });
235
+ api.registerTool({
236
+ name: "unraid_docker_unpause",
237
+ description: "Unpause a paused Docker container.",
238
+ parameters: {
239
+ type: "object",
240
+ properties: {
241
+ id: { type: "string", description: "Container ID or name" }
242
+ },
243
+ required: ["id"]
244
+ },
245
+ execute: async (_id, params) => {
246
+ try {
247
+ return textResult(await client.post(`/api/docker/containers/${params.id}/unpause`));
248
+ } catch (err) {
249
+ return errorResult(err);
250
+ }
251
+ }
252
+ });
253
+ api.registerTool({
254
+ name: "unraid_docker_remove",
255
+ description: "Remove a Docker container. This is a destructive operation that cannot be undone.",
256
+ parameters: {
257
+ type: "object",
258
+ properties: {
259
+ id: { type: "string", description: "Container ID or name" }
260
+ },
261
+ required: ["id"]
262
+ },
263
+ execute: async (_id, params) => {
264
+ try {
265
+ return textResult(await client.delete(`/api/docker/containers/${params.id}`));
266
+ } catch (err) {
267
+ return errorResult(err);
268
+ }
269
+ }
270
+ });
271
+ }
272
+
273
+ // src/tools/vms.ts
274
+ function registerVMTools(api, client) {
275
+ api.registerTool({
276
+ name: "unraid_vm_list",
277
+ description: "List all virtual machines on the Unraid server with their current state.",
278
+ parameters: { type: "object" },
279
+ execute: async () => {
280
+ try {
281
+ return textResult(await client.get("/api/vms"));
282
+ } catch (err) {
283
+ return errorResult(err);
284
+ }
285
+ }
286
+ });
287
+ api.registerTool({
288
+ name: "unraid_vm_inspect",
289
+ description: "Get detailed information about a specific virtual machine.",
290
+ parameters: {
291
+ type: "object",
292
+ properties: {
293
+ id: { type: "string", description: "VM ID or name" }
294
+ },
295
+ required: ["id"]
296
+ },
297
+ execute: async (_id, params) => {
298
+ try {
299
+ return textResult(await client.get(`/api/vms/${params.id}`));
300
+ } catch (err) {
301
+ return errorResult(err);
302
+ }
303
+ }
304
+ });
305
+ api.registerTool({
306
+ name: "unraid_vm_start",
307
+ description: "Start a stopped virtual machine.",
308
+ parameters: {
309
+ type: "object",
310
+ properties: {
311
+ id: { type: "string", description: "VM ID or name" }
312
+ },
313
+ required: ["id"]
314
+ },
315
+ execute: async (_id, params) => {
316
+ try {
317
+ return textResult(await client.post(`/api/vms/${params.id}/start`));
318
+ } catch (err) {
319
+ return errorResult(err);
320
+ }
321
+ }
322
+ });
323
+ api.registerTool({
324
+ name: "unraid_vm_stop",
325
+ description: "Gracefully stop a running virtual machine (ACPI shutdown).",
326
+ parameters: {
327
+ type: "object",
328
+ properties: {
329
+ id: { type: "string", description: "VM ID or name" }
330
+ },
331
+ required: ["id"]
332
+ },
333
+ execute: async (_id, params) => {
334
+ try {
335
+ return textResult(await client.post(`/api/vms/${params.id}/stop`));
336
+ } catch (err) {
337
+ return errorResult(err);
338
+ }
339
+ }
340
+ });
341
+ api.registerTool({
342
+ name: "unraid_vm_pause",
343
+ description: "Pause a running virtual machine (suspend to RAM).",
344
+ parameters: {
345
+ type: "object",
346
+ properties: {
347
+ id: { type: "string", description: "VM ID or name" }
348
+ },
349
+ required: ["id"]
350
+ },
351
+ execute: async (_id, params) => {
352
+ try {
353
+ return textResult(await client.post(`/api/vms/${params.id}/pause`));
354
+ } catch (err) {
355
+ return errorResult(err);
356
+ }
357
+ }
358
+ });
359
+ api.registerTool({
360
+ name: "unraid_vm_resume",
361
+ description: "Resume a paused virtual machine.",
362
+ parameters: {
363
+ type: "object",
364
+ properties: {
365
+ id: { type: "string", description: "VM ID or name" }
366
+ },
367
+ required: ["id"]
368
+ },
369
+ execute: async (_id, params) => {
370
+ try {
371
+ return textResult(await client.post(`/api/vms/${params.id}/resume`));
372
+ } catch (err) {
373
+ return errorResult(err);
374
+ }
375
+ }
376
+ });
377
+ api.registerTool(
378
+ {
379
+ name: "unraid_vm_force_stop",
380
+ description: "Force stop a virtual machine (equivalent to pulling the power plug). This is destructive and may cause data loss.",
381
+ parameters: {
382
+ type: "object",
383
+ properties: {
384
+ id: { type: "string", description: "VM ID or name" }
385
+ },
386
+ required: ["id"]
387
+ },
388
+ execute: async (_id, params) => {
389
+ try {
390
+ return textResult(await client.post(`/api/vms/${params.id}/force-stop`));
391
+ } catch (err) {
392
+ return errorResult(err);
393
+ }
394
+ }
395
+ }
396
+ );
397
+ api.registerTool({
398
+ name: "unraid_vm_reboot",
399
+ description: "Reboot a running virtual machine (ACPI reboot).",
400
+ parameters: {
401
+ type: "object",
402
+ properties: {
403
+ id: { type: "string", description: "VM ID or name" }
404
+ },
405
+ required: ["id"]
406
+ },
407
+ execute: async (_id, params) => {
408
+ try {
409
+ return textResult(await client.post(`/api/vms/${params.id}/reboot`));
410
+ } catch (err) {
411
+ return errorResult(err);
412
+ }
413
+ }
414
+ });
415
+ }
416
+
417
+ // src/tools/array.ts
418
+ function registerArrayTools(api, client) {
419
+ api.registerTool({
420
+ name: "unraid_array_status",
421
+ description: "Get the current status of the Unraid array including state, capacity, disks, and parities.",
422
+ parameters: { type: "object" },
423
+ execute: async () => {
424
+ try {
425
+ return textResult(await client.get("/api/array/status"));
426
+ } catch (err) {
427
+ return errorResult(err);
428
+ }
429
+ }
430
+ });
431
+ api.registerTool({
432
+ name: "unraid_array_start",
433
+ description: "Start the Unraid array. This will mount all disks and start Docker/VMs if configured.",
434
+ parameters: { type: "object" },
435
+ execute: async () => {
436
+ try {
437
+ return textResult(await client.post("/api/array/start"));
438
+ } catch (err) {
439
+ return errorResult(err);
440
+ }
441
+ }
442
+ });
443
+ api.registerTool({
444
+ name: "unraid_array_stop",
445
+ description: "Stop the Unraid array. This will stop all Docker containers and VMs, then unmount all disks.",
446
+ parameters: { type: "object" },
447
+ execute: async () => {
448
+ try {
449
+ return textResult(await client.post("/api/array/stop"));
450
+ } catch (err) {
451
+ return errorResult(err);
452
+ }
453
+ }
454
+ });
455
+ api.registerTool({
456
+ name: "unraid_parity_status",
457
+ description: "Get the current parity check status (running, progress, speed, errors).",
458
+ parameters: { type: "object" },
459
+ execute: async () => {
460
+ try {
461
+ return textResult(await client.get("/api/array/parity/status"));
462
+ } catch (err) {
463
+ return errorResult(err);
464
+ }
465
+ }
466
+ });
467
+ api.registerTool({
468
+ name: "unraid_parity_start",
469
+ description: "Start a parity check. Defaults to non-correcting (read-only) for safety. Set correct=true for a correcting check.",
470
+ parameters: {
471
+ type: "object",
472
+ properties: {
473
+ correct: {
474
+ type: "boolean",
475
+ description: "If true, run a correcting parity check. Defaults to false (non-correcting)."
476
+ }
477
+ }
478
+ },
479
+ execute: async (_id, params) => {
480
+ try {
481
+ return textResult(await client.post("/api/array/parity/start", { correct: params.correct ?? false }));
482
+ } catch (err) {
483
+ return errorResult(err);
484
+ }
485
+ }
486
+ });
487
+ api.registerTool({
488
+ name: "unraid_parity_pause",
489
+ description: "Pause a running parity check.",
490
+ parameters: { type: "object" },
491
+ execute: async () => {
492
+ try {
493
+ return textResult(await client.post("/api/array/parity/pause"));
494
+ } catch (err) {
495
+ return errorResult(err);
496
+ }
497
+ }
498
+ });
499
+ api.registerTool({
500
+ name: "unraid_parity_resume",
501
+ description: "Resume a paused parity check.",
502
+ parameters: { type: "object" },
503
+ execute: async () => {
504
+ try {
505
+ return textResult(await client.post("/api/array/parity/resume"));
506
+ } catch (err) {
507
+ return errorResult(err);
508
+ }
509
+ }
510
+ });
511
+ api.registerTool({
512
+ name: "unraid_parity_cancel",
513
+ description: "Cancel a running or paused parity check.",
514
+ parameters: { type: "object" },
515
+ execute: async () => {
516
+ try {
517
+ return textResult(await client.post("/api/array/parity/cancel"));
518
+ } catch (err) {
519
+ return errorResult(err);
520
+ }
521
+ }
522
+ });
523
+ }
524
+
525
+ // src/tools/disks.ts
526
+ function registerDiskTools(api, client) {
527
+ api.registerTool({
528
+ name: "unraid_disk_list",
529
+ description: "List all disks in the Unraid server with basic info (name, size, temp, status).",
530
+ parameters: { type: "object" },
531
+ execute: async () => {
532
+ try {
533
+ return textResult(await client.get("/api/disks"));
534
+ } catch (err) {
535
+ return errorResult(err);
536
+ }
537
+ }
538
+ });
539
+ api.registerTool({
540
+ name: "unraid_disk_details",
541
+ description: "Get detailed information about a specific disk including SMART data and health status.",
542
+ parameters: {
543
+ type: "object",
544
+ properties: {
545
+ id: { type: "string", description: "Disk ID (e.g., 'disk1', 'parity')" }
546
+ },
547
+ required: ["id"]
548
+ },
549
+ execute: async (_id, params) => {
550
+ try {
551
+ return textResult(await client.get(`/api/disks/${params.id}`));
552
+ } catch (err) {
553
+ return errorResult(err);
554
+ }
555
+ }
556
+ });
557
+ }
558
+
559
+ // src/tools/shares.ts
560
+ function registerShareTools(api, client) {
561
+ api.registerTool({
562
+ name: "unraid_share_list",
563
+ description: "List all user shares on the Unraid server with their settings and usage.",
564
+ parameters: { type: "object" },
565
+ execute: async () => {
566
+ try {
567
+ return textResult(await client.get("/api/shares"));
568
+ } catch (err) {
569
+ return errorResult(err);
570
+ }
571
+ }
572
+ });
573
+ api.registerTool({
574
+ name: "unraid_share_details",
575
+ description: "Get details for a specific user share by name.",
576
+ parameters: {
577
+ type: "object",
578
+ properties: {
579
+ name: { type: "string", description: "Share name" }
580
+ },
581
+ required: ["name"]
582
+ },
583
+ execute: async (_id, params) => {
584
+ try {
585
+ return textResult(await client.get(`/api/shares/${params.name}`));
586
+ } catch (err) {
587
+ return errorResult(err);
588
+ }
589
+ }
590
+ });
591
+ api.registerTool({
592
+ name: "unraid_share_update",
593
+ description: "Update safe settings for a user share. Only affects metadata and future write behavior \u2014 does not move existing data.",
594
+ parameters: {
595
+ type: "object",
596
+ properties: {
597
+ name: { type: "string", description: "Share name to update" },
598
+ comment: { type: "string", description: "Share description/comment" },
599
+ allocator: { type: "string", description: "Disk allocation method: highwater, fill, or most-free" },
600
+ floor: { type: "string", description: "Minimum free space per disk (e.g. '0' or '50000')" },
601
+ splitLevel: { type: "string", description: "Split level for distributing files across disks" }
602
+ },
603
+ required: ["name"]
604
+ },
605
+ execute: async (_id, params) => {
606
+ try {
607
+ const { name, ...updates } = params;
608
+ return textResult(await client.patch(`/api/shares/${name}`, updates));
609
+ } catch (err) {
610
+ return errorResult(err);
611
+ }
612
+ }
613
+ });
614
+ }
615
+
616
+ // src/tools/system.ts
617
+ function registerSystemTools(api, client) {
618
+ api.registerTool({
619
+ name: "unraid_system_info",
620
+ description: "Get system information including OS, CPU, memory, and Unraid/kernel versions.",
621
+ parameters: { type: "object" },
622
+ execute: async () => {
623
+ try {
624
+ return textResult(await client.get("/api/system/info"));
625
+ } catch (err) {
626
+ return errorResult(err);
627
+ }
628
+ }
629
+ });
630
+ api.registerTool({
631
+ name: "unraid_system_metrics",
632
+ description: "Get live system metrics: CPU usage, memory usage, load average, and uptime.",
633
+ parameters: { type: "object" },
634
+ execute: async () => {
635
+ try {
636
+ return textResult(await client.get("/api/system/metrics"));
637
+ } catch (err) {
638
+ return errorResult(err);
639
+ }
640
+ }
641
+ });
642
+ api.registerTool({
643
+ name: "unraid_service_list",
644
+ description: "List system services and their current state.",
645
+ parameters: { type: "object" },
646
+ execute: async () => {
647
+ try {
648
+ return textResult(await client.get("/api/system/services"));
649
+ } catch (err) {
650
+ return errorResult(err);
651
+ }
652
+ }
653
+ });
654
+ api.registerTool({
655
+ name: "unraid_system_reboot",
656
+ description: "Reboot the Unraid server. This is a destructive operation that will interrupt all running services, VMs, and containers.",
657
+ parameters: { type: "object" },
658
+ execute: async () => {
659
+ try {
660
+ return textResult(await client.post("/api/system/reboot"));
661
+ } catch (err) {
662
+ return errorResult(err);
663
+ }
664
+ }
665
+ });
666
+ api.registerTool({
667
+ name: "unraid_system_shutdown",
668
+ description: "Shut down the Unraid server. This is a destructive operation that will power off the server.",
669
+ parameters: { type: "object" },
670
+ execute: async () => {
671
+ try {
672
+ return textResult(await client.post("/api/system/shutdown"));
673
+ } catch (err) {
674
+ return errorResult(err);
675
+ }
676
+ }
677
+ });
678
+ }
679
+
680
+ // src/tools/notifications.ts
681
+ function registerNotificationTools(api, client) {
682
+ api.registerTool({
683
+ name: "unraid_notification_list",
684
+ description: "List all system notifications with their importance level and archive status.",
685
+ parameters: { type: "object" },
686
+ execute: async () => {
687
+ try {
688
+ return textResult(await client.get("/api/notifications"));
689
+ } catch (err) {
690
+ return errorResult(err);
691
+ }
692
+ }
693
+ });
694
+ api.registerTool({
695
+ name: "unraid_notification_create",
696
+ description: "Create a new system notification.",
697
+ parameters: {
698
+ type: "object",
699
+ properties: {
700
+ title: { type: "string", description: "Notification title" },
701
+ subject: { type: "string", description: "Notification subject" },
702
+ description: { type: "string", description: "Notification body text" },
703
+ importance: { type: "string", description: "Importance level: alert, warning, or normal" }
704
+ },
705
+ required: ["title", "subject", "description"]
706
+ },
707
+ execute: async (_id, params) => {
708
+ try {
709
+ const body = {
710
+ title: params.title,
711
+ subject: params.subject,
712
+ description: params.description
713
+ };
714
+ if (params.importance) body.importance = params.importance;
715
+ return textResult(await client.post("/api/notifications", body));
716
+ } catch (err) {
717
+ return errorResult(err);
718
+ }
719
+ }
720
+ });
721
+ api.registerTool({
722
+ name: "unraid_notification_archive",
723
+ description: "Archive a notification (mark as read/handled).",
724
+ parameters: {
725
+ type: "object",
726
+ properties: {
727
+ id: { type: "string", description: "Notification ID" }
728
+ },
729
+ required: ["id"]
730
+ },
731
+ execute: async (_id, params) => {
732
+ try {
733
+ return textResult(await client.post(`/api/notifications/${params.id}/archive`));
734
+ } catch (err) {
735
+ return errorResult(err);
736
+ }
737
+ }
738
+ });
739
+ api.registerTool({
740
+ name: "unraid_notification_delete",
741
+ description: "Delete a notification permanently.",
742
+ parameters: {
743
+ type: "object",
744
+ properties: {
745
+ id: { type: "string", description: "Notification ID" }
746
+ },
747
+ required: ["id"]
748
+ },
749
+ execute: async (_id, params) => {
750
+ try {
751
+ return textResult(await client.delete(`/api/notifications/${params.id}`));
752
+ } catch (err) {
753
+ return errorResult(err);
754
+ }
755
+ }
756
+ });
757
+ }
758
+
759
+ // src/tools/network.ts
760
+ function registerNetworkTools(api, client) {
761
+ api.registerTool({
762
+ name: "unraid_network_info",
763
+ description: "Get network information including hostname, gateway, DNS servers, and all network interfaces.",
764
+ parameters: { type: "object" },
765
+ execute: async () => {
766
+ try {
767
+ return textResult(await client.get("/api/network"));
768
+ } catch (err) {
769
+ return errorResult(err);
770
+ }
771
+ }
772
+ });
773
+ }
774
+
775
+ // src/tools/users.ts
776
+ function registerUserTools(api, client) {
777
+ api.registerTool({
778
+ name: "unraid_user_me",
779
+ description: "Get information about the current authenticated user.",
780
+ parameters: { type: "object" },
781
+ execute: async () => {
782
+ try {
783
+ return textResult(await client.get("/api/users/me"));
784
+ } catch (err) {
785
+ return errorResult(err);
786
+ }
787
+ }
788
+ });
789
+ }
790
+
791
+ // src/tools/logs.ts
792
+ function registerLogTools(api, client) {
793
+ api.registerTool({
794
+ name: "unraid_syslog",
795
+ description: "Get recent syslog entries from the Unraid server. Returns the most recent log lines.",
796
+ parameters: {
797
+ type: "object",
798
+ properties: {
799
+ lines: {
800
+ type: "number",
801
+ description: "Number of log lines to retrieve (1-1000, default 50)."
802
+ }
803
+ }
804
+ },
805
+ execute: async (_id, params) => {
806
+ try {
807
+ const query = {};
808
+ if (params.lines) query.lines = String(params.lines);
809
+ return textResult(await client.get("/api/logs/syslog", query));
810
+ } catch (err) {
811
+ return errorResult(err);
812
+ }
813
+ }
814
+ });
815
+ }
816
+
817
+ // src/index.ts
818
+ function resolveConfig(api) {
819
+ if (api.config?.serverUrl) return api.config;
820
+ if (api.pluginConfig?.serverUrl) return api.pluginConfig;
821
+ const nested = api.config?.plugins?.entries?.unraidclaw?.config;
822
+ if (nested?.serverUrl) return nested;
823
+ return { serverUrl: "", apiKey: "" };
824
+ }
825
+ function register(api) {
826
+ const log = api.logger || console;
827
+ const client = new UnraidClient(() => resolveConfig(api));
828
+ registerHealthTools(api, client);
829
+ registerDockerTools(api, client);
830
+ registerVMTools(api, client);
831
+ registerArrayTools(api, client);
832
+ registerDiskTools(api, client);
833
+ registerShareTools(api, client);
834
+ registerSystemTools(api, client);
835
+ registerNotificationTools(api, client);
836
+ registerNetworkTools(api, client);
837
+ registerUserTools(api, client);
838
+ registerLogTools(api, client);
839
+ const cfg = resolveConfig(api);
840
+ log.info(`UnraidClaw: registered 39 tools${cfg.serverUrl ? ", server: " + cfg.serverUrl : " (config will resolve at runtime)"}`);
841
+ }
842
+ export {
843
+ register as default
844
+ };
@@ -0,0 +1,36 @@
1
+ {
2
+ "id": "unraidclaw",
3
+ "configSchema": {
4
+ "type": "object",
5
+ "additionalProperties": false,
6
+ "properties": {
7
+ "serverUrl": {
8
+ "type": "string",
9
+ "description": "URL of the UnraidClaw server (e.g., http://192.168.1.100:9876)"
10
+ },
11
+ "apiKey": {
12
+ "type": "string",
13
+ "description": "API key for authenticating with UnraidClaw"
14
+ },
15
+ "tlsSkipVerify": {
16
+ "type": "boolean",
17
+ "description": "Skip TLS certificate verification (for self-signed certs)",
18
+ "default": false
19
+ }
20
+ },
21
+ "required": ["serverUrl", "apiKey"]
22
+ },
23
+ "uiHints": {
24
+ "serverUrl": {
25
+ "label": "Server URL",
26
+ "placeholder": "https://192.168.1.100:9876"
27
+ },
28
+ "apiKey": {
29
+ "label": "API Key",
30
+ "sensitive": true
31
+ },
32
+ "tlsSkipVerify": {
33
+ "label": "Skip TLS Verification"
34
+ }
35
+ }
36
+ }
package/package.json ADDED
@@ -0,0 +1,32 @@
1
+ {
2
+ "name": "unraidclaw",
3
+ "version": "0.1.0",
4
+ "description": "OpenClaw plugin to manage your Unraid server — Docker, VMs, array, shares, system, and more.",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js"
12
+ }
13
+ },
14
+ "openclaw": {
15
+ "extensions": ["./dist/index.js"]
16
+ },
17
+ "files": [
18
+ "dist",
19
+ "openclaw.plugin.json"
20
+ ],
21
+ "scripts": {
22
+ "build": "tsup",
23
+ "typecheck": "tsc --noEmit",
24
+ "clean": "rm -rf dist"
25
+ },
26
+ "devDependencies": {
27
+ "@unraidclaw/shared": "workspace:*",
28
+ "typescript": "^5.7.0",
29
+ "@types/node": "^22.0.0",
30
+ "tsup": "^8.5.0"
31
+ }
32
+ }