@innvisor/conny-ai 9.7.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 (175) hide show
  1. package/.env.example +68 -0
  2. package/CHANGELOG.md +54 -0
  3. package/LICENSE +21 -0
  4. package/README.md +369 -0
  5. package/brand-assets/A_dark_luxury_web_background_202605210700.jpeg +0 -0
  6. package/brand-assets/Conny.web.logo.png +0 -0
  7. package/brand-assets/Logo_Conny_Petalo_Claro.png +0 -0
  8. package/brand-assets/cl-nica-de-las-am-ricas/manifest.json +22 -0
  9. package/brand-assets/cl-nica-de-las-am-ricas/processed/business-identity.txt +11 -0
  10. package/brand-assets/cl-nica-de-las-am-ricas/raw/business-identity.txt +11 -0
  11. package/brand-assets/cl-nica-las-am-ricas/manifest.json +22 -0
  12. package/brand-assets/cl-nica-las-am-ricas/processed/business-identity.txt +11 -0
  13. package/brand-assets/cl-nica-las-am-ricas/raw/business-identity.txt +11 -0
  14. package/brand-assets/conny-demo/manifest.json +22 -0
  15. package/brand-assets/conny-demo/processed/business-identity.txt +7 -0
  16. package/brand-assets/conny-demo/raw/business-identity.txt +7 -0
  17. package/brand-assets/conny-logo.png +0 -0
  18. package/brand-assets/web.background.png +0 -0
  19. package/brand_assets.py +323 -0
  20. package/conny +28 -0
  21. package/conny-chat.py +579 -0
  22. package/conny-omni.py +3843 -0
  23. package/conny.py +113 -0
  24. package/conny_agents/__init__.py +1 -0
  25. package/conny_agents/agenda.py +1 -0
  26. package/conny_agents/captacion.py +1 -0
  27. package/conny_agents/conocimiento.py +1 -0
  28. package/conny_agents/escalacion.py +1 -0
  29. package/conny_agents/objeciones.py +1 -0
  30. package/conny_agents/seguimiento.py +1 -0
  31. package/conny_app.py +287 -0
  32. package/conny_audio.py +350 -0
  33. package/conny_audio_learn.py +84 -0
  34. package/conny_brain_v10.py +804 -0
  35. package/conny_bridge.py +656 -0
  36. package/conny_calendar.py +169 -0
  37. package/conny_cli.py +11784 -0
  38. package/conny_cli_bb.py +437 -0
  39. package/conny_commands.py +243 -0
  40. package/conny_config.py +215 -0
  41. package/conny_core/__init__.py +3 -0
  42. package/conny_core/conversation_engine.py +446 -0
  43. package/conny_core/first_turn_ops.py +287 -0
  44. package/conny_core/persona_registry.py +157 -0
  45. package/conny_core/prompt_ops.py +561 -0
  46. package/conny_cron.py +72 -0
  47. package/conny_demo_v2.py +209 -0
  48. package/conny_demo_voice.py +134 -0
  49. package/conny_design.py +43 -0
  50. package/conny_doctor.py +319 -0
  51. package/conny_domino.py +696 -0
  52. package/conny_generator.py +447 -0
  53. package/conny_google_auth.py +159 -0
  54. package/conny_i18n.py +619 -0
  55. package/conny_init.py +509 -0
  56. package/conny_integrations/__init__.py +4 -0
  57. package/conny_integrations/llm.py +1 -0
  58. package/conny_integrations/vault.py +77 -0
  59. package/conny_integrations/whatsapp.py +1 -0
  60. package/conny_intelligence.py +65 -0
  61. package/conny_learning.py +154 -0
  62. package/conny_memory.py +243 -0
  63. package/conny_memory_engine.py +292 -0
  64. package/conny_nova_proxy.py +170 -0
  65. package/conny_nuke_robot_phrases.py +493 -0
  66. package/conny_pairing.py +253 -0
  67. package/conny_patch.py +291 -0
  68. package/conny_persona_cli.py +150 -0
  69. package/conny_router.py +308 -0
  70. package/conny_runtime_ops.py +271 -0
  71. package/conny_session.py +516 -0
  72. package/conny_skills/__init__.py +1 -0
  73. package/conny_skills/demo_mode.py +35 -0
  74. package/conny_skills/text_processing.py +1 -0
  75. package/conny_skills/tone_detection.py +1 -0
  76. package/conny_smart_features.py +333 -0
  77. package/conny_studio.py +161 -0
  78. package/conny_sync_fix.py +306 -0
  79. package/conny_tui.py +512 -0
  80. package/conny_tui_select.py +202 -0
  81. package/conny_ultra_config.py +411 -0
  82. package/conny_uncertainty.py +174 -0
  83. package/conny_utils.py +87 -0
  84. package/conny_voice.py +156 -0
  85. package/conny_voice_engine.py +124 -0
  86. package/conny_web_search.py +66 -0
  87. package/conny_weekly_report.py +85 -0
  88. package/conny_worm.py +88 -0
  89. package/core/__init__.py +25 -0
  90. package/ecosystem.config.js +24 -0
  91. package/fix_init.py +27 -0
  92. package/install.sh +78 -0
  93. package/knowledge_base.py +330 -0
  94. package/nova/rules/default.yaml +37 -0
  95. package/nova_bridge.py +509 -0
  96. package/npm/conny.js +471 -0
  97. package/package.json +102 -0
  98. package/personas/conny/base/default.yaml +35 -0
  99. package/personas/conny/base/estetica_whatsapp.yaml +36 -0
  100. package/requirements.txt +14 -0
  101. package/run.sh +47 -0
  102. package/search.py +465 -0
  103. package/smart_handoff.py +1150 -0
  104. package/src/__init__.py +0 -0
  105. package/src/conny/__init__.py +0 -0
  106. package/src/conny/admin/__init__.py +0 -0
  107. package/src/conny/admin/api.py +234 -0
  108. package/src/conny/admin/dashboard.py +772 -0
  109. package/src/conny/api/__init__.py +0 -0
  110. package/src/conny/api/routes.py +8851 -0
  111. package/src/conny/brain/__init__.py +15 -0
  112. package/src/conny/brain/engine.py +804 -0
  113. package/src/conny/brain/learning.py +154 -0
  114. package/src/conny/brain/memory.py +324 -0
  115. package/src/conny/brain/smart_features.py +333 -0
  116. package/src/conny/brain/uncertainty.py +167 -0
  117. package/src/conny/channels/__init__.py +0 -0
  118. package/src/conny/channels/audio.py +316 -0
  119. package/src/conny/channels/cli.py +11795 -0
  120. package/src/conny/channels/logo_art.py +11 -0
  121. package/src/conny/channels/voice.py +156 -0
  122. package/src/conny/core/__init__.py +0 -0
  123. package/src/conny/core/config.py +215 -0
  124. package/src/conny/core/cron.py +72 -0
  125. package/src/conny/core/messenger.py +563 -0
  126. package/src/conny/core/router.py +297 -0
  127. package/src/conny/core/session.py +312 -0
  128. package/src/conny/demo/__init__.py +0 -0
  129. package/src/conny/demo/handler.py +3110 -0
  130. package/src/conny/integrations/__init__.py +19 -0
  131. package/src/conny/integrations/calendar.py +169 -0
  132. package/src/conny/integrations/knowledge.py +312 -0
  133. package/src/conny/integrations/search.py +66 -0
  134. package/src/conny/personas/__init__.py +0 -0
  135. package/src/conny/personas/generator.py +447 -0
  136. package/src/conny/production/__init__.py +0 -0
  137. package/src/conny/production/domino.py +696 -0
  138. package/src/conny/production/guard.py +550 -0
  139. package/src/conny/production/handoff.py +1150 -0
  140. package/src/conny/production/monitor.py +353 -0
  141. package/src/conny/utils/__init__.py +2 -0
  142. package/src/conny/utils/helpers.py +75 -0
  143. package/src/conny/utils/i18n.py +619 -0
  144. package/src/core/admin_engines.py +772 -0
  145. package/src/core/globals.py +11845 -0
  146. package/src/core/orchestrator.py +273 -0
  147. package/src/core/production_monitor.py +353 -0
  148. package/src/core/runtime.py +5487 -0
  149. package/src/domain/onboarding_flow.py +230 -0
  150. package/src/domain/prompts/__init__.py +1 -0
  151. package/src/domain/prompts/prospect_pitch.py +282 -0
  152. package/src/domain/send_guard.py +636 -0
  153. package/src/domain/swarm/queen.py +96 -0
  154. package/src/infrastructure/llm_providers/engine.py +487 -0
  155. package/src/interfaces/mcp_server.py +73 -0
  156. package/src/interfaces/nova_bridge.py +58 -0
  157. package/src/interfaces/web/admin_api.py +1379 -0
  158. package/src/interfaces/web/app.py +9408 -0
  159. package/src/interfaces/web/demo_handler.py +3450 -0
  160. package/src/interfaces/web/static/generate_avatars.py +46 -0
  161. package/v7/__init__.py +46 -0
  162. package/v7/agents/__init__.py +46 -0
  163. package/v7/agents/agenda.py +77 -0
  164. package/v7/agents/base.py +216 -0
  165. package/v7/agents/captacion.py +60 -0
  166. package/v7/agents/conocimiento.py +69 -0
  167. package/v7/agents/escalacion.py +83 -0
  168. package/v7/agents/objeciones.py +109 -0
  169. package/v7/agents/seguimiento.py +71 -0
  170. package/v7/memory/__init__.py +46 -0
  171. package/v7/memory/patient_profile.py +200 -0
  172. package/v7/orchestrator.py +275 -0
  173. package/v7/postprocess.py +127 -0
  174. package/v7/router.py +239 -0
  175. package/verify_conversation_impl.py +48 -0
