@innvisor/conny-ai 9.8.0 → 9.8.4

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/CHANGELOG.md CHANGED
@@ -1,5 +1,29 @@
1
1
  # Changelog
2
2
 
3
+ ## 9.8.4 - 2026-06-02
4
+
5
+ - made bare `conny` route to the modern onboarding/chat surface instead of the legacy help screen
6
+ - kept `conny init` untouched while preserving the same banner and branding in the post-onboarding chat UI
7
+ - aligned the legacy Python CLI fallback so direct `conny_cli.py` launches follow the same start behavior
8
+
9
+ ## 9.8.3 - 2026-06-02
10
+
11
+ - made `conny` open the real chat interface after onboarding instead of the setup flow
12
+ - kept `conny init` unchanged and preserved its banner/design exactly
13
+ - added slash-command chat shortcuts with a Codex-style launcher header
14
+
15
+ ## 9.8.2 - 2026-06-02
16
+
17
+ - persisted the language selected in `conny init` so the rest of the CLI loads it automatically
18
+ - made `conny_i18n` read the saved workspace language on startup
19
+ - kept the `conny init` banner unchanged while aligning other launch paths to the same brand
20
+
21
+ ## 9.8.1 - 2026-06-02
22
+
23
+ - made `conny` open the guided setup flow by default instead of the old help banner
24
+ - removed the default launcher banner from the primary `conny` path
25
+ - aligned the TUI version display with `package.json`
26
+
3
27
  ## 9.8.0 - 2026-06-02
4
28
 
5
29
  - blocked first-contact setup for unknown chats until a valid activation token is provided
package/conny_app.py CHANGED
@@ -282,10 +282,12 @@ def _sh(*a):
282
282
 
283
283
  def main():
284
284
  signal.signal(signal.SIGINT, lambda *_: sys.exit(0))
285
- if first_run() and not (len(sys.argv)>1 and sys.argv[1] in ("help","--help","-h","-v","--version")):
286
- onboard()
287
285
  if len(sys.argv) <= 1:
288
- cmd_help()
286
+ if first_run():
287
+ onboard()
288
+ return
289
+ cmd_chat()
290
+ return
289
291
  else:
290
292
  route(sys.argv[1], " ".join(sys.argv[2:]))
291
293
 
package/conny_cli.py CHANGED
@@ -11845,6 +11845,9 @@ def main():
11845
11845
  if not workspace_is_configured() or not get_instances():
11846
11846
  cmd_init(args)
11847
11847
  return
11848
+ modern_entrypoint = Path(os.environ.get("CONNY_DIR", os.path.dirname(os.path.abspath(__file__)))) / "conny_app.py"
11849
+ subprocess.call([sys.executable, str(modern_entrypoint)])
11850
+ return
11848
11851
 
11849
11852
  # Help
11850
11853
  if args.help or cmd in ("help", "--help", "-h", ""):
