@danieltmn/tamnora 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.
Files changed (123) hide show
  1. package/CHANGELOG.md +48 -0
  2. package/LICENSE +21 -0
  3. package/README.md +130 -0
  4. package/SECURITY.md +31 -0
  5. package/harness/packages/cli/bin/tamnora.mjs +11 -0
  6. package/harness/packages/cli/package.json +10 -0
  7. package/harness/packages/cli/src/cli.js +90 -0
  8. package/harness/packages/cli/src/commands/autostart.js +121 -0
  9. package/harness/packages/cli/src/commands/bridge.js +274 -0
  10. package/harness/packages/cli/src/commands/doctor.js +73 -0
  11. package/harness/packages/cli/src/commands/eval.js +183 -0
  12. package/harness/packages/cli/src/commands/init.js +31 -0
  13. package/harness/packages/cli/src/commands/mcp.js +101 -0
  14. package/harness/packages/cli/src/commands/repl.js +172 -0
  15. package/harness/packages/cli/src/commands/run.js +165 -0
  16. package/harness/packages/cli/src/commands/serve.js +1 -0
  17. package/harness/packages/cli/src/commands/server.js +345 -0
  18. package/harness/packages/cli/src/commands/tui.js +110 -0
  19. package/harness/packages/cli/src/commands/update.js +76 -0
  20. package/harness/packages/cli/src/commands/user.js +135 -0
  21. package/harness/packages/cli/src/lib/core.js +2 -0
  22. package/harness/packages/cli/src/lib/daemon.js +153 -0
  23. package/harness/packages/cli/src/lib/qr.js +203 -0
  24. package/harness/packages/cli/src/lib/runtime.js +110 -0
  25. package/harness/packages/cli/src/lib/tunnel.js +113 -0
  26. package/harness/packages/cli/src/lib/util.js +24 -0
  27. package/harness/packages/cli/src/tui/app.js +302 -0
  28. package/harness/packages/cli/src/tui/keys.js +78 -0
  29. package/harness/packages/cli/src/tui/layout.js +130 -0
  30. package/harness/packages/cli/src/tui/screen.js +13 -0
  31. package/harness/packages/cli/src/ui/color.js +15 -0
  32. package/harness/packages/cli/test/release.test.js +34 -0
  33. package/harness/packages/cli/test/tui.test.js +99 -0
  34. package/harness/packages/cli/test/tunnel.test.js +20 -0
  35. package/harness/packages/core/package.json +14 -0
  36. package/harness/packages/core/src/agent/context.js +66 -0
  37. package/harness/packages/core/src/agent/modes.js +26 -0
  38. package/harness/packages/core/src/agent/runAgent.js +257 -0
  39. package/harness/packages/core/src/config/app.js +49 -0
  40. package/harness/packages/core/src/config/home.js +98 -0
  41. package/harness/packages/core/src/config/identity.js +41 -0
  42. package/harness/packages/core/src/config/index.js +69 -0
  43. package/harness/packages/core/src/config/providers.js +24 -0
  44. package/harness/packages/core/src/env.js +19 -0
  45. package/harness/packages/core/src/eval/dataset.js +47 -0
  46. package/harness/packages/core/src/eval/judge.js +39 -0
  47. package/harness/packages/core/src/eval/report.js +48 -0
  48. package/harness/packages/core/src/eval/runTask.js +71 -0
  49. package/harness/packages/core/src/eval/runner.js +150 -0
  50. package/harness/packages/core/src/eval/scorers.js +217 -0
  51. package/harness/packages/core/src/eval/task.js +59 -0
  52. package/harness/packages/core/src/index.js +72 -0
  53. package/harness/packages/core/src/mcp/common.js +82 -0
  54. package/harness/packages/core/src/mcp/http.js +149 -0
  55. package/harness/packages/core/src/mcp/index.js +167 -0
  56. package/harness/packages/core/src/mcp/stdio.js +218 -0
  57. package/harness/packages/core/src/providers/index.js +119 -0
  58. package/harness/packages/core/src/providers/ollama.js +76 -0
  59. package/harness/packages/core/src/providers/openaiCompatible.js +57 -0
  60. package/harness/packages/core/src/providers/openrouter.js +57 -0
  61. package/harness/packages/core/src/providers/presets.js +19 -0
  62. package/harness/packages/core/src/providers/resilience.js +74 -0
  63. package/harness/packages/core/src/providers/stream.js +200 -0
  64. package/harness/packages/core/src/providers/utils.js +66 -0
  65. package/harness/packages/core/src/storage/sqlite.js +453 -0
  66. package/harness/packages/core/src/tools/diff.js +90 -0
  67. package/harness/packages/core/src/tools/executors.js +277 -0
  68. package/harness/packages/core/src/tools/glob.js +67 -0
  69. package/harness/packages/core/src/tools/index.js +28 -0
  70. package/harness/packages/core/src/tools/paths.js +29 -0
  71. package/harness/packages/core/src/tools/permissions.js +69 -0
  72. package/harness/packages/core/src/tools/registry.js +44 -0
  73. package/harness/packages/core/src/tools/schema.js +177 -0
  74. package/harness/packages/core/test/agent.test.js +237 -0
  75. package/harness/packages/core/test/config.test.js +33 -0
  76. package/harness/packages/core/test/context.test.js +44 -0
  77. package/harness/packages/core/test/diff.test.js +20 -0
  78. package/harness/packages/core/test/eval.test.js +144 -0
  79. package/harness/packages/core/test/git-tools.test.js +44 -0
  80. package/harness/packages/core/test/home.test.js +62 -0
  81. package/harness/packages/core/test/mcp.test.js +158 -0
  82. package/harness/packages/core/test/providers.test.js +80 -0
  83. package/harness/packages/core/test/resilience.test.js +61 -0
  84. package/harness/packages/core/test/storage.test.js +55 -0
  85. package/harness/packages/core/test/stream.test.js +91 -0
  86. package/harness/packages/core/test/tools.test.js +96 -0
  87. package/harness/packages/server/bin/tamnora-server.mjs +11 -0
  88. package/harness/packages/server/package.json +10 -0
  89. package/harness/packages/server/public/app.js +2188 -0
  90. package/harness/packages/server/public/img/fondo-agentes.webp +0 -0
  91. package/harness/packages/server/public/index.html +368 -0
  92. package/harness/packages/server/public/landing/index.html +523 -0
  93. package/harness/packages/server/public/landing/landing.js +89 -0
  94. package/harness/packages/server/public/markdown.js +174 -0
  95. package/harness/packages/server/public/styles.css +923 -0
  96. package/harness/packages/server/src/auth.js +146 -0
  97. package/harness/packages/server/src/cli.js +61 -0
  98. package/harness/packages/server/src/index.js +1 -0
  99. package/harness/packages/server/src/lib/approvals.js +51 -0
  100. package/harness/packages/server/src/lib/core.js +2 -0
  101. package/harness/packages/server/src/lib/http.js +74 -0
  102. package/harness/packages/server/src/lib/modelCache.js +32 -0
  103. package/harness/packages/server/src/lib/prompt.js +48 -0
  104. package/harness/packages/server/src/lib/workspace.js +74 -0
  105. package/harness/packages/server/src/routes/auth.js +56 -0
  106. package/harness/packages/server/src/routes/chat.js +222 -0
  107. package/harness/packages/server/src/routes/eval.js +100 -0
  108. package/harness/packages/server/src/routes/files.js +117 -0
  109. package/harness/packages/server/src/routes/folders.js +80 -0
  110. package/harness/packages/server/src/routes/mcp.js +50 -0
  111. package/harness/packages/server/src/routes/models.js +26 -0
  112. package/harness/packages/server/src/routes/providers.js +135 -0
  113. package/harness/packages/server/src/routes/sessions.js +157 -0
  114. package/harness/packages/server/src/server.js +249 -0
  115. package/harness/packages/server/src/static.js +67 -0
  116. package/harness/packages/server/test/markdown.test.js +36 -0
  117. package/harness/packages/server/test/modelCache.test.js +72 -0
  118. package/harness/packages/server/test/server.test.js +809 -0
  119. package/harness/scripts/example-dataset.jsonl +4 -0
  120. package/harness/scripts/mcp-echo-server.mjs +108 -0
  121. package/harness/scripts/mock-provider.mjs +134 -0
  122. package/harness/tamnora.example.json +18 -0
  123. package/package.json +70 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,48 @@
