@carlos-burelo/wyrm 1.0.0 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +41 -21
- package/bin/cli.js +1 -3
- package/package.json +34 -37
- package/vendor/wyrm.exe +0 -0
- package/scripts/install.js +0 -123
package/README.md
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
|
-
|
|
1
|
+

|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
# Wyrm
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
-
|
|
8
|
-
-
|
|
5
|
+
Gestor de procesos escrito en Rust para Windows Server. Mantiene aplicaciones Node.js, Next.js, Astro y Express en ejecución como servicio de Windows, con detección automática de `package.json`, `.next/standalone`, Astro y pnpm/yarn/bun.
|
|
6
|
+
|
|
7
|
+
- Supervisor con `JobObjects` (`KILL_ON_JOB_CLOSE`), auto-restart y restore al arrancar.
|
|
8
|
+
- IPC por Named Pipe con protocolo `Request/Response` NDJSON.
|
|
9
|
+
- CLI: `start/stop/restart/delete/list/status/logs/daemon/top/service`.
|
|
10
|
+
- TUI con Ratatui: lista, métricas CPU/MEM, logs en vivo, filtro y acciones.
|
|
9
11
|
- Logs por app en `%ProgramData%\wyrm\logs\<name>.log`.
|
|
10
12
|
- DB SQLite en `%ProgramData%\wyrm\wyrm.db`.
|
|
11
13
|
|
|
@@ -13,6 +15,7 @@
|
|
|
13
15
|
|
|
14
16
|
```powershell
|
|
15
17
|
npm install -g @carlos-burelo/wyrm
|
|
18
|
+
# también pnpm/yarn/bun: el tarball trae el binario, sin postinstall
|
|
16
19
|
```
|
|
17
20
|
|
|
18
21
|
O desde fuente:
|
|
@@ -105,12 +108,15 @@ wyrm rollback <app> # git reset al último deploy ok + restart
|
|
|
105
108
|
```
|
|
106
109
|
|
|
107
110
|
Hooks en `wyrm.json` por app: `pre_deploy` / `post_deploy` (shell `cmd /C`).
|
|
108
|
-
|
|
111
|
+
El `wyrm.json` debe estar en el `cwd` de la app (no se buscan directorios
|
|
112
|
+
padre). Historial en SQLite (`deploys`).
|
|
109
113
|
|
|
110
114
|
## API local + métricas (plano de control)
|
|
111
115
|
|
|
112
116
|
El demonio sirve `http://127.0.0.1:8379` (`WYRM_API_ADDR`, off con
|
|
113
|
-
`WYRM_NO_API=1`). Auth Bearer con token en `%ProgramData%\wyrm\token
|
|
117
|
+
`WYRM_NO_API=1`). Auth Bearer con token en `%ProgramData%\wyrm\token`
|
|
118
|
+
(el token se aplica al arrancar el demonio; tras `token --rotate`
|
|
119
|
+
reinicia el demonio o servicio):
|
|
114
120
|
|
|
115
121
|
```powershell
|
|
116
122
|
wyrm token # muestra el token
|
|
@@ -145,7 +151,7 @@ wyrm service install
|
|
|
145
151
|
wyrm service uninstall
|
|
146
152
|
```
|
|
147
153
|
|
|
148
|
-
## TUI (`wyrm top`)
|
|
154
|
+
## TUI (`wyrm top`)
|
|
149
155
|
|
|
150
156
|
Dashboard dos paneles: lista + detalle + sparklines CPU/MEM + preview logs.
|
|
151
157
|
Tabs `1/2/3` o `tab` para Dashboard / Logs / Help.
|
|
@@ -176,7 +182,7 @@ CLI (clap) ──Named Pipe NDJSON──> Daemon
|
|
|
176
182
|
│ ├─ HashMap<String, ManagedApp>
|
|
177
183
|
│ ├─ JobObject por proceso
|
|
178
184
|
│ ├─ logs a archivo (append)
|
|
179
|
-
│ └─ supervise loop cada 2s
|
|
185
|
+
│ └─ supervise loop cada 2s
|
|
180
186
|
└─ fallback DB (SQLite) si demonio off
|
|
181
187
|
|
|
182
188
|
crates/wyrm/src:
|
|
@@ -194,7 +200,7 @@ crates/wyrm/src:
|
|
|
194
200
|
tui/ state/data/actions/events/theme/views
|
|
195
201
|
```
|
|
196
202
|
|
|
197
|
-
## Límites conocidos
|
|
203
|
+
## Límites conocidos
|
|
198
204
|
|
|
199
205
|
- **ACME real sin probar E2E**: `cert issue --prod` requiere DNS público
|
|
200
206
|
apuntando al servidor + `wyrm edge` en :80. Usa `--staging` primero.
|
|
@@ -204,6 +210,13 @@ crates/wyrm/src:
|
|
|
204
210
|
CTRL+BREAK elegante queda futuro.
|
|
205
211
|
- **Policies fuera de SQLite**: viven en `wyrm.json`/payload `START`;
|
|
206
212
|
el restore tras reinicio usa defaults.
|
|
213
|
+
- **Hooks solo con `wyrm.json` en el cwd de la app**: no se buscan
|
|
214
|
+
directorios padre; con layout multi-app de raíz se ignoran en silencio.
|
|
215
|
+
- **Token en memoria**: `token --rotate` aplica al reiniciar el demonio.
|
|
216
|
+
- **Un solo demonio**: dos instancias compiten por el pipe; antes de
|
|
217
|
+
arrancar, termina restos con `taskkill /F /IM wyrm.exe`.
|
|
218
|
+
- **Reintento inmediato**: el backoff calculado no espera; el loop de 2s
|
|
219
|
+
es el único intervalo entre reintentos.
|
|
207
220
|
- **API solo loopback** (`127.0.0.1:8379`); el token vive en archivo con los
|
|
208
221
|
permisos del FS del SO.
|
|
209
222
|
- **Single-node**: un demonio por pipe, sin clustering multi-máquina.
|
|
@@ -212,31 +225,38 @@ crates/wyrm/src:
|
|
|
212
225
|
|
|
213
226
|
## Release (mantenedores)
|
|
214
227
|
|
|
215
|
-
|
|
228
|
+
El tag `vX.Y.Z` dispara CI: `cargo test` → `pnpm test:e2e` → build x64
|
|
229
|
+
release → GitHub Release con `wyrm-x86_64-pc-windows-msvc.exe` + `.sha256`.
|
|
230
|
+
CI nunca publica a npm (sin token a propósito).
|
|
216
231
|
|
|
217
232
|
```powershell
|
|
218
233
|
git tag v0.1.0
|
|
219
234
|
git push origin v0.1.0
|
|
220
235
|
```
|
|
221
236
|
|
|
222
|
-
|
|
223
|
-
`wyrm-x86_64-pc-windows-msvc.exe` + `.sha256` → sync de versión en
|
|
224
|
-
`npm/package.json` → `npm publish`. El `postinstall` descarga el asset de
|
|
225
|
-
tu misma versión y verifica SHA256.
|
|
226
|
-
|
|
227
|
-
Requisitos: secret `NPM_TOKEN` en el repo. Verificación local previa:
|
|
237
|
+
Publicación en un comando (tu único trabajo: el OAuth de npm en el navegador):
|
|
228
238
|
|
|
229
239
|
```powershell
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
npm pack --dry-run # en npm/: confirma 4 archivos, sin vendor/
|
|
240
|
+
pnpm release # auto-bump: patch sobre lo último publicado
|
|
241
|
+
pnpm release 1.0.2 # versión exacta
|
|
233
242
|
```
|
|
234
243
|
|
|
244
|
+
El script verifica sesión npm + maintainer primero (un 404 al publicar
|
|
245
|
+
casi siempre es identidad sin permiso, no versión), sincroniza
|
|
246
|
+
`Cargo.toml`/`npm/package.json`, corre unit + e2e, compila, mete el exe en
|
|
247
|
+
`npm/vendor/`, hace smoke del wrapper y del tarball, pushea `main` + tag
|
|
248
|
+
`vX.Y.Z` (CI construye el GitHub Release) y publica con `pnpm publish`.
|
|
249
|
+
Falla rápido ante cualquier error.
|
|
250
|
+
Simula sin efectos con `pnpm release --dry-run`.
|
|
251
|
+
|
|
252
|
+
El tarball es autocontenido (sin postinstall: funciona con npm/pnpm/yarn/bun).
|
|
253
|
+
|
|
235
254
|
## Desarrollo
|
|
236
255
|
|
|
237
256
|
```powershell
|
|
238
257
|
cargo check -p wyrm
|
|
239
258
|
cargo test -p wyrm
|
|
259
|
+
pnpm test:e2e # e2e fuera de .rs: tests/e2e/ (deploy, ciclo CLI, prod)
|
|
240
260
|
cargo fmt
|
|
241
261
|
cargo clippy -p wyrm
|
|
242
262
|
```
|
package/bin/cli.js
CHANGED
|
@@ -7,9 +7,7 @@ const path = require('path');
|
|
|
7
7
|
const binPath = path.join(__dirname, '..', 'vendor', 'wyrm.exe');
|
|
8
8
|
|
|
9
9
|
if (!fs.existsSync(binPath)) {
|
|
10
|
-
console.error(
|
|
11
|
-
'[wyrm] Falta vendor/wyrm.exe. Reinstala (npm i -g @carlos-burelo/wyrm) o compila local con `npm run build`.'
|
|
12
|
-
);
|
|
10
|
+
console.error('[wyrm] Falta vendor/wyrm.exe: instalación corrupta. Reinstala el paquete.');
|
|
13
11
|
process.exit(1);
|
|
14
12
|
}
|
|
15
13
|
|
package/package.json
CHANGED
|
@@ -1,38 +1,35 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@carlos-burelo/wyrm",
|
|
3
|
-
"version": "1.0.
|
|
4
|
-
"description": "El gestor de procesos definitivo para Windows Server escrito en Rust",
|
|
5
|
-
"main": "bin/cli.js",
|
|
6
|
-
"files": [
|
|
7
|
-
"bin/",
|
|
8
|
-
"
|
|
9
|
-
"README.md"
|
|
10
|
-
],
|
|
11
|
-
"bin": {
|
|
12
|
-
"wyrm": "bin/cli.js"
|
|
13
|
-
},
|
|
14
|
-
"
|
|
15
|
-
"
|
|
16
|
-
|
|
17
|
-
"
|
|
18
|
-
"
|
|
19
|
-
],
|
|
20
|
-
"
|
|
21
|
-
"
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
"
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
"
|
|
29
|
-
"
|
|
30
|
-
"
|
|
31
|
-
"
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
],
|
|
36
|
-
"author": "Carlos Burelo",
|
|
37
|
-
"license": "MIT"
|
|
1
|
+
{
|
|
2
|
+
"name": "@carlos-burelo/wyrm",
|
|
3
|
+
"version": "1.0.2",
|
|
4
|
+
"description": "El gestor de procesos definitivo para Windows Server escrito en Rust",
|
|
5
|
+
"main": "bin/cli.js",
|
|
6
|
+
"files": [
|
|
7
|
+
"bin/",
|
|
8
|
+
"vendor/",
|
|
9
|
+
"README.md"
|
|
10
|
+
],
|
|
11
|
+
"bin": {
|
|
12
|
+
"wyrm": "bin/cli.js"
|
|
13
|
+
},
|
|
14
|
+
"os": [
|
|
15
|
+
"win32"
|
|
16
|
+
],
|
|
17
|
+
"cpu": [
|
|
18
|
+
"x64"
|
|
19
|
+
],
|
|
20
|
+
"repository": {
|
|
21
|
+
"type": "git",
|
|
22
|
+
"url": "git+https://github.com/carlos-burelo/wyrm.git"
|
|
23
|
+
},
|
|
24
|
+
"keywords": [
|
|
25
|
+
"wyrm",
|
|
26
|
+
"pm2",
|
|
27
|
+
"windows-server",
|
|
28
|
+
"rust",
|
|
29
|
+
"process-manager",
|
|
30
|
+
"nextjs",
|
|
31
|
+
"astro"
|
|
32
|
+
],
|
|
33
|
+
"author": "Carlos Burelo",
|
|
34
|
+
"license": "MIT"
|
|
38
35
|
}
|
package/vendor/wyrm.exe
ADDED
|
Binary file
|
package/scripts/install.js
DELETED
|
@@ -1,123 +0,0 @@
|
|
|
1
|
-
// Postinstall de wyrm: deja vendor/wyrm.exe listo.
|
|
2
|
-
// - Versión desde package.json (la sincroniza el workflow de release).
|
|
3
|
-
// - Salta si ya hay binario local (npm run build) o WYRM_SKIP_DOWNLOAD.
|
|
4
|
-
// - Verifica SHA256 del asset .sha256 cuando existe.
|
|
5
|
-
const fs = require('fs');
|
|
6
|
-
const path = require('path');
|
|
7
|
-
const https = require('https');
|
|
8
|
-
const crypto = require('crypto');
|
|
9
|
-
|
|
10
|
-
const REPO = 'carlos-burelo/wyrm';
|
|
11
|
-
const BIN_DIR = path.join(__dirname, '..', 'vendor');
|
|
12
|
-
const BIN_NAME = 'wyrm.exe';
|
|
13
|
-
const BIN_PATH = path.join(BIN_DIR, BIN_NAME);
|
|
14
|
-
|
|
15
|
-
const TARGETS = {
|
|
16
|
-
'win32-x64': 'x86_64-pc-windows-msvc',
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
function pkgVersion() {
|
|
20
|
-
const { version } = require('../package.json');
|
|
21
|
-
if (!version) throw new Error('package.json sin version');
|
|
22
|
-
return String(version).replace(/^v/, '');
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
function targetTriple(platform = process.platform, arch = process.arch) {
|
|
26
|
-
const triple = TARGETS[`${platform}-${arch}`];
|
|
27
|
-
if (!triple) {
|
|
28
|
-
throw new Error(
|
|
29
|
-
`Plataforma no soportada: ${platform}-${arch} (wyrm v1 solo publica win32-x64)`
|
|
30
|
-
);
|
|
31
|
-
}
|
|
32
|
-
return triple;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
function assetName(triple) {
|
|
36
|
-
return `wyrm-${triple}.exe`;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
function download(url, dest, redirects = 5) {
|
|
40
|
-
return new Promise((resolve, reject) => {
|
|
41
|
-
https
|
|
42
|
-
.get(url, (res) => {
|
|
43
|
-
const { statusCode, headers } = res;
|
|
44
|
-
if ((statusCode === 301 || statusCode === 302) && headers.location) {
|
|
45
|
-
if (redirects <= 0) return reject(new Error('demasiadas redirecciones'));
|
|
46
|
-
res.resume();
|
|
47
|
-
return resolve(download(headers.location, dest, redirects - 1));
|
|
48
|
-
}
|
|
49
|
-
if (statusCode !== 200) {
|
|
50
|
-
res.resume();
|
|
51
|
-
return reject(new Error(`HTTP ${statusCode} en ${url}`));
|
|
52
|
-
}
|
|
53
|
-
const file = fs.createWriteStream(dest);
|
|
54
|
-
res.pipe(file);
|
|
55
|
-
file.on('finish', () => file.close(resolve));
|
|
56
|
-
file.on('error', reject);
|
|
57
|
-
})
|
|
58
|
-
.on('error', reject);
|
|
59
|
-
});
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
function sha256File(p) {
|
|
63
|
-
return new Promise((resolve, reject) => {
|
|
64
|
-
const h = crypto.createHash('sha256');
|
|
65
|
-
fs.createReadStream(p)
|
|
66
|
-
.on('data', (d) => h.update(d))
|
|
67
|
-
.on('end', () => resolve(h.digest('hex')))
|
|
68
|
-
.on('error', reject);
|
|
69
|
-
});
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
async function install() {
|
|
73
|
-
if (process.env.WYRM_SKIP_DOWNLOAD) {
|
|
74
|
-
console.log('[wyrm] WYRM_SKIP_DOWNLOAD=1: salto la descarga.');
|
|
75
|
-
return;
|
|
76
|
-
}
|
|
77
|
-
if (fs.existsSync(BIN_PATH)) {
|
|
78
|
-
console.log('[wyrm] Binario local existente, salto la descarga.');
|
|
79
|
-
return;
|
|
80
|
-
}
|
|
81
|
-
const version = pkgVersion();
|
|
82
|
-
const triple = targetTriple();
|
|
83
|
-
const base = `https://github.com/${REPO}/releases/download/v${version}`;
|
|
84
|
-
const asset = assetName(triple);
|
|
85
|
-
fs.mkdirSync(BIN_DIR, { recursive: true });
|
|
86
|
-
|
|
87
|
-
console.log(`[wyrm] Descargando ${asset} v${version}...`);
|
|
88
|
-
try {
|
|
89
|
-
await download(`${base}/${asset}`, BIN_PATH);
|
|
90
|
-
} catch (e) {
|
|
91
|
-
try {
|
|
92
|
-
fs.unlinkSync(BIN_PATH);
|
|
93
|
-
} catch (_) {}
|
|
94
|
-
throw new Error(
|
|
95
|
-
`No se pudo descargar el binario (${e.message}). ¿Existe el release v${version}?`
|
|
96
|
-
);
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
try {
|
|
100
|
-
await download(`${base}/${asset}.sha256`, `${BIN_PATH}.sha256`);
|
|
101
|
-
const expected = fs.readFileSync(`${BIN_PATH}.sha256`, 'utf8').trim().toLowerCase();
|
|
102
|
-
const actual = await sha256File(BIN_PATH);
|
|
103
|
-
if (expected !== actual) {
|
|
104
|
-
fs.unlinkSync(BIN_PATH);
|
|
105
|
-
throw new Error('SHA256 no coincide: binario corrupto o manipulado.');
|
|
106
|
-
}
|
|
107
|
-
fs.unlinkSync(`${BIN_PATH}.sha256`);
|
|
108
|
-
console.log('[wyrm] SHA256 verificado.');
|
|
109
|
-
} catch (e) {
|
|
110
|
-
if (/corrupto|manipulado/.test(e.message)) throw e;
|
|
111
|
-
console.warn(`[wyrm] Aviso: sin checksum (${e.message}), continúo sin verificar.`);
|
|
112
|
-
}
|
|
113
|
-
console.log('[wyrm] Instalación nativa completada.');
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
if (require.main === module) {
|
|
117
|
-
install().catch((e) => {
|
|
118
|
-
console.error(`[wyrm] ${e.message}`);
|
|
119
|
-
process.exit(1);
|
|
120
|
-
});
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
module.exports = { pkgVersion, targetTriple, assetName, REPO, BIN_PATH };
|