package/conny.py ADDED
@@ -0,0 +1,113 @@
1
+ # -*- coding: utf-8 -*-
2
+ """Conny AI - Monolithic Bootstrap Facade."""
3
+ from __future__ import annotations
4
+
5
+ import sys
6
+ import os
7
+
8
+ # Ensure project root is in sys.path
9
+ sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
10
+
11
+ # Force full reload of submodules for dynamic test environments to maintain perfect isolation
12
+ if __name__ != "__main__":
13
+ for k in list(sys.modules.keys()):
14
+ if k.startswith("src.core") or k.startswith("src.interfaces"):
15
+ del sys.modules[k]
16
+
17
+ # 1. Import all globals dynamically
18
+ import src.core.globals as globals_module
19
+ globals_module.__facade_name__ = __name__
20
+ for name in dir(globals_module):
21
+ if not name.startswith("__"):
22
+ globals()[name] = getattr(globals_module, name)
23
+
24
+ # 2. Import all runtime attributes dynamically
25
+ import src.core.runtime as runtime_module
26
+ runtime_module.__facade_name__ = __name__
27
+ for name in dir(runtime_module):
28
+ if not name.startswith("__"):
29
+ globals()[name] = getattr(runtime_module, name)
30
+
31
+ # 3. Import all web app attributes dynamically
32
+ import src.interfaces.web.app as app_module
33
+ app_module.__facade_name__ = __name__
34
+ for name in dir(app_module):
35
+ if not name.startswith("__"):
36
+ globals()[name] = getattr(app_module, name)
37
+
38
+ # 4. Define conny global instance and initialization helpers
39
+ conny: ConnyUltra = None
40
+ ADMIN_PENDING_CONFIRMATIONS: Dict[str, Dict] = {}
41
+ db = None
42
+ llm_engine = None
43
+ anti_robot_filter = None
44
+ conversation_simulator = None
45
+ response_variation = None
46
+ hallucination_guard = None
47
+ owner_style_controller = None
48
+ auth_engine = None
49
+ mcp_manager = None
50
+ prompt_evolver = None
51
+ trainer_gateway = None
52
+ v8_build_quality_system_prompt_addon = None
53
+ trainer_get_system_prompt_addon = None
54
+ task_manager = None
55
+ v8_process_response = None
56
+ v8_process_agentic_intent = None
57
+
58
+ async def init_conny():
59
+ """Inicializa Conny Ultra."""
60
+ global conny
61
+ conny = ConnyUltra()
62
+ await conny.initialize()
63
+
64
+ import src.core.globals as g
65
+ global db, llm_engine, auth_engine, mcp_manager
66
+ db = g.db
67
+ llm_engine = g.llm_engine
68
+ auth_engine = getattr(g, "auth_engine", None)
69
+ mcp_manager = getattr(g, "mcp_manager", None)
70
+
71
+
72
+ # 5. CLI entrypoint
73
+ if __name__ == "__main__":
74
+ import uvicorn
75
+
76
+ port = int(os.getenv("PORT", "8001"))
77
+ host = os.getenv("HOST", "0.0.0.0")
78
+
79
+ print("""
80
+ ╔══════════════════════════════════════════════════════════════════╗
81
+ ║ ║
82
+ ║ ██████╗ ██████╗ ███╗ ██╗███╗ ██╗██╗ ██╗ ║
83
+ ║ ██╔════╝ ██╔═══██╗████╗ ██║████╗ ██║╚██╗ ██╔╝ ║
84
+ ║ ██║ ██║ ██║██╔██╗ ██║██╔██╗ ██║ ╚████╔╝ ║
85
+ ║ ██║ ██║ ██║██║╚██╗██║██║╚██╗██║ ╚██╔╝ ║
86
+ ║ ╚██████╗ ╚██████╔╝██║ ╚████║██║ ╚████║ ██║ ║
87
+ ║ ╚═════╝ ╚═════╝ ╚═╝ ╚═══╝╚═╝ ╚═══╝ ╚═╝ ║
88
+ ║ ║
89
+ ║ U L T R A v 9 . 7 . 0 ║
90
+ ║ ║
91
+ ║ Agente de Recepción Hipernaturalmente Humana ║
92
+ ║ ║
93
+ ╠══════════════════════════════════════════════════════════════════╣
94
+ ║ • AntiRobotFilter — elimina cada patrón de bot antes de enviar ║
95
+ ║ • ConversationIntelligence — etapas, emociones, compromiso ║
96
+ ║ • HyperHumanEngine — valida humanidad en cada respuesta ║
97
+ ║ • SmartVariety — nunca repite apertura ni cierre igual ║
98
+ ║ • /modelo — admin cambia el LLM en caliente ║
99
+ ║ • Ortografía perfecta forzada (tildes, puntuación) ║
100
+ ║ • PersonaEvolution — aprende el estilo de cada cliente ║
101
+ ║ • ConversionFunnelTracker — sabe en qué etapa está cada lead ║
102
+ ║ • Demo V2 — comportamiento idéntico al de producción ║
103
+ ║ • MultilingualHandler — español/inglés/portugués automático ║
104
+ ╚══════════════════════════════════════════════════════════════════╝
105
+ """)
106
+
107
+ uvicorn.run(
108
+ "conny:app",
109
+ host=host,
110
+ port=port,
111
+ reload=False,
112
+ log_level="info"
113
+ )
@@ -0,0 +1 @@
1
+ """Conny agents scaffold."""
@@ -0,0 +1 @@
1
+ """Agente de agenda scaffold."""
@@ -0,0 +1 @@
1
+ """Agente de captacion scaffold."""
@@ -0,0 +1 @@
1
+ """Agente de conocimiento scaffold."""
@@ -0,0 +1 @@
1
+ """Agente de escalacion scaffold."""
@@ -0,0 +1 @@
1
+ """Agente de objeciones scaffold."""
@@ -0,0 +1 @@
1
+ """Agente de seguimiento scaffold."""
package/conny_app.py ADDED
@@ -0,0 +1,287 @@
1
+ #!/usr/bin/env python3
2
+ """conny — AI receptionist platform."""
3
+ from __future__ import annotations
4
+
5
+ import os, sys, time, json, subprocess, signal, random
6
+ from pathlib import Path
7
+
8
+ from rich.console import Console
9
+ from rich.table import Table
10
+ from rich.panel import Panel
11
+ from rich import box
12
+ from rich.theme import Theme
13
+ from rich.padding import Padding
14
+ from rich.progress import Progress, SpinnerColumn, TextColumn
15
+
16
+ from conny_design import (
17
+ COLORS, LOGO_FULL, WORM_RESTING, SEP,
18
+ ICON_ONLINE, ICON_OFFLINE, ICON_OK, ICON_ERR, ICON_WARN, ICON_BRAND,
19
+ ICON_CORE, ICON_BOT, ICON_INT, ICON_OPS,
20
+ )
21
+
22
+ THEME = Theme({
23
+ "m": f"bold {COLORS['primary']}",
24
+ "m.dim": COLORS['secondary'],
25
+ "ok": COLORS["success"],
26
+ "warn": COLORS["warning"],
27
+ "err": COLORS["error"],
28
+ "dim": COLORS["dim"],
29
+ "text": COLORS["text"],
30
+ })
31
+ con = Console(theme=THEME)
32
+
33
+ VERSION = "9.3.5"
34
+ try: VERSION = json.loads((Path(__file__).parent / "package.json").read_text()).get("version", VERSION)
35
+ except: pass
36
+
37
+ TAGLINES = [
38
+ "Enterprise AI Receptionist",
39
+ "Smart Conversational Interface",
40
+ "Omni-channel Business Intelligence",
41
+ "Autonomous Customer Engagement",
42
+ "Scalable AI Agent Infrastructure",
43
+ ]
44
+
45
+ BOOT_FILE = Path.home() / ".conny" / ".boot_shown"
46
+
47
+
48
+ # ─── Boot ─────────────────────────────────────────────────────────────────────
49
+
50
+ def _should_boot():
51
+ """Show boot animation once per session."""
52
+ if not sys.stdout.isatty():
53
+ return False
54
+ if BOOT_FILE.exists():
55
+ ts = BOOT_FILE.read_text().strip()
56
+ if ts == time.strftime("%Y-%m-%d"):
57
+ return False
58
+ return True
59
+
60
+ def _mark_boot():
61
+ BOOT_FILE.parent.mkdir(parents=True, exist_ok=True)
62
+ BOOT_FILE.write_text(time.strftime("%Y-%m-%d"))
63
+
64
+
65
+ # ─── Help (main screen) ──────────────────────────────────────────────────────
66
+
67
+ def cmd_help(args=""):
68
+ # Boot animation (first time today)
69
+ if _should_boot():
70
+ try:
71
+ from conny_worm import boot_sequence
72
+ boot_sequence(duration=1.8)
73
+ _mark_boot()
74
+ except Exception:
75
+ _mark_boot()
76
+
77
+ con.print()
78
+ con.print(LOGO_FULL)
79
+ con.print(f" {ICON_BRAND} v{VERSION} [dim]·[/dim] [m.dim]{random.choice(TAGLINES)}[/m.dim] {WORM_RESTING}")
80
+ con.print(SEP)
81
+
82
+ # Live status
83
+ procs = _pm2()
84
+ online = [p for p in procs if "conny" in p.get("name","") and p.get("pm2_env",{}).get("status")=="online"]
85
+ if online:
86
+ for p in online:
87
+ name = p["name"].replace("conny-","").replace("conny","main")
88
+ up = _uptime(p.get("pm2_env",{}).get("pm_uptime",0))
89
+ mem = p.get("monit",{}).get("memory",0)/1024/1024
90
+ con.print(f" {ICON_ONLINE} [bold]{name:30s}[/bold] [dim]{mem:.0f}M {up}[/dim]")
91
+ else:
92
+ con.print(f" {ICON_OFFLINE} [dim]No active Conny instances found[/dim]")
93
+ con.print(SEP)
94
+
95
+ # Commands
96
+ sections = [
97
+ (f"{ICON_CORE} CORE", [
98
+ ("new", "Launch a new instance", "n"),
99
+ ("list", "See all your agents", "l"),
100
+ ("status", "Check vital signs", "s"),
101
+ ("doctor", "Auto-heal & diagnostics", "d"),
102
+ ("chat", "Talk to Conny (monitor)", "c"),
103
+ ("logs", "Live brain stream", ""),
104
+ ]),
105
+ (f"{ICON_BOT} CONTROL", [
106
+ ("persona", "Design her personality", ""),
107
+ ("demo", "Toggle simulation mode", ""),
108
+ ("modelo", "Switch LLM provider", ""),
109
+ ("config", "Deep settings", ""),
110
+ ("sync", "Deploy latest upgrades", ""),
111
+ ]),
112
+ (f"{ICON_INT} INTELLIGENCE", [
113
+ ("aprender", "Teach new knowledge", ""),
114
+ ("gaps", "Unanswered questions", ""),
115
+ ("reporte", "Performance metrics", ""),
116
+ ("studio", "Live session studio", ""),
117
+ ]),
118
+ (f"{ICON_OPS} OPERATIONS", [
119
+ ("restart", "Quick reboot", "r"),
120
+ ("stop", "Shutdown instance", ""),
121
+ ("backup", "Snapshot your agent", ""),
122
+ ("bridge", "WhatsApp connection", ""),
123
+ ("pair", "Connect Telegram bot", ""),
124
+ ]),
125
+ ]
126
+
127
+
128
+ for title, cmds in sections:
129
+ con.print(f" [dim]{title}[/dim]")
130
+ for cmd, desc, sc in cmds:
131
+ sc_text = f"[dim]{sc}[/dim]" if sc else ""
132
+ con.print(f" [m]{cmd:12s}[/m] [text]{desc:35s}[/text] {sc_text}")
133
+ con.print()
134
+
135
+ con.print(SEP)
136
+ con.print(f" [dim]shortcuts: n=new l=list s=status d=doctor c=chat r=restart[/dim]")
137
+ con.print(f" [dim]docs: github.com/sxrubyo/conny[/dim]")
138
+ con.print()
139
+
140
+
141
+ # ─── Commands ────────────────────────────────────────────────────────────────
142
+
143
+ def cmd_status(args=""):
144
+ t = Table(box=box.SIMPLE, border_style=COLORS["primary"], show_edge=False, padding=(0,1))
145
+ t.add_column("", width=3); t.add_column("instance", style="bold")
146
+ t.add_column("status"); t.add_column("mem", justify="right", style="dim")
147
+ t.add_column("up", justify="right", style="dim")
148
+ for p in _pm2():
149
+ if "conny" not in p.get("name",""): continue
150
+ st = p.get("pm2_env",{}).get("status","?")
151
+ mem = p.get("monit",{}).get("memory",0)/1024/1024
152
+ up = _uptime(p.get("pm2_env",{}).get("pm_uptime",0))
153
+ icon = ICON_ONLINE if st=="online" else ICON_OFFLINE
154
+ st_s = f"[ok]{st}[/ok]" if st=="online" else f"[err]{st}[/err]"
155
+ t.add_row(icon, p["name"], st_s, f"{mem:.0f}M", up)
156
+ con.print(); con.print(Padding(t,(0,2))); con.print()
157
+
158
+ def cmd_list(args=""):
159
+ idir = Path("/home/ubuntu/conny-instances")
160
+ if not idir.exists(): con.print(" [dim]no instances[/dim]"); return
161
+ t = Table(box=box.SIMPLE, border_style=COLORS["primary"], show_edge=False, padding=(0,1))
162
+ t.add_column("", width=3); t.add_column("name", style="bold")
163
+ t.add_column("port", style="dim"); t.add_column("sector", style="m.dim")
164
+ for d in sorted(idir.iterdir()):
165
+ if not d.is_dir() or not (d/".env").exists(): continue
166
+ port=sector=""
167
+ for l in (d/".env").read_text().splitlines():
168
+ if l.startswith("PORT="): port=l.split("=",1)[1]
169
+ if l.startswith("SECTOR="): sector=l.split("=",1)[1]
170
+ t.add_row("[m]⬡[/m]", d.name, f":{port}" if port else "-", sector or "-")
171
+ con.print(); con.print(Padding(t,(0,2))); con.print()
172
+
173
+ def cmd_doctor(args=""):
174
+ _py("conny_doctor.py", *(args.split() if args.strip() else []))
175
+
176
+ def cmd_new(args=""): _py("conny_init.py")
177
+ def cmd_chat(args=""): _py("conny_studio.py", "--instance", args.strip() or "default")
178
+ def cmd_persona(args=""): _py("conny_persona_cli.py", *(args.split() if args else ["list"]))
179
+ def cmd_logs(args=""): _sh("pm2","logs",args.strip() or "conny","--lines","30","--nostream")
180
+ def cmd_sync(args=""): _py("conny_cli.py","sync")
181
+ def cmd_demo(args=""): _py("conny_cli.py","demo",args or "")
182
+ def cmd_modelo(args=""): _py("conny_cli.py","modelo",args or "")
183
+ def cmd_restart(args=""): _sh("pm2","restart",args.strip() or "conny")
184
+ def cmd_stop(args=""): _sh("pm2","stop",args.strip() or "conny")
185
+ def cmd_backup(args=""): _py("conny_cli.py","backup",args or "")
186
+ def cmd_bridge(args=""): _py("conny_cli.py","bridge",args or "")
187
+ def cmd_pair(args=""): _py("conny_cli.py","pair",args or "")
188
+ def cmd_reporte(args=""): _py("conny_weekly_report.py",args or "default")
189
+ def cmd_gaps(args=""):
190
+ from datetime import datetime
191
+ f=Path("knowledge_gaps")/f"{datetime.now().strftime('%Y-%m-%d')}.jsonl"
192
+ if not f.exists(): con.print(f" {ICON_OK} [dim]no gaps[/dim]"); return
193
+ con.print()
194
+ for line in open(f):
195
+ g=json.loads(line)
196
+ con.print(f" {ICON_WARN} {g.get('user_msg','')[:55]} [dim]({g.get('confidence',0):.0%})[/dim]")
197
+ con.print()
198
+ def cmd_aprender(args=""):
199
+ if not args or ("→" not in args and "->" not in args):
200
+ con.print(f' [dim]uso: conny aprender "pregunta" → "respuesta"[/dim]'); return
201
+ sep="→" if "→" in args else "->"
202
+ q,a=args.split(sep,1); q=q.strip().strip('"'); a=a.strip().strip('"')
203
+ Path("teachings").mkdir(exist_ok=True)
204
+ with open("teachings/default.jsonl","a") as f:
205
+ f.write(json.dumps({"ts":time.time(),"question":q,"answer":a})+"\n")
206
+ con.print(f" {ICON_OK} [m]{q[:35]}[/m] → {a[:35]}")
207
+ def cmd_config(args=""):
208
+ argv = ["config"]
209
+ if args.strip():
210
+ argv.extend(args.split())
211
+ _py("conny_cli.py", *argv)
212
+
213
+
214
+ # ─── Router ──────────────────────────────────────────────────────────────────
215
+
216
+ CMDS = {
217
+ "help":cmd_help,"--help":cmd_help,"-h":cmd_help,"?":cmd_help,
218
+ "status":cmd_status,"s":cmd_status,
219
+ "list":cmd_list,"l":cmd_list,
220
+ "new":cmd_new,"init":cmd_new,"n":cmd_new,
221
+ "doctor":cmd_doctor,"d":cmd_doctor,"doc":cmd_doctor,
222
+ "chat":cmd_chat,"c":cmd_chat,"studio":cmd_chat,
223
+ "persona":cmd_persona,"logs":cmd_logs,"demo":cmd_demo,
224
+ "modelo":cmd_modelo,"sync":cmd_sync,"sincronizar":cmd_sync,
225
+ "config":cmd_config,"gaps":cmd_gaps,"aprender":cmd_aprender,
226
+ "reporte":cmd_reporte,"restart":cmd_restart,"r":cmd_restart,
227
+ "stop":cmd_stop,"backup":cmd_backup,"bridge":cmd_bridge,"pair":cmd_pair,
228
+ }
229
+
230
+ def route(cmd, args=""):
231
+ if cmd in ("--version","-v"): con.print(f" conny v{VERSION}"); return
232
+ fn = CMDS.get(cmd.lower())
233
+ if fn: fn(args)
234
+ else: _py("conny_cli.py", cmd, args)
235
+
236
+
237
+ # ─── Onboarding ──────────────────────────────────────────────────────────────
238
+
239
+ def first_run():
240
+ return not Path(os.path.expanduser("~/.conny/initialized")).exists()
241
+
242
+ def onboard():
243
+ con.print(); con.print(LOGO_FULL)
244
+ con.print(f"\n [bold]First time setup.[/bold] [dim]~3 minutes.[/dim]\n")
245
+ cmd_new()
246
+ Path(os.path.expanduser("~/.conny")).mkdir(parents=True,exist_ok=True)
247
+ Path(os.path.expanduser("~/.conny/initialized")).write_text(time.strftime("%Y-%m-%d"))
248
+
249
+
250
+ # ─── Helpers ─────────────────────────────────────────────────────────────────
251
+
252
+ def _pm2():
253
+ try:
254
+ r=subprocess.run(["pm2","jlist"],capture_output=True,text=True,timeout=5)
255
+ return json.loads(r.stdout)
256
+ except: return []
257
+
258
+ def _uptime(ms):
259
+ if not ms: return "-"
260
+ s=(time.time()*1000-ms)/1000
261
+ if s<60: return f"{int(s)}s"
262
+ if s<3600: return f"{int(s/60)}m"
263
+ if s<86400: return f"{int(s/3600)}h"
264
+ return f"{int(s/86400)}d"
265
+
266
+ def _py(*a):
267
+ try: subprocess.run([sys.executable]+[str(x) for x in a],cwd="/home/ubuntu/conny")
268
+ except Exception as e: con.print(f" [err]{e}[/err]")
269
+
270
+ def _sh(*a):
271
+ try: subprocess.run(list(a))
272
+ except Exception as e: con.print(f" [err]{e}[/err]")
273
+
274
+
275
+ # ─── Entry ───────────────────────────────────────────────────────────────────
276
+
277
+ def main():
278
+ signal.signal(signal.SIGINT, lambda *_: sys.exit(0))
279
+ if first_run() and not (len(sys.argv)>1 and sys.argv[1] in ("help","--help","-h","-v","--version")):
280
+ onboard()
281
+ if len(sys.argv) <= 1:
282
+ cmd_help()
283
+ else:
284
+ route(sys.argv[1], " ".join(sys.argv[2:]))
285
+
286
+ if __name__ == "__main__":
287
+ main()