package/conny_i18n.py CHANGED
@@ -5,6 +5,9 @@ Supported languages: es, en, pt, fr, de
5
5
  """
6
6
 
7
7
  from __future__ import annotations
8
+ import json
9
+ import os
10
+ from pathlib import Path
8
11
  from typing import Dict, Optional
9
12
  from dataclasses import dataclass
10
13
 
@@ -20,6 +23,79 @@ SUPPORTED_LANGUAGES = {
20
23
  DEFAULT_LANGUAGE = "es"
21
24
 
22
25
 
26
+ def _workspace_config_path() -> Path:
27
+ explicit = os.environ.get("CONNY_WORKSPACE_CONFIG")
28
+ if explicit:
29
+ return Path(explicit)
30
+ conny_home = os.environ.get("CONNY_HOME")
31
+ if conny_home:
32
+ return Path(conny_home) / "config.json"
33
+ return Path.home() / ".conny" / "config.json"
34
+
35
+
36
+ def _extract_language(payload: object) -> str:
37
+ if not isinstance(payload, dict):
38
+ return ""
39
+ candidates = [
40
+ payload.get("language"),
41
+ payload.get("ui_language"),
42
+ payload.get("locale"),
43
+ ]
44
+ for nested_key in ("agent", "meta"):
45
+ nested = payload.get(nested_key)
46
+ if isinstance(nested, dict):
47
+ candidates.append(nested.get("language"))
48
+ candidates.append(nested.get("ui_language"))
49
+ candidates.append(nested.get("locale"))
50
+ for value in candidates:
51
+ if isinstance(value, str) and value in SUPPORTED_LANGUAGES:
52
+ return value
53
+ return ""
54
+
55
+
56
+ def _load_persisted_language() -> str:
57
+ for candidate in (
58
+ os.environ.get("CONNY_LANG"),
59
+ os.environ.get("CONNY_UI_LANG"),
60
+ os.environ.get("CONNY_INIT_LANG"),
61
+ ):
62
+ if candidate in SUPPORTED_LANGUAGES:
63
+ return candidate
64
+ path = _workspace_config_path()
65
+ try:
66
+ if path.exists():
67
+ payload = json.loads(path.read_text(encoding="utf-8"))
68
+ lang = _extract_language(payload)
69
+ if lang:
70
+ return lang
71
+ except Exception:
72
+ pass
73
+ return DEFAULT_LANGUAGE
74
+
75
+
76
+ def _persist_language(lang: str) -> None:
77
+ if lang not in SUPPORTED_LANGUAGES:
78
+ return
79
+ os.environ["CONNY_LANG"] = lang
80
+ os.environ["CONNY_UI_LANG"] = lang
81
+ try:
82
+ path = _workspace_config_path()
83
+ path.parent.mkdir(parents=True, exist_ok=True)
84
+ payload = {}
85
+ if path.exists():
86
+ try:
87
+ payload = json.loads(path.read_text(encoding="utf-8"))
88
+ if not isinstance(payload, dict):
89
+ payload = {}
90
+ except Exception:
91
+ payload = {}
92
+ payload["language"] = lang
93
+ payload["ui_language"] = lang
94
+ path.write_text(json.dumps(payload, indent=2, ensure_ascii=False), encoding="utf-8")
95
+ except Exception:
96
+ pass
97
+
98
+
23
99
  @dataclass
24
100
  class TranslationSet:
25
101
  ui: Dict[str, str]
@@ -523,7 +599,9 @@ _TRANSLATIONS: Dict[str, TranslationSet] = {
523
599
 
524
600
 
525
601
  class I18n:
526
- def __init__(self, lang: str = DEFAULT_LANGUAGE):
602
+ def __init__(self, lang: str = None):
603
+ if lang is None:
604
+ lang = _load_persisted_language()
527
605
  self.lang = lang if lang in _TRANSLATIONS else DEFAULT_LANGUAGE
528
606
  self._cache: Dict[str, str] = {}
529
607
 
@@ -576,6 +654,7 @@ def get_i18n() -> I18n:
576
654
 
577
655
  def set_language(lang: str) -> None:
578
656
  _global_i18n.set_lang(lang)
657
+ _persist_language(lang)
579
658
 
580
659
 
581
660
  def t(key: str, category: str = "ui") -> str:
@@ -616,4 +695,4 @@ LANGUAGE_MENU = {
616
695
  "pt": "🇧🇷 Português",
617
696
  "fr": "🇫🇷 Français",
618
697
  "de": "🇩🇪 Deutsch",
619
- }
698
+ }
package/conny_init.py CHANGED
@@ -214,6 +214,25 @@ def validate_api_key(provider, key):
214
214
  return True
215
215
 
216
216
 
217
+ def _persist_language(lang: str) -> None:
218
+ try:
219
+ workspace_config_path = Path(os.environ.get("CONNY_WORKSPACE_CONFIG", str(CONNY_HOME / "config.json")))
220
+ workspace_config_path.parent.mkdir(parents=True, exist_ok=True)
221
+ payload = {}
222
+ if workspace_config_path.exists():
223
+ try:
224
+ payload = json.loads(workspace_config_path.read_text(encoding="utf-8"))
225
+ if not isinstance(payload, dict):
226
+ payload = {}
227
+ except Exception:
228
+ payload = {}
229
+ payload["language"] = lang
230
+ payload["ui_language"] = lang
231
+ workspace_config_path.write_text(json.dumps(payload, indent=2, ensure_ascii=False), encoding="utf-8")
232
+ except Exception:
233
+ pass
234
+
235
+
217
236
  def _next_available_port() -> int:
218
237
  existing_ports = []
219
238
  if INSTANCES_DIR.exists():
@@ -379,6 +398,7 @@ def run_wizard():
379
398
  lang = LANGUAGES[i][0]
380
399
  global CURRENT_LANG
381
400
  CURRENT_LANG = lang
401
+ _persist_language(lang)
382
402
 
383
403
  # 1. Identity
384
404
  clear()
package/conny_studio.py CHANGED
@@ -1,20 +1,32 @@
1
1
  #!/usr/bin/env python3
2
- """conny_studio.py — Interactive CLI session with live monitoring."""
2
+ """conny_studio.py — Interactive CLI chat with live monitoring."""
3
3
  import asyncio
4
4
  import json
5
5
  import os
6
6
  import sys
7
7
  import time
8
8
  import uuid
9
+ import subprocess
9
10
  from pathlib import Path
10
11
  from datetime import datetime
11
12
 
12
13
  import httpx
14
+ from rich.console import Console
13
15
 
14
16
  sys.path.insert(0, str(Path(__file__).parent))
17
+ from conny_design import LOGO_FULL, SEP, ICON_BRAND
15
18
  from conny_uncertainty import UncertaintyDetector
16
19
  from conny_voice import ConnyVoice
17
20
 
21
+ CONSOLE = Console()
22
+ VERSION = "9.8.2"
23
+ try:
24
+ package_path = Path(__file__).resolve().parent / "package.json"
25
+ if package_path.exists():
26
+ VERSION = json.loads(package_path.read_text(encoding="utf-8")).get("version", VERSION)
27
+ except Exception:
28
+ pass
29
+
18
30
  STUDIO_DIR = Path.home() / ".conny" / "studio" / "memory"
19
31
  API_URL = "http://localhost:8001/test"
20
32
 
@@ -69,11 +81,16 @@ class ConnyStudio:
69
81
  }, ensure_ascii=False) + "\n")
70
82
 
71
83
  async def handle_command(self, cmd: str) -> str:
84
+ if cmd in ("/help", "/menu", "/start"):
85
+ return (
86
+ "Comandos: /help /menu /clear /history /models /config /language "
87
+ "/export /reload-persona /fix-last"
88
+ )
72
89
  if cmd == "/clear":
73
90
  self.history = []
74
91
  self.chat_id = f"studio_{uuid.uuid4().hex[:8]}"
75
92
  return "Session cleared. New conversation started."
76
- elif cmd == "/show-memory":
93
+ elif cmd in ("/history", "/show-memory"):
77
94
  if not self.history:
78
95
  return "No turns in memory yet."
79
96
  lines = []
@@ -81,6 +98,14 @@ class ConnyStudio:
81
98
  role = "YOU" if h["role"] == "user" else "MEL"
82
99
  lines.append(f" [{role}] {h['content'][:80]}")
83
100
  return "\n".join(lines)
101
+ elif cmd == "/models":
102
+ return self._run_cli_command("modelo")
103
+ elif cmd == "/config":
104
+ return self._run_cli_command("config")
105
+ elif cmd == "/language":
106
+ return self._run_cli_command("language")
107
+ elif cmd in ("/new", "/init"):
108
+ return self._run_cli_command("init")
84
109
  elif cmd == "/show-failures":
85
110
  if not self.failures_file.exists():
86
111
  return "No failures detected this session."
@@ -101,13 +126,28 @@ class ConnyStudio:
101
126
  return "No previous turn to fix."
102
127
  return f"Unknown command: {cmd}"
103
128
 
129
+ def _run_cli_command(self, command: str) -> str:
130
+ cli = Path(__file__).resolve().parent / "conny_cli.py"
131
+ if not cli.exists():
132
+ return f"CLI no disponible para /{command}"
133
+ try:
134
+ print(f"\033[90m[system] launching: conny {command}\033[0m")
135
+ subprocess.run([sys.executable, str(cli), command], check=False)
136
+ return f"/{command} closed. Back in chat."
137
+ except Exception as exc:
138
+ return f"No pude abrir /{command}: {exc}"
139
+
104
140
  def print_header(self):
105
- print("\033[1;36m╔══════════════════════════════════════════════╗\033[0m")
106
- print("\033[1;36m║ CONNY STUDIO v1.0 ║\033[0m")
107
- print(f"\033[1;36m║ Instance: {self.instance_id:<33}║\033[0m")
108
- print(f"\033[1;36m║ Session: {self.session_id:<34}║\033[0m")
109
- print("\033[1;36m╚══════════════════════════════════════════════╝\033[0m")
110
- print("\033[90mCommands: /clear /show-memory /show-failures /fix-last /reload-persona /export-session\033[0m\n")
141
+ print()
142
+ CONSOLE.print(LOGO_FULL)
143
+ CONSOLE.print(f" {ICON_BRAND} v{VERSION} · chat real")
144
+ CONSOLE.print(SEP)
145
+ print(f" Instance: {self.instance_id}")
146
+ print(f" Session: {self.session_id}")
147
+ print(" Comandos: /help /menu /clear /history /models /config /language /export")
148
+ print(" Atajos: 1=models 2=config 3=language 4=help")
149
+ CONSOLE.print(SEP)
150
+ print()
111
151
 
112
152
  def print_scores(self, scores):
113
153
  conf = scores["confidence"]
@@ -122,14 +162,16 @@ class ConnyStudio:
122
162
  self.print_header()
123
163
  while True:
124
164
  try:
125
- user_input = input("\033[1;32m[YOU]\033[0m ")
165
+ user_input = input("\033[1;32m[YOU]\033[0m ").strip()
126
166
  except (EOFError, KeyboardInterrupt):
127
167
  print("\n\033[90mSession ended.\033[0m")
128
168
  break
129
- if not user_input.strip():
169
+ if not user_input:
130
170
  continue
171
+ if user_input in ("1", "2", "3", "4"):
172
+ user_input = { "1": "/models", "2": "/config", "3": "/language", "4": "/help" }[user_input]
131
173
  if user_input.startswith("/"):
132
- result = await self.handle_command(user_input.strip())
174
+ result = await self.handle_command(user_input)
133
175
  print(f"\033[1;33m[SYSTEM]\033[0m {result}")
134
176
  continue
135
177
  try:
package/conny_tui.py CHANGED
@@ -30,6 +30,13 @@ CONNY_DIR = os.getenv("CONNY_DIR", str(Path(__file__).resolve().parent))
30
30
  INSTANCES_DIR = os.getenv("INSTANCES_DIR", str(Path.home() / "conny-instances"))
31
31
  CLI_SCRIPT = os.getenv("CONNY_CLI", str(Path(__file__).resolve().parent / "conny_cli.py"))
32
32
 
33
+ try:
34
+ _package_path = Path(CONNY_DIR) / "package.json"
35
+ if _package_path.exists():
36
+ VERSION = json.loads(_package_path.read_text(encoding="utf-8")).get("version", VERSION)
37
+ except Exception:
38
+ pass
39
+
33
40
  # ── Colores curses (índices de par) ─────────────────────────────────────────
34
41
  CP_LOGO1 = 1 # Rosa — letras CONNY (gradiente línea 1)
35
42
  CP_LOGO2 = 10 # Rosa claro (gradiente línea 2)
package/npm/conny.js CHANGED
@@ -485,7 +485,7 @@ function execConny(argv) {
485
485
  }
486
486
 
487
487
  const args = process.argv.slice(2);
488
- const isHelp = args.length === 0 || args.includes("-h") || args.includes("--help") || args.includes("help");
488
+ const isHelp = args.includes("-h") || args.includes("--help") || args.includes("help");
489
489
  const isVersion = args.includes("-v") || args.includes("--version") || args.includes("version");
490
490
  const isBootstrapCheck = args.includes("--bootstrap-check");
491
491
  const isJson = args.includes("--json");
@@ -518,10 +518,8 @@ if (isHelp) {
518
518
  process.exit(0);
519
519
  }
520
520
 
521
- if (!isJson && process.stdout.isTTY) {
522
- printBanner();
523
- }
521
+ const launchArgs = args;
524
522
 
525
- if (!execConny(args)) {
523
+ if (!execConny(launchArgs)) {
526
524
  fail(`No pude iniciar Conny desde ${connyHome}`);
527
525
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@innvisor/conny-ai",
3
- "version": "9.8.0",
3
+ "version": "9.8.4",
4
4
  "description": "Open-source CLI and runtime for building Conny AI receptionist agents on WhatsApp and Telegram.",
5
5
  "license": "MIT",
6
6
  "author": "Santiago Rubio",
@@ -109,4 +109,4 @@
109
109
  "figlet": "^1.11.0",
110
110
  "ora": "^5.4.1"
111
111
  }
112
- }
112
+ }
@@ -11785,6 +11785,9 @@ def main():
11785
11785
  if not workspace_is_configured() or not get_instances():
11786
11786
  cmd_init(args)
11787
11787
  return
11788
+ modern_entrypoint = Path(os.environ.get("CONNY_DIR", os.path.dirname(os.path.abspath(__file__)))) / "conny_app.py"
11789
+ subprocess.call([sys.executable, str(modern_entrypoint)])
11790
+ return
11788
11791
 
11789
11792
  # Help
11790
11793
  if args.help or cmd in ("help", "--help", "-h", ""):