llamactl 0.3.13__tar.gz → 0.3.14__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.
- {llamactl-0.3.13 → llamactl-0.3.14}/PKG-INFO +5 -5
- {llamactl-0.3.13 → llamactl-0.3.14}/pyproject.toml +5 -5
- {llamactl-0.3.13 → llamactl-0.3.14}/src/llama_deploy/cli/commands/deployment.py +127 -4
- {llamactl-0.3.13 → llamactl-0.3.14}/src/llama_deploy/cli/commands/init.py +24 -47
- {llamactl-0.3.13 → llamactl-0.3.14}/README.md +0 -0
- {llamactl-0.3.13 → llamactl-0.3.14}/src/llama_deploy/cli/__init__.py +0 -0
- {llamactl-0.3.13 → llamactl-0.3.14}/src/llama_deploy/cli/app.py +0 -0
- {llamactl-0.3.13 → llamactl-0.3.14}/src/llama_deploy/cli/auth/client.py +0 -0
- {llamactl-0.3.13 → llamactl-0.3.14}/src/llama_deploy/cli/client.py +0 -0
- {llamactl-0.3.13 → llamactl-0.3.14}/src/llama_deploy/cli/commands/aliased_group.py +0 -0
- {llamactl-0.3.13 → llamactl-0.3.14}/src/llama_deploy/cli/commands/auth.py +0 -0
- {llamactl-0.3.13 → llamactl-0.3.14}/src/llama_deploy/cli/commands/env.py +0 -0
- {llamactl-0.3.13 → llamactl-0.3.14}/src/llama_deploy/cli/commands/serve.py +0 -0
- {llamactl-0.3.13 → llamactl-0.3.14}/src/llama_deploy/cli/config/_config.py +0 -0
- {llamactl-0.3.13 → llamactl-0.3.14}/src/llama_deploy/cli/config/_migrations.py +0 -0
- {llamactl-0.3.13 → llamactl-0.3.14}/src/llama_deploy/cli/config/auth_service.py +0 -0
- {llamactl-0.3.13 → llamactl-0.3.14}/src/llama_deploy/cli/config/env_service.py +0 -0
- {llamactl-0.3.13 → llamactl-0.3.14}/src/llama_deploy/cli/config/migrations/0001_init.sql +0 -0
- {llamactl-0.3.13 → llamactl-0.3.14}/src/llama_deploy/cli/config/migrations/0002_add_auth_fields.sql +0 -0
- {llamactl-0.3.13 → llamactl-0.3.14}/src/llama_deploy/cli/config/migrations/__init__.py +0 -0
- {llamactl-0.3.13 → llamactl-0.3.14}/src/llama_deploy/cli/config/schema.py +0 -0
- {llamactl-0.3.13 → llamactl-0.3.14}/src/llama_deploy/cli/debug.py +0 -0
- {llamactl-0.3.13 → llamactl-0.3.14}/src/llama_deploy/cli/env.py +0 -0
- {llamactl-0.3.13 → llamactl-0.3.14}/src/llama_deploy/cli/interactive_prompts/session_utils.py +0 -0
- {llamactl-0.3.13 → llamactl-0.3.14}/src/llama_deploy/cli/interactive_prompts/utils.py +0 -0
- {llamactl-0.3.13 → llamactl-0.3.14}/src/llama_deploy/cli/options.py +0 -0
- {llamactl-0.3.13 → llamactl-0.3.14}/src/llama_deploy/cli/py.typed +0 -0
- {llamactl-0.3.13 → llamactl-0.3.14}/src/llama_deploy/cli/styles.py +0 -0
- {llamactl-0.3.13 → llamactl-0.3.14}/src/llama_deploy/cli/textual/deployment_form.py +0 -0
- {llamactl-0.3.13 → llamactl-0.3.14}/src/llama_deploy/cli/textual/deployment_help.py +0 -0
- {llamactl-0.3.13 → llamactl-0.3.14}/src/llama_deploy/cli/textual/deployment_monitor.py +0 -0
- {llamactl-0.3.13 → llamactl-0.3.14}/src/llama_deploy/cli/textual/git_validation.py +0 -0
- {llamactl-0.3.13 → llamactl-0.3.14}/src/llama_deploy/cli/textual/github_callback_server.py +0 -0
- {llamactl-0.3.13 → llamactl-0.3.14}/src/llama_deploy/cli/textual/llama_loader.py +0 -0
- {llamactl-0.3.13 → llamactl-0.3.14}/src/llama_deploy/cli/textual/secrets_form.py +0 -0
- {llamactl-0.3.13 → llamactl-0.3.14}/src/llama_deploy/cli/textual/styles.tcss +0 -0
- {llamactl-0.3.13 → llamactl-0.3.14}/src/llama_deploy/cli/utils/env_inject.py +0 -0
- {llamactl-0.3.13 → llamactl-0.3.14}/src/llama_deploy/cli/utils/redact.py +0 -0
- {llamactl-0.3.13 → llamactl-0.3.14}/src/llama_deploy/cli/utils/version.py +0 -0
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: llamactl
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.14
|
|
4
4
|
Summary: A command-line interface for managing LlamaDeploy projects and deployments
|
|
5
5
|
Author: Adrian Lyjak
|
|
6
6
|
Author-email: Adrian Lyjak <adrianlyjak@gmail.com>
|
|
7
7
|
License: MIT
|
|
8
|
-
Requires-Dist: llama-deploy-core[client]>=0.3.
|
|
9
|
-
Requires-Dist: llama-deploy-appserver>=0.3.
|
|
10
|
-
Requires-Dist:
|
|
8
|
+
Requires-Dist: llama-deploy-core[client]>=0.3.14,<0.4.0
|
|
9
|
+
Requires-Dist: llama-deploy-appserver>=0.3.14,<0.4.0
|
|
10
|
+
Requires-Dist: vibe-llama-core>=0.1.0
|
|
11
11
|
Requires-Dist: rich>=13.0.0
|
|
12
12
|
Requires-Dist: questionary>=2.0.0
|
|
13
13
|
Requires-Dist: click>=8.2.1
|
|
@@ -15,7 +15,7 @@ Requires-Dist: python-dotenv>=1.0.0
|
|
|
15
15
|
Requires-Dist: tenacity>=9.1.2
|
|
16
16
|
Requires-Dist: textual>=6.0.0
|
|
17
17
|
Requires-Dist: aiohttp>=3.12.14
|
|
18
|
-
Requires-Dist: copier>=9.
|
|
18
|
+
Requires-Dist: copier>=9.10.2
|
|
19
19
|
Requires-Dist: pyjwt[crypto]>=2.10.1
|
|
20
20
|
Requires-Python: >=3.11, <4
|
|
21
21
|
Description-Content-Type: text/markdown
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "llamactl"
|
|
3
|
-
version = "0.3.
|
|
3
|
+
version = "0.3.14"
|
|
4
4
|
description = "A command-line interface for managing LlamaDeploy projects and deployments"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = { text = "MIT" }
|
|
@@ -9,9 +9,9 @@ authors = [
|
|
|
9
9
|
]
|
|
10
10
|
requires-python = ">=3.11, <4"
|
|
11
11
|
dependencies = [
|
|
12
|
-
"llama-deploy-core[client]>=0.3.
|
|
13
|
-
"llama-deploy-appserver>=0.3.
|
|
14
|
-
"
|
|
12
|
+
"llama-deploy-core[client]>=0.3.14,<0.4.0",
|
|
13
|
+
"llama-deploy-appserver>=0.3.14,<0.4.0",
|
|
14
|
+
"vibe-llama-core>=0.1.0",
|
|
15
15
|
"rich>=13.0.0",
|
|
16
16
|
"questionary>=2.0.0",
|
|
17
17
|
"click>=8.2.1",
|
|
@@ -19,7 +19,7 @@ dependencies = [
|
|
|
19
19
|
"tenacity>=9.1.2",
|
|
20
20
|
"textual>=6.0.0",
|
|
21
21
|
"aiohttp>=3.12.14",
|
|
22
|
-
"copier>=9.
|
|
22
|
+
"copier>=9.10.2",
|
|
23
23
|
"pyjwt[crypto]>=2.10.1",
|
|
24
24
|
]
|
|
25
25
|
|
|
@@ -12,13 +12,17 @@ import click
|
|
|
12
12
|
import questionary
|
|
13
13
|
from llama_deploy.cli.commands.auth import validate_authenticated_profile
|
|
14
14
|
from llama_deploy.cli.styles import HEADER_COLOR, MUTED_COL, PRIMARY_COL, WARNING
|
|
15
|
-
from llama_deploy.core.schema.deployments import
|
|
15
|
+
from llama_deploy.core.schema.deployments import (
|
|
16
|
+
DeploymentHistoryResponse,
|
|
17
|
+
DeploymentResponse,
|
|
18
|
+
DeploymentUpdate,
|
|
19
|
+
)
|
|
16
20
|
from rich import print as rprint
|
|
17
21
|
from rich.table import Table
|
|
18
22
|
from rich.text import Text
|
|
19
23
|
|
|
20
24
|
from ..app import app, console
|
|
21
|
-
from ..client import get_project_client
|
|
25
|
+
from ..client import get_project_client, project_client_context
|
|
22
26
|
from ..interactive_prompts.session_utils import (
|
|
23
27
|
is_interactive_session,
|
|
24
28
|
)
|
|
@@ -221,8 +225,15 @@ def edit_deployment(deployment_id: str | None, interactive: bool) -> None:
|
|
|
221
225
|
@deployments.command("update")
|
|
222
226
|
@global_options
|
|
223
227
|
@click.argument("deployment_id", required=False)
|
|
228
|
+
@click.option(
|
|
229
|
+
"--git-ref",
|
|
230
|
+
help="Reference branch or commit SHA for the deployment. If not provided, the current reference branch and latest commit on it will be used.",
|
|
231
|
+
default=None,
|
|
232
|
+
)
|
|
224
233
|
@interactive_option
|
|
225
|
-
def refresh_deployment(
|
|
234
|
+
def refresh_deployment(
|
|
235
|
+
deployment_id: str | None, git_ref: str | None, interactive: bool
|
|
236
|
+
) -> None:
|
|
226
237
|
"""Update the deployment, pulling the latest code from it's branch"""
|
|
227
238
|
validate_authenticated_profile(interactive)
|
|
228
239
|
try:
|
|
@@ -240,7 +251,9 @@ def refresh_deployment(deployment_id: str | None, interactive: bool) -> None:
|
|
|
240
251
|
|
|
241
252
|
# Create an empty update to force git SHA refresh with spinner
|
|
242
253
|
with console.status(f"Refreshing {deployment_name}..."):
|
|
243
|
-
deployment_update = DeploymentUpdate(
|
|
254
|
+
deployment_update = DeploymentUpdate(
|
|
255
|
+
git_ref=git_ref,
|
|
256
|
+
)
|
|
244
257
|
updated_deployment = asyncio.run(
|
|
245
258
|
get_project_client().update_deployment(
|
|
246
259
|
deployment_id,
|
|
@@ -263,6 +276,116 @@ def refresh_deployment(deployment_id: str | None, interactive: bool) -> None:
|
|
|
263
276
|
raise click.Abort()
|
|
264
277
|
|
|
265
278
|
|
|
279
|
+
@deployments.command("history")
|
|
280
|
+
@global_options
|
|
281
|
+
@click.argument("deployment_id", required=False)
|
|
282
|
+
@interactive_option
|
|
283
|
+
def show_history(deployment_id: str | None, interactive: bool) -> None:
|
|
284
|
+
"""Show release history for a deployment."""
|
|
285
|
+
validate_authenticated_profile(interactive)
|
|
286
|
+
try:
|
|
287
|
+
deployment_id = select_deployment(deployment_id, interactive=interactive)
|
|
288
|
+
if not deployment_id:
|
|
289
|
+
rprint(f"[{WARNING}]No deployment selected[/]")
|
|
290
|
+
return
|
|
291
|
+
|
|
292
|
+
async def _fetch_history() -> DeploymentHistoryResponse:
|
|
293
|
+
async with project_client_context() as client:
|
|
294
|
+
return await client.get_deployment_history(deployment_id)
|
|
295
|
+
|
|
296
|
+
history = asyncio.run(_fetch_history())
|
|
297
|
+
items = history.history
|
|
298
|
+
if not items:
|
|
299
|
+
rprint(f"No history recorded for {deployment_id}")
|
|
300
|
+
return
|
|
301
|
+
|
|
302
|
+
table = Table(show_edge=False, box=None, header_style=f"bold {HEADER_COLOR}")
|
|
303
|
+
table.add_column("Released At", style=MUTED_COL)
|
|
304
|
+
table.add_column("Git SHA", style=PRIMARY_COL)
|
|
305
|
+
# newest first
|
|
306
|
+
items_sorted = sorted(
|
|
307
|
+
items,
|
|
308
|
+
key=lambda it: it.released_at,
|
|
309
|
+
reverse=True,
|
|
310
|
+
)
|
|
311
|
+
for item in items_sorted:
|
|
312
|
+
ts = item.released_at.isoformat()
|
|
313
|
+
sha = item.git_sha
|
|
314
|
+
table.add_row(ts, sha)
|
|
315
|
+
console.print(table)
|
|
316
|
+
except Exception as e:
|
|
317
|
+
rprint(f"[red]Error: {e}[/red]")
|
|
318
|
+
raise click.Abort()
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
@deployments.command("rollback", hidden=True)
|
|
322
|
+
@global_options
|
|
323
|
+
@click.argument("deployment_id", required=False)
|
|
324
|
+
@click.option("--git-sha", required=False, help="Git SHA to roll back to")
|
|
325
|
+
@interactive_option
|
|
326
|
+
def rollback(deployment_id: str | None, git_sha: str | None, interactive: bool) -> None:
|
|
327
|
+
"""Rollback a deployment to a previous git sha."""
|
|
328
|
+
validate_authenticated_profile(interactive)
|
|
329
|
+
try:
|
|
330
|
+
deployment_id = select_deployment(deployment_id, interactive=interactive)
|
|
331
|
+
if not deployment_id:
|
|
332
|
+
rprint(f"[{WARNING}]No deployment selected[/]")
|
|
333
|
+
return
|
|
334
|
+
|
|
335
|
+
if not git_sha:
|
|
336
|
+
# If not provided, prompt from history
|
|
337
|
+
async def _fetch_current_and_history() -> tuple[
|
|
338
|
+
DeploymentResponse, DeploymentHistoryResponse
|
|
339
|
+
]:
|
|
340
|
+
async with project_client_context() as client:
|
|
341
|
+
current = await client.get_deployment(deployment_id)
|
|
342
|
+
hist = await client.get_deployment_history(deployment_id)
|
|
343
|
+
return current, hist
|
|
344
|
+
|
|
345
|
+
current_deployment, history = asyncio.run(_fetch_current_and_history())
|
|
346
|
+
current_sha = current_deployment.git_sha or ""
|
|
347
|
+
|
|
348
|
+
items = history.history or []
|
|
349
|
+
# Sort newest first
|
|
350
|
+
items_sorted = sorted(items, key=lambda it: it.released_at, reverse=True)
|
|
351
|
+
choices = []
|
|
352
|
+
for it in items_sorted:
|
|
353
|
+
short = it.git_sha[:7]
|
|
354
|
+
suffix = (
|
|
355
|
+
" [current]" if current_sha and it.git_sha == current_sha else ""
|
|
356
|
+
)
|
|
357
|
+
choices.append(
|
|
358
|
+
questionary.Choice(
|
|
359
|
+
title=f"{short}{suffix} ({it.released_at})", value=it.git_sha
|
|
360
|
+
)
|
|
361
|
+
)
|
|
362
|
+
if not choices:
|
|
363
|
+
rprint(f"[{WARNING}]No history available to rollback[/]")
|
|
364
|
+
return
|
|
365
|
+
git_sha = questionary.select("Select git sha:", choices=choices).ask()
|
|
366
|
+
if not git_sha:
|
|
367
|
+
rprint(f"[{WARNING}]Cancelled[/]")
|
|
368
|
+
return
|
|
369
|
+
|
|
370
|
+
if interactive and not confirm_action(
|
|
371
|
+
f"Rollback '{deployment_id}' to {git_sha[:7]}?"
|
|
372
|
+
):
|
|
373
|
+
rprint(f"[{WARNING}]Cancelled[/]")
|
|
374
|
+
return
|
|
375
|
+
|
|
376
|
+
async def _do_rollback() -> DeploymentResponse:
|
|
377
|
+
async with project_client_context() as client:
|
|
378
|
+
return await client.rollback_deployment(deployment_id, git_sha)
|
|
379
|
+
|
|
380
|
+
updated = asyncio.run(_do_rollback())
|
|
381
|
+
rprint(
|
|
382
|
+
f"[green]Rollback initiated[/green]: {deployment_id} → {updated.git_sha[:7] if updated.git_sha else 'unknown'}"
|
|
383
|
+
)
|
|
384
|
+
except Exception as e:
|
|
385
|
+
rprint(f"[red]Error: {e}[/red]")
|
|
386
|
+
raise click.Abort()
|
|
387
|
+
|
|
388
|
+
|
|
266
389
|
def select_deployment(
|
|
267
390
|
deployment_id: str | None = None, interactive: bool = is_interactive_session()
|
|
268
391
|
) -> str | None:
|
|
@@ -9,7 +9,6 @@ from pathlib import Path
|
|
|
9
9
|
|
|
10
10
|
import click
|
|
11
11
|
import copier
|
|
12
|
-
import httpx
|
|
13
12
|
import questionary
|
|
14
13
|
from click.exceptions import Exit
|
|
15
14
|
from llama_deploy.cli.app import app
|
|
@@ -18,9 +17,9 @@ from llama_deploy.cli.options import (
|
|
|
18
17
|
interactive_option,
|
|
19
18
|
)
|
|
20
19
|
from llama_deploy.cli.styles import HEADER_COLOR_HEX
|
|
21
|
-
from llama_deploy.core.client.ssl_util import get_httpx_verify_param
|
|
22
20
|
from rich import print as rprint
|
|
23
21
|
from rich.text import Text
|
|
22
|
+
from vibe_llama_core.docs import get_agent_rules
|
|
24
23
|
|
|
25
24
|
|
|
26
25
|
@app.command()
|
|
@@ -295,14 +294,16 @@ def _create(
|
|
|
295
294
|
# Extract a short error message if present
|
|
296
295
|
err_msg = ""
|
|
297
296
|
if isinstance(e, subprocess.CalledProcessError):
|
|
298
|
-
|
|
299
|
-
if isinstance(
|
|
297
|
+
stderr_bytes = e.stderr or b""
|
|
298
|
+
if isinstance(stderr_bytes, (bytes, bytearray)):
|
|
300
299
|
try:
|
|
301
|
-
|
|
300
|
+
stderr_text = stderr_bytes.decode("utf-8", "ignore")
|
|
302
301
|
except Exception:
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
302
|
+
stderr_text = ""
|
|
303
|
+
else:
|
|
304
|
+
stderr_text = str(stderr_bytes)
|
|
305
|
+
if stderr_text.strip():
|
|
306
|
+
err_msg = stderr_text.strip().split("\n")[-1]
|
|
306
307
|
elif isinstance(e, FileNotFoundError):
|
|
307
308
|
err_msg = "git executable not found"
|
|
308
309
|
|
|
@@ -406,14 +407,6 @@ async def _download_and_write_agents_md(include_llama_cloud: bool) -> bool:
|
|
|
406
407
|
|
|
407
408
|
Returns True if any documentation was fetched, False otherwise.
|
|
408
409
|
"""
|
|
409
|
-
BASE_URL = "https://raw.githubusercontent.com/run-llama/vibe-llama/main"
|
|
410
|
-
|
|
411
|
-
services: dict[str, str] = {
|
|
412
|
-
"LlamaIndex": f"{BASE_URL}/documentation/llamaindex.md",
|
|
413
|
-
"LlamaCloud Services": f"{BASE_URL}/documentation/llamacloud.md",
|
|
414
|
-
"llama-index-workflows": f"{BASE_URL}/documentation/llama-index-workflows.md",
|
|
415
|
-
"LlamaDeploy": f"{BASE_URL}/documentation/llamadeploy.md",
|
|
416
|
-
}
|
|
417
410
|
|
|
418
411
|
selected_services: list[str] = [
|
|
419
412
|
"LlamaDeploy",
|
|
@@ -423,35 +416,19 @@ async def _download_and_write_agents_md(include_llama_cloud: bool) -> bool:
|
|
|
423
416
|
if include_llama_cloud:
|
|
424
417
|
selected_services.append("LlamaCloud Services")
|
|
425
418
|
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
return text
|
|
441
|
-
except Exception:
|
|
442
|
-
# best-effort: skip failures
|
|
443
|
-
rprint(
|
|
444
|
-
f"[yellow]Failed to fetch documentation for {service}, skipping[/]"
|
|
445
|
-
)
|
|
446
|
-
return None
|
|
447
|
-
|
|
448
|
-
results = await asyncio.gather(
|
|
449
|
-
*[get_docs(service, url) for service, url in urls]
|
|
450
|
-
)
|
|
451
|
-
contents = [r for r in results if r is not None]
|
|
452
|
-
|
|
453
|
-
if contents:
|
|
454
|
-
agents_md = "\n\n---\n\n".join(contents) + "\n"
|
|
455
|
-
Path("AGENTS.md").write_text(agents_md, encoding="utf-8")
|
|
419
|
+
downloads = 0
|
|
420
|
+
|
|
421
|
+
for service in selected_services:
|
|
422
|
+
try:
|
|
423
|
+
await get_agent_rules(
|
|
424
|
+
agent="OpenAI Codex CLI",
|
|
425
|
+
service=service,
|
|
426
|
+
overwrite_files=False,
|
|
427
|
+
verbose=False,
|
|
428
|
+
) # type: ignore
|
|
429
|
+
except Exception:
|
|
430
|
+
rprint(f"[yellow]Failed to fetch documentation for {service}, skipping[/]")
|
|
431
|
+
else:
|
|
432
|
+
downloads += 1
|
|
456
433
|
|
|
457
|
-
return
|
|
434
|
+
return downloads > 0
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{llamactl-0.3.13 → llamactl-0.3.14}/src/llama_deploy/cli/config/migrations/0002_add_auth_fields.sql
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{llamactl-0.3.13 → llamactl-0.3.14}/src/llama_deploy/cli/interactive_prompts/session_utils.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|