@nordbyte/nordrelay-auto-updater 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.
- package/LICENSE +22 -0
- package/README.md +139 -0
- package/index.js +24 -0
- package/nordrelay.plugin.json +293 -0
- package/package.json +42 -0
- package/src/commands.js +20 -0
- package/src/format.js +90 -0
- package/src/npm-packages.js +92 -0
- package/src/os-updates.js +19 -0
- package/src/package-managers.js +175 -0
- package/src/render-panel.js +446 -0
- package/src/runtime.js +319 -0
- package/src/storage.js +337 -0
- package/src/subprocess.js +74 -0
- package/src/update-actions.js +108 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Ricardo
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
22
|
+
|
package/README.md
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
# NordRelay Auto Updater Plugin
|
|
2
|
+
|
|
3
|
+
Auto Updater is a NordRelay plugin that shows update visibility for Linux/macOS
|
|
4
|
+
package managers and global npm packages on every peer where it is installed
|
|
5
|
+
and enabled. Admins can explicitly run selected updates, update all listed
|
|
6
|
+
updates, or uninstall global npm packages from the WebUI panel.
|
|
7
|
+
|
|
8
|
+
The plugin is self-contained. NordRelay provides plugin installation, peer
|
|
9
|
+
routing, scheduling, permissions, and the WebUI panel container; all update
|
|
10
|
+
checks, parsing, history, cleanup, and rendering logic lives in this plugin.
|
|
11
|
+
|
|
12
|
+
## Install
|
|
13
|
+
|
|
14
|
+
Install on the current node:
|
|
15
|
+
|
|
16
|
+
```sh
|
|
17
|
+
nordrelay plugin install github:nordbyte/nordrelay-plugin-auto-updater --enable --approve
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Install from a local checkout:
|
|
21
|
+
|
|
22
|
+
```sh
|
|
23
|
+
nordrelay plugin install /path/to/nordrelay-plugin-auto-updater --enable --approve
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
The npm package is published as:
|
|
27
|
+
|
|
28
|
+
```text
|
|
29
|
+
@nordbyte/nordrelay-auto-updater
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
In the WebUI, open **Plugins**, select the target node in the header, and install
|
|
33
|
+
the GitHub source. Use **Install on all enabled peers** to install the same
|
|
34
|
+
plugin on reachable peers.
|
|
35
|
+
|
|
36
|
+
## Capabilities
|
|
37
|
+
|
|
38
|
+
- Collector: `updates.snapshot`
|
|
39
|
+
- Commands: `refresh`, `latest`, `panel-data`, `os-updates`, `npm-packages`,
|
|
40
|
+
`update-os`, `update-npm`, `configure-npm-auto`, `uninstall-npm`,
|
|
41
|
+
`history`, `export`, `storage-health`, `cleanup`
|
|
42
|
+
- Web panel: `dashboard`
|
|
43
|
+
- Diagnostics: enabled
|
|
44
|
+
|
|
45
|
+
Required permissions:
|
|
46
|
+
|
|
47
|
+
```text
|
|
48
|
+
system.packages.read
|
|
49
|
+
system.packages.write
|
|
50
|
+
system.updates.read
|
|
51
|
+
system.updates.write
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
The dashboard aggregates data only from peers where this plugin is installed,
|
|
55
|
+
enabled, and approved.
|
|
56
|
+
|
|
57
|
+
## What It Checks
|
|
58
|
+
|
|
59
|
+
Linux package managers:
|
|
60
|
+
|
|
61
|
+
- `apt list --upgradable`
|
|
62
|
+
- `dnf check-update`
|
|
63
|
+
- `pacman -Qu`
|
|
64
|
+
- `zypper list-updates`
|
|
65
|
+
- `snap refresh --list`
|
|
66
|
+
- `flatpak remote-ls --updates`
|
|
67
|
+
|
|
68
|
+
macOS:
|
|
69
|
+
|
|
70
|
+
- `softwareupdate -l`
|
|
71
|
+
- `brew outdated --json=v2`
|
|
72
|
+
|
|
73
|
+
All platforms:
|
|
74
|
+
|
|
75
|
+
- `npm ls -g --depth=0 --json --long`
|
|
76
|
+
- `npm outdated -g --json --long`
|
|
77
|
+
|
|
78
|
+
Windows currently reports npm package status only.
|
|
79
|
+
|
|
80
|
+
Update, uninstall, and automatic npm install actions run as the same
|
|
81
|
+
operating-system user that runs NordRelay. The plugin does not use `sudo`, so
|
|
82
|
+
package managers that require root privileges must be handled by running
|
|
83
|
+
NordRelay with the required permissions or by using package managers that allow
|
|
84
|
+
user-level updates.
|
|
85
|
+
|
|
86
|
+
Global npm packages can be configured individually for automatic installation.
|
|
87
|
+
Each package has its own interval in hours or days. By default the automatic
|
|
88
|
+
action installs only when the package is outdated; enabling **Install latest**
|
|
89
|
+
also runs `npm install -g <package>@latest` when the package already reports the
|
|
90
|
+
latest version.
|
|
91
|
+
|
|
92
|
+
## Settings
|
|
93
|
+
|
|
94
|
+
| Setting | Default | Description |
|
|
95
|
+
| --- | ---: | --- |
|
|
96
|
+
| `checkIntervalMs` | `1800000` | Preferred collector interval in milliseconds |
|
|
97
|
+
| `retentionDays` | `30` | Snapshot history retention in days |
|
|
98
|
+
| `commandTimeoutMs` | `20000` | Timeout for individual package-manager commands |
|
|
99
|
+
| `updateTimeoutMs` | `600000` | Timeout for package update or uninstall commands |
|
|
100
|
+
| `enableOsUpdates` | `true` | Check Linux/macOS package-manager updates |
|
|
101
|
+
| `enableNpmPackages` | `true` | Check global npm package versions |
|
|
102
|
+
| `npmRegistry` | `https://registry.npmjs.org` | Registry used by npm outdated checks |
|
|
103
|
+
| `refreshPackageIndexes` | `false` | Reserved for future explicit index refresh flows |
|
|
104
|
+
|
|
105
|
+
## Data
|
|
106
|
+
|
|
107
|
+
Snapshots are stored in the plugin data directory:
|
|
108
|
+
|
|
109
|
+
```text
|
|
110
|
+
~/.nordrelay/plugins/data/auto-updater/
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
The plugin writes:
|
|
114
|
+
|
|
115
|
+
- `updates.sqlite`
|
|
116
|
+
|
|
117
|
+
Useful commands:
|
|
118
|
+
|
|
119
|
+
```sh
|
|
120
|
+
nordrelay plugin invoke auto-updater command refresh
|
|
121
|
+
nordrelay plugin invoke auto-updater command panel-data
|
|
122
|
+
nordrelay plugin invoke auto-updater command os-updates
|
|
123
|
+
nordrelay plugin invoke auto-updater command npm-packages
|
|
124
|
+
nordrelay plugin invoke auto-updater command update-os --input-json '{"packages":[{"manager":"apt","name":"openssl"}]}'
|
|
125
|
+
nordrelay plugin invoke auto-updater command update-npm --input-json '{"packages":["typescript"]}'
|
|
126
|
+
nordrelay plugin invoke auto-updater command configure-npm-auto --input-json '{"package":"typescript","enabled":true,"intervalValue":12,"intervalUnit":"hours","installLatest":false}'
|
|
127
|
+
nordrelay plugin invoke auto-updater command uninstall-npm --input-json '{"packages":["typescript"]}'
|
|
128
|
+
nordrelay plugin invoke auto-updater command history --input-json '{"range":"30d","limit":500}'
|
|
129
|
+
nordrelay plugin invoke auto-updater command export --input-json '{"format":"csv","range":"30d"}'
|
|
130
|
+
nordrelay plugin invoke auto-updater command storage-health
|
|
131
|
+
nordrelay plugin invoke auto-updater command cleanup
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## Development
|
|
135
|
+
|
|
136
|
+
```sh
|
|
137
|
+
npm run check
|
|
138
|
+
npm test
|
|
139
|
+
```
|
package/index.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { runPlugin } from "./src/runtime.js";
|
|
5
|
+
|
|
6
|
+
export * from "./src/runtime.js";
|
|
7
|
+
export * from "./src/os-updates.js";
|
|
8
|
+
export * from "./src/npm-packages.js";
|
|
9
|
+
export * from "./src/package-managers.js";
|
|
10
|
+
export * from "./src/update-actions.js";
|
|
11
|
+
export * from "./src/subprocess.js";
|
|
12
|
+
export * from "./src/storage.js";
|
|
13
|
+
export * from "./src/render-panel.js";
|
|
14
|
+
export * from "./src/format.js";
|
|
15
|
+
export * from "./src/commands.js";
|
|
16
|
+
|
|
17
|
+
if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
|
|
18
|
+
runPlugin().catch((error) => {
|
|
19
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
20
|
+
process.stderr.write(`${message}\n`);
|
|
21
|
+
process.stdout.write(`${JSON.stringify({ ok: false, stderr: message })}\n`);
|
|
22
|
+
process.exitCode = 1;
|
|
23
|
+
});
|
|
24
|
+
}
|
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "auto-updater",
|
|
3
|
+
"name": "Auto Updater",
|
|
4
|
+
"version": "0.1.0",
|
|
5
|
+
"description": "Checks Linux/macOS package-manager updates and global npm package versions across NordRelay peers.",
|
|
6
|
+
"author": "Ricardo <github@nordbyte.de>",
|
|
7
|
+
"homepage": "https://github.com/nordbyte/nordrelay-plugin-auto-updater",
|
|
8
|
+
"repository": "https://github.com/nordbyte/nordrelay-plugin-auto-updater",
|
|
9
|
+
"license": "MIT",
|
|
10
|
+
"nordrelay": ">=0.9.12",
|
|
11
|
+
"entry": "index.js",
|
|
12
|
+
"permissions": [
|
|
13
|
+
"runtime.read",
|
|
14
|
+
"peers.read",
|
|
15
|
+
"system.packages.read",
|
|
16
|
+
"system.packages.write",
|
|
17
|
+
"system.updates.read",
|
|
18
|
+
"system.updates.write",
|
|
19
|
+
"network"
|
|
20
|
+
],
|
|
21
|
+
"capabilities": {
|
|
22
|
+
"collectors": [
|
|
23
|
+
{
|
|
24
|
+
"id": "updates.snapshot",
|
|
25
|
+
"title": "Collect update snapshot",
|
|
26
|
+
"description": "Checks available OS package-manager updates and global npm package versions on this node.",
|
|
27
|
+
"intervalMs": 1800000,
|
|
28
|
+
"runOnStart": true,
|
|
29
|
+
"timeoutMs": 120000
|
|
30
|
+
}
|
|
31
|
+
],
|
|
32
|
+
"commands": [
|
|
33
|
+
{
|
|
34
|
+
"name": "refresh",
|
|
35
|
+
"title": "Refresh update data",
|
|
36
|
+
"description": "Run a fresh read-only update check and persist the snapshot.",
|
|
37
|
+
"timeoutMs": 120000
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
"name": "latest",
|
|
41
|
+
"title": "Latest update snapshot",
|
|
42
|
+
"description": "Return the latest snapshot, collecting one if none exists.",
|
|
43
|
+
"timeoutMs": 120000
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
"name": "panel-data",
|
|
47
|
+
"title": "Panel data",
|
|
48
|
+
"description": "Return latest update data, history, storage health, and diagnostics for the WebUI panel.",
|
|
49
|
+
"inputSchema": {
|
|
50
|
+
"type": "object",
|
|
51
|
+
"properties": {
|
|
52
|
+
"force": {
|
|
53
|
+
"type": "boolean",
|
|
54
|
+
"title": "Force refresh",
|
|
55
|
+
"default": false
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
"timeoutMs": 120000
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
"name": "os-updates",
|
|
63
|
+
"title": "OS updates",
|
|
64
|
+
"description": "Return the latest available OS package-manager updates.",
|
|
65
|
+
"timeoutMs": 30000
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
"name": "npm-packages",
|
|
69
|
+
"title": "npm packages",
|
|
70
|
+
"description": "Return global npm packages with installed and latest versions.",
|
|
71
|
+
"timeoutMs": 30000
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
"name": "update-os",
|
|
75
|
+
"title": "Update OS packages",
|
|
76
|
+
"description": "Install selected OS package-manager updates on this node.",
|
|
77
|
+
"permission": "system.updates.write",
|
|
78
|
+
"inputSchema": {
|
|
79
|
+
"type": "object",
|
|
80
|
+
"properties": {
|
|
81
|
+
"packages": {
|
|
82
|
+
"type": "array",
|
|
83
|
+
"title": "Packages"
|
|
84
|
+
},
|
|
85
|
+
"all": {
|
|
86
|
+
"type": "boolean",
|
|
87
|
+
"title": "Update all selected packages",
|
|
88
|
+
"default": false
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
"timeoutMs": 600000
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
"name": "update-npm",
|
|
96
|
+
"title": "Update npm packages",
|
|
97
|
+
"description": "Update selected global npm packages or all global npm packages on this node.",
|
|
98
|
+
"permission": "system.packages.write",
|
|
99
|
+
"inputSchema": {
|
|
100
|
+
"type": "object",
|
|
101
|
+
"properties": {
|
|
102
|
+
"packages": {
|
|
103
|
+
"type": "array",
|
|
104
|
+
"title": "Packages"
|
|
105
|
+
},
|
|
106
|
+
"all": {
|
|
107
|
+
"type": "boolean",
|
|
108
|
+
"title": "Update all packages",
|
|
109
|
+
"default": false
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
},
|
|
113
|
+
"timeoutMs": 600000
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
"name": "configure-npm-auto",
|
|
117
|
+
"title": "Configure npm auto install",
|
|
118
|
+
"description": "Configure automatic global npm install checks for a package on this node.",
|
|
119
|
+
"permission": "system.packages.write",
|
|
120
|
+
"inputSchema": {
|
|
121
|
+
"type": "object",
|
|
122
|
+
"properties": {
|
|
123
|
+
"package": {
|
|
124
|
+
"type": "string",
|
|
125
|
+
"title": "Package"
|
|
126
|
+
},
|
|
127
|
+
"enabled": {
|
|
128
|
+
"type": "boolean",
|
|
129
|
+
"title": "Enabled",
|
|
130
|
+
"default": false
|
|
131
|
+
},
|
|
132
|
+
"intervalValue": {
|
|
133
|
+
"type": "number",
|
|
134
|
+
"title": "Interval",
|
|
135
|
+
"default": 24
|
|
136
|
+
},
|
|
137
|
+
"intervalUnit": {
|
|
138
|
+
"type": "string",
|
|
139
|
+
"title": "Interval unit",
|
|
140
|
+
"default": "hours"
|
|
141
|
+
},
|
|
142
|
+
"installLatest": {
|
|
143
|
+
"type": "boolean",
|
|
144
|
+
"title": "Install even when latest",
|
|
145
|
+
"default": false
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
},
|
|
149
|
+
"timeoutMs": 30000
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
"name": "uninstall-npm",
|
|
153
|
+
"title": "Uninstall npm packages",
|
|
154
|
+
"description": "Uninstall selected global npm packages from this node.",
|
|
155
|
+
"permission": "system.packages.write",
|
|
156
|
+
"inputSchema": {
|
|
157
|
+
"type": "object",
|
|
158
|
+
"properties": {
|
|
159
|
+
"packages": {
|
|
160
|
+
"type": "array",
|
|
161
|
+
"title": "Packages"
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
},
|
|
165
|
+
"timeoutMs": 600000
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
"name": "history",
|
|
169
|
+
"title": "Update history",
|
|
170
|
+
"description": "Return snapshot history for charts and trend tables.",
|
|
171
|
+
"inputSchema": {
|
|
172
|
+
"type": "object",
|
|
173
|
+
"properties": {
|
|
174
|
+
"range": {
|
|
175
|
+
"type": "string",
|
|
176
|
+
"title": "Range",
|
|
177
|
+
"default": "7d"
|
|
178
|
+
},
|
|
179
|
+
"limit": {
|
|
180
|
+
"type": "number",
|
|
181
|
+
"title": "Limit",
|
|
182
|
+
"default": 200
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
},
|
|
186
|
+
"timeoutMs": 30000
|
|
187
|
+
},
|
|
188
|
+
{
|
|
189
|
+
"name": "export",
|
|
190
|
+
"title": "Export update data",
|
|
191
|
+
"description": "Export latest data or history as JSON or CSV.",
|
|
192
|
+
"inputSchema": {
|
|
193
|
+
"type": "object",
|
|
194
|
+
"properties": {
|
|
195
|
+
"format": {
|
|
196
|
+
"type": "string",
|
|
197
|
+
"title": "Format",
|
|
198
|
+
"default": "json"
|
|
199
|
+
},
|
|
200
|
+
"range": {
|
|
201
|
+
"type": "string",
|
|
202
|
+
"title": "Range",
|
|
203
|
+
"default": "30d"
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
},
|
|
207
|
+
"timeoutMs": 30000
|
|
208
|
+
},
|
|
209
|
+
{
|
|
210
|
+
"name": "storage-health",
|
|
211
|
+
"title": "Storage health",
|
|
212
|
+
"description": "Return SQLite storage health for the plugin database.",
|
|
213
|
+
"timeoutMs": 10000
|
|
214
|
+
},
|
|
215
|
+
{
|
|
216
|
+
"name": "cleanup",
|
|
217
|
+
"title": "Cleanup old snapshots",
|
|
218
|
+
"description": "Delete snapshots older than the configured retention window.",
|
|
219
|
+
"timeoutMs": 30000
|
|
220
|
+
}
|
|
221
|
+
],
|
|
222
|
+
"webPanels": [
|
|
223
|
+
{
|
|
224
|
+
"id": "dashboard",
|
|
225
|
+
"title": "Auto Updater",
|
|
226
|
+
"description": "Peer-aware OS and npm update overview.",
|
|
227
|
+
"aggregateCommand": "panel-data",
|
|
228
|
+
"allowClientScript": true,
|
|
229
|
+
"placement": "plugins",
|
|
230
|
+
"timeoutMs": 120000
|
|
231
|
+
}
|
|
232
|
+
],
|
|
233
|
+
"diagnostics": true
|
|
234
|
+
},
|
|
235
|
+
"settings": [
|
|
236
|
+
{
|
|
237
|
+
"key": "checkIntervalMs",
|
|
238
|
+
"label": "Collector interval",
|
|
239
|
+
"type": "number",
|
|
240
|
+
"default": 1800000,
|
|
241
|
+
"description": "Preferred interval in milliseconds between scheduled update checks."
|
|
242
|
+
},
|
|
243
|
+
{
|
|
244
|
+
"key": "retentionDays",
|
|
245
|
+
"label": "Retention days",
|
|
246
|
+
"type": "number",
|
|
247
|
+
"default": 30,
|
|
248
|
+
"description": "How long snapshot history is kept in SQLite."
|
|
249
|
+
},
|
|
250
|
+
{
|
|
251
|
+
"key": "commandTimeoutMs",
|
|
252
|
+
"label": "Command timeout",
|
|
253
|
+
"type": "number",
|
|
254
|
+
"default": 20000,
|
|
255
|
+
"description": "Timeout for individual package-manager commands."
|
|
256
|
+
},
|
|
257
|
+
{
|
|
258
|
+
"key": "updateTimeoutMs",
|
|
259
|
+
"label": "Update timeout",
|
|
260
|
+
"type": "number",
|
|
261
|
+
"default": 600000,
|
|
262
|
+
"description": "Timeout for package update or uninstall commands."
|
|
263
|
+
},
|
|
264
|
+
{
|
|
265
|
+
"key": "enableOsUpdates",
|
|
266
|
+
"label": "Check OS updates",
|
|
267
|
+
"type": "boolean",
|
|
268
|
+
"default": true,
|
|
269
|
+
"description": "Enable Linux/macOS read-only package-manager update checks."
|
|
270
|
+
},
|
|
271
|
+
{
|
|
272
|
+
"key": "enableNpmPackages",
|
|
273
|
+
"label": "Check npm packages",
|
|
274
|
+
"type": "boolean",
|
|
275
|
+
"default": true,
|
|
276
|
+
"description": "Enable global npm package version checks."
|
|
277
|
+
},
|
|
278
|
+
{
|
|
279
|
+
"key": "npmRegistry",
|
|
280
|
+
"label": "npm registry",
|
|
281
|
+
"type": "string",
|
|
282
|
+
"default": "https://registry.npmjs.org",
|
|
283
|
+
"description": "Registry used by npm outdated checks."
|
|
284
|
+
},
|
|
285
|
+
{
|
|
286
|
+
"key": "refreshPackageIndexes",
|
|
287
|
+
"label": "Refresh package indexes",
|
|
288
|
+
"type": "boolean",
|
|
289
|
+
"default": false,
|
|
290
|
+
"description": "Reserved for future explicit index refresh flows. The plugin never runs privileged updates automatically."
|
|
291
|
+
}
|
|
292
|
+
]
|
|
293
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@nordbyte/nordrelay-auto-updater",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Update visibility plugin for NordRelay peers, OS package managers, and global npm packages.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"homepage": "https://github.com/nordbyte/nordrelay-plugin-auto-updater#readme",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/nordbyte/nordrelay-plugin-auto-updater.git"
|
|
10
|
+
},
|
|
11
|
+
"bugs": {
|
|
12
|
+
"url": "https://github.com/nordbyte/nordrelay-plugin-auto-updater/issues"
|
|
13
|
+
},
|
|
14
|
+
"main": "./index.js",
|
|
15
|
+
"files": [
|
|
16
|
+
"index.js",
|
|
17
|
+
"src",
|
|
18
|
+
"nordrelay.plugin.json",
|
|
19
|
+
"README.md",
|
|
20
|
+
"LICENSE"
|
|
21
|
+
],
|
|
22
|
+
"scripts": {
|
|
23
|
+
"check": "node --check index.js && node --check src/runtime.js && node --check src/os-updates.js && node --check src/npm-packages.js && node --check src/package-managers.js && node --check src/update-actions.js && node --check src/subprocess.js && node --check src/storage.js && node --check src/render-panel.js && node --check src/format.js && node --check src/commands.js",
|
|
24
|
+
"test": "node --test test/*.test.mjs",
|
|
25
|
+
"prepack": "npm run check && npm test"
|
|
26
|
+
},
|
|
27
|
+
"keywords": [
|
|
28
|
+
"nordrelay",
|
|
29
|
+
"plugin",
|
|
30
|
+
"updates",
|
|
31
|
+
"npm",
|
|
32
|
+
"package-manager"
|
|
33
|
+
],
|
|
34
|
+
"author": "Ricardo <github@nordbyte.de>",
|
|
35
|
+
"license": "MIT",
|
|
36
|
+
"engines": {
|
|
37
|
+
"node": ">=22"
|
|
38
|
+
},
|
|
39
|
+
"publishConfig": {
|
|
40
|
+
"access": "public"
|
|
41
|
+
}
|
|
42
|
+
}
|
package/src/commands.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export const COMMANDS = new Set([
|
|
2
|
+
"refresh",
|
|
3
|
+
"latest",
|
|
4
|
+
"panel-data",
|
|
5
|
+
"os-updates",
|
|
6
|
+
"npm-packages",
|
|
7
|
+
"update-os",
|
|
8
|
+
"update-npm",
|
|
9
|
+
"configure-npm-auto",
|
|
10
|
+
"uninstall-npm",
|
|
11
|
+
"history",
|
|
12
|
+
"export",
|
|
13
|
+
"storage-health",
|
|
14
|
+
"cleanup",
|
|
15
|
+
]);
|
|
16
|
+
|
|
17
|
+
export function normalizeCommand(request) {
|
|
18
|
+
const command = String(request.command || request.capabilityId || request.input?.command || "").trim().toLowerCase();
|
|
19
|
+
return COMMANDS.has(command) ? command : command || "latest";
|
|
20
|
+
}
|
package/src/format.js
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
export function escapeHtml(value) {
|
|
2
|
+
return String(value ?? "")
|
|
3
|
+
.replace(/&/g, "&")
|
|
4
|
+
.replace(/</g, "<")
|
|
5
|
+
.replace(/>/g, ">")
|
|
6
|
+
.replace(/"/g, """)
|
|
7
|
+
.replace(/'/g, "'");
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function attr(value) {
|
|
11
|
+
return escapeHtml(value);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function badge(text, status = "disabled") {
|
|
15
|
+
return `<span class="badge ${statusClass(status)}">${escapeHtml(text)}</span>`;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function metric(label, value, detail = "", status = "") {
|
|
19
|
+
const statusAttr = status ? ` data-status="${attr(status)}"` : "";
|
|
20
|
+
return `<div class="metric"${statusAttr}><div class="label">${escapeHtml(label)}</div><div class="value">${escapeHtml(value)}</div>${detail ? `<small>${escapeHtml(detail)}</small>` : ""}</div>`;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function table(headers, rows, empty = "No data available.") {
|
|
24
|
+
if (!rows.length) {
|
|
25
|
+
return `<div class="empty-state">${escapeHtml(empty)}</div>`;
|
|
26
|
+
}
|
|
27
|
+
const head = headers.map((header) => `<th>${escapeHtml(header)}</th>`).join("");
|
|
28
|
+
return `<div class="data-table-wrap"><table class="data-table"><thead><tr>${head}</tr></thead><tbody>${rows.join("")}</tbody></table></div>`;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function truncate(value, max = 120) {
|
|
32
|
+
const text = String(value ?? "");
|
|
33
|
+
return text.length > max ? `${text.slice(0, max - 1)}…` : text;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function formatDuration(ms) {
|
|
37
|
+
const value = Math.max(0, Number(ms) || 0);
|
|
38
|
+
if (value < 1000) return `${Math.round(value)}ms`;
|
|
39
|
+
const seconds = Math.round(value / 1000);
|
|
40
|
+
if (seconds < 60) return `${seconds}s`;
|
|
41
|
+
const minutes = Math.floor(seconds / 60);
|
|
42
|
+
return `${minutes}m ${seconds % 60}s`;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function formatAge(iso) {
|
|
46
|
+
const ts = Date.parse(iso || "");
|
|
47
|
+
if (!Number.isFinite(ts)) return "-";
|
|
48
|
+
const seconds = Math.max(0, Math.floor((Date.now() - ts) / 1000));
|
|
49
|
+
if (seconds < 60) return `${seconds}s ago`;
|
|
50
|
+
const minutes = Math.floor(seconds / 60);
|
|
51
|
+
if (minutes < 60) return `${minutes}m ${seconds % 60}s ago`;
|
|
52
|
+
const hours = Math.floor(minutes / 60);
|
|
53
|
+
if (hours < 24) return `${hours}h ${minutes % 60}m ago`;
|
|
54
|
+
const days = Math.floor(hours / 24);
|
|
55
|
+
return `${days}d ${hours % 24}h ago`;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function statusForCount(count) {
|
|
59
|
+
return Number(count) > 0 ? "warning" : "enabled";
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function statusClass(status) {
|
|
63
|
+
switch (status) {
|
|
64
|
+
case "ok":
|
|
65
|
+
case "enabled":
|
|
66
|
+
case "latest":
|
|
67
|
+
return "enabled";
|
|
68
|
+
case "warn":
|
|
69
|
+
case "warning":
|
|
70
|
+
case "outdated":
|
|
71
|
+
return "warning";
|
|
72
|
+
case "error":
|
|
73
|
+
case "failed":
|
|
74
|
+
return "failed";
|
|
75
|
+
default:
|
|
76
|
+
return "disabled";
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function parseRangeMs(range, fallbackMs = 24 * 60 * 60 * 1000) {
|
|
81
|
+
const text = String(range || "").trim().toLowerCase();
|
|
82
|
+
const match = /^(\d+)(m|h|d)$/.exec(text);
|
|
83
|
+
if (!match) return fallbackMs;
|
|
84
|
+
const value = Number(match[1]);
|
|
85
|
+
if (!Number.isFinite(value) || value <= 0) return fallbackMs;
|
|
86
|
+
if (match[2] === "m") return value * 60 * 1000;
|
|
87
|
+
if (match[2] === "h") return value * 60 * 60 * 1000;
|
|
88
|
+
return value * 24 * 60 * 60 * 1000;
|
|
89
|
+
}
|
|
90
|
+
|