@7ots/cli 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 (97) hide show
  1. package/.env.example +140 -0
  2. package/LICENSE +21 -0
  3. package/README.md +655 -0
  4. package/brand/apple-touch-icon.png +0 -0
  5. package/brand/favicon.svg +4 -0
  6. package/brand/icon-512.png +0 -0
  7. package/brand/mark-mono.svg +3 -0
  8. package/brand/mark.svg +4 -0
  9. package/brand/og-es.png +0 -0
  10. package/brand/og-pt.png +0 -0
  11. package/brand/og.png +0 -0
  12. package/brand/ots.json +11 -0
  13. package/brand/tokens.css +59 -0
  14. package/brand/wordmark.svg +5 -0
  15. package/cli/7ots.mjs +400 -0
  16. package/cli/lib/brain.mjs +143 -0
  17. package/cli/lib/config.mjs +70 -0
  18. package/cli/lib/hooks.mjs +151 -0
  19. package/cli/lib/i18n.mjs +480 -0
  20. package/cli/lib/identity.mjs +51 -0
  21. package/cli/lib/install.mjs +79 -0
  22. package/cli/lib/lines.mjs +73 -0
  23. package/cli/lib/meet.mjs +166 -0
  24. package/cli/lib/open.mjs +19 -0
  25. package/cli/lib/orquesta.mjs +84 -0
  26. package/cli/lib/paths.mjs +49 -0
  27. package/cli/lib/pet-core.mjs +184 -0
  28. package/cli/lib/pet-server.mjs +296 -0
  29. package/cli/lib/prompts.mjs +75 -0
  30. package/cli/lib/terminal.mjs +145 -0
  31. package/cli/lib/ui.mjs +234 -0
  32. package/cli/lib/wizard.mjs +279 -0
  33. package/cli/pet/electron/main.cjs +86 -0
  34. package/cli/pet/pet.html +404 -0
  35. package/dist/7ots.esm.js +627 -0
  36. package/dist/7ots.esm.js.map +7 -0
  37. package/dist/7ots.iife.js +627 -0
  38. package/dist/7ots.iife.js.map +7 -0
  39. package/llms.txt +57 -0
  40. package/package.json +73 -0
  41. package/server/admin.mjs +213 -0
  42. package/server/apuchat-relay.mjs +170 -0
  43. package/server/channels/agent.mjs +136 -0
  44. package/server/channels/apuchat.mjs +364 -0
  45. package/server/channels/apumail.mjs +211 -0
  46. package/server/channels/index.mjs +69 -0
  47. package/server/contact.mjs +151 -0
  48. package/server/demo.mjs +154 -0
  49. package/server/i18n.mjs +54 -0
  50. package/server/identity.mjs +108 -0
  51. package/server/llm.mjs +413 -0
  52. package/server/platform/auth.mjs +290 -0
  53. package/server/platform/crypto.mjs +80 -0
  54. package/server/platform/db.mjs +130 -0
  55. package/server/platform/routes.mjs +388 -0
  56. package/server/platform/runtime.mjs +180 -0
  57. package/server/platform/store.mjs +331 -0
  58. package/server/sdk.mjs +125 -0
  59. package/server/server.mjs +456 -0
  60. package/server/settings.mjs +177 -0
  61. package/server/tts.mjs +186 -0
  62. package/skills/7ots/SKILL.md +59 -0
  63. package/src/actions/ActionRegistry.js +254 -0
  64. package/src/actions/PageTools.js +270 -0
  65. package/src/actions/builtins.js +565 -0
  66. package/src/auth/AuthManager.js +128 -0
  67. package/src/avatar/AvatarStage.js +473 -0
  68. package/src/character/AvatarEditor.js +829 -0
  69. package/src/character/Character.js +1191 -0
  70. package/src/character/motion.js +794 -0
  71. package/src/character/parts.js +559 -0
  72. package/src/context/ContextManager.js +472 -0
  73. package/src/core/AgentBrain.js +341 -0
  74. package/src/core/AgentWidget.js +1059 -0
  75. package/src/core/EventBus.js +61 -0
  76. package/src/core/Proactivity.js +363 -0
  77. package/src/core/storage.js +35 -0
  78. package/src/i18n/index.js +193 -0
  79. package/src/i18n/messages/character.js +543 -0
  80. package/src/i18n/messages/identity.js +141 -0
  81. package/src/i18n/messages/platform.js +114 -0
  82. package/src/i18n/messages/server.js +630 -0
  83. package/src/i18n/messages/widget.js +362 -0
  84. package/src/identity/ContactCard.js +351 -0
  85. package/src/identity/Face.js +148 -0
  86. package/src/identity/random.js +112 -0
  87. package/src/identity/schema.js +262 -0
  88. package/src/index.js +117 -0
  89. package/src/integrations/apuchat.js +212 -0
  90. package/src/integrations/apumail.js +88 -0
  91. package/src/llm/ProxyLLM.js +87 -0
  92. package/src/mcp/McpClient.js +153 -0
  93. package/src/ui/Companion.js +498 -0
  94. package/src/ui/UIManager.js +425 -0
  95. package/src/ui/VirtualPointer.js +746 -0
  96. package/src/ui/markdown.js +94 -0
  97. package/src/voice/VoiceEngine.js +227 -0
