@dotrino/ia-agent 0.1.0 → 0.1.1

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 (2) hide show
  1. package/bin/cli.js +77 -32
  2. package/package.json +1 -1
package/bin/cli.js CHANGED
@@ -1,43 +1,88 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * dotrino-ia-agent — CLI del agente de IA.
4
- * enroll <qr-json | -> enropa esta máquina al vault como agente 'ia-agent'
5
- * run arranca el agente (escucha chats desde la PWA)
3
+ * dotrino-ia-agent — agente de Dotrino IA.
4
+ *
5
+ * dotrino-ia-agent enlaza (si falta) y CORRE el agente
6
+ * dotrino-ia-agent enroll re-enlaza (sobrescribe) y corre el agente
7
+ * opciones: [--label <nombre>] [--proxy <wss://…>] [--dir <ruta>]
8
+ *
9
+ * El agente es un dispositivo enrolado del vault (label 'ia-agent'): puede vivir en
10
+ * cualquier máquina y aparece solo en ia.dotrino.com para chatear con tus IAs
11
+ * (Claude, OpenCode…). Con un solo comando queda enlazado y sirviendo.
6
12
  */
7
- import { enroll, parseQr, dataDir } from '@dotrino/remote-agent/link'
13
+ import readline from 'node:readline'
8
14
  import { startIaAgent } from '../index.js'
15
+ import { enroll, parseQr, loadLink, dataDir } from '@dotrino/remote-agent/link'
9
16
 
10
- const DIR = dataDir('dotrino-ia-agent')
11
- const cmd = process.argv[2]
17
+ const args = process.argv.slice(2)
18
+ const cmd = args[0] && !args[0].startsWith('-') ? args[0] : 'run'
19
+ const opt = (name) => { const i = args.indexOf(name); return i >= 0 ? args[i + 1] : undefined }
12
20
 
13
- function readStdin () {
14
- return new Promise((resolve) => {
15
- let d = ''
16
- process.stdin.setEncoding('utf8')
17
- process.stdin.on('data', (c) => { d += c })
18
- process.stdin.on('end', () => resolve(d.trim()))
21
+ function ask (q) {
22
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout })
23
+ return new Promise((res) => rl.question(q, (a) => { rl.close(); res(a) }))
24
+ }
25
+
26
+ if (args.includes('-h') || args.includes('--help')) {
27
+ console.log(`uso:
28
+ dotrino-ia-agent enlaza esta máquina (si falta) y corre el agente
29
+ dotrino-ia-agent enroll re-enlaza (sobrescribe el enlace) y corre el agente
30
+ opciones: [--label <nombre>] [--proxy <wss://…>] [--dir <ruta>]
31
+
32
+ datos en ${dataDir('dotrino-ia-agent')} (override DOTRINO_REMOTE_AGENT_DIR)`)
33
+ process.exit(0)
34
+ }
35
+
36
+ async function doEnroll (dir, label) {
37
+ console.log('Enlazar esta máquina con tu vault.')
38
+ console.log('El código lo generas en el CERTIFICADOR (tu vault). Hay dos formas:')
39
+ console.log(' · Sin vault externo → abre https://ia.dotrino.com, elige')
40
+ console.log(' "Usar este dispositivo como bóveda" → "Enlazar otra máquina" y copia el código.')
41
+ console.log(' · Con vault en un PC → ahí corre `dotrino-vault pair` y copia el QR/JSON.\n')
42
+ const text = await ask('Pega el código y Enter:\n> ')
43
+ const qr = parseQr(text)
44
+ console.log('\nConectando…')
45
+ await enroll({
46
+ qr,
47
+ dir,
48
+ label,
49
+ onChallenge: ({ deviceId, code }) => {
50
+ console.log('\n Escribe ESTE código en tu bóveda para aprobar esta máquina:')
51
+ console.log(` código: ${code}`)
52
+ console.log(` máquina: ${deviceId}`)
53
+ console.log(' (en ia.dotrino.com escríbelo en el campo y pulsa "Aprobar";')
54
+ console.log(` en el PC del vault: dotrino-vault approve ${code})\n`)
55
+ console.log(' Esperando aprobación…')
56
+ }
19
57
  })
58
+ console.log('\n ✓ Máquina enlazada — levantando el agente…\n')
20
59
  }
21
60
 
22
- if (cmd === 'enroll') {
23
- let qrText = process.argv[3]
24
- if (!qrText || qrText === '-') qrText = await readStdin()
25
- if (!qrText) { console.error('Uso: dotrino-ia-agent enroll "<qr-json>" (o pega el QR por stdin con -)'); process.exit(1) }
26
- try {
27
- const link = await enroll({
28
- qr: parseQr(qrText),
29
- label: 'ia-agent',
30
- dir: DIR,
31
- onChallenge: ({ deviceId, code }) => console.log(`\nMáquina ${deviceId}\n Tipeá este código en el vault: ${code}\n`)
32
- })
33
- console.log(`\n✓ Enrolado como 'ia-agent' (vault ${link.iss.slice(0, 16)}…).\n Ahora arranca el agente: dotrino-ia-agent run\n`)
34
- } catch (e) { console.error('Error:', e.message); process.exit(1) }
35
- } else if (cmd === 'run' || !cmd) {
36
- try {
37
- await startIaAgent({ dir: DIR })
38
- console.log('(Ctrl+C para detener)')
39
- } catch (e) { console.error('Error:', e.message); process.exit(1) }
40
- } else {
41
- console.error('Comandos:\n dotrino-ia-agent enroll "<qr-json>" | -\n dotrino-ia-agent run')
61
+ try {
62
+ const dir = opt('--dir')
63
+ const label = opt('--label') || 'ia-agent'
64
+ // El comando por defecto enrola SOLO si aún no está enlazada; `enroll` fuerza
65
+ // re-enrolar (sobrescribe) aunque ya lo esté. En ambos casos, al terminar sigue
66
+ // y LEVANTA el servicio.
67
+ if (cmd === 'enroll' || !loadLink(dir)) {
68
+ if (cmd === 'enroll' && loadLink(dir)) console.log('Re-enlazando esta máquina (sobrescribe el enlace actual).\n')
69
+ await doEnroll(dir, label)
70
+ }
71
+
72
+ const agent = await startIaAgent({
73
+ dir, proxyUrl: opt('--proxy'),
74
+ onRevoked: () => { console.log(' Esta máquina fue revocada desde tu bóveda. Para reconectarla, vuelve a enrolarla.\n'); process.exit(0) }
75
+ })
76
+ console.log('\n Dotrino IA agente activo')
77
+ console.log(' máquina:', agent.machineId)
78
+ console.log(' aparece solo en ia.dotrino.com\n')
79
+ // Mantener vivo el servicio aunque stdin no sea una TTY (systemd/pm2/`nohup </dev/null`):
80
+ // los sockets del proxy están `unref`'d, así que sin este keep-alive el proceso saldría
81
+ // justo después de arrancar. Vive hasta SIGINT/SIGTERM (o auto-borrado por revocación).
82
+ const keepAlive = setInterval(() => {}, 1 << 30)
83
+ const bye = () => { clearInterval(keepAlive); agent.close(); process.exit(0) }
84
+ process.on('SIGINT', bye); process.on('SIGTERM', bye)
85
+ } catch (e) {
86
+ console.error('error:', e.message)
42
87
  process.exit(1)
43
88
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dotrino/ia-agent",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Agente de Dotrino IA: expone tus CLIs de IA (Claude, OpenCode) al chat remoto de ia.dotrino.com, vía @dotrino/remote-agent.",
5
5
  "type": "module",
6
6
  "bin": {