agentstack-cli 0.6.0rc1__py3-none-manylinux_2_34_x86_64.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.
@@ -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()
@@ -0,0 +1,2 @@
1
+ **
2
+ !.gitignore
Binary file
Binary file
@@ -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-x86_64.gz,sha256=oTxE-irob08yCM0tUKPvA-3VWZE-J_fNRBctQPjBkGA,14063398
23
+ agentstack_cli/data/limactl,sha256=dyapITv2qRIwD1rNURF80qML_q5iuzps18-is1GHst4,30064932
24
+ agentstack_cli-0.6.0rc1.dist-info/METADATA,sha256=_nGoPQUw9g7ezoNYHn62EDXc3GbyKx1141cZLcy0eTg,3766
25
+ agentstack_cli-0.6.0rc1.dist-info/WHEEL,sha256=z33Seh25UGKhBe_ujaMmiVT8LE9_gL3TcnPOPJ3BWlQ,97
26
+ agentstack_cli-0.6.0rc1.dist-info/entry_points.txt,sha256=g3RAenJ90a0mLfx8x-ETzsr3uEdFnvuhTX3Amy1AUbA,87
27
+ agentstack_cli-0.6.0rc1.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: uv 0.9.5
3
+ Root-Is-Purelib: false
4
+ Tag: py3-none-manylinux_2_34_x86_64
@@ -0,0 +1,4 @@
1
+ [console_scripts]
2
+ agentstack = agentstack_cli:app
3
+ agentstack-cli = agentstack_cli:app
4
+