cade-cli 0.3.3__py3-none-any.whl
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.
- cade_cli-0.3.3.dist-info/METADATA +151 -0
- cade_cli-0.3.3.dist-info/RECORD +44 -0
- cade_cli-0.3.3.dist-info/WHEEL +4 -0
- cade_cli-0.3.3.dist-info/entry_points.txt +2 -0
- cadecoder/__init__.py +1 -0
- cadecoder/ai/__init__.py +6 -0
- cadecoder/ai/prompts.py +572 -0
- cadecoder/cli/__init__.py +0 -0
- cadecoder/cli/app.py +147 -0
- cadecoder/cli/auth.py +483 -0
- cadecoder/cli/commands/__init__.py +5 -0
- cadecoder/cli/commands/auth.py +143 -0
- cadecoder/cli/commands/chat.py +264 -0
- cadecoder/cli/commands/mcp.py +477 -0
- cadecoder/cli/commands/tools.py +226 -0
- cadecoder/core/__init__.py +12 -0
- cadecoder/core/config.py +380 -0
- cadecoder/core/constants.py +281 -0
- cadecoder/core/errors.py +145 -0
- cadecoder/core/logging.py +148 -0
- cadecoder/core/types.py +235 -0
- cadecoder/core/utils.py +279 -0
- cadecoder/execution/__init__.py +46 -0
- cadecoder/execution/context_window.py +521 -0
- cadecoder/execution/orchestrator.py +562 -0
- cadecoder/execution/parallel.py +287 -0
- cadecoder/providers/__init__.py +60 -0
- cadecoder/providers/base.py +294 -0
- cadecoder/providers/openai.py +251 -0
- cadecoder/storage/__init__.py +0 -0
- cadecoder/storage/threads.py +489 -0
- cadecoder/templates/login_failed.html +21 -0
- cadecoder/templates/login_success.html +21 -0
- cadecoder/templates/styles.css +87 -0
- cadecoder/tools/__init__.py +19 -0
- cadecoder/tools/builtin.py +644 -0
- cadecoder/tools/filesystem.py +315 -0
- cadecoder/tools/git.py +221 -0
- cadecoder/tools/manager.py +1635 -0
- cadecoder/ui/__init__.py +7 -0
- cadecoder/ui/display.py +338 -0
- cadecoder/ui/input.py +145 -0
- cadecoder/ui/session.py +455 -0
- cadecoder/ui/state.py +20 -0
cadecoder/core/utils.py
ADDED
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
"""Utility functions for URL computation, date context, and console output.
|
|
2
|
+
|
|
3
|
+
This module provides pure utility functions for common operations:
|
|
4
|
+
- URL computation for Arcade Engine and login endpoints
|
|
5
|
+
- Date/time context generation
|
|
6
|
+
- Console output helpers
|
|
7
|
+
|
|
8
|
+
All functions are pure except console printing functions which have
|
|
9
|
+
side effects (output to console).
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import datetime
|
|
13
|
+
import ipaddress
|
|
14
|
+
from urllib.parse import ParseResult, urlencode, urlparse
|
|
15
|
+
|
|
16
|
+
import idna
|
|
17
|
+
from rich.console import Console
|
|
18
|
+
from rich.text import Text
|
|
19
|
+
|
|
20
|
+
from cadecoder.core.constants import (
|
|
21
|
+
DEFAULT_CALLBACK_PORT,
|
|
22
|
+
DEFAULT_DEV_PORT,
|
|
23
|
+
DEFAULT_LOGIN_PORT,
|
|
24
|
+
LOCALHOST,
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
# --- URL Computation ---
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _normalize_host(host: str) -> str:
|
|
31
|
+
"""Normalize host to localhost if it's a localhost alias.
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
host: Hostname or IP address
|
|
35
|
+
|
|
36
|
+
Returns:
|
|
37
|
+
Normalized hostname (localhost for 127.0.0.1 or 0.0.0.0)
|
|
38
|
+
"""
|
|
39
|
+
return LOCALHOST if host in ["127.0.0.1", "0.0.0.0"] else host # noqa: S104
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _determine_tls_setting(force_tls: bool, force_no_tls: bool, host: str) -> bool:
|
|
43
|
+
"""Determine TLS setting based on flags and host.
|
|
44
|
+
|
|
45
|
+
Args:
|
|
46
|
+
force_tls: Force TLS enabled
|
|
47
|
+
force_no_tls: Force TLS disabled (takes precedence)
|
|
48
|
+
host: Hostname
|
|
49
|
+
|
|
50
|
+
Returns:
|
|
51
|
+
True if TLS should be used, False otherwise
|
|
52
|
+
"""
|
|
53
|
+
if force_no_tls:
|
|
54
|
+
return False
|
|
55
|
+
elif force_tls:
|
|
56
|
+
return True
|
|
57
|
+
else:
|
|
58
|
+
return host != LOCALHOST
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _encode_idn(host: str) -> str:
|
|
62
|
+
"""Encode Internationalized Domain Name to ASCII.
|
|
63
|
+
|
|
64
|
+
Args:
|
|
65
|
+
host: Hostname potentially containing IDN characters
|
|
66
|
+
|
|
67
|
+
Returns:
|
|
68
|
+
ASCII-encoded hostname
|
|
69
|
+
"""
|
|
70
|
+
try:
|
|
71
|
+
return idna.encode(host).decode("ascii")
|
|
72
|
+
except idna.IDNAError:
|
|
73
|
+
return host
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def _is_ip_address(host: str) -> bool:
|
|
77
|
+
"""Check if host is a valid IP address (IPv4 or IPv6).
|
|
78
|
+
|
|
79
|
+
Args:
|
|
80
|
+
host: Hostname or IP address
|
|
81
|
+
|
|
82
|
+
Returns:
|
|
83
|
+
True if host is an IP address, False otherwise
|
|
84
|
+
"""
|
|
85
|
+
try:
|
|
86
|
+
ipaddress.ip_address(host)
|
|
87
|
+
return True
|
|
88
|
+
except ValueError:
|
|
89
|
+
return False
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def _is_fqdn(parsed_host: ParseResult, is_ip: bool) -> bool:
|
|
93
|
+
"""Check if host is a fully qualified domain name.
|
|
94
|
+
|
|
95
|
+
Args:
|
|
96
|
+
parsed_host: Parsed URL host
|
|
97
|
+
is_ip: Whether host is an IP address
|
|
98
|
+
|
|
99
|
+
Returns:
|
|
100
|
+
True if host is an FQDN, False otherwise
|
|
101
|
+
"""
|
|
102
|
+
return "." in parsed_host.netloc and not is_ip and "_" not in parsed_host.netloc
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def _extract_existing_port(parsed_host: ParseResult, is_ip: bool) -> str | None:
|
|
106
|
+
"""Extract port from host if it already includes one.
|
|
107
|
+
|
|
108
|
+
Args:
|
|
109
|
+
parsed_host: Parsed URL host
|
|
110
|
+
is_ip: Whether host is an IP address
|
|
111
|
+
|
|
112
|
+
Returns:
|
|
113
|
+
Full host:port string if port exists, None otherwise
|
|
114
|
+
"""
|
|
115
|
+
if ":" in parsed_host.netloc and not is_ip:
|
|
116
|
+
host, existing_port = parsed_host.netloc.rsplit(":", 1)
|
|
117
|
+
if existing_port.isdigit():
|
|
118
|
+
return parsed_host.netloc
|
|
119
|
+
return None
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def compute_base_url(
|
|
123
|
+
force_tls: bool,
|
|
124
|
+
force_no_tls: bool,
|
|
125
|
+
host: str,
|
|
126
|
+
port: int | None,
|
|
127
|
+
) -> str:
|
|
128
|
+
"""Compute the base URL for the Arcade Engine from the provided overrides.
|
|
129
|
+
|
|
130
|
+
Pure function: no side effects, returns computed URL string.
|
|
131
|
+
|
|
132
|
+
Treats 127.0.0.1 and 0.0.0.0 as aliases for localhost.
|
|
133
|
+
|
|
134
|
+
force_no_tls takes precedence over force_tls. For example, if both are
|
|
135
|
+
set to True, the resulting URL will use http.
|
|
136
|
+
|
|
137
|
+
The port is included in the URL unless the host is a fully qualified
|
|
138
|
+
domain name (excluding IP addresses) and no port is specified. Handles
|
|
139
|
+
IPv4, IPv6, IDNs, and hostnames with underscores.
|
|
140
|
+
|
|
141
|
+
Args:
|
|
142
|
+
force_tls: Force TLS enabled
|
|
143
|
+
force_no_tls: Force TLS disabled (takes precedence)
|
|
144
|
+
host: Hostname or IP address
|
|
145
|
+
port: Optional port number
|
|
146
|
+
|
|
147
|
+
Returns:
|
|
148
|
+
Fully constructed URL for the Arcade Engine
|
|
149
|
+
"""
|
|
150
|
+
host = _normalize_host(host)
|
|
151
|
+
is_tls = _determine_tls_setting(force_tls, force_no_tls, host)
|
|
152
|
+
protocol = "https" if is_tls else "http"
|
|
153
|
+
|
|
154
|
+
# Default to dev port for localhost
|
|
155
|
+
if host == LOCALHOST and port is None:
|
|
156
|
+
port = DEFAULT_DEV_PORT
|
|
157
|
+
|
|
158
|
+
encoded_host = _encode_idn(host)
|
|
159
|
+
is_ip = _is_ip_address(encoded_host)
|
|
160
|
+
|
|
161
|
+
# Parse host, handling IPv6 addresses
|
|
162
|
+
host_for_parsing = f"[{encoded_host}]" if is_ip and ":" in encoded_host else encoded_host
|
|
163
|
+
parsed_host = urlparse(f"//{host_for_parsing}")
|
|
164
|
+
|
|
165
|
+
# Check for existing port in host
|
|
166
|
+
existing_port_url = _extract_existing_port(parsed_host, is_ip)
|
|
167
|
+
if existing_port_url:
|
|
168
|
+
return f"{protocol}://{existing_port_url}"
|
|
169
|
+
|
|
170
|
+
# Build URL based on FQDN and port
|
|
171
|
+
is_fqdn = _is_fqdn(parsed_host, is_ip)
|
|
172
|
+
if is_fqdn and port is None:
|
|
173
|
+
return f"{protocol}://{encoded_host}"
|
|
174
|
+
elif port is not None:
|
|
175
|
+
return f"{protocol}://{encoded_host}:{port}"
|
|
176
|
+
else:
|
|
177
|
+
return f"{protocol}://{encoded_host}"
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
def compute_login_url(host: str, state: str, port: int | None) -> str:
|
|
181
|
+
"""Compute the full URL for the CLI login endpoint.
|
|
182
|
+
|
|
183
|
+
Pure function: no side effects, returns computed URL string.
|
|
184
|
+
|
|
185
|
+
Args:
|
|
186
|
+
host: Hostname or IP address
|
|
187
|
+
state: OAuth state parameter
|
|
188
|
+
port: Optional port number (defaults to DEFAULT_LOGIN_PORT for localhost)
|
|
189
|
+
|
|
190
|
+
Returns:
|
|
191
|
+
Complete login URL with query parameters
|
|
192
|
+
"""
|
|
193
|
+
callback_uri = f"http://{LOCALHOST}:{DEFAULT_CALLBACK_PORT}/callback"
|
|
194
|
+
params = urlencode({"callback_uri": callback_uri, "state": state})
|
|
195
|
+
|
|
196
|
+
port = port if port else DEFAULT_LOGIN_PORT
|
|
197
|
+
|
|
198
|
+
login_base_url = (
|
|
199
|
+
f"http://{LOCALHOST}:{port}"
|
|
200
|
+
if host in [LOCALHOST, "127.0.0.1", "0.0.0.0"] # noqa: S104
|
|
201
|
+
else f"https://{host}"
|
|
202
|
+
)
|
|
203
|
+
endpoint = "/api/v1/auth/cli_login"
|
|
204
|
+
|
|
205
|
+
return f"{login_base_url}{endpoint}?{params}"
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
# --- Date/Time Context ---
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
def get_today_context() -> str:
|
|
212
|
+
"""Generate system prompt with today's date and day of the week.
|
|
213
|
+
|
|
214
|
+
Pure function: no side effects, returns formatted date string.
|
|
215
|
+
|
|
216
|
+
This helps the AI with time-sensitive requests and reasoning about dates.
|
|
217
|
+
|
|
218
|
+
Returns:
|
|
219
|
+
Formatted string with today's date and day of week
|
|
220
|
+
"""
|
|
221
|
+
today = datetime.datetime.now()
|
|
222
|
+
day_of_week = today.strftime("%A")
|
|
223
|
+
date_str = today.strftime("%B %d, %Y")
|
|
224
|
+
|
|
225
|
+
return (
|
|
226
|
+
f"You are a helpful AI assistant. "
|
|
227
|
+
f"Today is {day_of_week}, {date_str}. "
|
|
228
|
+
"Please keep this in mind when helping with time-sensitive tasks."
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
# --- Console Output ---
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
_console = Console(stderr=True)
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
def print_info(message: str) -> None:
|
|
239
|
+
"""Print an informational message in blue.
|
|
240
|
+
|
|
241
|
+
Side effect: writes to console.
|
|
242
|
+
|
|
243
|
+
Args:
|
|
244
|
+
message: Message to print
|
|
245
|
+
"""
|
|
246
|
+
_console.print(Text(message, style="bold blue"))
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
def print_success(message: str) -> None:
|
|
250
|
+
"""Print a success message in green.
|
|
251
|
+
|
|
252
|
+
Side effect: writes to console.
|
|
253
|
+
|
|
254
|
+
Args:
|
|
255
|
+
message: Message to print
|
|
256
|
+
"""
|
|
257
|
+
_console.print(Text(message, style="bold green"))
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
def print_warning(message: str) -> None:
|
|
261
|
+
"""Print a warning message in yellow.
|
|
262
|
+
|
|
263
|
+
Side effect: writes to console.
|
|
264
|
+
|
|
265
|
+
Args:
|
|
266
|
+
message: Message to print
|
|
267
|
+
"""
|
|
268
|
+
_console.print(Text(message, style="bold yellow"))
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
def print_error(message: str) -> None:
|
|
272
|
+
"""Print an error message in red.
|
|
273
|
+
|
|
274
|
+
Side effect: writes to console.
|
|
275
|
+
|
|
276
|
+
Args:
|
|
277
|
+
message: Message to print
|
|
278
|
+
"""
|
|
279
|
+
_console.print(Text(message, style="bold red"))
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"""Execution package for task processing.
|
|
2
|
+
|
|
3
|
+
This package contains:
|
|
4
|
+
- Orchestrator: Central control flow for agent execution
|
|
5
|
+
- Context window management: Token tracking and compaction
|
|
6
|
+
- Parallel execution: Concurrent tool execution with dependency analysis
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from cadecoder.execution.context_window import (
|
|
10
|
+
CompactionStrategy,
|
|
11
|
+
ContextBackup,
|
|
12
|
+
ContextWindowManager,
|
|
13
|
+
TokenEstimate,
|
|
14
|
+
ToolOutputCollection,
|
|
15
|
+
create_context_manager,
|
|
16
|
+
)
|
|
17
|
+
from cadecoder.execution.orchestrator import (
|
|
18
|
+
ContinuationDecision,
|
|
19
|
+
ExecutionContext,
|
|
20
|
+
ExecutionEvent,
|
|
21
|
+
ExecutionMode,
|
|
22
|
+
ExecutionResult,
|
|
23
|
+
Orchestrator,
|
|
24
|
+
create_orchestrator,
|
|
25
|
+
)
|
|
26
|
+
from cadecoder.execution.parallel import ParallelToolExecutor
|
|
27
|
+
|
|
28
|
+
__all__ = [
|
|
29
|
+
# Context window
|
|
30
|
+
"CompactionStrategy",
|
|
31
|
+
"ContextBackup",
|
|
32
|
+
"ContextWindowManager",
|
|
33
|
+
"TokenEstimate",
|
|
34
|
+
"ToolOutputCollection",
|
|
35
|
+
"create_context_manager",
|
|
36
|
+
# Orchestrator
|
|
37
|
+
"ContinuationDecision",
|
|
38
|
+
"ExecutionContext",
|
|
39
|
+
"ExecutionEvent",
|
|
40
|
+
"ExecutionMode",
|
|
41
|
+
"ExecutionResult",
|
|
42
|
+
"Orchestrator",
|
|
43
|
+
"create_orchestrator",
|
|
44
|
+
# Parallel execution
|
|
45
|
+
"ParallelToolExecutor",
|
|
46
|
+
]
|