@@ -0,0 +1,630 @@
1
+ /**
2
+ * Textos del proxy de 7ots (server/*): errores que ve el visitante o el backoffice, avisos al
3
+ * equipo (correo, DM al operador), frases de los canales del agente, el LLM de demostración
4
+ * (mock) y el backend de demo. Namespace `server.*`.
5
+ *
6
+ * Los prompts para el LLM (instrucciones de sistema) NO van aquí: siguen en español y piden
7
+ * al modelo contestar en el idioma de la persona.
8
+ */
9
+
10
+ import { addCatalog } from '../index.js';
11
+
12
+ const es = {
13
+ server: {
14
+ http: {
15
+ originNotAllowed: 'Origen no permitido',
16
+ notFound: 'No encontrado',
17
+ internal: 'Error interno del asistente',
18
+ methodNotAllowed: 'Método no permitido',
19
+ badSiteKey: 'Site key inválida',
20
+ rateLimited: 'Demasiadas peticiones, espera un momento',
21
+ badRequest: 'Petición inválida',
22
+ tooLarge: 'Petición demasiado grande',
23
+ callLimited: 'Ya has pedido varias llamadas; espera unos minutos',
24
+ unknownRoute: 'Ruta desconocida',
25
+ noDist: 'Falta dist/: ejecuta npm run build',
26
+ forbidden: 'Prohibido',
27
+ bodyTooLarge: 'Cuerpo demasiado grande',
28
+ badJson: 'JSON inválido',
29
+ },
30
+ log: {
31
+ listening: 'proxy en http://localhost:{port} · LLM {llm} · TTS {tts}',
32
+ demo: 'demo: {url}',
33
+ backoffice: 'backoffice: {url}',
34
+ localOnly: ' (sin ADMIN_PASSWORD: solo desde esta máquina)',
35
+ applied: 'configuración aplicada · LLM {llm} · TTS {tts}',
36
+ channelsRestarted: ' · canales reiniciados',
37
+ },
38
+ admin: {
39
+ sameOrigin: 'El backoffice solo se usa desde su propio origen',
40
+ missingHeader: 'Falta la cabecera X-7ots-Admin',
41
+ noPasswordLocal: 'No hace falta contraseña en local',
42
+ definePassword: 'Define ADMIN_PASSWORD en el servidor para entrar desde fuera',
43
+ tooManyAttempts: 'Demasiados intentos. Espera unos minutos.',
44
+ wrongPassword: 'Contraseña incorrecta',
45
+ closed: 'Backoffice cerrado: define ADMIN_PASSWORD en el servidor',
46
+ login: 'Inicia sesión',
47
+ unknownRoute: 'Ruta del backoffice desconocida',
48
+ invalidWidget: 'Configuración del widget inválida',
49
+ testSystem: 'Eres una prueba de conexión. Responde en una frase corta en español.',
50
+ testPrompt: 'Saluda y di qué modelo eres.',
51
+ ttsSample: 'Hola',
52
+ },
53
+ contact: {
54
+ mailNotConfigured: 'El envío por correo no está configurado en el servidor (APUMAIL_INBOX / APUMAIL_INBOX_TOKEN / APUMAIL_TO).',
55
+ mailMissing: 'Faltan asunto, mensaje o un email válido',
56
+ mailRateLimited: 'Se alcanzó el límite de envíos de correo.',
57
+ mailRateLimitedWait: 'Se alcanzó el límite de envíos de correo; prueba en {minutes} min.',
58
+ mailFailed: 'No se pudo enviar el correo ahora mismo.',
59
+ chatBusy: 'El equipo está recibiendo muchas solicitudes. Prueba en un minuto o déjanos un correo.',
60
+ chatHubError: 'apuchat respondió {status}',
61
+ mail: {
62
+ user: 'Usuario',
63
+ agent: 'Asistente',
64
+ team: 'Equipo',
65
+ otherCategory: 'otro',
66
+ category: '— Categoría: {category}',
67
+ page: '— Página: {url}',
68
+ userLine: '— Usuario: {user}',
69
+ anonymous: '— Usuario: visitante sin sesión',
70
+ conversation: 'Conversación con el asistente:',
71
+ },
72
+ dm: {
73
+ head: '🔔 Cliente pide ayuda ({urgency}): {reason}',
74
+ normal: 'normal',
75
+ user: 'Usuario: {name}',
76
+ visitor: 'Usuario: visitante',
77
+ page: 'Página: {url}',
78
+ link: 'Entrar al chat/videollamada: {url}',
79
+ summary: 'Resumen: {summary}',
80
+ },
81
+ },
82
+ relay: {
83
+ tooMany: 'Demasiadas conversaciones abiertas con el equipo; prueba en unos minutos.',
84
+ empty: 'Mensaje vacío',
85
+ slowDown: 'Vas muy rápido; espera un momento.',
86
+ sendFailed: 'No se pudo enviar el mensaje al equipo.',
87
+ noResponse: 'El canal del equipo no responde',
88
+ visitorLeft: 'El visitante cerró el chat y volvió al asistente.',
89
+ gone: 'La conversación con el equipo ya no existe.',
90
+ closed: 'El canal de soporte se cerró.',
91
+ joinFailed: 'apuchat join {status}',
92
+ },
93
+ tts: {
94
+ empty: 'Texto vacío',
95
+ noElevenVoice: 'Falta la voz de ElevenLabs (identidad → voz, o ELEVENLABS_VOICE_ID)',
96
+ noKey: 'Falta la clave de {provider} ({key})',
97
+ noApuchatToken: 'Falta el token de audio de apuchat (APUCHAT_VOICE_TOKEN)',
98
+ notConfigured: 'TTS no configurado en el servidor',
99
+ },
100
+ channels: {
101
+ noApumail: 'Canal apumail del agente no configurado',
102
+ noApuchat: 'El agente no tiene apuchat configurado',
103
+ badSignature: 'firma inválida',
104
+ tooManyCalls: 'Ahora mismo estoy en demasiadas llamadas; prueba en unos minutos.',
105
+ callFailed: 'No se pudo crear la llamada ({status}).',
106
+ ring: '📞 Te llamo: {url}',
107
+ ringAbout: '📞 Te llamo para hablar de {topic}: {url}',
108
+ busyDm: 'Ahora mismo estoy en demasiadas llamadas; escríbeme por aquí y seguimos.',
109
+ joinFailedDm: 'No he podido entrar en la llamada. ¿Me vuelves a llamar?',
110
+ hello: 'Hola, soy {name}. ¿En qué te ayudo?',
111
+ here: '{name} está aquí',
112
+ thinking: 'pensando…',
113
+ hangUp: 'Cuelgo ya. ¡Escríbeme cuando quieras!',
114
+ limit: 'Hemos llegado al límite de la llamada. ¡Seguimos por mensaje!',
115
+ repeat: 'Perdona, ¿me lo repites?',
116
+ replySubject: 'tu mensaje',
117
+ },
118
+ llm: {
119
+ refusal: 'Lo siento, no puedo ayudarte con eso. Si quieres, te pongo en contacto con una persona del equipo.',
120
+ },
121
+ mock: {
122
+ moreHelp: '¿Te enseño algo más?',
123
+ callFailed: 'No pude llamarte: {error}',
124
+ calling: 'Te estoy llamando: mira la app de apuchat.',
125
+ failed: 'No pude completar {name}: {error}',
126
+ done: 'Listo ({name}). {result}',
127
+ helloCompanion: '¡Hola! Soy tu compañero (modo mock). Puedo pasearme por la página: prueba "enséñame la página", "¿dónde están los precios?" o "señala el login".',
128
+ helloPanel: '¡Hola! Soy el asistente del panel (modo mock). Prueba: "modo oscuro", "crea la carpeta Viajes", "enséñame a crear la carpeta Fotos", "mueve factura-0931.pdf a Facturas", "busca contrato" o "invita a eva@ejemplo.com como editor".',
129
+ hello: '¡Hola! Soy el asistente de demostración (modo mock, sin IA real). Prueba a escribir "precios", "saldo" o "/tool highlight {\\"target\\":\\"h1\\"}".',
130
+ newPage: 'Ya estamos en la nueva página.',
131
+ showPrices: 'Te enseño los precios.',
132
+ pricesHere: 'Aquí están los planes',
133
+ callingNow: '¡Te llamo ahora!',
134
+ escalateReason: 'El usuario pidió hablar con una persona',
135
+ emailSubject: 'Consulta desde el asistente',
136
+ emailCategory: 'soporte',
137
+ echo: '(mock) Recibí: "{text}". Configura LLM_PROVIDER=anthropic u openai para respuestas reales.',
138
+ pro: {
139
+ clicks: 'Eso no reacciona así; déjame que te enseñe.',
140
+ welcomeSay: '¡Hola! Mira, esto es lo primero que tienes que ver.',
141
+ welcomeTitle: 'Bienvenido 👋',
142
+ welcomeContent: 'Te voy enseñando la página según la recorres. Puedes cambiar mi iniciativa en el menú ⋮.',
143
+ qTour: 'Enséñame la página',
144
+ qPrices: '¿Dónde están los precios?',
145
+ plansSay: 'Te resumo los planes.',
146
+ plansTitle: 'Planes en 10 segundos',
147
+ perUser: '{price} €/usuario',
148
+ trial: 'Prueba',
149
+ trialValue: '30 días gratis',
150
+ qWhich: '¿Cuál me conviene?',
151
+ section: 'Aquí tienes «{section}».',
152
+ selTitle: 'Sobre lo que has seleccionado',
153
+ selContent: '(mock) Aquí te explicaría «{text}» con un LLM real.',
154
+ qSimpler: 'Explícamelo más fácil',
155
+ hesitate: 'Si lo pulsas no pasa nada irreversible: pruébalo.',
156
+ tipTitle: '¿Sabías que…?',
157
+ tipContent: 'Puedo hacer cosas por ti: prueba «enséñame la página» o pregúntame lo que quieras.',
158
+ exitSay: '¡Antes de irte!',
159
+ exitTitle: 'Antes de irte',
160
+ exitContent: 'Si te queda alguna duda, te la resuelvo en un momento.',
161
+ qDoubt: 'Tengo una duda',
162
+ },
163
+ tour: {
164
+ go: '¡Vamos! Sígueme.',
165
+ panelSummary: 'Aquí ves el **resumen**: archivos, carpetas y actividad.',
166
+ panelFiles: 'En **Archivos** buscas, creas carpetas y arrastras archivos.',
167
+ panelTeam: 'En **Equipo** invitas a gente con un rol.',
168
+ panelSettings: 'Y en **Ajustes**: idioma, tema, notificaciones…',
169
+ panelLogin: 'Sin sesión, empieza por aquí.',
170
+ siteWelcome: '¡Bienvenido! Te enseño lo importante.',
171
+ siteFeatures: 'Estas son las **funciones** principales.',
172
+ sitePrices: 'Aquí están los **planes y precios**.',
173
+ siteFaq: 'Las dudas típicas, resueltas.',
174
+ siteContact: 'Y si necesitas algo, escríbenos aquí.',
175
+ },
176
+ point: {
177
+ prices: 'Aquí tienes los planes.',
178
+ login: 'Pulsa aquí para entrar.',
179
+ contact: 'Aquí puedes escribirnos.',
180
+ faq: 'Las preguntas frecuentes.',
181
+ features: 'Estas son las funciones.',
182
+ settings: 'Los ajustes están en esta pestaña.',
183
+ files: 'Tus archivos, aquí.',
184
+ team: 'El equipo se gestiona aquí.',
185
+ search: 'Escribe aquí para buscar.',
186
+ },
187
+ panel: {
188
+ themeMouse: 'Así se cambia: Ajustes → Tema → {theme} → Guardar.',
189
+ themeDone: 'Hecho, tema {theme}.',
190
+ openFolder: 'Mira: abro «Nueva carpeta»…',
191
+ folderMouse: 'Listo: escribí «{name}» y pulsé Enter. Ya está en Archivos.',
192
+ folderDone: 'Carpeta «{name}» creada.',
193
+ dragging: 'Arrastro {file} a {folder}…',
194
+ moved: 'Movido: {file} → {folder}.',
195
+ filtered: 'Filtré los archivos por «{query}».',
196
+ invited: 'Invitación enviada a {email}.',
197
+ state: 'Ahí tienes tu estado actual (mira el resultado de la herramienta).',
198
+ },
199
+ },
200
+ demo: {
201
+ badEmail: 'Email inválido',
202
+ unauthorized: 'No autorizado',
203
+ postOnly: 'Solo POST',
204
+ unknownSession: 'Sesión MCP desconocida',
205
+ planChanged: 'Plan cambiado a {plan}',
206
+ unknownTool: 'Herramienta desconocida: {name}',
207
+ unsupported: 'Método no soportado: {method}',
208
+ tools: {
209
+ listTitle: 'Listar pedidos',
210
+ listDescription: 'Lista los pedidos/facturas del usuario con su estado.',
211
+ planTitle: 'Cambiar plan',
212
+ planDescription: 'Cambia el plan de suscripción del usuario. Tiene coste: requiere confirmación.',
213
+ },
214
+ },
215
+ },
216
+ };
217
+
218
+ const en = {
219
+ server: {
220
+ http: {
221
+ originNotAllowed: 'Origin not allowed',
222
+ notFound: 'Not found',
223
+ internal: 'Internal assistant error',
224
+ methodNotAllowed: 'Method not allowed',
225
+ badSiteKey: 'Invalid site key',
226
+ rateLimited: 'Too many requests, please wait a moment',
227
+ badRequest: 'Invalid request',
228
+ tooLarge: 'Request too large',
229
+ callLimited: 'You have already requested several calls; please wait a few minutes',
230
+ unknownRoute: 'Unknown route',
231
+ noDist: 'dist/ is missing: run npm run build',
232
+ forbidden: 'Forbidden',
233
+ bodyTooLarge: 'Body too large',
234
+ badJson: 'Invalid JSON',
235
+ },
236
+ log: {
237
+ listening: 'proxy at http://localhost:{port} · LLM {llm} · TTS {tts}',
238
+ demo: 'demo: {url}',
239
+ backoffice: 'backoffice: {url}',
240
+ localOnly: ' (no ADMIN_PASSWORD: only from this machine)',
241
+ applied: 'settings applied · LLM {llm} · TTS {tts}',
242
+ channelsRestarted: ' · channels restarted',
243
+ },
244
+ admin: {
245
+ sameOrigin: 'The backoffice can only be used from its own origin',
246
+ missingHeader: 'Missing X-7ots-Admin header',
247
+ noPasswordLocal: 'No password needed locally',
248
+ definePassword: 'Set ADMIN_PASSWORD on the server to sign in from outside',
249
+ tooManyAttempts: 'Too many attempts. Wait a few minutes.',
250
+ wrongPassword: 'Wrong password',
251
+ closed: 'Backoffice closed: set ADMIN_PASSWORD on the server',
252
+ login: 'Please sign in',
253
+ unknownRoute: 'Unknown backoffice route',
254
+ invalidWidget: 'Invalid widget configuration',
255
+ testSystem: 'You are a connection test. Reply with one short sentence in English.',
256
+ testPrompt: 'Say hello and tell me which model you are.',
257
+ ttsSample: 'Hello',
258
+ },
259
+ contact: {
260
+ mailNotConfigured: 'Email sending is not configured on the server (APUMAIL_INBOX / APUMAIL_INBOX_TOKEN / APUMAIL_TO).',
261
+ mailMissing: 'Subject, message or a valid email is missing',
262
+ mailRateLimited: 'The email sending limit has been reached.',
263
+ mailRateLimitedWait: 'The email sending limit has been reached; try again in {minutes} min.',
264
+ mailFailed: 'The email could not be sent right now.',
265
+ chatBusy: 'The team is receiving many requests. Try again in a minute or leave us an email.',
266
+ chatHubError: 'apuchat responded {status}',
267
+ mail: {
268
+ user: 'User',
269
+ agent: 'Assistant',
270
+ team: 'Team',
271
+ otherCategory: 'other',
272
+ category: '— Category: {category}',
273
+ page: '— Page: {url}',
274
+ userLine: '— User: {user}',
275
+ anonymous: '— User: visitor without a session',
276
+ conversation: 'Conversation with the assistant:',
277
+ },
278
+ dm: {
279
+ head: '🔔 Customer needs help ({urgency}): {reason}',
280
+ normal: 'normal',
281
+ user: 'User: {name}',
282
+ visitor: 'User: visitor',
283
+ page: 'Page: {url}',
284
+ link: 'Join the chat/video call: {url}',
285
+ summary: 'Summary: {summary}',
286
+ },
287
+ },
288
+ relay: {
289
+ tooMany: 'Too many open conversations with the team; try again in a few minutes.',
290
+ empty: 'Empty message',
291
+ slowDown: 'You are going too fast; wait a moment.',
292
+ sendFailed: 'The message could not be sent to the team.',
293
+ noResponse: 'The team channel is not responding',
294
+ visitorLeft: 'The visitor closed the chat and went back to the assistant.',
295
+ gone: 'The conversation with the team no longer exists.',
296
+ closed: 'The support channel was closed.',
297
+ joinFailed: 'apuchat join {status}',
298
+ },
299
+ tts: {
300
+ empty: 'Empty text',
301
+ noElevenVoice: 'The ElevenLabs voice is missing (identity → voice, or ELEVENLABS_VOICE_ID)',
302
+ noKey: 'The {provider} key is missing ({key})',
303
+ noApuchatToken: 'The apuchat audio token is missing (APUCHAT_VOICE_TOKEN)',
304
+ notConfigured: 'TTS is not configured on the server',
305
+ },
306
+ channels: {
307
+ noApumail: 'The agent apumail channel is not configured',
308
+ noApuchat: 'The agent has no apuchat configured',
309
+ badSignature: 'invalid signature',
310
+ tooManyCalls: 'I am on too many calls right now; try again in a few minutes.',
311
+ callFailed: 'The call could not be created ({status}).',
312
+ ring: '📞 Calling you: {url}',
313
+ ringAbout: '📞 Calling you to talk about {topic}: {url}',
314
+ busyDm: 'I am on too many calls right now; write to me here and we will carry on.',
315
+ joinFailedDm: 'I could not join the call. Could you call me again?',
316
+ hello: 'Hi, I am {name}. How can I help you?',
317
+ here: '{name} is here',
318
+ thinking: 'thinking…',
319
+ hangUp: 'I will hang up now. Write to me whenever you need!',
320
+ limit: 'We have reached the call limit. Talk soon!',
321
+ repeat: 'Sorry, could you say that again?',
322
+ replySubject: 'your message',
323
+ },
324
+ llm: {
325
+ refusal: 'Sorry, I cannot help you with that. If you like, I can put you in touch with someone from the team.',
326
+ },
327
+ mock: {
328
+ moreHelp: 'Shall I show you something else?',
329
+ callFailed: 'I could not call you: {error}',
330
+ calling: 'I am calling you: check the apuchat app.',
331
+ failed: 'I could not complete {name}: {error}',
332
+ done: 'Done ({name}). {result}',
333
+ helloCompanion: 'Hi! I am your companion (mock mode). I can walk around the page: try "show me the page", "where are the prices?" or "point at the login".',
334
+ helloPanel: 'Hi! I am the dashboard assistant (mock mode). Try: "modo oscuro", "crea la carpeta Viajes", "enséñame a crear la carpeta Fotos", "mueve factura-0931.pdf a Facturas", "busca contrato" or "invita a eva@ejemplo.com como editor".',
335
+ hello: 'Hi! I am the demo assistant (mock mode, no real AI). Try typing "prices", "balance" or "/tool highlight {\\"target\\":\\"h1\\"}".',
336
+ newPage: 'We are on the new page now.',
337
+ showPrices: 'Let me show you the prices.',
338
+ pricesHere: 'Here are the plans',
339
+ callingNow: 'Calling you now!',
340
+ escalateReason: 'The user asked to talk to a person',
341
+ emailSubject: 'Question from the assistant',
342
+ emailCategory: 'support',
343
+ echo: '(mock) I received: "{text}". Set LLM_PROVIDER=anthropic or openai for real answers.',
344
+ pro: {
345
+ clicks: 'That does not respond like that; let me show you.',
346
+ welcomeSay: 'Hi! Look, this is the first thing you should see.',
347
+ welcomeTitle: 'Welcome 👋',
348
+ welcomeContent: 'I will show you the page as you go through it. You can change my initiative in the ⋮ menu.',
349
+ qTour: 'Show me the page',
350
+ qPrices: 'Where are the prices?',
351
+ plansSay: 'Here is a summary of the plans.',
352
+ plansTitle: 'Plans in 10 seconds',
353
+ perUser: '€{price}/user',
354
+ trial: 'Trial',
355
+ trialValue: '30 days free',
356
+ qWhich: 'Which one suits me?',
357
+ section: 'Here is “{section}”.',
358
+ selTitle: 'About what you selected',
359
+ selContent: '(mock) With a real LLM I would explain “{text}” here.',
360
+ qSimpler: 'Explain it more simply',
361
+ hesitate: 'Nothing irreversible happens if you click it: give it a try.',
362
+ tipTitle: 'Did you know…?',
363
+ tipContent: 'I can do things for you: try “show me the page” or ask me anything.',
364
+ exitSay: 'Before you go!',
365
+ exitTitle: 'Before you go',
366
+ exitContent: 'If you have any questions left, I can answer them in a moment.',
367
+ qDoubt: 'I have a question',
368
+ },
369
+ tour: {
370
+ go: 'Let’s go! Follow me.',
371
+ panelSummary: 'Here you see the **summary**: files, folders and activity.',
372
+ panelFiles: 'In **Files** you search, create folders and drag files.',
373
+ panelTeam: 'In **Team** you invite people with a role.',
374
+ panelSettings: 'And in **Settings**: language, theme, notifications…',
375
+ panelLogin: 'Without a session, start here.',
376
+ siteWelcome: 'Welcome! Let me show you what matters.',
377
+ siteFeatures: 'These are the main **features**.',
378
+ sitePrices: 'Here are the **plans and prices**.',
379
+ siteFaq: 'Common questions, answered.',
380
+ siteContact: 'And if you need anything, write to us here.',
381
+ },
382
+ point: {
383
+ prices: 'Here are the plans.',
384
+ login: 'Click here to sign in.',
385
+ contact: 'You can write to us here.',
386
+ faq: 'The frequently asked questions.',
387
+ features: 'These are the features.',
388
+ settings: 'The settings are in this tab.',
389
+ files: 'Your files, here.',
390
+ team: 'The team is managed here.',
391
+ search: 'Type here to search.',
392
+ },
393
+ panel: {
394
+ themeMouse: 'This is how you change it: Settings → Theme → {theme} → Save.',
395
+ themeDone: 'Done, theme {theme}.',
396
+ openFolder: 'Look: I open “New folder”…',
397
+ folderMouse: 'Done: I typed “{name}” and pressed Enter. It is now in Files.',
398
+ folderDone: 'Folder “{name}” created.',
399
+ dragging: 'Dragging {file} to {folder}…',
400
+ moved: 'Moved: {file} → {folder}.',
401
+ filtered: 'I filtered the files by “{query}”.',
402
+ invited: 'Invitation sent to {email}.',
403
+ state: 'Here is your current state (see the tool result).',
404
+ },
405
+ },
406
+ demo: {
407
+ badEmail: 'Invalid email',
408
+ unauthorized: 'Unauthorized',
409
+ postOnly: 'POST only',
410
+ unknownSession: 'Unknown MCP session',
411
+ planChanged: 'Plan changed to {plan}',
412
+ unknownTool: 'Unknown tool: {name}',
413
+ unsupported: 'Unsupported method: {method}',
414
+ tools: {
415
+ listTitle: 'List orders',
416
+ listDescription: 'Lists the user’s orders/invoices with their status.',
417
+ planTitle: 'Change plan',
418
+ planDescription: 'Changes the user’s subscription plan. It has a cost: requires confirmation.',
419
+ },
420
+ },
421
+ },
422
+ };
423
+
424
+ const pt = {
425
+ server: {
426
+ http: {
427
+ originNotAllowed: 'Origem não permitida',
428
+ notFound: 'Não encontrado',
429
+ internal: 'Erro interno do assistente',
430
+ methodNotAllowed: 'Método não permitido',
431
+ badSiteKey: 'Site key inválida',
432
+ rateLimited: 'Pedidos demais, aguarde um momento',
433
+ badRequest: 'Pedido inválido',
434
+ tooLarge: 'Pedido grande demais',
435
+ callLimited: 'Você já pediu várias chamadas; aguarde alguns minutos',
436
+ unknownRoute: 'Rota desconhecida',
437
+ noDist: 'Falta dist/: execute npm run build',
438
+ forbidden: 'Proibido',
439
+ bodyTooLarge: 'Corpo grande demais',
440
+ badJson: 'JSON inválido',
441
+ },
442
+ log: {
443
+ listening: 'proxy em http://localhost:{port} · LLM {llm} · TTS {tts}',
444
+ demo: 'demo: {url}',
445
+ backoffice: 'backoffice: {url}',
446
+ localOnly: ' (sem ADMIN_PASSWORD: só a partir desta máquina)',
447
+ applied: 'configuração aplicada · LLM {llm} · TTS {tts}',
448
+ channelsRestarted: ' · canais reiniciados',
449
+ },
450
+ admin: {
451
+ sameOrigin: 'O backoffice só pode ser usado a partir da sua própria origem',
452
+ missingHeader: 'Falta o cabeçalho X-7ots-Admin',
453
+ noPasswordLocal: 'Não é preciso senha localmente',
454
+ definePassword: 'Defina ADMIN_PASSWORD no servidor para entrar de fora',
455
+ tooManyAttempts: 'Tentativas demais. Aguarde alguns minutos.',
456
+ wrongPassword: 'Senha incorreta',
457
+ closed: 'Backoffice fechado: defina ADMIN_PASSWORD no servidor',
458
+ login: 'Inicie sessão',
459
+ unknownRoute: 'Rota do backoffice desconhecida',
460
+ invalidWidget: 'Configuração do widget inválida',
461
+ testSystem: 'Você é um teste de conexão. Responda com uma frase curta em português.',
462
+ testPrompt: 'Cumprimente e diga qual modelo você é.',
463
+ ttsSample: 'Olá',
464
+ },
465
+ contact: {
466
+ mailNotConfigured: 'O envio de e-mail não está configurado no servidor (APUMAIL_INBOX / APUMAIL_INBOX_TOKEN / APUMAIL_TO).',
467
+ mailMissing: 'Faltam assunto, mensagem ou um e-mail válido',
468
+ mailRateLimited: 'O limite de envios de e-mail foi atingido.',
469
+ mailRateLimitedWait: 'O limite de envios de e-mail foi atingido; tente em {minutes} min.',
470
+ mailFailed: 'Não foi possível enviar o e-mail agora.',
471
+ chatBusy: 'A equipe está recebendo muitos pedidos. Tente em um minuto ou deixe-nos um e-mail.',
472
+ chatHubError: 'apuchat respondeu {status}',
473
+ mail: {
474
+ user: 'Usuário',
475
+ agent: 'Assistente',
476
+ team: 'Equipe',
477
+ otherCategory: 'outro',
478
+ category: '— Categoria: {category}',
479
+ page: '— Página: {url}',
480
+ userLine: '— Usuário: {user}',
481
+ anonymous: '— Usuário: visitante sem sessão',
482
+ conversation: 'Conversa com o assistente:',
483
+ },
484
+ dm: {
485
+ head: '🔔 Cliente pede ajuda ({urgency}): {reason}',
486
+ normal: 'normal',
487
+ user: 'Usuário: {name}',
488
+ visitor: 'Usuário: visitante',
489
+ page: 'Página: {url}',
490
+ link: 'Entrar no chat/videochamada: {url}',
491
+ summary: 'Resumo: {summary}',
492
+ },
493
+ },
494
+ relay: {
495
+ tooMany: 'Conversas demais abertas com a equipe; tente em alguns minutos.',
496
+ empty: 'Mensagem vazia',
497
+ slowDown: 'Você está indo rápido demais; aguarde um momento.',
498
+ sendFailed: 'Não foi possível enviar a mensagem à equipe.',
499
+ noResponse: 'O canal da equipe não responde',
500
+ visitorLeft: 'O visitante fechou o chat e voltou ao assistente.',
501
+ gone: 'A conversa com a equipe já não existe.',
502
+ closed: 'O canal de suporte foi fechado.',
503
+ joinFailed: 'apuchat join {status}',
504
+ },
505
+ tts: {
506
+ empty: 'Texto vazio',
507
+ noElevenVoice: 'Falta a voz do ElevenLabs (identidade → voz, ou ELEVENLABS_VOICE_ID)',
508
+ noKey: 'Falta a chave de {provider} ({key})',
509
+ noApuchatToken: 'Falta o token de áudio do apuchat (APUCHAT_VOICE_TOKEN)',
510
+ notConfigured: 'TTS não configurado no servidor',
511
+ },
512
+ channels: {
513
+ noApumail: 'Canal apumail do agente não configurado',
514
+ noApuchat: 'O agente não tem apuchat configurado',
515
+ badSignature: 'assinatura inválida',
516
+ tooManyCalls: 'Agora estou em chamadas demais; tente em alguns minutos.',
517
+ callFailed: 'Não foi possível criar a chamada ({status}).',
518
+ ring: '📞 Estou te ligando: {url}',
519
+ ringAbout: '📞 Estou te ligando para falar de {topic}: {url}',
520
+ busyDm: 'Agora estou em chamadas demais; escreva-me por aqui e continuamos.',
521
+ joinFailedDm: 'Não consegui entrar na chamada. Pode me ligar de novo?',
522
+ hello: 'Olá, sou {name}. Como posso ajudar?',
523
+ here: '{name} está aqui',
524
+ thinking: 'pensando…',
525
+ hangUp: 'Vou desligar. Escreva-me quando quiser!',
526
+ limit: 'Chegamos ao limite da chamada. Seguimos por mensagem!',
527
+ repeat: 'Desculpe, pode repetir?',
528
+ replySubject: 'sua mensagem',
529
+ },
530
+ llm: {
531
+ refusal: 'Desculpe, não posso ajudar com isso. Se quiser, coloco você em contato com uma pessoa da equipe.',
532
+ },
533
+ mock: {
534
+ moreHelp: 'Quer que eu mostre mais alguma coisa?',
535
+ callFailed: 'Não consegui te ligar: {error}',
536
+ calling: 'Estou te ligando: veja o app do apuchat.',
537
+ failed: 'Não consegui concluir {name}: {error}',
538
+ done: 'Pronto ({name}). {result}',
539
+ helloCompanion: 'Olá! Sou seu companheiro (modo mock). Posso passear pela página: experimente "mostre a página", "onde estão os preços?" ou "aponte o login".',
540
+ helloPanel: 'Olá! Sou o assistente do painel (modo mock). Experimente: "modo oscuro", "crea la carpeta Viajes", "enséñame a crear la carpeta Fotos", "mueve factura-0931.pdf a Facturas", "busca contrato" ou "invita a eva@ejemplo.com como editor".',
541
+ hello: 'Olá! Sou o assistente de demonstração (modo mock, sem IA real). Experimente escrever "preços", "saldo" ou "/tool highlight {\\"target\\":\\"h1\\"}".',
542
+ newPage: 'Já estamos na nova página.',
543
+ showPrices: 'Vou te mostrar os preços.',
544
+ pricesHere: 'Aqui estão os planos',
545
+ callingNow: 'Vou te ligar agora!',
546
+ escalateReason: 'O usuário pediu para falar com uma pessoa',
547
+ emailSubject: 'Consulta a partir do assistente',
548
+ emailCategory: 'suporte',
549
+ echo: '(mock) Recebi: "{text}". Configure LLM_PROVIDER=anthropic ou openai para respostas reais.',
550
+ pro: {
551
+ clicks: 'Isso não reage assim; deixe-me mostrar.',
552
+ welcomeSay: 'Olá! Veja, isto é a primeira coisa que você precisa ver.',
553
+ welcomeTitle: 'Bem-vindo 👋',
554
+ welcomeContent: 'Vou te mostrando a página conforme você a percorre. Você pode mudar minha iniciativa no menu ⋮.',
555
+ qTour: 'Mostre-me a página',
556
+ qPrices: 'Onde estão os preços?',
557
+ plansSay: 'Vou resumir os planos.',
558
+ plansTitle: 'Planos em 10 segundos',
559
+ perUser: '{price} €/usuário',
560
+ trial: 'Teste',
561
+ trialValue: '30 dias grátis',
562
+ qWhich: 'Qual me convém?',
563
+ section: 'Aqui está «{section}».',
564
+ selTitle: 'Sobre o que você selecionou',
565
+ selContent: '(mock) Aqui eu explicaria «{text}» com um LLM real.',
566
+ qSimpler: 'Explique de forma mais simples',
567
+ hesitate: 'Se clicar, nada irreversível acontece: experimente.',
568
+ tipTitle: 'Sabia que…?',
569
+ tipContent: 'Posso fazer coisas por você: experimente «mostre-me a página» ou pergunte o que quiser.',
570
+ exitSay: 'Antes de ir embora!',
571
+ exitTitle: 'Antes de ir embora',
572
+ exitContent: 'Se ficou alguma dúvida, resolvo num instante.',
573
+ qDoubt: 'Tenho uma dúvida',
574
+ },
575
+ tour: {
576
+ go: 'Vamos! Siga-me.',
577
+ panelSummary: 'Aqui você vê o **resumo**: arquivos, pastas e atividade.',
578
+ panelFiles: 'Em **Arquivos** você busca, cria pastas e arrasta arquivos.',
579
+ panelTeam: 'Em **Equipe** você convida pessoas com uma função.',
580
+ panelSettings: 'E em **Configurações**: idioma, tema, notificações…',
581
+ panelLogin: 'Sem sessão, comece por aqui.',
582
+ siteWelcome: 'Bem-vindo! Vou te mostrar o importante.',
583
+ siteFeatures: 'Estas são as principais **funções**.',
584
+ sitePrices: 'Aqui estão os **planos e preços**.',
585
+ siteFaq: 'As dúvidas típicas, resolvidas.',
586
+ siteContact: 'E se precisar de algo, escreva-nos aqui.',
587
+ },
588
+ point: {
589
+ prices: 'Aqui estão os planos.',
590
+ login: 'Clique aqui para entrar.',
591
+ contact: 'Aqui você pode nos escrever.',
592
+ faq: 'As perguntas frequentes.',
593
+ features: 'Estas são as funções.',
594
+ settings: 'As configurações estão nesta aba.',
595
+ files: 'Seus arquivos, aqui.',
596
+ team: 'A equipe é gerenciada aqui.',
597
+ search: 'Escreva aqui para buscar.',
598
+ },
599
+ panel: {
600
+ themeMouse: 'É assim que se muda: Configurações → Tema → {theme} → Salvar.',
601
+ themeDone: 'Feito, tema {theme}.',
602
+ openFolder: 'Veja: abro «Nova pasta»…',
603
+ folderMouse: 'Pronto: escrevi «{name}» e pressionei Enter. Já está em Arquivos.',
604
+ folderDone: 'Pasta «{name}» criada.',
605
+ dragging: 'Arrasto {file} para {folder}…',
606
+ moved: 'Movido: {file} → {folder}.',
607
+ filtered: 'Filtrei os arquivos por «{query}».',
608
+ invited: 'Convite enviado para {email}.',
609
+ state: 'Aí está seu estado atual (veja o resultado da ferramenta).',
610
+ },
611
+ },
612
+ demo: {
613
+ badEmail: 'E-mail inválido',
614
+ unauthorized: 'Não autorizado',
615
+ postOnly: 'Só POST',
616
+ unknownSession: 'Sessão MCP desconhecida',
617
+ planChanged: 'Plano alterado para {plan}',
618
+ unknownTool: 'Ferramenta desconhecida: {name}',
619
+ unsupported: 'Método não suportado: {method}',
620
+ tools: {
621
+ listTitle: 'Listar pedidos',
622
+ listDescription: 'Lista os pedidos/faturas do usuário com o seu estado.',
623
+ planTitle: 'Mudar de plano',
624
+ planDescription: 'Muda o plano de assinatura do usuário. Tem custo: requer confirmação.',
625
+ },
626
+ },
627
+ },
628
+ };
629
+
630
+ addCatalog({ es, en, pt });