1
+ # Changelog
2
+
3
+ Todos los cambios relevantes de este proyecto se documentan en este archivo.
4
+
5
+ El formato sigue [Keep a Changelog](https://keepachangelog.com/es-ES/1.1.0/)
6
+ y el proyecto usa [Versionado Semantico](https://semver.org/lang/es/).
7
+
8
+ ## [0.1.0] - 2026-10-01
9
+
10
+ ### Agregado
11
+
12
+ - Casa global de Tamnora (`TAMNORA_HOME` / datos del sistema) con `app.json`,
13
+ base SQLite global, logs y runtime.
14
+ - `tamnora server --init|--start|--reload|--stop|--status|--logs`: daemon en
15
+ segundo plano que sobrevive al cierre de la consola.
16
+ - Login real (cookie firmada + CSRF + lockout) y `tamnora user` (scrypt).
17
+ - `tamnora bridge --start|--reload|--stop|--status`: tunel publico (tunnelmole)
18
+ con QR para abrir la URL desde el celular.
19
+ - `tamnora doctor`, `tamnora update` y `tamnora autostart install|remove`.
20
+ - `scripts/release.mjs`: release con guard de credenciales, changelog, tag, push
21
+ y `npm publish` (con `--dry-run`).
22
+ - Reintentos con backoff ante errores transitorios (429/5xx/red) y cancelacion
23
+ real entre rondas/tools en el loop del agente (evento `retry`).
24
+ - Gestion de contexto: recorte de tool-results viejos y descarte de turnos
25
+ antiguos cuando la sesion se acerca al limite del modelo (evento `trim`).
26
+ - Consola responsive: en movil la barra lateral es un drawer con backdrop, el
27
+ topbar envuelve y la conversacion usa todo el ancho.
28
+ - Tools de Git (`git_status`, `git_diff`, `git_log`, `git_commit`) y diff
29
+ coloreado en la consola para los cambios de `write`/`edit`.
30
+ - Acciones por mensaje en la consola: copiar, editar, regenerar y borrar desde
31
+ ese punto (`POST /api/sessions/:id/truncate`).
32
+ - Ajustes del agente (modo, proveedor, modelo y auto) en un modal; el topbar
33
+ queda con solo los botones de panel y tema.
34
+ - Busqueda global (sesiones y mensajes, `GET /api/search`) y command palette
35
+ (`Ctrl/Cmd+K`) con comandos rapidos.
36
+ - MCP completo: recursos y prompts (tools sinteticas `list_/read_resource`,
37
+ `list_/get_prompt`), reconexion de stdio y refresco por `tools/list_changed`.
38
+ - TUI: edicion con cursor (flechas/home/end/delete), scroll (pgup/pgdn),
39
+ bracketed paste, contador de contexto, `--mode plan` y restauracion de la
40
+ terminal ante salida/senal.
41
+ - CI multi-plataforma y analisis de la consola web del harness.
42
+
43
+ ### Cambiado
44
+
45
+ - El chat SPA se separo a su propio repositorio (`tamnora-chat`).
46
+ - La raiz del repo pasa a ser el paquete publicable `@danieltmn/tamnora`.
47
+ - Puerto default `4517`; la base SQLite es global (migra la legacy por proyecto).
48
+ - La landing pasa de login cosmetico a autenticacion real.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Tamnora
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.
package/README.md ADDED
@@ -0,0 +1,130 @@
1
+ # Tamnora Harness
2
+
3
+ Harness local para IAs: un agente **CLI-first** (estilo opencode) en Node puro,
4
+ con TUI, consola web, tools locales, MCP y evaluacion de modelos.
5
+
6
+ - **Sin dependencias**: solo modulos de Node (`node:sqlite`, `node:crypto`, ...).
7
+ - **Local**: corre en tu maquina y guarda el estado en tu casa local.
8
+ - **Multi-proveedor**: OpenRouter, OpenAI-compatible (DeepSeek, Groq, Mistral,
9
+ xAI, Together, Fireworks, Cerebras, Perplexity) y Ollama.
10
+
11
+ ## Instalacion
12
+
13
+ ```bash
14
+ npx @danieltmn/tamnora --help
15
+ # o global
16
+ npm i -g @danieltmn/tamnora
17
+ ```
18
+
19
+ Requiere **Node.js >= 22.5** (por `node:sqlite`).
20
+
21
+ ## Uso rapido
22
+
23
+ ```bash
24
+ tamnora init # crea la configuracion (proveedor, modelo)
25
+ tamnora # consola TUI full-screen (REPL si no hay TTY)
26
+ tamnora run "hola" # tarea headless
27
+ tamnora eval <dataset.jsonl> # evaluacion de modelos
28
+ tamnora mcp [--json] # servidores MCP: tools, recursos y prompts
29
+ tamnora serve # consola web
30
+ ```
31
+
32
+ ### Consola TUI
33
+
34
+ ```bash
35
+ tamnora # TUI full-screen (REPL si no hay TTY)
36
+ tamnora tui --mode plan # modo plan: solo lectura, sin escritura ni bash
37
+ ```
38
+
39
+ Atajos: `enter` enviar · `↑/↓` historial · `←/→` cursor · `home/end` inicio/fin ·
40
+ `pgup/pgdn` scroll · `ctrl+l` limpiar · `ctrl+c` cancelar/salir · `/salir`,
41
+ `/limpiar`. El pegado de bloques multilinea entra como un solo texto y el header
42
+ muestra el contexto usado (`ctx <tokens>`).
43
+
44
+ ### Server como servicio
45
+
46
+ ```bash
47
+ tamnora server --init # crea la casa y arranca (segundo plano)
48
+ tamnora server --start # arranca (default)
49
+ tamnora server --reload # reinicia aplicando config/codigo
50
+ tamnora server --stop
51
+ tamnora server --status
52
+ tamnora server --logs --follow
53
+ tamnora doctor # verifica Node, casa, puerto y base
54
+ tamnora update # busca/instala una version nueva
55
+ tamnora autostart install # arranque automatico del server
56
+ ```
57
+
58
+ `tamnora server` corre en segundo plano y **sobrevive al cierre de la consola**
59
+ (puerto default `4517`). La configuracion de la app vive en la **casa** de
60
+ Tamnora (override con `--home` o `TAMNORA_HOME`):
61
+
62
+ - Windows: `%APPDATA%\tamnora`
63
+ - macOS: `~/Library/Application Support/tamnora`
64
+ - Linux: `~/.config/tamnora`
65
+
66
+ ### Login
67
+
68
+ ```bash
69
+ tamnora user --name <n> --password <p> # crea/cambia el usuario local
70
+ tamnora user # muestra el usuario
71
+ tamnora user remove # el server deja de pedir login
72
+ ```
73
+
74
+ Con un usuario configurado, **todas** las rutas `/api/*` piden sesion (cookie
75
+ firmada + CSRF) y la landing pasa a ser el login real; sin usuario, el server
76
+ corre en modo local sin login. Al cambiar el usuario se detiene el server
77
+ (volvelo a arrancar con `tamnora server --start`).
78
+
79
+ ### Acceso remoto
80
+
81
+ ```bash
82
+ tamnora bridge --start # abre un tunel publico (tunnelmole) + QR
83
+ tamnora bridge --status # URL publica
84
+ tamnora bridge --reload # reinicia el tunel
85
+ tamnora bridge --stop
86
+ ```
87
+
88
+ `tamnora bridge` abre un tunel (tunnelmole) hacia tu server local y muestra la
89
+ URL publica **y un QR** para abrirlo desde el celular. Trabaja protegido por el
90
+ login del server: configura un usuario antes de exponerlo (ver
91
+ [`SECURITY.md`](SECURITY.md)).
92
+
93
+ ## MCP
94
+
95
+ Los servidores MCP se configuran en `tamnora.json` (`mcp[]`) y soportan HTTP y
96
+ stdio. Las tools se exponen como `mcp__<servidor>__<tool>` y, si el servidor los
97
+ anuncia, tambien sus **recursos** y **prompts** (`list_resources`,
98
+ `read_resource`, `list_prompts`, `get_prompt`).
99
+
100
+ ## Documentacion
101
+
102
+ - [`CHANGELOG.md`](CHANGELOG.md) — historial de versiones.
103
+ - [`AGENTS.md`](AGENTS.md) — arquitectura del harness, tools, MCP, evaluacion y
104
+ convenciones.
105
+ - [`harness/tamnora.example.json`](harness/tamnora.example.json) — plantilla de
106
+ configuracion.
107
+ - [`SECURITY.md`](SECURITY.md) — modelo de amenaza y reporte de vulnerabilidades.
108
+
109
+ > El chat SPA (Tamnora Chat) vive en su propio repositorio: `tamnora-chat`.
110
+
111
+ ## Versiones
112
+
113
+ El proyecto sigue [Versionado Semantico](https://semver.org/lang/es/). Cada
114
+ version se documenta en [`CHANGELOG.md`](CHANGELOG.md) y se publica como un tag
115
+ `vX.Y.Z` en git mas `npm publish`.
116
+
117
+ ## Desarrollo
118
+
119
+ ```bash
120
+ pnpm install
121
+ pnpm tamnora # TUI / REPL
122
+ pnpm serve # consola web
123
+ pnpm mock # proveedor local de prueba (OpenAI-compatible + Ollama)
124
+ pnpm test # tests (node:test)
125
+ pnpm lint # ESLint
126
+ ```
127
+
128
+ ## Licencia
129
+
130
+ MIT.
package/SECURITY.md ADDED
@@ -0,0 +1,31 @@
1
+ # Seguridad
2
+
3
+ ## Reportar una vulnerabilidad
4
+
5
+ No abras un issue publico para vulnerabilidades. Escribi a **security@tamnora.com**
6
+ con: descripcion, pasos para reproducir, impacto y version afectada. Respondemos
7
+ dentro de 72 horas.
8
+
9
+ ## Modelo de amenaza
10
+
11
+ Tamnora es un harness **local**: corre en tu maquina y, por defecto, escucha en
12
+ `127.0.0.1`. Ejecuta comandos y escribe archivos dentro del workspace que elijas,
13
+ asi que se lo debe tratar como codigo con acceso a tu usuario.
14
+
15
+ - **Local (loopback)**: quien ya tiene tu sesion de usuario ya puede correr
16
+ comandos; el server no agrega una barrera adicional mas alla del login.
17
+ - **Acceso remoto por tunel (tunnelmole)**: la URL publica es accesible desde
18
+ Internet. Es obligatorio (a) una passphrase fuerte, (b) login real en toda la
19
+ API, (c) limite de intentos y (d) restringir el picker de carpetas a roots
20
+ permitidos. Nunca expongas un server sin passphrase.
21
+ - **Credenciales**: las API keys de proveedores se guardan en la casa local del
22
+ usuario (fuera del repo) y se cifran con una clave derivada de la passphrase.
23
+ Nunca se commitean y se devuelven enmascaradas al cliente.
24
+
25
+ ## Buenas practicas
26
+
27
+ - Mantene Node actualizado (`node:sqlite` requiere Node >= 22.5).
28
+ - No commitees `.env`, `tamnora.json`, bases `.db` ni `.npmrc`; estan en
29
+ `.gitignore`.
30
+ - Rota cualquier credencial que se haya compartido en texto plano.
31
+ - Cierra el tunel (`tamnora bridge --stop`) cuando no lo uses.
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env node
2
+ import { runCli } from '../src/cli.js';
3
+
4
+ runCli(process.argv.slice(2))
5
+ .then((code) => {
6
+ process.exitCode = typeof code === 'number' ? code : 0;
7
+ })
8
+ .catch((err) => {
9
+ console.error(err?.stack ?? String(err));
10
+ process.exitCode = 1;
11
+ });
@@ -0,0 +1,10 @@
1
+ {
2
+ "name": "@tamnora/cli",
3
+ "version": "0.0.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "description": "Tamnora harness CLI (local, headless-first).",
7
+ "bin": {
8
+ "tamnora": "bin/tamnora.mjs"
9
+ }
10
+ }
@@ -0,0 +1,90 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { fileURLToPath } from 'node:url';
3
+ import path from 'node:path';
4
+ import { runCommand } from './commands/run.js';
5
+ import { replCommand } from './commands/repl.js';
6
+ import { evalCommand } from './commands/eval.js';
7
+ import { initCommand } from './commands/init.js';
8
+ import { mcpCommand } from './commands/mcp.js';
9
+ import { serveCommand } from './commands/serve.js';
10
+ import { serverCommand } from './commands/server.js';
11
+ import { userCommand } from './commands/user.js';
12
+ import { bridgeCommand } from './commands/bridge.js';
13
+ import { updateCommand } from './commands/update.js';
14
+ import { autostartCommand } from './commands/autostart.js';
15
+ import { doctorCommand } from './commands/doctor.js';
16
+ import { tuiCommand } from './commands/tui.js';
17
+ import { color } from './ui/color.js';
18
+
19
+ const COMMANDS = {
20
+ run: runCommand,
21
+ repl: replCommand,
22
+ tui: tuiCommand,
23
+ eval: evalCommand,
24
+ init: initCommand,
25
+ mcp: mcpCommand,
26
+ serve: serveCommand,
27
+ server: serverCommand,
28
+ user: userCommand,
29
+ bridge: bridgeCommand,
30
+ update: updateCommand,
31
+ autostart: autostartCommand,
32
+ doctor: doctorCommand,
33
+ };
34
+
35
+ function readVersion() {
36
+ try {
37
+ const pkgPath = path.join(path.dirname(fileURLToPath(import.meta.url)), '../../../../package.json');
38
+ const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'));
39
+ return pkg.version || '0.0.0';
40
+ } catch {
41
+ return '0.0.0';
42
+ }
43
+ }
44
+
45
+ export async function runCli(argv) {
46
+ const [command, ...rest] = argv;
47
+
48
+ if (command === '-h' || command === '--help' || command === 'help') {
49
+ printHelp();
50
+ return 0;
51
+ }
52
+ if (command === '-v' || command === '--version') {
53
+ console.log(`tamnora ${readVersion()}`);
54
+ return 0;
55
+ }
56
+
57
+ const handler = command && COMMANDS[command];
58
+ if (handler) return handler(rest);
59
+
60
+ if (command && !command.startsWith('-')) {
61
+ console.error(color.red(`Comando desconocido: ${command}`));
62
+ printHelp();
63
+ return 1;
64
+ }
65
+
66
+ // No subcommand → full-screen console on a TTY, else the line REPL
67
+ if (process.stdin.isTTY && process.stdout.isTTY) return tuiCommand(argv);
68
+ return replCommand(argv);
69
+ }
70
+
71
+ function printHelp() {
72
+ console.log(`tamnora — harness local para IAs
73
+
74
+ Uso:
75
+ tamnora Consola full-screen (REPL si no hay TTY)
76
+ tamnora run "<prompt>" Ejecutar una tarea headless
77
+ tamnora eval <dataset.jsonl> Correr un dataset de evaluación
78
+ tamnora mcp Listar servidores y herramientas MCP
79
+ tamnora server [--init|--start|--reload|--stop|--status|--logs] Server en segundo plano
80
+ tamnora user Usuario local (login del server)
81
+ tamnora bridge [--start|--reload|--stop|--status] Tunel publico (tunnelmole) + QR
82
+ tamnora autostart install|remove Arranque automatico del server
83
+ tamnora serve Consola web local (primer plano)
84
+ tamnora doctor Verificar Node, casa, puerto y base
85
+ tamnora update Buscar/instalar una version nueva
86
+ tamnora init Crear tamnora.json
87
+
88
+ También: tamnora repl · tamnora tui
89
+ Corré "tamnora <comando> --help" para ver las opciones de cada comando.`);
90
+ }
@@ -0,0 +1,121 @@
1
+ import { spawnSync } from 'node:child_process';
2
+ import { mkdirSync, unlinkSync, writeFileSync } from 'node:fs';
3
+ import os from 'node:os';
4
+ import path from 'node:path';
5
+ import { homeDir } from '../../../core/src/index.js';
6
+ import { parse } from '../lib/util.js';
7
+
8
+ const OPTIONS = { home: { type: 'string' }, help: { type: 'boolean', short: 'h' } };
9
+
10
+ function autostartPath() {
11
+ if (process.platform === 'win32') {
12
+ const appData = process.env.APPDATA || path.join(os.homedir(), 'AppData', 'Roaming');
13
+ return path.join(appData, 'Microsoft', 'Windows', 'Start Menu', 'Programs', 'Startup', 'Tamnora.lnk');
14
+ }
15
+ if (process.platform === 'darwin') {
16
+ return path.join(os.homedir(), 'Library', 'LaunchAgents', 'net.tamnora.server.plist');
17
+ }
18
+ return path.join(os.homedir(), '.config', 'systemd', 'user', 'tamnora.service');
19
+ }
20
+
21
+ function psQuote(s) {
22
+ return `'${String(s).replace(/'/g, "''")}'`;
23
+ }
24
+
25
+ function vbsStr(s) {
26
+ return `"${String(s).replace(/"/g, '""')}"`;
27
+ }
28
+
29
+ export async function autostartCommand(argv) {
30
+ const { positionals, values } = parse(argv, OPTIONS);
31
+ if (values.help) {
32
+ console.log('Uso: tamnora autostart install|remove\n\nInstala/quita el arranque automatico del server.');
33
+ return 0;
34
+ }
35
+ const action = String(positionals[0] || '').toLowerCase();
36
+ if (action !== 'install' && action !== 'remove') {
37
+ console.error('Uso: tamnora autostart install|remove');
38
+ return 1;
39
+ }
40
+
41
+ const bin = process.argv[1];
42
+ const node = process.execPath;
43
+ const target = autostartPath();
44
+ const homeArg = process.env.TAMNORA_HOME ? ` --home "${homeDir()}"` : '';
45
+ const workdir = os.homedir();
46
+
47
+ if (action === 'remove') {
48
+ let removed = false;
49
+ try {
50
+ unlinkSync(target);
51
+ removed = true;
52
+ } catch {
53
+ /* no estaba */
54
+ }
55
+ try {
56
+ unlinkSync(path.join(homeDir(), 'autostart.vbs'));
57
+ } catch {
58
+ /* nada */
59
+ }
60
+ console.log(removed ? `autostart quitado: ${target}` : 'no habia autostart instalado.');
61
+ return 0;
62
+ }
63
+
64
+ mkdirSync(path.dirname(target), { recursive: true });
65
+
66
+ if (process.platform === 'win32') {
67
+ // Lanzador oculto: wscript.exe corre un .vbs que abre node sin consola.
68
+ const wscript = path.join(process.env.SystemRoot || 'C:\\Windows', 'System32', 'wscript.exe');
69
+ const vbsPath = path.join(homeDir(), 'autostart.vbs');
70
+ const vbsCmd = `"${node}" "${bin}" server --start${homeArg}`;
71
+ const vbs = ['Set sh = CreateObject("WScript.Shell")', `sh.Run ${vbsStr(vbsCmd)}, 0, False`, ''].join('\r\n');
72
+ writeFileSync(vbsPath, vbs);
73
+ const ps = [
74
+ '$ws = New-Object -ComObject WScript.Shell;',
75
+ `$sc = $ws.CreateShortcut(${psQuote(target)});`,
76
+ `$sc.TargetPath = ${psQuote(wscript)};`,
77
+ `$sc.Arguments = ${psQuote(`"${vbsPath}"`)};`,
78
+ `$sc.WorkingDirectory = ${psQuote(workdir)};`,
79
+ '$sc.WindowStyle = 1;',
80
+ '$sc.Save();',
81
+ ].join(' ');
82
+ const r = spawnSync('powershell', ['-NoProfile', '-Command', ps], { encoding: 'utf8', windowsHide: true });
83
+ if (r.status !== 0) {
84
+ console.error(`No se pudo crear el acceso directo: ${(r.stderr || '').trim()}`);
85
+ return 1;
86
+ }
87
+ console.log(`autostart instalado: ${target}`);
88
+ console.log(` (arranca \`tamnora server --start\` en ${workdir})`);
89
+ return 0;
90
+ }
91
+
92
+ if (process.platform === 'darwin') {
93
+ const plist =
94
+ '<?xml version="1.0" encoding="UTF-8"?>\n' +
95
+ '<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">\n' +
96
+ '<plist version="1.0"><dict>' +
97
+ '<key>Label</key><string>net.tamnora.server</string>' +
98
+ '<key>ProgramArguments</key><array>' +
99
+ `<string>${node}</string><string>${bin}</string>` +
100
+ '<string>server</string><string>--stream</string>' +
101
+ (process.env.TAMNORA_HOME ? `<string>--home</string><string>${homeDir()}</string>` : '') +
102
+ '</array>' +
103
+ `<key>WorkingDirectory</key><string>${workdir}</string>` +
104
+ '<key>RunAtLoad</key><true/><key>KeepAlive</key><true/>' +
105
+ '</dict></plist>\n';
106
+ writeFileSync(target, plist);
107
+ } else {
108
+ const unit =
109
+ '[Unit]\nDescription=Tamnora Harness\nAfter=network-online.target\n\n' +
110
+ '[Service]\nType=simple\n' +
111
+ `ExecStart=${node} ${bin} server --stream${homeArg}\n` +
112
+ `WorkingDirectory=${workdir}\nRestart=on-failure\n\n` +
113
+ '[Install]\nWantedBy=default.target\n';
114
+ writeFileSync(target, unit);
115
+ spawnSync('systemctl', ['--user', 'daemon-reload'], { windowsHide: true });
116
+ spawnSync('systemctl', ['--user', 'enable', '--now', 'tamnora.service'], { windowsHide: true });
117
+ }
118
+ console.log(`autostart instalado: ${target}`);
119
+ console.log(` (arranca \`tamnora server\` en ${workdir})`);
120
+ return 0;
121
+ }