diff --git a/.gitignore b/.gitignore index f9ad727..31e76aa 100644 --- a/.gitignore +++ b/.gitignore @@ -152,6 +152,7 @@ testremote/ *.db *.patch scratch.py +connpy.code-workspace # Internal planning and implementation docs PLAN_CAPA_SERVICIOS.md diff --git a/README.md b/README.md index 020107c..b93008b 100644 --- a/README.md +++ b/README.md @@ -17,8 +17,12 @@ The v6 release introduces the **AI Copilot**, an interactive terminal assistant ## 🤖 AI Copilot (New in v6) The AI Copilot is deeply integrated into your terminal workflow: - **Terminal Context Awareness**: The Copilot can "see" your screen output, helping you diagnose errors or analyze command results in real-time. +- **Dynamic Context Selection**: Flexibly select single, range, or line-based terminal blocks to feed the Copilot, filtering out interactive scrolling garbage automatically (e.g., Cisco IOS/XR scrolling, paginators). - **Hybrid Multi-Agent System**: Automatically escalates complex tasks between the **Network Engineer** (execution) and the **Network Architect** (strategy). - **MCP Integration**: Dynamically load tools from external providers (6WIND, AWS, etc.) via the Model Context Protocol. +- **Flexible Auth & Keyless AI**: Support for advanced LiteLLM credentials (`--engineer-auth` / `--architect-auth`) allowing keyless local models (Ollama), cloud engines (Vertex AI), or custom endpoints. +- **Enhanced Session Management**: Uniquely generated sessions, robust pagination, and interactive styling translating prompt themes directly to terminal escapes. +- **Semantic Prompt Integration**: Emit standard OSC prompt sequences (`\x1b]133;B`) for real-time remote/web front-end command tracking. - **Interactive Chat**: Launch with `conn ai` for a collaborative troubleshooting session. diff --git a/connpy/__init__.py b/connpy/__init__.py index c540f19..a3f0113 100644 --- a/connpy/__init__.py +++ b/connpy/__init__.py @@ -19,8 +19,12 @@ The v6 release introduces the **AI Copilot**, an interactive terminal assistant ## 🤖 AI Copilot (New in v6) The AI Copilot is deeply integrated into your terminal workflow: - **Terminal Context Awareness**: The Copilot can "see" your screen output, helping you diagnose errors or analyze command results in real-time. +- **Dynamic Context Selection**: Flexibly select single, range, or line-based terminal blocks to feed the Copilot, filtering out interactive scrolling garbage automatically (e.g., Cisco IOS/XR scrolling, paginators). - **Hybrid Multi-Agent System**: Automatically escalates complex tasks between the **Network Engineer** (execution) and the **Network Architect** (strategy). - **MCP Integration**: Dynamically load tools from external providers (6WIND, AWS, etc.) via the Model Context Protocol. +- **Flexible Auth & Keyless AI**: Support for advanced LiteLLM credentials (`--engineer-auth` / `--architect-auth`) allowing keyless local models (Ollama), cloud engines (Vertex AI), or custom endpoints. +- **Enhanced Session Management**: Uniquely generated sessions, robust pagination, and interactive styling translating prompt themes directly to terminal escapes. +- **Semantic Prompt Integration**: Emit standard OSC prompt sequences (`\x1b]133;B`) for real-time remote/web front-end command tracking. - **Interactive Chat**: Launch with `conn ai` for a collaborative troubleshooting session. @@ -203,5 +207,7 @@ __pdoc__ = { 'nodes.deferred_class_hooks': False, 'connapp': False, 'connapp.encrypt': True, - 'printer': False + 'printer': False, + 'tests': False } + diff --git a/connpy/_version.py b/connpy/_version.py index 4c61376..ba9efd8 100644 --- a/connpy/_version.py +++ b/connpy/_version.py @@ -1 +1 @@ -__version__ = "6.0.0b11" +__version__ = "6.0.0b12" diff --git a/connpy/tests/test_utils.py b/connpy/tests/test_utils.py new file mode 100644 index 0000000..36f19ea --- /dev/null +++ b/connpy/tests/test_utils.py @@ -0,0 +1,32 @@ +import pytest +from connpy.utils import log_cleaner + +def test_log_cleaner_empty(): + assert log_cleaner("") == "" + assert log_cleaner(None) == "" + +def test_log_cleaner_plain_text(): + assert log_cleaner("hello world") == "hello world" + +def test_log_cleaner_ansi_colors(): + # \x1b[31m is red, \x1b[0m is reset + assert log_cleaner("\x1b[31mhello\x1b[0m world") == "hello world" + +def test_log_cleaner_osc_window_title(): + # Set window title OSC: \x1b]0;my title\x07 followed by prompt + sample = "\x1b]0;fluzzi32@norman: ~\x07fluzzi32@norman:~$" + assert log_cleaner(sample) == "fluzzi32@norman:~$" + +def test_log_cleaner_osc_with_st_terminator(): + # OSC can also be terminated by \x1b\\ (ST) + sample = "\x1b]0;some title\x1b\\my_prompt>" + assert log_cleaner(sample) == "my_prompt>" + +def test_log_cleaner_mixed_ansi_and_osc(): + sample = "\x1b]0;title\x07\x1b[32muser@host\x1b[0m:\x1b[34m/path\x1b[0m$ " + assert log_cleaner(sample) == "user@host:/path$" + +def test_log_cleaner_carriage_return_and_backspace(): + # Test that standard control sequences like \r and \b still work as expected + assert log_cleaner("hello\rworld") == "world" + assert log_cleaner("hell\bo") == "helo" diff --git a/connpy/utils.py b/connpy/utils.py index 8181977..2a4e2d3 100644 --- a/connpy/utils.py +++ b/connpy/utils.py @@ -7,6 +7,9 @@ def log_cleaner(data: str) -> str: if not data: return "" + # Remove OSC (Operating System Command) sequences (e.g., set window title \x1b]0;...\x07) + data = re.sub(r'\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)', '', data) + lines = data.split('\n') cleaned_lines = [] diff --git a/docs/connpy/cli/ai_handler.html b/docs/connpy/cli/ai_handler.html index 3f88ec2..a60ecf6 100644 --- a/docs/connpy/cli/ai_handler.html +++ b/docs/connpy/cli/ai_handler.html @@ -3,7 +3,7 @@
- +def dispatch(self, args):
if args.list_sessions:
- sessions = self.app.services.ai.list_sessions()
+ limit = 20 if not getattr(args, "all", False) else None
+ sessions, total = self.app.services.ai.list_sessions(limit=limit)
if not sessions:
printer.info("No saved AI sessions found.")
return
+
columns = ["ID", "Title", "Created At", "Model"]
rows = [[s["id"], s["title"], s["created_at"], s["model"]] for s in sessions]
- printer.table("AI Persisted Sessions", columns, rows)
+
+ title = "AI Persisted Sessions"
+ if limit and total > limit:
+ title += f" (Showing last {limit} of {total})"
+
+ printer.table(title, columns, rows)
+ if limit and total > limit:
+ printer.info(f"Use '--list --all' to see all {total} sessions.")
return
if args.delete_session:
@@ -454,7 +505,7 @@ el.replaceWith(d);
# Determinar session_id para retomar
session_id = None
if args.resume:
- sessions = self.app.services.ai.list_sessions()
+ sessions, _ = self.app.services.ai.list_sessions()
session_id = sessions[0]["id"] if sessions else None
if not session_id:
printer.warning("No previous session found to resume.")
@@ -472,16 +523,23 @@ el.replaceWith(d);
arguments[key] = cli_val[0]
elif settings.get(key):
arguments[key] = settings.get(key)
+
+ for key in ["engineer_auth", "architect_auth"]:
+ cli_val = getattr(args, key, None)
+ if cli_val:
+ arguments[key] = self._parse_auth_value(cli_val[0])
+ elif settings.get(key):
+ arguments[key] = settings.get(key)
# Check keys only if running in local mode (not remote)
if getattr(self.app.services, "mode", "local") == "local":
- if not arguments.get("engineer_api_key"):
- printer.error("Engineer API key not configured. The chat cannot start.")
- printer.info("Use 'connpy config --engineer-api-key <key>' to set it.")
+ if not arguments.get("engineer_api_key") and not arguments.get("engineer_auth"):
+ printer.error("Engineer API key/auth not configured. The chat cannot start.")
+ printer.info("Use 'connpy config --engineer-api-key <key>' or 'connpy config --engineer-auth <auth>' to set it.")
sys.exit(1)
- if not arguments.get("architect_api_key"):
- printer.warning("Architect API key not configured. Architect will be unavailable.")
- printer.info("Use 'connpy config --architect-api-key <key>' to enable it.")
+ if not arguments.get("architect_api_key") and not arguments.get("architect_auth"):
+ printer.warning("Architect API key/auth not configured. Architect will be unavailable.")
+ printer.info("Use 'connpy config --architect-api-key <key>' or 'connpy config --architect-auth <auth>' to enable it.")
# El resto de la interacción el CLI la maneja con el agente subyacente
self.app.myai = self.app.services.ai
@@ -512,7 +570,7 @@ el.replaceWith(d);
if history:
mdprint(f"[debug]Analyzing {len(history)} previous messages...[/debug]\n")
else:
- printer.error(f"Could not load session {session_id}. Starting clean.")
+ printer.info(f"Session '{session_id}' not found. Starting clean.")
if not history:
mdprint(Rule(style="engineer"))
@@ -526,7 +584,7 @@ el.replaceWith(d);
if user_query.lower() in ['exit', 'quit', 'bye', 'cancel']: break
with console.status("[ai_status]Agent is thinking...") as status:
- result = self.app.myai.ask(user_query, chat_history=history, status=status, debug=args.debug, trust=args.trust, **self.ai_overrides)
+ result = self.app.myai.ask(user_query, chat_history=history, status=status, debug=args.debug, trust=args.trust, session_id=session_id, **self.ai_overrides)
new_history = result.get("chat_history")
if new_history is not None:
@@ -608,7 +666,7 @@ el.replaceWith(d);