vipcode 0.2.2 → 0.2.3

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
@@ -118,7 +118,7 @@ vipcode trust # confiar en la configuración que trae esta carpet
118
118
  - **Con npm** (`npm install -g vipcode` o el paquete de la release): mira cuál es la última versión publicada en npm y en GitHub y, si es más nueva que la tuya, la instala.
119
119
  - **Desde el código** (`git clone` + `npm link`): trae lo último del repositorio con `git pull`, instala las dependencias y compila. Si tienes cambios sin guardar en esa carpeta, no toca nada y te avisa.
120
120
 
121
- Después cierra vipcode y vuelve a abrirlo. Además, una vez al día vipcode mira si hay una versión nueva y te avisa al abrirlo; se desactiva con `"checkUpdates": false` en la configuración.
121
+ Después cierra vipcode y vuelve a abrirlo. Además, una vez al día vipcode mira si hay una versión nueva y te avisa al abrirlo con un recuadro verde, y te lo recuerda en la barra de abajo hasta que actualizas; se desactiva con `"checkUpdates": false` en la configuración.
122
122
 
123
123
  ### En la interfaz
124
124
 
package/dist/app.js CHANGED
@@ -127,6 +127,10 @@ export class App {
127
127
  yolo = false;
128
128
  local = {};
129
129
  lastSessions = [];
130
+ // Versión nueva de vipcode publicada: la interfaz la recuerda abajo hasta que la instalas con /update
131
+ newVersion = null;
132
+ // /update ya la ha instalado: se usa al cerrar y volver a abrir vipcode
133
+ updateInstalled = false;
130
134
  // La interfaz pone aquí cómo preguntar los permisos
131
135
  promptPermission = async () => ({
132
136
  allow: false,
@@ -206,7 +210,11 @@ export class App {
206
210
  }
207
211
  privacyNotice() {
208
212
  const names = [...new Set(this.router.slots.filter((slot) => slot.anonymous).map((slot) => (slot.provider.label || slot.provider.name).replace(/ \(sin clave\)$/, "")))];
209
- return `Sin clave, vipcode usa proveedores gratuitos que no piden registro (${names.join(", ")}). Pueden guardar lo que les envías y usarlo para mejorar sus modelos: no les mandes contraseñas, claves ni código confidencial. Con /connect añades un proveedor con clave, y con "keyless": false en la configuración dejas de usar los anónimos.`;
213
+ return [
214
+ `Para que puedas empezar ya mismo, vipcode usa IAs gratuitas que no piden registro (${names.join(", ")}).`,
215
+ "Como son gratis, pueden guardar lo que les escribes para mejorar sus modelos, así que mejor no compartas contraseñas, claves ni datos privados.",
216
+ "Cuando quieras, conecta una cuenta gratuita con /connect y tendrás más mensajes y más modelos.",
217
+ ].join("\n");
210
218
  }
211
219
  acceptPrivacyNotice() {
212
220
  let data = {};
@@ -303,7 +311,7 @@ export class App {
303
311
  }
304
312
  anonymousNotice() {
305
313
  const names = [...new Set(this.router.slots.map((slot) => (slot.provider.label || slot.provider.name).replace(/ \(sin clave\)/, "")))];
306
- return `Sin claves: usando modelos gratis anónimos (${names.join(", ")}). Tienen pocas peticiones por hora y pueden guardar lo que envías; con /connect añades claves gratis con más límites.`;
314
+ return `Estás usando IAs gratuitas sin registro (${names.join(", ")}). Consejo: conecta una cuenta gratuita con /connect para tener más mensajes y más modelos (y recuerda no compartir datos privados).`;
307
315
  }
308
316
  // Siguiente agente principal (Tab)
309
317
  cycleAgent(direction = 1) {
@@ -913,6 +921,8 @@ export class App {
913
921
  }
914
922
  else {
915
923
  ui.info(result.message);
924
+ this.newVersion = null;
925
+ this.updateInstalled ||= result.status === "updated";
916
926
  }
917
927
  return {};
918
928
  }
package/dist/tui/app.js CHANGED
@@ -200,6 +200,8 @@ function ItemView(props) {
200
200
  const shown = lines.slice(-30);
201
201
  return (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsxs(Text, { children: [_jsxs(Text, { color: "yellow", bold: true, children: ["$", " "] }), _jsx(Text, { bold: true, children: showControl(item.command) })] }), lines.length > shown.length ? _jsxs(Text, { dimColor: true, children: [" \u2026 ", lines.length - shown.length, " l\u00EDneas antes"] }) : null, shown.length > 0 ? _jsx(Text, { children: shown.map((line) => ` ${line}`).join("\n") }) : null, _jsxs(Text, { dimColor: true, children: [" ", item.code === 0 ? "✓" : "✗", " c\u00F3digo ", item.code ?? "?", " \u00B7 el agente ver\u00E1 la salida con tu siguiente mensaje"] })] }));
202
202
  }
203
+ case "update":
204
+ return (_jsxs(Box, { marginY: 1, alignSelf: "flex-start", flexDirection: "column", borderStyle: "round", borderColor: "green", paddingX: 2, children: [_jsx(Text, { bold: true, color: "green", children: "\u2191 \u00A1Hay una versi\u00F3n nueva de vipcode!" }), _jsxs(Text, { children: [VERSION, " \u2192 ", _jsx(Text, { bold: true, color: "green", children: stripControl(item.version) }), " \u00B7 escribe", " ", _jsx(Text, { bold: true, color: "cyan", children: "/update" }), " ", "para instalarla"] })] }));
203
205
  // Avisos y bloques pueden llevar mensajes de los proveedores o datos de fuera: solo se dejan los colores
204
206
  case "info":
205
207
  return _jsx(Text, { dimColor: true, children: stripUnsafe(item.text) });
@@ -275,11 +277,17 @@ function StatusBar(props) {
275
277
  const usage = app.agent.session.usage;
276
278
  const spent = usage.prompt + usage.completion;
277
279
  const queued = app.agent.queue.length;
278
- let hint = "ctrl+p comandos · /help";
280
+ let hint = _jsx(Text, { dimColor: true, children: "ctrl+p comandos \u00B7 /help" });
279
281
  if (props.busy) {
280
- hint = "esc interrumpe · escribe para añadir instrucciones";
282
+ hint = _jsx(Text, { dimColor: true, children: "esc interrumpe \u00B7 escribe para a\u00F1adir instrucciones" });
281
283
  }
282
- return (_jsxs(Box, { justifyContent: "space-between", paddingX: 1, children: [_jsxs(Text, { wrap: "truncate-end", children: [_jsx(Text, { bold: true, color: agent?.color || "magenta", children: props.agentName }), _jsxs(Text, { dimColor: true, children: [" ", "\u00B7 ", current || "rotación automática", slot && slot.score !== null ? ` ★${slot.score}` : "", app.router.pinned ? " (fijado)" : ""] }), _jsx(ContextGauge, { used: app.agent.lastTokens, total: context }), _jsxs(Text, { dimColor: true, children: [" ", "\u00B7 ", available, "/", app.router.slots.length, " modelos", spent > 0 ? ` · ${shortTokens(spent)} tokens` : "", queued > 0 ? ` · ${queued} en cola` : ""] }), app.yolo ? _jsx(Text, { color: "red", children: " \u00B7 yolo" }) : null] }), _jsx(Text, { dimColor: true, wrap: "truncate-start", children: hint })] }));
284
+ else if (app.updateInstalled) {
285
+ hint = _jsx(Text, { color: "green", children: "\u2713 vipcode actualizado \u00B7 ci\u00E9rralo y vuelve a abrirlo para usarlo" });
286
+ }
287
+ else if (app.newVersion) {
288
+ hint = (_jsxs(Text, { bold: true, color: "green", children: ["\u2191 vipcode ", stripControl(app.newVersion), " disponible \u00B7 escribe /update"] }));
289
+ }
290
+ return (_jsxs(Box, { justifyContent: "space-between", paddingX: 1, children: [_jsxs(Text, { wrap: "truncate-end", children: [_jsx(Text, { bold: true, color: agent?.color || "magenta", children: props.agentName }), _jsxs(Text, { dimColor: true, children: [" ", "\u00B7 ", current || "rotación automática", slot && slot.score !== null ? ` ★${slot.score}` : "", app.router.pinned ? " (fijado)" : ""] }), _jsx(ContextGauge, { used: app.agent.lastTokens, total: context }), _jsxs(Text, { dimColor: true, children: [" ", "\u00B7 ", available, "/", app.router.slots.length, " modelos", spent > 0 ? ` · ${shortTokens(spent)} tokens` : "", queued > 0 ? ` · ${queued} en cola` : ""] }), app.yolo ? _jsx(Text, { color: "red", children: " \u00B7 yolo" }) : null] }), _jsx(Text, { wrap: "truncate-start", children: hint })] }));
283
291
  }
284
292
  function Main(props) {
285
293
  const { app } = props;
@@ -513,10 +521,10 @@ function Main(props) {
513
521
  };
514
522
  // La primera vez que se van a usar proveedores sin clave, explicamos qué pasa con lo que envías
515
523
  const askPrivacy = async () => {
516
- const choice = await adapter.select(`Antes de empezar: ${app.privacyNotice()}`, [
517
- { label: "Entendido, empezar", value: "ok" },
518
- { label: "Añadir antes un proveedor con clave", description: "como /connect", value: "connect" },
519
- { label: "No usar nunca los que no piden clave", description: 'guarda "keyless": false en tu configuración', value: "off" },
524
+ const choice = await adapter.select(`¡Te damos la bienvenida a vipcode!\n${app.privacyNotice()}`, [
525
+ { label: "Empezar", description: "con las IAs gratuitas sin registro", value: "ok" },
526
+ { label: "Conectar antes una cuenta gratuita", description: "para tener más mensajes y más modelos", value: "connect" },
527
+ { label: "Prefiero usar solo mis cuentas", description: "y no las IAs sin registro", value: "off" },
520
528
  ]);
521
529
  if (!choice) {
522
530
  // Con Esc se vuelve a preguntar la próxima vez
@@ -528,7 +536,7 @@ function Main(props) {
528
536
  }
529
537
  else if (choice === "off") {
530
538
  app.disableKeyless();
531
- push({ kind: "info", text: "No usaré los proveedores sin clave (lo puedes cambiar con \"keyless\" en la configuración)." });
539
+ push({ kind: "info", text: 'Hecho: solo usaré las cuentas que conectes. Si cambias de idea, pon "keyless": true en tu configuración (/config te dice dónde está).' });
532
540
  if (app.router.slots.length === 0) {
533
541
  await app.connect(adapter, "");
534
542
  }
@@ -683,7 +691,7 @@ function Main(props) {
683
691
  if (app.router.slots.length === 0) {
684
692
  push({
685
693
  kind: "notice",
686
- text: "Todavía no tienes ningún modelo. Elige un proveedor gratuito y pega su clave (puedes repetirlo con /connect).",
694
+ text: "Para empezar, conecta una IA gratuita: elige una de la lista y sigue los pasos (puedes repetirlo cuando quieras con /connect).",
687
695
  });
688
696
  void app.connect(adapter, "").then(refresh);
689
697
  }
@@ -705,7 +713,8 @@ function Main(props) {
705
713
  if (app.config.checkUpdates !== false) {
706
714
  void checkForUpdate().then((version) => {
707
715
  if (version) {
708
- push({ kind: "notice", text: `Hay una versión nueva de vipcode (${version}, tienes la ${VERSION}). Escribe /update para instalarla.` });
716
+ app.newVersion = version;
717
+ push({ kind: "update", version });
709
718
  }
710
719
  });
711
720
  }
@@ -154,7 +154,9 @@ export function SelectDialog(props) {
154
154
  const windowSize = 12;
155
155
  const start = Math.max(0, Math.min(selected - Math.floor(windowSize / 2), filtered.length - windowSize));
156
156
  const visible = filtered.slice(start, start + windowSize);
157
- return (_jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1, children: [_jsx(Text, { bold: true, color: "cyan", children: props.title }), _jsx(Text, { dimColor: true, children: filter ? `Filtro: ${filter}` : "Escribe para filtrar · ↑↓ elegir · Enter aceptar · Esc salir" }), visible.length === 0 ? _jsx(Text, { dimColor: true, children: "(nada coincide)" }) : null, visible.map((item, index) => {
157
+ // La primera línea del título va destacada; las siguientes, si las hay, explican la pregunta
158
+ const [heading, ...body] = props.title.split("\n");
159
+ return (_jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1, children: [_jsx(Text, { bold: true, color: "cyan", children: heading }), body.length > 0 ? (_jsx(Box, { marginBottom: 1, children: _jsx(Text, { children: body.join("\n") }) })) : null, _jsx(Text, { dimColor: true, children: filter ? `Filtro: ${filter}` : "Escribe para filtrar · ↑↓ elegir · Enter aceptar · Esc salir" }), visible.length === 0 ? _jsx(Text, { dimColor: true, children: "(nada coincide)" }) : null, visible.map((item, index) => {
158
160
  const active = start + index === selected;
159
161
  return (_jsxs(Text, { color: active ? "cyan" : undefined, wrap: "truncate-end", children: [active ? "❯ " : " ", stripControl(item.label), item.description ? _jsxs(Text, { dimColor: true, children: [" ", stripControl(item.description)] }) : null] }, `${start + index}-${item.label}`));
160
162
  }), filtered.length > windowSize ? (_jsxs(Text, { dimColor: true, children: [selected + 1, "/", filtered.length] })) : null] }));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vipcode",
3
- "version": "0.2.2",
3
+ "version": "0.2.3",
4
4
  "description": "Agente de programación para la terminal que usa IAs gratuitas y salta a otro proveedor cuando uno se agota",
5
5
  "keywords": [
6
6
  "ai",