agentstack-cli 0.6.0rc1__py3-none-manylinux_2_34_aarch64.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.
- agentstack_cli/__init__.py +164 -0
- agentstack_cli/api.py +160 -0
- agentstack_cli/async_typer.py +113 -0
- agentstack_cli/auth_manager.py +242 -0
- agentstack_cli/commands/__init__.py +3 -0
- agentstack_cli/commands/agent.py +1386 -0
- agentstack_cli/commands/build.py +222 -0
- agentstack_cli/commands/connector.py +301 -0
- agentstack_cli/commands/model.py +653 -0
- agentstack_cli/commands/platform/__init__.py +198 -0
- agentstack_cli/commands/platform/base_driver.py +217 -0
- agentstack_cli/commands/platform/lima_driver.py +277 -0
- agentstack_cli/commands/platform/wsl_driver.py +229 -0
- agentstack_cli/commands/self.py +213 -0
- agentstack_cli/commands/server.py +315 -0
- agentstack_cli/commands/user.py +87 -0
- agentstack_cli/configuration.py +79 -0
- agentstack_cli/console.py +25 -0
- agentstack_cli/data/.gitignore +2 -0
- agentstack_cli/data/helm-chart.tgz +0 -0
- agentstack_cli/data/lima-guestagent.Linux-aarch64.gz +0 -0
- agentstack_cli/data/limactl +0 -0
- agentstack_cli/utils.py +389 -0
- agentstack_cli-0.6.0rc1.dist-info/METADATA +107 -0
- agentstack_cli-0.6.0rc1.dist-info/RECORD +27 -0
- agentstack_cli-0.6.0rc1.dist-info/WHEEL +4 -0
- agentstack_cli-0.6.0rc1.dist-info/entry_points.txt +4 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Copyright 2025 © BeeAI a Series of LF Projects, LLC
|
|
2
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
|
|
4
|
+
from rich.console import Console
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class ExtendedConsole(Console):
|
|
8
|
+
def error(self, message: str):
|
|
9
|
+
self.print(f"💥 [red]ERROR[/red]: {message}")
|
|
10
|
+
|
|
11
|
+
def warning(self, message: str):
|
|
12
|
+
self.print(f"❗ [yellow]WARNING[/yellow]: {message}")
|
|
13
|
+
|
|
14
|
+
def hint(self, message: str):
|
|
15
|
+
self.print(f"💡 [bright_cyan]HINT[/bright_cyan]: {message}")
|
|
16
|
+
|
|
17
|
+
def success(self, message: str):
|
|
18
|
+
self.print(f"✅ [green]SUCCESS[/green]: {message}")
|
|
19
|
+
|
|
20
|
+
def info(self, message: str):
|
|
21
|
+
self.print(f"📝 INFO: {message}")
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
err_console = ExtendedConsole(stderr=True)
|
|
25
|
+
console = ExtendedConsole()
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
agentstack_cli/utils.py
ADDED
|
@@ -0,0 +1,389 @@
|
|
|
1
|
+
# Copyright 2025 © BeeAI a Series of LF Projects, LLC
|
|
2
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
|
|
4
|
+
import contextlib
|
|
5
|
+
import functools
|
|
6
|
+
import json
|
|
7
|
+
import os
|
|
8
|
+
import re
|
|
9
|
+
import subprocess
|
|
10
|
+
import sys
|
|
11
|
+
from collections import Counter
|
|
12
|
+
from collections.abc import AsyncIterator, Mapping, MutableMapping
|
|
13
|
+
from contextlib import asynccontextmanager
|
|
14
|
+
from contextvars import ContextVar
|
|
15
|
+
from copy import deepcopy
|
|
16
|
+
from io import BytesIO
|
|
17
|
+
from typing import TYPE_CHECKING, Any
|
|
18
|
+
|
|
19
|
+
import anyio
|
|
20
|
+
import anyio.abc
|
|
21
|
+
import httpx
|
|
22
|
+
import typer
|
|
23
|
+
import yaml
|
|
24
|
+
from anyio import create_task_group
|
|
25
|
+
from anyio.abc import ByteReceiveStream, TaskGroup
|
|
26
|
+
from InquirerPy import inquirer
|
|
27
|
+
from jsf import JSF
|
|
28
|
+
from prompt_toolkit import PromptSession
|
|
29
|
+
from prompt_toolkit.shortcuts import CompleteStyle
|
|
30
|
+
from pydantic import BaseModel
|
|
31
|
+
from rich.console import Capture, Console
|
|
32
|
+
from rich.text import Text
|
|
33
|
+
|
|
34
|
+
from agentstack_cli.configuration import Configuration
|
|
35
|
+
from agentstack_cli.console import console, err_console
|
|
36
|
+
|
|
37
|
+
if TYPE_CHECKING:
|
|
38
|
+
from prompt_toolkit.completion import Completer
|
|
39
|
+
from prompt_toolkit.validation import Validator
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def format_model(value: BaseModel | list[BaseModel]) -> str:
|
|
43
|
+
if isinstance(value, BaseException):
|
|
44
|
+
return str(value)
|
|
45
|
+
if isinstance(value, list):
|
|
46
|
+
return yaml.dump([item.model_dump(mode="json") for item in value])
|
|
47
|
+
return yaml.dump(value.model_dump(mode="json"))
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def format_error(name: str, message: str) -> str:
|
|
51
|
+
return f":boom: [bold red]{name}:[/bold red] {message}"
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def extract_messages(exc):
|
|
55
|
+
if isinstance(exc, BaseExceptionGroup):
|
|
56
|
+
return [(exc_type, msg) for e in exc.exceptions for exc_type, msg in extract_messages(e)]
|
|
57
|
+
else:
|
|
58
|
+
message = str(exc)
|
|
59
|
+
if isinstance(exc, httpx.HTTPStatusError):
|
|
60
|
+
with contextlib.suppress(Exception):
|
|
61
|
+
message = str(exc).split(" for url", maxsplit=1)[0]
|
|
62
|
+
message = f"{message}: {exc.response.json()['detail']}"
|
|
63
|
+
|
|
64
|
+
return [(type(exc).__name__, message)]
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def parse_env_var(env_var: str) -> tuple[str, str]:
|
|
68
|
+
"""Parse environment variable string in format NAME=VALUE."""
|
|
69
|
+
if "=" not in env_var:
|
|
70
|
+
raise ValueError(f"Environment variable {env_var} is invalid, use format --env NAME=VALUE")
|
|
71
|
+
key, value = env_var.split("=", 1)
|
|
72
|
+
return key.strip(), value.strip()
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def check_json(value: Any) -> dict[str, Any]:
|
|
76
|
+
try:
|
|
77
|
+
return json.loads(value)
|
|
78
|
+
except json.decoder.JSONDecodeError as e:
|
|
79
|
+
raise typer.BadParameter(f"Invalid JSON '{value}'") from e
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
@functools.cache
|
|
83
|
+
def generate_schema_example(json_schema: dict[str, Any]) -> dict[str, Any]:
|
|
84
|
+
json_schema = deepcopy(remove_nullable(json_schema))
|
|
85
|
+
|
|
86
|
+
def _make_fakes_better(schema: dict[str, Any] | None):
|
|
87
|
+
if not schema:
|
|
88
|
+
return
|
|
89
|
+
match schema["type"]:
|
|
90
|
+
case "array":
|
|
91
|
+
schema["maxItems"] = 3
|
|
92
|
+
schema["minItems"] = 1
|
|
93
|
+
schema["uniqueItems"] = True
|
|
94
|
+
_make_fakes_better(schema["items"])
|
|
95
|
+
case "object":
|
|
96
|
+
for property in schema["properties"].values():
|
|
97
|
+
_make_fakes_better(property)
|
|
98
|
+
|
|
99
|
+
_make_fakes_better(json_schema)
|
|
100
|
+
return JSF(json_schema, allow_none_optionals=0).generate()
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def remove_nullable(schema: dict[str, Any]) -> dict[str, Any]:
|
|
104
|
+
if "anyOf" not in schema and "oneOf" not in schema:
|
|
105
|
+
return schema
|
|
106
|
+
enum_discriminator = "anyOf" if "anyOf" in schema else "oneOf"
|
|
107
|
+
if len(schema[enum_discriminator]) == 2:
|
|
108
|
+
obj1, obj2 = schema[enum_discriminator]
|
|
109
|
+
match (obj1["type"], obj2["type"]):
|
|
110
|
+
case ("null", _):
|
|
111
|
+
return obj2
|
|
112
|
+
case (_, "null"):
|
|
113
|
+
return obj1
|
|
114
|
+
case _:
|
|
115
|
+
return schema
|
|
116
|
+
return schema
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
prompt_session = None
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def require_active_server() -> str:
|
|
123
|
+
"""Return the active server URL or exit if none is selected."""
|
|
124
|
+
if url := Configuration().auth_manager.active_server:
|
|
125
|
+
return url
|
|
126
|
+
console.error("No server selected.")
|
|
127
|
+
console.hint(
|
|
128
|
+
"Run [green]agentstack platform start[/green] to start a local server, or [green]agentstack server login[/green] to connect to a remote one."
|
|
129
|
+
)
|
|
130
|
+
sys.exit(1)
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def announce_server_action(message: str, url: str | None = None) -> str:
|
|
134
|
+
"""Log an info message that includes the active server URL and return it."""
|
|
135
|
+
url = url or require_active_server()
|
|
136
|
+
console.info(f"{message} [cyan]{url}[/cyan]")
|
|
137
|
+
return url
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
async def confirm_server_action(message: str, url: str | None = None, *, yes: bool = False) -> None:
|
|
141
|
+
"""Ask for confirmation before continuing with an action on the active server."""
|
|
142
|
+
if yes:
|
|
143
|
+
return
|
|
144
|
+
url = url or require_active_server()
|
|
145
|
+
confirmed = await inquirer.confirm( # type: ignore
|
|
146
|
+
message=f"{message} {url}?", default=False
|
|
147
|
+
).execute_async()
|
|
148
|
+
if not confirmed:
|
|
149
|
+
console.info("Action cancelled.")
|
|
150
|
+
raise typer.Exit(1)
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def prompt_user(
|
|
154
|
+
prompt: str | None = None,
|
|
155
|
+
completer: "Completer | None" = None,
|
|
156
|
+
placeholder: str | None = None,
|
|
157
|
+
validator: "Validator | None" = None,
|
|
158
|
+
open_autocomplete_by_default=False,
|
|
159
|
+
) -> str:
|
|
160
|
+
global prompt_session
|
|
161
|
+
# This is necessary because we are in a weird sync-under-async situation and the PromptSession
|
|
162
|
+
# tries calling asyncio.run
|
|
163
|
+
from prompt_toolkit import HTML
|
|
164
|
+
from prompt_toolkit.application.current import get_app
|
|
165
|
+
from prompt_toolkit.auto_suggest import AutoSuggestFromHistory
|
|
166
|
+
from prompt_toolkit.completion import DummyCompleter
|
|
167
|
+
from prompt_toolkit.validation import DummyValidator
|
|
168
|
+
|
|
169
|
+
if not prompt_session:
|
|
170
|
+
prompt_session = PromptSession()
|
|
171
|
+
|
|
172
|
+
def prompt_autocomplete():
|
|
173
|
+
buffer = get_app().current_buffer
|
|
174
|
+
if buffer.complete_state:
|
|
175
|
+
buffer.complete_next()
|
|
176
|
+
else:
|
|
177
|
+
buffer.start_completion(select_first=False)
|
|
178
|
+
|
|
179
|
+
if placeholder is None:
|
|
180
|
+
placeholder = "Type your message (/? for help, /q to quit)"
|
|
181
|
+
|
|
182
|
+
return prompt_session.prompt(
|
|
183
|
+
prompt or ">>> ",
|
|
184
|
+
auto_suggest=AutoSuggestFromHistory(),
|
|
185
|
+
placeholder=HTML(f"<ansibrightblack> {placeholder}</ansibrightblack>"),
|
|
186
|
+
complete_style=CompleteStyle.COLUMN,
|
|
187
|
+
completer=completer or DummyCompleter(),
|
|
188
|
+
pre_run=prompt_autocomplete if open_autocomplete_by_default else None,
|
|
189
|
+
complete_while_typing=True,
|
|
190
|
+
validator=validator or DummyValidator(),
|
|
191
|
+
in_thread=True,
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
@asynccontextmanager
|
|
196
|
+
async def capture_output(process: anyio.abc.Process, stream_contents: list | None = None) -> AsyncIterator[TaskGroup]:
|
|
197
|
+
async def receive_logs(stream: ByteReceiveStream, index=0):
|
|
198
|
+
buffer = BytesIO()
|
|
199
|
+
async for chunk in stream:
|
|
200
|
+
err_console.print(Text.from_ansi(chunk.decode(errors="replace")), style="dim")
|
|
201
|
+
buffer.write(chunk)
|
|
202
|
+
if stream_contents:
|
|
203
|
+
stream_contents[index] = buffer.getvalue()
|
|
204
|
+
|
|
205
|
+
async with create_task_group() as tg:
|
|
206
|
+
if process.stdout:
|
|
207
|
+
tg.start_soon(receive_logs, process.stdout, 0)
|
|
208
|
+
if process.stderr:
|
|
209
|
+
tg.start_soon(receive_logs, process.stderr, 1)
|
|
210
|
+
yield tg
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
async def run_command(
|
|
214
|
+
command: list[str],
|
|
215
|
+
message: str,
|
|
216
|
+
env: dict[str, str] | None = None,
|
|
217
|
+
cwd: str = ".",
|
|
218
|
+
check: bool = True,
|
|
219
|
+
input: bytes | None = None,
|
|
220
|
+
) -> subprocess.CompletedProcess[bytes]:
|
|
221
|
+
"""Helper function to run a subprocess command and handle common errors."""
|
|
222
|
+
env = env or {}
|
|
223
|
+
try:
|
|
224
|
+
with status(message):
|
|
225
|
+
err_console.print(f"Command: {command}", style="dim")
|
|
226
|
+
async with await anyio.open_process(
|
|
227
|
+
command, stdin=subprocess.PIPE if input else None, env={**os.environ, **env}, cwd=cwd
|
|
228
|
+
) as process:
|
|
229
|
+
stream_contents: list[bytes | None] = [None, None]
|
|
230
|
+
async with capture_output(process, stream_contents=stream_contents):
|
|
231
|
+
if process.stdin and input:
|
|
232
|
+
await process.stdin.send(input)
|
|
233
|
+
await process.stdin.aclose()
|
|
234
|
+
await process.wait()
|
|
235
|
+
|
|
236
|
+
output, errors = stream_contents
|
|
237
|
+
if check and process.returncode != 0:
|
|
238
|
+
raise subprocess.CalledProcessError(process.returncode or 0, command, output, errors)
|
|
239
|
+
|
|
240
|
+
if SHOW_SUCCESS_STATUS.get():
|
|
241
|
+
console.print(f"{message} [[green]DONE[/green]]")
|
|
242
|
+
return subprocess.CompletedProcess(command, process.returncode or 0, output, errors)
|
|
243
|
+
except FileNotFoundError:
|
|
244
|
+
console.print(f"{message} [[red]ERROR[/red]]")
|
|
245
|
+
tool_name = command[0]
|
|
246
|
+
console.error(f"{tool_name} is not installed. Please install {tool_name} first.")
|
|
247
|
+
sys.exit(1)
|
|
248
|
+
except subprocess.CalledProcessError as e:
|
|
249
|
+
console.print(f"{message} [[red]ERROR[/red]]")
|
|
250
|
+
err_console.print(f"[red]Exit code: {e.returncode} [/red]")
|
|
251
|
+
if e.stderr:
|
|
252
|
+
err_console.print(f"[red]Error: {e.stderr.strip()}[/red]")
|
|
253
|
+
if e.stdout:
|
|
254
|
+
err_console.print(f"[red]Output: {e.stdout.strip()}[/red]")
|
|
255
|
+
raise
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
IN_VERBOSITY_CONTEXT: ContextVar[bool] = ContextVar("verbose", default=False)
|
|
259
|
+
VERBOSE: ContextVar[bool] = ContextVar("verbose", default=False)
|
|
260
|
+
SHOW_SUCCESS_STATUS: ContextVar[bool] = ContextVar("show_command_status", default=True)
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
@contextlib.contextmanager
|
|
264
|
+
def status(message: str):
|
|
265
|
+
if VERBOSE.get():
|
|
266
|
+
console.print(f"{message}...")
|
|
267
|
+
yield
|
|
268
|
+
return
|
|
269
|
+
else:
|
|
270
|
+
err_console.print(f"\n[bold]{message}[/bold]")
|
|
271
|
+
with console.status(f"{message}...", spinner="dots"):
|
|
272
|
+
yield
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
@contextlib.contextmanager
|
|
276
|
+
def verbosity(verbose: bool, show_success_status: bool = True):
|
|
277
|
+
if IN_VERBOSITY_CONTEXT.get():
|
|
278
|
+
yield # Already in a verbosity context, act as a null context manager
|
|
279
|
+
return
|
|
280
|
+
|
|
281
|
+
IN_VERBOSITY_CONTEXT.set(True)
|
|
282
|
+
token = VERBOSE.set(verbose)
|
|
283
|
+
token_command_status = SHOW_SUCCESS_STATUS.set(show_success_status)
|
|
284
|
+
capture: Capture | None = None
|
|
285
|
+
try:
|
|
286
|
+
with err_console.capture() if not verbose else contextlib.nullcontext() as capture:
|
|
287
|
+
yield
|
|
288
|
+
|
|
289
|
+
except Exception:
|
|
290
|
+
if not verbose and capture and (logs := capture.get().strip()):
|
|
291
|
+
err_console.print("\n[yellow]--- Captured logs ---[/yellow]\n")
|
|
292
|
+
err_console.print(Text.from_ansi(logs, style="dim"))
|
|
293
|
+
err_console.print("\n[red]------- Error -------[/red]\n")
|
|
294
|
+
raise
|
|
295
|
+
finally:
|
|
296
|
+
VERBOSE.reset(token)
|
|
297
|
+
IN_VERBOSITY_CONTEXT.set(False)
|
|
298
|
+
SHOW_SUCCESS_STATUS.reset(token_command_status)
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
def print_log(line, ansi_mode=False, out_console: Console | None = None):
|
|
302
|
+
if "error" in line:
|
|
303
|
+
|
|
304
|
+
class CustomError(Exception): ...
|
|
305
|
+
|
|
306
|
+
CustomError.__name__ = line["error"]["type"]
|
|
307
|
+
|
|
308
|
+
raise CustomError(line["error"]["detail"])
|
|
309
|
+
|
|
310
|
+
def decode(text: str):
|
|
311
|
+
return Text.from_ansi(text) if ansi_mode else text
|
|
312
|
+
|
|
313
|
+
match line:
|
|
314
|
+
case {"stream": "stderr"}:
|
|
315
|
+
(out_console or err_console).print(decode(line["message"]))
|
|
316
|
+
case {"stream": "stdout"}:
|
|
317
|
+
(out_console or console).print(decode(line["message"]))
|
|
318
|
+
case {"event": "[DONE]"}:
|
|
319
|
+
return
|
|
320
|
+
case _:
|
|
321
|
+
(out_console or console).print(line)
|
|
322
|
+
|
|
323
|
+
|
|
324
|
+
def is_github_url(url: str) -> bool:
|
|
325
|
+
"""This pattern is taken from agentstack_server.utils.github.GithubUrl, make sure to keep it in sync"""
|
|
326
|
+
|
|
327
|
+
pattern = r"""
|
|
328
|
+
^
|
|
329
|
+
(?:git\+)? # Optional git+ prefix
|
|
330
|
+
https?://(?P<host>github(?:\.[^/]+)+)/ # GitHub host (github.com or github.enterprise.com)
|
|
331
|
+
(?P<org>[^/]+)/ # Organization
|
|
332
|
+
(?P<repo>
|
|
333
|
+
(?: # Non-capturing group for repo name
|
|
334
|
+
(?!\.git(?:$|[@#])) # Negative lookahead for .git at end or followed by @#
|
|
335
|
+
[^/@#] # Any char except /@#
|
|
336
|
+
)+ # One or more of these chars
|
|
337
|
+
)
|
|
338
|
+
(?:\.git)? # Optional .git suffix
|
|
339
|
+
(?:@(?P<version>[^#]+))? # Optional version after @
|
|
340
|
+
(?:\#path=(?P<path>.+))? # Optional path after #path=
|
|
341
|
+
$
|
|
342
|
+
"""
|
|
343
|
+
return bool(re.match(pattern, url, re.VERBOSE))
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
# Inspired by: https://github.com/clarketm/mergedeep/blob/master/mergedeep/mergedeep.py
|
|
347
|
+
|
|
348
|
+
|
|
349
|
+
def _is_recursive_merge(a: Any, b: Any) -> bool:
|
|
350
|
+
both_mapping = isinstance(a, Mapping) and isinstance(b, Mapping)
|
|
351
|
+
both_counter = isinstance(a, Counter) and isinstance(b, Counter)
|
|
352
|
+
return both_mapping and not both_counter
|
|
353
|
+
|
|
354
|
+
|
|
355
|
+
def _handle_merge_replace(destination, source, key):
|
|
356
|
+
if isinstance(destination[key], Counter) and isinstance(source[key], Counter):
|
|
357
|
+
# Merge both destination and source `Counter` as if they were a standard dict.
|
|
358
|
+
_deepmerge(destination[key], source[key])
|
|
359
|
+
else:
|
|
360
|
+
# If a key exists in both objects and the values are `different`, the value from the `source` object will be used.
|
|
361
|
+
destination[key] = deepcopy(source[key])
|
|
362
|
+
|
|
363
|
+
|
|
364
|
+
def _deepmerge(dst, src):
|
|
365
|
+
for key in src:
|
|
366
|
+
if key in dst:
|
|
367
|
+
if _is_recursive_merge(dst[key], src[key]):
|
|
368
|
+
# If the key for both `dst` and `src` are both Mapping types (e.g. dict), then recurse.
|
|
369
|
+
_deepmerge(dst[key], src[key])
|
|
370
|
+
elif dst[key] is src[key]:
|
|
371
|
+
# If a key exists in both objects and the values are `same`, the value from the `dst` object will be used.
|
|
372
|
+
pass
|
|
373
|
+
else:
|
|
374
|
+
_handle_merge_replace(dst, src, key)
|
|
375
|
+
else:
|
|
376
|
+
# If the key exists only in `src`, the value from the `src` object will be used.
|
|
377
|
+
dst[key] = deepcopy(src[key])
|
|
378
|
+
return dst
|
|
379
|
+
|
|
380
|
+
|
|
381
|
+
def merge(destination: MutableMapping[str, Any], *sources: Mapping[str, Any]) -> MutableMapping[str, Any]:
|
|
382
|
+
"""
|
|
383
|
+
A deep merge function for 🐍.
|
|
384
|
+
|
|
385
|
+
:param destination: The destination mapping.
|
|
386
|
+
:param sources: The source mappings.
|
|
387
|
+
:return:
|
|
388
|
+
"""
|
|
389
|
+
return functools.reduce(_deepmerge, sources, destination)
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: agentstack-cli
|
|
3
|
+
Version: 0.6.0rc1
|
|
4
|
+
Summary: Agent Stack CLI
|
|
5
|
+
Author: IBM Corp.
|
|
6
|
+
Requires-Dist: agentstack-sdk==0.6.0rc1
|
|
7
|
+
Requires-Dist: a2a-sdk==0.3.21
|
|
8
|
+
Requires-Dist: annotated-doc==0.0.4
|
|
9
|
+
Requires-Dist: annotated-types==0.7.0
|
|
10
|
+
Requires-Dist: anyio==4.10.0
|
|
11
|
+
Requires-Dist: asgiref==3.11.0
|
|
12
|
+
Requires-Dist: async-lru==2.0.5
|
|
13
|
+
Requires-Dist: asyncclick==8.3.0.7
|
|
14
|
+
Requires-Dist: attrs==25.4.0
|
|
15
|
+
Requires-Dist: authlib==1.6.6
|
|
16
|
+
Requires-Dist: cachetools==6.2.3
|
|
17
|
+
Requires-Dist: certifi==2025.11.12
|
|
18
|
+
Requires-Dist: cffi==2.0.0 ; platform_python_implementation != 'PyPy'
|
|
19
|
+
Requires-Dist: charset-normalizer==3.4.4
|
|
20
|
+
Requires-Dist: click==8.2.1
|
|
21
|
+
Requires-Dist: colorama==0.4.6 ; sys_platform == 'win32'
|
|
22
|
+
Requires-Dist: cryptography==46.0.3
|
|
23
|
+
Requires-Dist: distro==1.9.0
|
|
24
|
+
Requires-Dist: faker==38.2.0
|
|
25
|
+
Requires-Dist: fastapi==0.124.4
|
|
26
|
+
Requires-Dist: gnureadline==8.2.13 ; sys_platform != 'win32'
|
|
27
|
+
Requires-Dist: google-api-core==2.28.1
|
|
28
|
+
Requires-Dist: google-auth==2.43.0
|
|
29
|
+
Requires-Dist: googleapis-common-protos==1.72.0
|
|
30
|
+
Requires-Dist: h11==0.16.0
|
|
31
|
+
Requires-Dist: httpcore==1.0.9
|
|
32
|
+
Requires-Dist: httpx==0.28.1
|
|
33
|
+
Requires-Dist: httpx-sse==0.4.3
|
|
34
|
+
Requires-Dist: idna==3.11
|
|
35
|
+
Requires-Dist: importlib-metadata==8.7.0
|
|
36
|
+
Requires-Dist: iniconfig==2.3.0
|
|
37
|
+
Requires-Dist: inquirerpy==0.3.4
|
|
38
|
+
Requires-Dist: janus==2.0.0
|
|
39
|
+
Requires-Dist: jiter==0.12.0
|
|
40
|
+
Requires-Dist: jsf==0.11.2
|
|
41
|
+
Requires-Dist: jsonschema==4.25.1
|
|
42
|
+
Requires-Dist: jsonschema-specifications==2025.9.1
|
|
43
|
+
Requires-Dist: markdown-it-py==4.0.0
|
|
44
|
+
Requires-Dist: mcp==1.24.0
|
|
45
|
+
Requires-Dist: mdurl==0.1.2
|
|
46
|
+
Requires-Dist: nodeenv==1.9.1
|
|
47
|
+
Requires-Dist: objprint==0.3.0
|
|
48
|
+
Requires-Dist: openai==1.107.3
|
|
49
|
+
Requires-Dist: opentelemetry-api==1.39.1
|
|
50
|
+
Requires-Dist: opentelemetry-exporter-otlp-proto-common==1.39.1
|
|
51
|
+
Requires-Dist: opentelemetry-exporter-otlp-proto-http==1.39.1
|
|
52
|
+
Requires-Dist: opentelemetry-instrumentation==0.60b1
|
|
53
|
+
Requires-Dist: opentelemetry-instrumentation-asgi==0.60b1
|
|
54
|
+
Requires-Dist: opentelemetry-instrumentation-fastapi==0.60b1
|
|
55
|
+
Requires-Dist: opentelemetry-proto==1.39.1
|
|
56
|
+
Requires-Dist: opentelemetry-sdk==1.39.1
|
|
57
|
+
Requires-Dist: opentelemetry-semantic-conventions==0.60b1
|
|
58
|
+
Requires-Dist: opentelemetry-util-http==0.60b1
|
|
59
|
+
Requires-Dist: packaging==25.0
|
|
60
|
+
Requires-Dist: pfzy==0.3.4
|
|
61
|
+
Requires-Dist: pluggy==1.6.0
|
|
62
|
+
Requires-Dist: prompt-toolkit==3.0.52
|
|
63
|
+
Requires-Dist: proto-plus==1.26.1
|
|
64
|
+
Requires-Dist: protobuf==6.33.2
|
|
65
|
+
Requires-Dist: psutil==7.0.0
|
|
66
|
+
Requires-Dist: pyasn1==0.6.1
|
|
67
|
+
Requires-Dist: pyasn1-modules==0.4.2
|
|
68
|
+
Requires-Dist: pycparser==2.23 ; implementation_name != 'PyPy' and platform_python_implementation != 'PyPy'
|
|
69
|
+
Requires-Dist: pydantic==2.11.10
|
|
70
|
+
Requires-Dist: pydantic-core==2.33.2
|
|
71
|
+
Requires-Dist: pydantic-settings==2.10.1
|
|
72
|
+
Requires-Dist: pygments==2.19.2
|
|
73
|
+
Requires-Dist: pyjwt==2.10.1
|
|
74
|
+
Requires-Dist: pyright==1.1.407
|
|
75
|
+
Requires-Dist: pytest==9.0.2
|
|
76
|
+
Requires-Dist: python-dotenv==1.2.1
|
|
77
|
+
Requires-Dist: python-multipart==0.0.20
|
|
78
|
+
Requires-Dist: pywin32==311 ; sys_platform == 'win32'
|
|
79
|
+
Requires-Dist: pyyaml==6.0.3
|
|
80
|
+
Requires-Dist: referencing==0.37.0
|
|
81
|
+
Requires-Dist: requests==2.32.5
|
|
82
|
+
Requires-Dist: rich==14.2.0
|
|
83
|
+
Requires-Dist: rpds-py==0.30.0
|
|
84
|
+
Requires-Dist: rsa==4.9.1
|
|
85
|
+
Requires-Dist: rstr==3.2.2
|
|
86
|
+
Requires-Dist: ruff==0.14.9
|
|
87
|
+
Requires-Dist: shellingham==1.5.4
|
|
88
|
+
Requires-Dist: smart-open==7.5.0
|
|
89
|
+
Requires-Dist: sniffio==1.3.1
|
|
90
|
+
Requires-Dist: sse-starlette==3.0.4
|
|
91
|
+
Requires-Dist: starlette==0.50.0
|
|
92
|
+
Requires-Dist: tenacity==9.1.2
|
|
93
|
+
Requires-Dist: tqdm==4.67.1
|
|
94
|
+
Requires-Dist: typer==0.17.5
|
|
95
|
+
Requires-Dist: typing-extensions==4.15.0
|
|
96
|
+
Requires-Dist: typing-inspection==0.4.2
|
|
97
|
+
Requires-Dist: tzdata==2025.3
|
|
98
|
+
Requires-Dist: urllib3==2.6.2
|
|
99
|
+
Requires-Dist: uvicorn==0.38.0
|
|
100
|
+
Requires-Dist: wcwidth==0.2.14
|
|
101
|
+
Requires-Dist: wheel==0.45.1
|
|
102
|
+
Requires-Dist: wrapt==1.17.3
|
|
103
|
+
Requires-Dist: zipp==3.23.0
|
|
104
|
+
Requires-Python: >=3.13, <3.14
|
|
105
|
+
Description-Content-Type: text/markdown
|
|
106
|
+
|
|
107
|
+
# Agent Stack CLI
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
agentstack_cli/__init__.py,sha256=VML7VgV0JvGJ4xDnQ-zPulWQKXZWe56Qd19Hp7moq-8,7019
|
|
2
|
+
agentstack_cli/api.py,sha256=3bjdlSp2vaEWv94Aa5z_s0nW1euFP_etQ2bWZ4G7Gdg,5619
|
|
3
|
+
agentstack_cli/async_typer.py,sha256=mTc1B6fMd4EAT60nj5KuJMuIKEfRGRbPHMFo_7fcVt0,4354
|
|
4
|
+
agentstack_cli/auth_manager.py,sha256=kr4tIVGW-fUROU3oJT1l2B1bb1spRbL4iiSrh7u4ZIo,10076
|
|
5
|
+
agentstack_cli/configuration.py,sha256=Na0lkoLw2RlfORb3x8QwHRfU8V3plPHlJF5NTTRj4mk,2734
|
|
6
|
+
agentstack_cli/console.py,sha256=x7YdbrUtc6gnFMViI8gEVLjF2ns7EKNybanW3h8Q1qU,710
|
|
7
|
+
agentstack_cli/utils.py,sha256=Cevvx7SAb-f07g_sfz8EXZoGnhodaLVUR20Tc4WHr0s,14187
|
|
8
|
+
agentstack_cli/commands/__init__.py,sha256=jr8otByt8HFY9OWptuWdpq4WHE7A1srCSRXZV066FrI,94
|
|
9
|
+
agentstack_cli/commands/agent.py,sha256=6wSPKvM3SYpJRkMpjl8iwpGaWCqtbRA8y-YaHVi_QZE,57685
|
|
10
|
+
agentstack_cli/commands/build.py,sha256=7oRwd_2-vpUnKHvwykPHqqMBMyHGCZ99lIRdg1yDPVE,9002
|
|
11
|
+
agentstack_cli/commands/connector.py,sha256=GnY0MeHnA53PlIti-iIibsIw6caOXyTgs8DVo1LZX6M,12497
|
|
12
|
+
agentstack_cli/commands/model.py,sha256=9htp0J03u4Pg9qroxWAT-RF5ADPMpm264nzIhg13tso,30695
|
|
13
|
+
agentstack_cli/commands/self.py,sha256=1zX9Qu0ma146LUiHWrNOXXIj32i44Cvi9O030PiGCXg,9363
|
|
14
|
+
agentstack_cli/commands/server.py,sha256=IXjCWEKPqnwqQNVvEwBOFITEJ79WpcW1RSBp32sd88s,12157
|
|
15
|
+
agentstack_cli/commands/user.py,sha256=6Hr_UIybDMJgM4QN73zM4J6mP_4BZnW_C2Neu0f2yPE,2893
|
|
16
|
+
agentstack_cli/commands/platform/__init__.py,sha256=rzWgMds-aDA2wCxw3lEG4KywDpW1Wrb6AgLY8zYtM8M,8970
|
|
17
|
+
agentstack_cli/commands/platform/base_driver.py,sha256=oyfqCjL5xsD9gzZ9QhF3U7MPF9RECDplg0CRIFUwjt8,7725
|
|
18
|
+
agentstack_cli/commands/platform/lima_driver.py,sha256=a30flm1EEbsCPbzMugGHjShnljqS1kBu9aspJDY3SgY,11020
|
|
19
|
+
agentstack_cli/commands/platform/wsl_driver.py,sha256=wbQaf9wyuVsXJfqTKddbcgB3KeRSZrO-kwSMi7DUyxE,9422
|
|
20
|
+
agentstack_cli/data/.gitignore,sha256=W0urBl_DVtzJyKHGXo53wMVzMPJseiK9RMn46No6i70,14
|
|
21
|
+
agentstack_cli/data/helm-chart.tgz,sha256=od7mjrLX7pa0pE6FBlHbdcdrIDNY1vhT2po9UW9IfOs,316477
|
|
22
|
+
agentstack_cli/data/lima-guestagent.Linux-aarch64.gz,sha256=d0ETQF6s9EH6amZLM-FKytcM6gvEfDA25vGoU-zxLAY,12640740
|
|
23
|
+
agentstack_cli/data/limactl,sha256=lWiYtwyaPVSjzPzxQ0tMdz016ZTsKavDV7SN-c5X7mc,28836132
|
|
24
|
+
agentstack_cli-0.6.0rc1.dist-info/METADATA,sha256=_nGoPQUw9g7ezoNYHn62EDXc3GbyKx1141cZLcy0eTg,3766
|
|
25
|
+
agentstack_cli-0.6.0rc1.dist-info/WHEEL,sha256=kCR43x5YbIrhlM9KrTJUZPltQ9TUdx4xhfEber8zsU4,98
|
|
26
|
+
agentstack_cli-0.6.0rc1.dist-info/entry_points.txt,sha256=g3RAenJ90a0mLfx8x-ETzsr3uEdFnvuhTX3Amy1AUbA,87
|
|
27
|
+
agentstack_cli-0.6.0rc1.dist-info/RECORD,,
|