utim-cli 2.1.2__tar.gz → 2.2.0__tar.gz
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.
- {utim_cli-2.1.2 → utim_cli-2.2.0}/CHANGELOG.md +5 -0
- {utim_cli-2.1.2/utim_cli.egg-info → utim_cli-2.2.0}/PKG-INFO +1 -1
- {utim_cli-2.1.2 → utim_cli-2.2.0}/pyproject.toml +1 -1
- {utim_cli-2.1.2 → utim_cli-2.2.0}/utim_cli/__init__.py +1 -1
- {utim_cli-2.1.2 → utim_cli-2.2.0}/utim_cli/_version.py +1 -1
- {utim_cli-2.1.2 → utim_cli-2.2.0}/utim_cli/agent.py +95 -69
- {utim_cli-2.1.2 → utim_cli-2.2.0}/utim_cli/auth.py +15 -2
- utim_cli-2.2.0/utim_cli/brain.py +734 -0
- {utim_cli-2.1.2 → utim_cli-2.2.0}/utim_cli/mcp_clean_wrapper.py +28 -0
- {utim_cli-2.1.2 → utim_cli-2.2.0}/utim_cli/orchestrator.py +1024 -550
- {utim_cli-2.1.2 → utim_cli-2.2.0}/utim_cli/reflection.py +330 -253
- {utim_cli-2.1.2 → utim_cli-2.2.0}/utim_cli/server/batch_processor.py +7 -7
- {utim_cli-2.1.2 → utim_cli-2.2.0}/utim_cli/server/captcha.py +1 -2
- {utim_cli-2.1.2 → utim_cli-2.2.0}/utim_cli/server/cli_auth.py +5 -1
- {utim_cli-2.1.2 → utim_cli-2.2.0}/utim_cli/server/db.py +108 -3
- {utim_cli-2.1.2 → utim_cli-2.2.0}/utim_cli/server/email_utils.py +509 -509
- utim_cli-2.2.0/utim_cli/server/local_llm.py +380 -0
- utim_cli-2.2.0/utim_cli/server/micro_batcher.py +280 -0
- utim_cli-2.2.0/utim_cli/server/model_agent.py +218 -0
- {utim_cli-2.1.2 → utim_cli-2.2.0}/utim_cli/server/models.py +237 -20
- utim_cli-2.2.0/utim_cli/server/rewards_engine.py +403 -0
- {utim_cli-2.1.2 → utim_cli-2.2.0}/utim_cli/server/router.py +17 -3
- {utim_cli-2.1.2 → utim_cli-2.2.0}/utim_cli/server/routes/__init__.py +2 -3
- {utim_cli-2.1.2 → utim_cli-2.2.0}/utim_cli/server/routes/auth_routes.py +8 -0
- {utim_cli-2.1.2 → utim_cli-2.2.0}/utim_cli/server/routes/completion_routes.py +74 -42
- {utim_cli-2.1.2 → utim_cli-2.2.0}/utim_cli/server/routes/credit_routes.py +8 -0
- {utim_cli-2.1.2 → utim_cli-2.2.0}/utim_cli/server/routes/marketplace_routes.py +2 -2
- {utim_cli-2.1.2 → utim_cli-2.2.0}/utim_cli/server/routes/quota_routes.py +33 -0
- utim_cli-2.2.0/utim_cli/server/routes/rewards_routes.py +558 -0
- {utim_cli-2.1.2 → utim_cli-2.2.0}/utim_cli/server/routes/security_routes.py +1 -1
- {utim_cli-2.1.2 → utim_cli-2.2.0}/utim_cli/server/routes/share_routes.py +146 -51
- {utim_cli-2.1.2 → utim_cli-2.2.0}/utim_cli/server/server.py +60 -60
- {utim_cli-2.1.2 → utim_cli-2.2.0}/utim_cli/server/storage_nodes.py +134 -0
- {utim_cli-2.1.2 → utim_cli-2.2.0}/utim_cli/share.py +116 -47
- {utim_cli-2.1.2 → utim_cli-2.2.0}/utim_cli/share_tui.py +12 -12
- utim_cli-2.2.0/utim_cli/situational_scoring.py +369 -0
- utim_cli-2.2.0/utim_cli/subagent_manager.py +366 -0
- {utim_cli-2.1.2 → utim_cli-2.2.0}/utim_cli/tools.py +231 -78
- {utim_cli-2.1.2 → utim_cli-2.2.0}/utim_cli/tui/feedback_dialog.py +34 -34
- {utim_cli-2.1.2 → utim_cli-2.2.0}/utim_cli/tui/history_dialog.py +10 -10
- {utim_cli-2.1.2 → utim_cli-2.2.0}/utim_cli/tui/marketplace_app_state.py +63 -63
- {utim_cli-2.1.2 → utim_cli-2.2.0}/utim_cli/tui/marketplace_dialog.py +132 -132
- {utim_cli-2.1.2 → utim_cli-2.2.0}/utim_cli/tui/marketplace_layout_engine.py +182 -182
- {utim_cli-2.1.2 → utim_cli-2.2.0}/utim_cli/tui/mcp_dialog.py +627 -627
- {utim_cli-2.1.2 → utim_cli-2.2.0}/utim_cli/tui/miniagents_dialog.py +369 -369
- {utim_cli-2.1.2 → utim_cli-2.2.0}/utim_cli/tui/model_dialog.py +1789 -1909
- {utim_cli-2.1.2 → utim_cli-2.2.0}/utim_cli/tui/publish_dialog.py +30 -30
- {utim_cli-2.1.2 → utim_cli-2.2.0}/utim_cli/tui/quota_dialog.py +36 -36
- {utim_cli-2.1.2 → utim_cli-2.2.0}/utim_cli/tui/quota_redeem_dialog.py +181 -181
- {utim_cli-2.1.2 → utim_cli-2.2.0}/utim_cli/tui/quota_share_dialog.py +491 -491
- {utim_cli-2.1.2 → utim_cli-2.2.0}/utim_cli/tui/resume_dialog.py +147 -147
- utim_cli-2.2.0/utim_cli/tui/rewards_tui.py +224 -0
- {utim_cli-2.1.2 → utim_cli-2.2.0}/utim_cli/tui/skills_dialog.py +320 -320
- {utim_cli-2.1.2 → utim_cli-2.2.0}/utim_cli/tui/subagents_dialog.py +314 -314
- utim_cli-2.2.0/utim_cli/tui/thinking_display.py +137 -0
- {utim_cli-2.1.2 → utim_cli-2.2.0}/utim_cli/tui/tools_dialog.py +248 -248
- {utim_cli-2.1.2 → utim_cli-2.2.0}/utim_cli/tui/update_dialog.py +2 -2
- {utim_cli-2.1.2 → utim_cli-2.2.0}/utim_cli/utim.py +269 -137
- utim_cli-2.2.0/utim_cli/wheel.py +26 -0
- {utim_cli-2.1.2 → utim_cli-2.2.0/utim_cli.egg-info}/PKG-INFO +1 -1
- {utim_cli-2.1.2 → utim_cli-2.2.0}/utim_cli.egg-info/SOURCES.txt +9 -0
- utim_cli-2.1.2/utim_cli/server/micro_batcher.py +0 -95
- utim_cli-2.1.2/utim_cli/situational_scoring.py +0 -281
- {utim_cli-2.1.2 → utim_cli-2.2.0}/LICENSE +0 -0
- {utim_cli-2.1.2 → utim_cli-2.2.0}/MANIFEST.in +0 -0
- {utim_cli-2.1.2 → utim_cli-2.2.0}/README.md +0 -0
- {utim_cli-2.1.2 → utim_cli-2.2.0}/setup.cfg +0 -0
- {utim_cli-2.1.2 → utim_cli-2.2.0}/setup.py +0 -0
- {utim_cli-2.1.2 → utim_cli-2.2.0}/utim_cli/_check_obj.py +0 -0
- {utim_cli-2.1.2 → utim_cli-2.2.0}/utim_cli/_find_mcp.py +0 -0
- {utim_cli-2.1.2 → utim_cli-2.2.0}/utim_cli/_find_plan.py +0 -0
- {utim_cli-2.1.2 → utim_cli-2.2.0}/utim_cli/_find_tool.py +0 -0
- {utim_cli-2.1.2 → utim_cli-2.2.0}/utim_cli/_patch_multimodal.py +0 -0
- {utim_cli-2.1.2 → utim_cli-2.2.0}/utim_cli/_test_regex.py +0 -0
- {utim_cli-2.1.2 → utim_cli-2.2.0}/utim_cli/backup.py +0 -0
- {utim_cli-2.1.2 → utim_cli-2.2.0}/utim_cli/billing.py +0 -0
- {utim_cli-2.1.2 → utim_cli-2.2.0}/utim_cli/blender_agent.py +0 -0
- {utim_cli-2.1.2 → utim_cli-2.2.0}/utim_cli/bootstrap.py +0 -0
- {utim_cli-2.1.2 → utim_cli-2.2.0}/utim_cli/client_utils.py +0 -0
- {utim_cli-2.1.2 → utim_cli-2.2.0}/utim_cli/config.py +0 -0
- {utim_cli-2.1.2 → utim_cli-2.2.0}/utim_cli/constants.py +0 -0
- {utim_cli-2.1.2 → utim_cli-2.2.0}/utim_cli/context_pruner.py +0 -0
- {utim_cli-2.1.2 → utim_cli-2.2.0}/utim_cli/doctor.py +0 -0
- {utim_cli-2.1.2 → utim_cli-2.2.0}/utim_cli/harbor.py +0 -0
- {utim_cli-2.1.2 → utim_cli-2.2.0}/utim_cli/knowledge_graph.py +0 -0
- {utim_cli-2.1.2 → utim_cli-2.2.0}/utim_cli/local_db.py +0 -0
- {utim_cli-2.1.2 → utim_cli-2.2.0}/utim_cli/logger.py +0 -0
- {utim_cli-2.1.2 → utim_cli-2.2.0}/utim_cli/mcp_client.py +0 -0
- {utim_cli-2.1.2 → utim_cli-2.2.0}/utim_cli/mcp_registry.json +0 -0
- {utim_cli-2.1.2 → utim_cli-2.2.0}/utim_cli/report.py +0 -0
- {utim_cli-2.1.2 → utim_cli-2.2.0}/utim_cli/scrapy_search.py +0 -0
- {utim_cli-2.1.2 → utim_cli-2.2.0}/utim_cli/server/__init__.py +0 -0
- {utim_cli-2.1.2 → utim_cli-2.2.0}/utim_cli/server/attribution.py +0 -0
- {utim_cli-2.1.2 → utim_cli-2.2.0}/utim_cli/server/audit_log.py +0 -0
- {utim_cli-2.1.2 → utim_cli-2.2.0}/utim_cli/server/auth.py +0 -0
- {utim_cli-2.1.2 → utim_cli-2.2.0}/utim_cli/server/docs_md/SECRET_PROVISIONING.md +0 -0
- {utim_cli-2.1.2 → utim_cli-2.2.0}/utim_cli/server/docs_md/about.md +0 -0
- {utim_cli-2.1.2 → utim_cli-2.2.0}/utim_cli/server/docs_md/changelog.md +0 -0
- {utim_cli-2.1.2 → utim_cli-2.2.0}/utim_cli/server/docs_md/docs.md +0 -0
- {utim_cli-2.1.2 → utim_cli-2.2.0}/utim_cli/server/docs_md/features.md +0 -0
- {utim_cli-2.1.2 → utim_cli-2.2.0}/utim_cli/server/docs_md/license.md +0 -0
- {utim_cli-2.1.2 → utim_cli-2.2.0}/utim_cli/server/docs_md/pricing.md +0 -0
- {utim_cli-2.1.2 → utim_cli-2.2.0}/utim_cli/server/docs_md/privacy.md +0 -0
- {utim_cli-2.1.2 → utim_cli-2.2.0}/utim_cli/server/docs_md/refund.md +0 -0
- {utim_cli-2.1.2 → utim_cli-2.2.0}/utim_cli/server/docs_md/support.md +0 -0
- {utim_cli-2.1.2 → utim_cli-2.2.0}/utim_cli/server/docs_md/terms.md +0 -0
- {utim_cli-2.1.2 → utim_cli-2.2.0}/utim_cli/server/exchange_rate.py +0 -0
- {utim_cli-2.1.2 → utim_cli-2.2.0}/utim_cli/server/firebase.py +0 -0
- {utim_cli-2.1.2 → utim_cli-2.2.0}/utim_cli/server/fix_duplicate_users.py +0 -0
- {utim_cli-2.1.2 → utim_cli-2.2.0}/utim_cli/server/history.py +0 -0
- {utim_cli-2.1.2 → utim_cli-2.2.0}/utim_cli/server/logging_config.py +0 -0
- {utim_cli-2.1.2 → utim_cli-2.2.0}/utim_cli/server/pricing_updater.py +0 -0
- {utim_cli-2.1.2 → utim_cli-2.2.0}/utim_cli/server/provision_build.py +0 -0
- {utim_cli-2.1.2 → utim_cli-2.2.0}/utim_cli/server/rate_limit.py +0 -0
- {utim_cli-2.1.2 → utim_cli-2.2.0}/utim_cli/server/routes/feedback_routes.py +0 -0
- {utim_cli-2.1.2 → utim_cli-2.2.0}/utim_cli/server/routes/quota_share_routes.py +0 -0
- {utim_cli-2.1.2 → utim_cli-2.2.0}/utim_cli/server/routes/referral_routes.py +0 -0
- {utim_cli-2.1.2 → utim_cli-2.2.0}/utim_cli/server/routes/session_routes.py +0 -0
- {utim_cli-2.1.2 → utim_cli-2.2.0}/utim_cli/state.py +0 -0
- {utim_cli-2.1.2 → utim_cli-2.2.0}/utim_cli/tui/__init__.py +0 -0
- {utim_cli-2.1.2 → utim_cli-2.2.0}/utim_cli/utilities.py +0 -0
- {utim_cli-2.1.2 → utim_cli-2.2.0}/utim_cli/vector_memory.py +0 -0
- {utim_cli-2.1.2 → utim_cli-2.2.0}/utim_cli/workspace.py +0 -0
- {utim_cli-2.1.2 → utim_cli-2.2.0}/utim_cli.egg-info/dependency_links.txt +0 -0
- {utim_cli-2.1.2 → utim_cli-2.2.0}/utim_cli.egg-info/entry_points.txt +0 -0
- {utim_cli-2.1.2 → utim_cli-2.2.0}/utim_cli.egg-info/requires.txt +0 -0
- {utim_cli-2.1.2 → utim_cli-2.2.0}/utim_cli.egg-info/top_level.txt +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "utim-cli"
|
|
7
|
-
version = "2.
|
|
7
|
+
version = "2.2.0"
|
|
8
8
|
description = "UTIM – Universal Terminal Intelligence Manager. A powerful agentic AI coding assistant for your terminal."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.9"
|
|
@@ -7,7 +7,6 @@ from rich.console import Console
|
|
|
7
7
|
from rich.live import Live
|
|
8
8
|
from rich.spinner import Spinner
|
|
9
9
|
from rich.markdown import Markdown
|
|
10
|
-
from rich.panel import Panel
|
|
11
10
|
from rich.text import Text
|
|
12
11
|
from rich.rule import Rule
|
|
13
12
|
from rich.syntax import Syntax
|
|
@@ -19,23 +18,16 @@ BLUE = "#42bcf5"
|
|
|
19
18
|
YELLOW = "#f9e2af"
|
|
20
19
|
|
|
21
20
|
TOOL_META = {
|
|
22
|
-
"read_file": {"icon": "
|
|
23
|
-
"write_file": {"icon": "
|
|
24
|
-
"edit_file": {"icon": "
|
|
25
|
-
"move_file": {"icon": "
|
|
26
|
-
"delete_file": {"icon": "
|
|
27
|
-
"run_command": {"icon": "
|
|
28
|
-
"list_directory": {"icon": "
|
|
21
|
+
"read_file": {"icon": "", "verb": "Read", "color": BLUE},
|
|
22
|
+
"write_file": {"icon": "", "verb": "Write", "color": YELLOW},
|
|
23
|
+
"edit_file": {"icon": "", "verb": "Edit", "color": YELLOW},
|
|
24
|
+
"move_file": {"icon": "", "verb": "Move", "color": BLUE},
|
|
25
|
+
"delete_file": {"icon": "", "verb": "Delete", "color": PURPLE},
|
|
26
|
+
"run_command": {"icon": "", "verb": "Bash", "color": YELLOW},
|
|
27
|
+
"list_directory": {"icon": "", "verb": "ListDir", "color": PURPLE},
|
|
29
28
|
}
|
|
30
29
|
|
|
31
|
-
TIPS = [
|
|
32
|
-
"Tip: Use /help to see all available commands and the help panel",
|
|
33
|
-
"Tip: Save your current conversation with /resume save <tag>",
|
|
34
|
-
"Tip: Clear conversation history with /clear",
|
|
35
|
-
"Tip: Check your quota limits and credit usage with /usage",
|
|
36
|
-
"Tip: Enable or disable individual tools with /tools",
|
|
37
|
-
"Tip: Switch models with /model",
|
|
38
|
-
]
|
|
30
|
+
TIPS = []
|
|
39
31
|
|
|
40
32
|
class ReActAgent:
|
|
41
33
|
"""A standalone agent that can reason and execute tools with streaming."""
|
|
@@ -48,7 +40,7 @@ class ReActAgent:
|
|
|
48
40
|
|
|
49
41
|
api_key = os.getenv("OPENROUTER_API_KEY") or os.getenv("OPENRouter_API_KEY")
|
|
50
42
|
if not api_key:
|
|
51
|
-
self.console.print("[dim]
|
|
43
|
+
self.console.print("[dim]Warning: OPENROUTER_API_KEY is not set.[/dim]")
|
|
52
44
|
|
|
53
45
|
try:
|
|
54
46
|
from openai import OpenAI
|
|
@@ -80,23 +72,23 @@ class ReActAgent:
|
|
|
80
72
|
return f"{elapsed // 60}m {elapsed % 60}s" if elapsed >= 60 else f"{elapsed}s"
|
|
81
73
|
|
|
82
74
|
def _next_tip(self):
|
|
75
|
+
if not TIPS:
|
|
76
|
+
return ""
|
|
83
77
|
tip = TIPS[self._tip_index % len(TIPS)]
|
|
84
78
|
self._tip_index += 1
|
|
85
79
|
return tip
|
|
86
80
|
|
|
81
|
+
|
|
87
82
|
def _render_tool_panel(self, func_name: str, args: dict, result: str):
|
|
88
|
-
"""Render a
|
|
89
|
-
meta = TOOL_META.get(func_name, {"icon": "●", "verb": func_name, "color": "white"})
|
|
90
|
-
icon = meta["icon"]
|
|
91
|
-
verb = meta["verb"]
|
|
92
|
-
color = meta["color"]
|
|
83
|
+
"""Render a completed tool call in Antigravity inline style — no box, no border.
|
|
93
84
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
85
|
+
Line 1: \u25cf Verb(path/arg)
|
|
86
|
+
Line 2: \u2514 summary
|
|
87
|
+
"""
|
|
88
|
+
meta = TOOL_META.get(func_name, {"icon": "\u25cf", "verb": func_name, "color": "white"})
|
|
89
|
+
verb = meta["verb"]
|
|
90
|
+
color = meta["color"]
|
|
98
91
|
|
|
99
|
-
# Show the primary argument (filepath or command)
|
|
100
92
|
primary_arg = (
|
|
101
93
|
args.get("filepath")
|
|
102
94
|
or args.get("command")
|
|
@@ -104,51 +96,37 @@ class ReActAgent:
|
|
|
104
96
|
or args.get("src")
|
|
105
97
|
or ""
|
|
106
98
|
)
|
|
107
|
-
if primary_arg:
|
|
108
|
-
header.append(f" {primary_arg}", style="white")
|
|
109
99
|
|
|
110
|
-
#
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
if
|
|
114
|
-
|
|
100
|
+
# Line 1: \u25cf Verb(arg)
|
|
101
|
+
header = Text()
|
|
102
|
+
header.append(verb, style="bold white")
|
|
103
|
+
if primary_arg:
|
|
104
|
+
header.append(f"({primary_arg})", style="dim white")
|
|
105
|
+
header.append(" (ctrl+o to expand)", style="dim #585b70")
|
|
115
106
|
|
|
116
|
-
|
|
117
|
-
|
|
107
|
+
# Line 2: \u2514 summary
|
|
108
|
+
summary = Text()
|
|
109
|
+
summary.append(" \u2514 ", style="dim #585b70")
|
|
118
110
|
|
|
119
|
-
|
|
111
|
+
if func_name == "read_file":
|
|
120
112
|
lines = result.split("\n")
|
|
121
|
-
|
|
122
|
-
|
|
113
|
+
summary.append(f"{len(lines)} lines", style="dim white")
|
|
114
|
+
elif func_name in ("write_file", "edit_file"):
|
|
115
|
+
first = result.strip().splitlines()[0][:80] if result.strip() else "done"
|
|
116
|
+
summary.append(first, style="dim white")
|
|
123
117
|
elif func_name == "run_command":
|
|
124
118
|
out = str(result).strip()
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
body.append(out, style="dim")
|
|
128
|
-
|
|
119
|
+
first = out.splitlines()[0][:80] if out else "(no output)"
|
|
120
|
+
summary.append(first, style="dim white")
|
|
129
121
|
elif func_name == "list_directory":
|
|
130
122
|
items = [l for l in str(result).split("\n") if l.strip()]
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
elif func_name == "move_file":
|
|
134
|
-
body.append(result, style="dim")
|
|
135
|
-
|
|
136
|
-
elif func_name == "delete_file":
|
|
137
|
-
body.append(result, style="dim")
|
|
138
|
-
|
|
123
|
+
summary.append(f"{max(0, len(items)-1)} items", style="dim white")
|
|
139
124
|
else:
|
|
140
125
|
out = str(result).strip()
|
|
141
|
-
if
|
|
142
|
-
out = out[:300] + "\n[…]"
|
|
143
|
-
body.append(out, style="dim")
|
|
126
|
+
summary.append((out.splitlines()[0][:80] if out else "done"), style="dim white")
|
|
144
127
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
self.console.print(Panel(
|
|
148
|
-
content,
|
|
149
|
-
border_style=color,
|
|
150
|
-
expand=False,
|
|
151
|
-
))
|
|
128
|
+
self.console.print(header)
|
|
129
|
+
self.console.print(summary)
|
|
152
130
|
|
|
153
131
|
def _execute_tool_call(self, tool_call):
|
|
154
132
|
"""Execute a single tool call by name."""
|
|
@@ -196,23 +174,53 @@ class ReActAgent:
|
|
|
196
174
|
|
|
197
175
|
arguments = json.loads(tool_call["function"]["arguments"])
|
|
198
176
|
|
|
199
|
-
# Show the tool is running
|
|
177
|
+
# Show the tool is running - Antigravity style: ● Verb(arg) (ctrl+o to expand)
|
|
200
178
|
meta = TOOL_META.get(tool_name, {"icon": "●", "verb": tool_name, "color": "white"})
|
|
201
|
-
|
|
179
|
+
_pre = Text()
|
|
180
|
+
_pre.append(meta['verb'], style="bold white")
|
|
181
|
+
primary_arg = (
|
|
182
|
+
arguments.get("filepath")
|
|
183
|
+
or arguments.get("command")
|
|
184
|
+
or arguments.get("path")
|
|
185
|
+
or ""
|
|
186
|
+
)
|
|
187
|
+
if primary_arg:
|
|
188
|
+
_pre.append(f"({primary_arg})", style="dim white")
|
|
189
|
+
_pre.append(" (ctrl+o to expand)", style="dim #585b70")
|
|
190
|
+
self.console.print(_pre)
|
|
191
|
+
pre_plain = f"{meta['verb']}({primary_arg}) (ctrl+o to expand)" if primary_arg else f"{meta['verb']} (ctrl+o to expand)"
|
|
192
|
+
self._last_running_indicator_len = len(pre_plain)
|
|
202
193
|
|
|
203
194
|
if "__" in tool_name:
|
|
204
195
|
server_name, actual_tool_name = tool_name.split("__", 1)
|
|
205
196
|
try:
|
|
206
197
|
from utim_cli.mcp_client import mcp_manager
|
|
207
198
|
if server_name in mcp_manager.sessions:
|
|
208
|
-
self.console.print(f"
|
|
199
|
+
self.console.print(f" Calling MCP tool {server_name} ➔ {actual_tool_name}...", style="dim #cba6f7")
|
|
209
200
|
result = mcp_manager.call_tool(server_name, actual_tool_name, arguments)
|
|
210
201
|
|
|
211
202
|
# Temporarily register metadata for render
|
|
212
|
-
TOOL_META[tool_name] = {"icon": "
|
|
203
|
+
TOOL_META[tool_name] = {"icon": "", "verb": f"Calling {server_name} ➔ {actual_tool_name}", "color": "#cba6f7"}
|
|
204
|
+
# Erase the running line first
|
|
205
|
+
try:
|
|
206
|
+
width = self.console.size.width
|
|
207
|
+
except Exception:
|
|
208
|
+
width = 80
|
|
209
|
+
import math
|
|
210
|
+
num_lines = math.ceil(getattr(self, "_last_running_indicator_len", 40) / max(1, width))
|
|
211
|
+
for _ in range(num_lines):
|
|
212
|
+
self.console.print("\033[F\033[K", end="")
|
|
213
213
|
self._render_tool_panel(tool_name, arguments, result)
|
|
214
214
|
return str(result)
|
|
215
215
|
except Exception as e:
|
|
216
|
+
try:
|
|
217
|
+
width = self.console.size.width
|
|
218
|
+
except Exception:
|
|
219
|
+
width = 80
|
|
220
|
+
import math
|
|
221
|
+
num_lines = math.ceil(getattr(self, "_last_running_indicator_len", 40) / max(1, width))
|
|
222
|
+
for _ in range(num_lines):
|
|
223
|
+
self.console.print("\033[F\033[K", end="")
|
|
216
224
|
return f"Error executing MCP tool {tool_name}: {str(e)}"
|
|
217
225
|
|
|
218
226
|
utim_tools, tool_functions = get_tools()
|
|
@@ -224,11 +232,29 @@ class ReActAgent:
|
|
|
224
232
|
|
|
225
233
|
try:
|
|
226
234
|
result = tool_functions[tool_name](**arguments)
|
|
235
|
+
# Erase the running line first
|
|
236
|
+
try:
|
|
237
|
+
width = self.console.size.width
|
|
238
|
+
except Exception:
|
|
239
|
+
width = 80
|
|
240
|
+
import math
|
|
241
|
+
num_lines = math.ceil(getattr(self, "_last_running_indicator_len", 40) / max(1, width))
|
|
242
|
+
for _ in range(num_lines):
|
|
243
|
+
self.console.print("\033[F\033[K", end="")
|
|
227
244
|
self._render_tool_panel(tool_name, arguments, result)
|
|
228
245
|
return str(result)
|
|
229
246
|
except Exception as e:
|
|
247
|
+
try:
|
|
248
|
+
width = self.console.size.width
|
|
249
|
+
except Exception:
|
|
250
|
+
width = 80
|
|
251
|
+
import math
|
|
252
|
+
num_lines = math.ceil(getattr(self, "_last_running_indicator_len", 40) / max(1, width))
|
|
253
|
+
for _ in range(num_lines):
|
|
254
|
+
self.console.print("\033[F\033[K", end="")
|
|
230
255
|
return f"Error executing {tool_name}: {str(e)}"
|
|
231
256
|
|
|
257
|
+
|
|
232
258
|
def run(self, max_iterations: int = 500, show_tools: bool = True):
|
|
233
259
|
"""Run the agent loop with streaming."""
|
|
234
260
|
self.start_time = time.time()
|
|
@@ -323,17 +349,17 @@ class ReActAgent:
|
|
|
323
349
|
"content": result,
|
|
324
350
|
})
|
|
325
351
|
else:
|
|
326
|
-
self.console.print(f"\n[bold yellow]
|
|
352
|
+
self.console.print(f"\n[bold yellow]Agent paused after reaching maximum iterations ({max_iterations}).[/bold yellow]")
|
|
327
353
|
self.console.print("[dim]You can type 'continue' to resume the task.[/dim]\n")
|
|
328
354
|
|
|
329
355
|
elapsed = self.get_elapsed_time()
|
|
330
356
|
tip = self._next_tip()
|
|
331
|
-
self.console.print(Rule(f"[dim]
|
|
357
|
+
self.console.print(Rule(f"[dim]{elapsed}[/dim]", style="dim"))
|
|
332
358
|
|
|
333
359
|
def list_tools(self):
|
|
334
360
|
"""Display all available tools in a formatted table."""
|
|
335
361
|
self.console.print()
|
|
336
|
-
self.console.print(Rule("[bold accent]
|
|
362
|
+
self.console.print(Rule("[bold accent]Available Tools[/bold accent]"))
|
|
337
363
|
self.console.print()
|
|
338
364
|
|
|
339
365
|
headers = ["Tool", "Description"]
|
|
@@ -374,7 +400,7 @@ class ReActAgent:
|
|
|
374
400
|
desc_str = f"{status_tag} [dim]{desc}[/dim]" if is_disabled else f"[dim]{desc}[/dim]"
|
|
375
401
|
|
|
376
402
|
if t_type == "mcp":
|
|
377
|
-
tool_str = f"
|
|
403
|
+
tool_str = f" {name.ljust(col_widths[0] - 4)} {desc_str}"
|
|
378
404
|
self.console.print(f"[#cba6f7]{tool_str}[/#cba6f7]")
|
|
379
405
|
else:
|
|
380
406
|
meta = TOOL_META.get(name, {"icon": "●", "color": "white"})
|
|
@@ -38,6 +38,19 @@ YELLOW = "#f9e2af"
|
|
|
38
38
|
SERVER_URL = os.environ.get("UTIM_SERVER_URL", "https://api.utim.dev")
|
|
39
39
|
WEB_URL = os.environ.get("UTIM_WEB_URL", "https://utim.dev")
|
|
40
40
|
|
|
41
|
+
def get_api_url() -> str:
|
|
42
|
+
"""Get base server URL from environment or fallback default."""
|
|
43
|
+
return os.environ.get("UTIM_SERVER_URL", SERVER_URL)
|
|
44
|
+
|
|
45
|
+
def get_auth_headers() -> dict[str, str]:
|
|
46
|
+
"""Get request headers containing current user API key."""
|
|
47
|
+
api_key = config.get("api_key") or ""
|
|
48
|
+
headers = {"Content-Type": "application/json"}
|
|
49
|
+
if api_key:
|
|
50
|
+
headers["X-API-Key"] = api_key
|
|
51
|
+
headers["Authorization"] = f"Bearer {api_key}"
|
|
52
|
+
return headers
|
|
53
|
+
|
|
41
54
|
# Firebase project config (public — safe to embed in CLI)
|
|
42
55
|
FIREBASE_PROJECT_ID = "u-t-i-m-39c26"
|
|
43
56
|
FIREBASE_API_KEY = "AIzaSyAV-L3jY6dS3wXMMNGnYnPTX3IuqBFqK4E"
|
|
@@ -201,7 +214,7 @@ def login(restart: bool = True) -> None:
|
|
|
201
214
|
pass
|
|
202
215
|
|
|
203
216
|
# Print compact Termux/Android friendly instructions
|
|
204
|
-
raw_print(f"\n {BOLD}
|
|
217
|
+
raw_print(f"\n {BOLD}Device Sign-In Required:{RESET}")
|
|
205
218
|
raw_print(f" 1. Open link: {BLUE}{verify_url}{RESET}")
|
|
206
219
|
if clipboard_notice:
|
|
207
220
|
raw_print(f" {clipboard_notice.strip()}")
|
|
@@ -276,7 +289,7 @@ def login(restart: bool = True) -> None:
|
|
|
276
289
|
restart_process()
|
|
277
290
|
return
|
|
278
291
|
else:
|
|
279
|
-
raw_print(f" {YELLOW}
|
|
292
|
+
raw_print(f" {YELLOW}Authorization pending. Please complete it at: {BLUE}{verify_url}{RESET}")
|
|
280
293
|
else:
|
|
281
294
|
raw_print(f"\n \033[1;31m✗ Verification failed (HTTP {pr.status_code}). Please try again.\033[0m\n")
|
|
282
295
|
except Exception as e:
|