@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 CHANGED
@@ -1,11 +1,13 @@
1
- # Wyrm 🐉
1
+ ![Wyrm](https://raw.githubusercontent.com/carlos-burelo/wyrm/main/banner.jpg)
2
2
 
3
- **Wyrm** es un gestor de procesos de ultra alto rendimiento escrito en **Rust**, enfocado en **Windows Server**. Mantiene aplicaciones Node.js, Next.js, Astro y Express corriendo como Servicio de Windows nativo con Zero-Config.
3
+ # Wyrm
4
4
 
5
- - Supervisor real con `JobObjects` (`KILL_ON_JOB_CLOSE`), auto-restart con backoff y restore al arrancar.
6
- - IPC por Named Pipe con protocolo tipado `Request/Response` NDJSON.
7
- - CLI completa: `start/stop/restart/delete/list/status/logs/daemon/top/service`.
8
- - TUI de primer nivel con Ratatui: lista, métricas CPU/MEM, logs en vivo, filtro y acciones.
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
- Historial en SQLite (`deploys`).
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`) — mejor que `pm2 monit`
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 + backoff
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 (honestos)
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
- Todo automático al pushear un tag `vX.Y.Z`:
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
- CI hace: `cargo test` → build x64 release → GitHub Release con
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
- cargo test -p wyrm
231
- node --check npm/scripts/install.js
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.0",
4
- "description": "El gestor de procesos definitivo para Windows Server escrito en Rust",
5
- "main": "bin/cli.js",
6
- "files": [
7
- "bin/",
8
- "scripts/",
9
- "README.md"
10
- ],
11
- "bin": {
12
- "wyrm": "bin/cli.js"
13
- },
14
- "scripts": {
15
- "postinstall": "node scripts/install.js"
16
- },
17
- "os": [
18
- "win32"
19
- ],
20
- "cpu": [
21
- "x64"
22
- ],
23
- "repository": {
24
- "type": "git",
25
- "url": "git+https://github.com/carlos-burelo/wyrm.git"
26
- },
27
- "keywords": [
28
- "wyrm",
29
- "pm2",
30
- "windows-server",
31
- "rust",
32
- "process-manager",
33
- "nextjs",
34
- "astro"
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
  }
Binary file
@@ -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 };