llamactl 0.3.19__tar.gz → 0.3.21__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.19 → llamactl-0.3.21}/PKG-INFO +3 -3
- {llamactl-0.3.19 → llamactl-0.3.21}/pyproject.toml +3 -3
- {llamactl-0.3.19 → llamactl-0.3.21}/src/llama_deploy/cli/__init__.py +11 -1
- {llamactl-0.3.19 → llamactl-0.3.21}/src/llama_deploy/cli/commands/deployment.py +1 -0
- llamactl-0.3.21/src/llama_deploy/cli/commands/dev.py +176 -0
- {llamactl-0.3.19 → llamactl-0.3.21}/src/llama_deploy/cli/commands/init.py +52 -29
- {llamactl-0.3.19 → llamactl-0.3.21}/src/llama_deploy/cli/textual/deployment_form.py +11 -0
- {llamactl-0.3.19 → llamactl-0.3.21}/src/llama_deploy/cli/textual/deployment_monitor.py +31 -5
- {llamactl-0.3.19 → llamactl-0.3.21}/README.md +0 -0
- {llamactl-0.3.19 → llamactl-0.3.21}/src/llama_deploy/cli/app.py +0 -0
- {llamactl-0.3.19 → llamactl-0.3.21}/src/llama_deploy/cli/auth/client.py +0 -0
- {llamactl-0.3.19 → llamactl-0.3.21}/src/llama_deploy/cli/client.py +0 -0
- {llamactl-0.3.19 → llamactl-0.3.21}/src/llama_deploy/cli/commands/aliased_group.py +0 -0
- {llamactl-0.3.19 → llamactl-0.3.21}/src/llama_deploy/cli/commands/auth.py +0 -0
- {llamactl-0.3.19 → llamactl-0.3.21}/src/llama_deploy/cli/commands/env.py +0 -0
- {llamactl-0.3.19 → llamactl-0.3.21}/src/llama_deploy/cli/commands/pkg.py +0 -0
- {llamactl-0.3.19 → llamactl-0.3.21}/src/llama_deploy/cli/commands/serve.py +0 -0
- {llamactl-0.3.19 → llamactl-0.3.21}/src/llama_deploy/cli/config/_config.py +0 -0
- {llamactl-0.3.19 → llamactl-0.3.21}/src/llama_deploy/cli/config/_migrations.py +0 -0
- {llamactl-0.3.19 → llamactl-0.3.21}/src/llama_deploy/cli/config/auth_service.py +0 -0
- {llamactl-0.3.19 → llamactl-0.3.21}/src/llama_deploy/cli/config/env_service.py +0 -0
- {llamactl-0.3.19 → llamactl-0.3.21}/src/llama_deploy/cli/config/migrations/0001_init.sql +0 -0
- {llamactl-0.3.19 → llamactl-0.3.21}/src/llama_deploy/cli/config/migrations/0002_add_auth_fields.sql +0 -0
- {llamactl-0.3.19 → llamactl-0.3.21}/src/llama_deploy/cli/config/migrations/__init__.py +0 -0
- {llamactl-0.3.19 → llamactl-0.3.21}/src/llama_deploy/cli/config/schema.py +0 -0
- {llamactl-0.3.19 → llamactl-0.3.21}/src/llama_deploy/cli/debug.py +0 -0
- {llamactl-0.3.19 → llamactl-0.3.21}/src/llama_deploy/cli/env.py +0 -0
- {llamactl-0.3.19 → llamactl-0.3.21}/src/llama_deploy/cli/interactive_prompts/session_utils.py +0 -0
- {llamactl-0.3.19 → llamactl-0.3.21}/src/llama_deploy/cli/interactive_prompts/utils.py +0 -0
- {llamactl-0.3.19 → llamactl-0.3.21}/src/llama_deploy/cli/options.py +0 -0
- {llamactl-0.3.19 → llamactl-0.3.21}/src/llama_deploy/cli/pkg/__init__.py +0 -0
- {llamactl-0.3.19 → llamactl-0.3.21}/src/llama_deploy/cli/pkg/defaults.py +0 -0
- {llamactl-0.3.19 → llamactl-0.3.21}/src/llama_deploy/cli/pkg/options.py +0 -0
- {llamactl-0.3.19 → llamactl-0.3.21}/src/llama_deploy/cli/pkg/utils.py +0 -0
- {llamactl-0.3.19 → llamactl-0.3.21}/src/llama_deploy/cli/py.typed +0 -0
- {llamactl-0.3.19 → llamactl-0.3.21}/src/llama_deploy/cli/styles.py +0 -0
- {llamactl-0.3.19 → llamactl-0.3.21}/src/llama_deploy/cli/textual/deployment_help.py +0 -0
- {llamactl-0.3.19 → llamactl-0.3.21}/src/llama_deploy/cli/textual/git_validation.py +0 -0
- {llamactl-0.3.19 → llamactl-0.3.21}/src/llama_deploy/cli/textual/github_callback_server.py +0 -0
- {llamactl-0.3.19 → llamactl-0.3.21}/src/llama_deploy/cli/textual/llama_loader.py +0 -0
- {llamactl-0.3.19 → llamactl-0.3.21}/src/llama_deploy/cli/textual/secrets_form.py +0 -0
- {llamactl-0.3.19 → llamactl-0.3.21}/src/llama_deploy/cli/textual/styles.tcss +0 -0
- {llamactl-0.3.19 → llamactl-0.3.21}/src/llama_deploy/cli/utils/env_inject.py +0 -0
- {llamactl-0.3.19 → llamactl-0.3.21}/src/llama_deploy/cli/utils/redact.py +0 -0
- {llamactl-0.3.19 → llamactl-0.3.21}/src/llama_deploy/cli/utils/version.py +0 -0
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: llamactl
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.21
|
|
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.
|
|
8
|
+
Requires-Dist: llama-deploy-core[client]>=0.3.21,<0.4.0
|
|
9
|
+
Requires-Dist: llama-deploy-appserver>=0.3.21,<0.4.0
|
|
10
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
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "llamactl"
|
|
3
|
-
version = "0.3.
|
|
3
|
+
version = "0.3.21"
|
|
4
4
|
description = "A command-line interface for managing LlamaDeploy projects and deployments"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = { text = "MIT" }
|
|
@@ -9,8 +9,8 @@ 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.
|
|
12
|
+
"llama-deploy-core[client]>=0.3.21,<0.4.0",
|
|
13
|
+
"llama-deploy-appserver>=0.3.21,<0.4.0",
|
|
14
14
|
"vibe-llama-core>=0.1.0",
|
|
15
15
|
"rich>=13.0.0",
|
|
16
16
|
"questionary>=2.0.0",
|
|
@@ -2,6 +2,7 @@ import warnings
|
|
|
2
2
|
|
|
3
3
|
from llama_deploy.cli.commands.auth import auth
|
|
4
4
|
from llama_deploy.cli.commands.deployment import deployments
|
|
5
|
+
from llama_deploy.cli.commands.dev import dev
|
|
5
6
|
from llama_deploy.cli.commands.env import env_group
|
|
6
7
|
from llama_deploy.cli.commands.init import init
|
|
7
8
|
from llama_deploy.cli.commands.pkg import pkg
|
|
@@ -23,7 +24,16 @@ def main() -> None:
|
|
|
23
24
|
app()
|
|
24
25
|
|
|
25
26
|
|
|
26
|
-
__all__ = [
|
|
27
|
+
__all__ = [
|
|
28
|
+
"app",
|
|
29
|
+
"deployments",
|
|
30
|
+
"auth",
|
|
31
|
+
"serve",
|
|
32
|
+
"init",
|
|
33
|
+
"env_group",
|
|
34
|
+
"pkg",
|
|
35
|
+
"dev",
|
|
36
|
+
]
|
|
27
37
|
|
|
28
38
|
|
|
29
39
|
if __name__ == "__main__":
|
|
@@ -119,6 +119,7 @@ def get_deployment(deployment_id: str | None, interactive: bool) -> None:
|
|
|
119
119
|
table.add_row("Repository", Text(deployment.repo_url))
|
|
120
120
|
table.add_row("Deployment File", Text(deployment.deployment_file_path))
|
|
121
121
|
table.add_row("Git Ref", Text(deployment.git_ref or "-"))
|
|
122
|
+
table.add_row("Last Deployed Commit", Text((deployment.git_sha or "-")[:7]))
|
|
122
123
|
|
|
123
124
|
apiserver_url = deployment.apiserver_url
|
|
124
125
|
table.add_row(
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import subprocess
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
import click
|
|
8
|
+
from click.exceptions import Abort, Exit
|
|
9
|
+
from llama_deploy.appserver.app import prepare_server, start_preflight_in_target_venv
|
|
10
|
+
from llama_deploy.appserver.deployment_config_parser import get_deployment_config
|
|
11
|
+
from llama_deploy.appserver.settings import configure_settings, settings
|
|
12
|
+
from llama_deploy.appserver.workflow_loader import (
|
|
13
|
+
load_environment_variables,
|
|
14
|
+
parse_environment_variables,
|
|
15
|
+
validate_required_env_vars,
|
|
16
|
+
)
|
|
17
|
+
from llama_deploy.cli.commands.aliased_group import AliasedGroup
|
|
18
|
+
from llama_deploy.cli.commands.serve import (
|
|
19
|
+
_maybe_inject_llama_cloud_credentials,
|
|
20
|
+
_print_connection_summary,
|
|
21
|
+
)
|
|
22
|
+
from llama_deploy.cli.commands.serve import (
|
|
23
|
+
serve as serve_command,
|
|
24
|
+
)
|
|
25
|
+
from llama_deploy.cli.options import global_options, interactive_option
|
|
26
|
+
from llama_deploy.core.config import DEFAULT_DEPLOYMENT_FILE_PATH
|
|
27
|
+
from llama_deploy.core.deployment_config import DeploymentConfig
|
|
28
|
+
from rich import print as rprint
|
|
29
|
+
|
|
30
|
+
from ..app import app
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@app.group(
|
|
34
|
+
name="dev",
|
|
35
|
+
help="Development utilities for llama-deploy projects",
|
|
36
|
+
cls=AliasedGroup,
|
|
37
|
+
no_args_is_help=True,
|
|
38
|
+
)
|
|
39
|
+
@global_options
|
|
40
|
+
def dev() -> None:
|
|
41
|
+
"""Collection of development commands."""
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
dev.add_command(serve_command, name="serve")
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@dev.command(
|
|
48
|
+
"validate",
|
|
49
|
+
help="Load configured workflows and run their validation hooks",
|
|
50
|
+
)
|
|
51
|
+
@click.argument(
|
|
52
|
+
"deployment_file",
|
|
53
|
+
required=False,
|
|
54
|
+
default=DEFAULT_DEPLOYMENT_FILE_PATH,
|
|
55
|
+
type=click.Path(dir_okay=True, resolve_path=True, path_type=Path),
|
|
56
|
+
)
|
|
57
|
+
@interactive_option
|
|
58
|
+
@global_options
|
|
59
|
+
def validate_command(deployment_file: Path, interactive: bool) -> None:
|
|
60
|
+
"""Validate workflows defined in the deployment configuration."""
|
|
61
|
+
config_dir = _ensure_project_layout(
|
|
62
|
+
deployment_file, command_name="llamactl dev validate"
|
|
63
|
+
)
|
|
64
|
+
# Ensure cloud credentials/env are available to the subprocess (if required)
|
|
65
|
+
_maybe_inject_llama_cloud_credentials(
|
|
66
|
+
deployment_file, interactive, require_cloud=False
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
prepare_server(
|
|
70
|
+
deployment_file=deployment_file,
|
|
71
|
+
install=True,
|
|
72
|
+
build=False,
|
|
73
|
+
install_ui_deps=False,
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
# Delegate venv-targeted invocation to the appserver helper (mirrors start_server_in_target_venv)
|
|
77
|
+
|
|
78
|
+
try:
|
|
79
|
+
start_preflight_in_target_venv(cwd=Path.cwd(), deployment_file=deployment_file)
|
|
80
|
+
except subprocess.CalledProcessError as exc:
|
|
81
|
+
rprint("[red]Workflow validation failed. See errors above.[/red]")
|
|
82
|
+
raise Exit(exc.returncode)
|
|
83
|
+
|
|
84
|
+
_print_connection_summary()
|
|
85
|
+
rprint(f"[green]Validated workflows in {config_dir} successfully.[/green]")
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
@dev.command(
|
|
89
|
+
"run",
|
|
90
|
+
help=(
|
|
91
|
+
"Load env configuration and execute a command. Use '--' before the command "
|
|
92
|
+
"to avoid option parsing."
|
|
93
|
+
),
|
|
94
|
+
context_settings={"ignore_unknown_options": True, "allow_extra_args": True},
|
|
95
|
+
)
|
|
96
|
+
@global_options
|
|
97
|
+
@click.option(
|
|
98
|
+
"deployment_file",
|
|
99
|
+
"--deployment-file",
|
|
100
|
+
default=DEFAULT_DEPLOYMENT_FILE_PATH,
|
|
101
|
+
type=click.Path(dir_okay=True, resolve_path=True, path_type=Path),
|
|
102
|
+
help="The deployment file to use for the command",
|
|
103
|
+
)
|
|
104
|
+
@click.option(
|
|
105
|
+
"no_auth",
|
|
106
|
+
"--no-auth",
|
|
107
|
+
is_flag=True,
|
|
108
|
+
help="Do not inject/authenticate with Llama Cloud credentials",
|
|
109
|
+
)
|
|
110
|
+
@interactive_option
|
|
111
|
+
@click.argument("cmd", nargs=-1, type=click.UNPROCESSED)
|
|
112
|
+
def run_command(
|
|
113
|
+
deployment_file: Path, no_auth: bool, interactive: bool, cmd: tuple[str, ...]
|
|
114
|
+
) -> None: # type: ignore
|
|
115
|
+
"""Execute COMMAND with deployment environment variables applied."""
|
|
116
|
+
if not cmd:
|
|
117
|
+
raise click.ClickException(
|
|
118
|
+
"No command provided. Use '--' before the command arguments if needed."
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
try:
|
|
122
|
+
config, config_parent = _prepare_environment(
|
|
123
|
+
deployment_file, interactive, require_cloud=not no_auth
|
|
124
|
+
)
|
|
125
|
+
env_overrides = parse_environment_variables(config, config_parent)
|
|
126
|
+
env = os.environ.copy()
|
|
127
|
+
env.update({k: v for k, v in env_overrides.items() if v is not None})
|
|
128
|
+
|
|
129
|
+
_print_connection_summary()
|
|
130
|
+
result = subprocess.run(cmd, env=env, check=False)
|
|
131
|
+
if result.returncode != 0:
|
|
132
|
+
raise SystemExit(result.returncode)
|
|
133
|
+
except (Exit, Abort, SystemExit, click.ClickException):
|
|
134
|
+
raise
|
|
135
|
+
except FileNotFoundError as exc:
|
|
136
|
+
rprint(f"[red]Command not found: {exc.filename}[/red]")
|
|
137
|
+
raise click.Abort()
|
|
138
|
+
except Exception as exc: # pragma: no cover - unexpected errors reported to user
|
|
139
|
+
rprint(f"[red]Failed to run command: {exc}[/red]")
|
|
140
|
+
raise click.Abort()
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def _ensure_project_layout(deployment_file: Path, *, command_name: str) -> Path:
|
|
144
|
+
if not deployment_file.exists():
|
|
145
|
+
rprint(f"[red]Deployment file '{deployment_file}' not found[/red]")
|
|
146
|
+
raise click.Abort()
|
|
147
|
+
|
|
148
|
+
config_dir = deployment_file if deployment_file.is_dir() else deployment_file.parent
|
|
149
|
+
if not (config_dir / "pyproject.toml").exists():
|
|
150
|
+
rprint(
|
|
151
|
+
"[red]No pyproject.toml found at[/red] "
|
|
152
|
+
f"[bold]{config_dir}[/bold].\n"
|
|
153
|
+
f"Add a pyproject.toml to your project and re-run '{command_name}'."
|
|
154
|
+
)
|
|
155
|
+
raise click.Abort()
|
|
156
|
+
return config_dir
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def _prepare_environment(
|
|
160
|
+
deployment_file: Path, interactive: bool, *, require_cloud: bool
|
|
161
|
+
) -> tuple[DeploymentConfig, Path]:
|
|
162
|
+
_maybe_inject_llama_cloud_credentials(
|
|
163
|
+
deployment_file, interactive, require_cloud=require_cloud
|
|
164
|
+
)
|
|
165
|
+
configure_settings(
|
|
166
|
+
deployment_file_path=deployment_file,
|
|
167
|
+
app_root=Path.cwd(),
|
|
168
|
+
)
|
|
169
|
+
config = get_deployment_config()
|
|
170
|
+
config_parent = settings.resolved_config_parent
|
|
171
|
+
load_environment_variables(config, config_parent)
|
|
172
|
+
validate_required_env_vars(config)
|
|
173
|
+
return config, config_parent
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
__all__ = ["dev", "validate_command", "run_command"]
|
|
@@ -279,41 +279,64 @@ def _create(
|
|
|
279
279
|
if agents_path.exists() and not claude_path.exists():
|
|
280
280
|
claude_path.symlink_to("AGENTS.md")
|
|
281
281
|
|
|
282
|
-
# Initialize a git repo
|
|
282
|
+
# Initialize a git repo unless we're already inside one.
|
|
283
283
|
if has_git:
|
|
284
|
+
# Detect whether the target directory is already inside a git work tree
|
|
285
|
+
inside_existing_repo = False
|
|
284
286
|
try:
|
|
285
|
-
subprocess.run(
|
|
286
|
-
|
|
287
|
-
subprocess.run(
|
|
288
|
-
["git", "commit", "-m", "Initial commit"],
|
|
287
|
+
result = subprocess.run(
|
|
288
|
+
["git", "rev-parse", "--is-inside-work-tree"],
|
|
289
289
|
check=True,
|
|
290
290
|
capture_output=True,
|
|
291
|
+
text=True,
|
|
291
292
|
)
|
|
292
|
-
|
|
293
|
-
except (subprocess.CalledProcessError, FileNotFoundError)
|
|
294
|
-
|
|
295
|
-
err_msg = ""
|
|
296
|
-
if isinstance(e, subprocess.CalledProcessError):
|
|
297
|
-
stderr_bytes = e.stderr or b""
|
|
298
|
-
if isinstance(stderr_bytes, (bytes, bytearray)):
|
|
299
|
-
try:
|
|
300
|
-
stderr_text = stderr_bytes.decode("utf-8", "ignore")
|
|
301
|
-
except Exception:
|
|
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]
|
|
307
|
-
elif isinstance(e, FileNotFoundError):
|
|
308
|
-
err_msg = "git executable not found"
|
|
293
|
+
inside_existing_repo = result.stdout.strip().lower() == "true"
|
|
294
|
+
except (subprocess.CalledProcessError, FileNotFoundError):
|
|
295
|
+
inside_existing_repo = False
|
|
309
296
|
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
297
|
+
if inside_existing_repo:
|
|
298
|
+
# Do not create a nested repo; user likely wants this within the parent repo
|
|
299
|
+
rprint(
|
|
300
|
+
"[yellow]Detected an existing Git repository in a parent directory; skipping git initialization for this app.[/]"
|
|
301
|
+
)
|
|
302
|
+
# Treat as initialized for purposes of what instructions to show later
|
|
303
|
+
git_initialized = True
|
|
304
|
+
else:
|
|
305
|
+
try:
|
|
306
|
+
subprocess.run(["git", "init"], check=True, capture_output=True)
|
|
307
|
+
subprocess.run(["git", "add", "."], check=True, capture_output=True)
|
|
308
|
+
subprocess.run(
|
|
309
|
+
["git", "commit", "-m", "Initial commit"],
|
|
310
|
+
check=True,
|
|
311
|
+
capture_output=True,
|
|
312
|
+
)
|
|
313
|
+
git_initialized = True
|
|
314
|
+
except (subprocess.CalledProcessError, FileNotFoundError) as e:
|
|
315
|
+
# Extract a short error message if present
|
|
316
|
+
err_msg = ""
|
|
317
|
+
if isinstance(e, subprocess.CalledProcessError):
|
|
318
|
+
stderr_bytes = e.stderr or b""
|
|
319
|
+
if isinstance(stderr_bytes, (bytes, bytearray)):
|
|
320
|
+
try:
|
|
321
|
+
stderr_text = stderr_bytes.decode("utf-8", "ignore")
|
|
322
|
+
except Exception:
|
|
323
|
+
stderr_text = ""
|
|
324
|
+
else:
|
|
325
|
+
stderr_text = str(stderr_bytes)
|
|
326
|
+
if stderr_text.strip():
|
|
327
|
+
err_msg = stderr_text.strip().split("\n")[-1]
|
|
328
|
+
elif isinstance(e, FileNotFoundError):
|
|
329
|
+
err_msg = "git executable not found"
|
|
330
|
+
|
|
331
|
+
rprint("")
|
|
332
|
+
rprint("⚠️ [bold]Skipping git initialization due to an error.[/]")
|
|
333
|
+
if err_msg:
|
|
334
|
+
rprint(f" {err_msg}")
|
|
335
|
+
rprint(" You can initialize it manually:")
|
|
336
|
+
rprint(
|
|
337
|
+
" git init && git add . && git commit -m 'Initial commit'"
|
|
338
|
+
)
|
|
339
|
+
rprint("")
|
|
317
340
|
finally:
|
|
318
341
|
os.chdir(original_cwd)
|
|
319
342
|
|
|
@@ -63,6 +63,7 @@ class DeploymentForm:
|
|
|
63
63
|
id: str | None = None
|
|
64
64
|
repo_url: str = ""
|
|
65
65
|
git_ref: str = "main"
|
|
66
|
+
git_sha: str | None = None
|
|
66
67
|
deployment_file_path: str = ""
|
|
67
68
|
personal_access_token: str = ""
|
|
68
69
|
# indicates if the deployment has a personal access token (value is unknown)
|
|
@@ -100,6 +101,7 @@ class DeploymentForm:
|
|
|
100
101
|
id=deployment.id,
|
|
101
102
|
repo_url=deployment.repo_url,
|
|
102
103
|
git_ref=deployment.git_ref or "main",
|
|
104
|
+
git_sha=deployment.git_sha or "-",
|
|
103
105
|
deployment_file_path=deployment.deployment_file_path,
|
|
104
106
|
personal_access_token="", # Always start empty for security
|
|
105
107
|
has_existing_pat=deployment.has_personal_access_token,
|
|
@@ -242,6 +244,15 @@ class DeploymentFormWidget(Widget):
|
|
|
242
244
|
compact=True,
|
|
243
245
|
)
|
|
244
246
|
|
|
247
|
+
yield Label("Last Deployed Commit:", classes="form-label", shrink=True)
|
|
248
|
+
yield Input(
|
|
249
|
+
value=(self.form_data.git_sha or "-")[:7],
|
|
250
|
+
placeholder="-",
|
|
251
|
+
id="git_sha",
|
|
252
|
+
compact=True,
|
|
253
|
+
disabled=True,
|
|
254
|
+
)
|
|
255
|
+
|
|
245
256
|
yield Static(classes="full-width")
|
|
246
257
|
yield Static(
|
|
247
258
|
Content.from_markup("[italic]Advanced[/]"),
|
|
@@ -133,6 +133,16 @@ class DeploymentMonitorWidget(Widget):
|
|
|
133
133
|
compact=True,
|
|
134
134
|
variant="default",
|
|
135
135
|
)
|
|
136
|
+
|
|
137
|
+
with HorizontalGroup(classes=""):
|
|
138
|
+
yield Static(" Last Deployed Commit: ", classes="deployment-link-label")
|
|
139
|
+
yield Button(
|
|
140
|
+
"",
|
|
141
|
+
id="last_deployed_commit",
|
|
142
|
+
classes="deployment-link",
|
|
143
|
+
compact=True,
|
|
144
|
+
variant="default",
|
|
145
|
+
)
|
|
136
146
|
yield Static("", classes="error-message", id="error_message")
|
|
137
147
|
|
|
138
148
|
# Single-line status bar with colored icon and deployment ID
|
|
@@ -168,6 +178,13 @@ class DeploymentMonitorWidget(Widget):
|
|
|
168
178
|
self.app.copy_to_clipboard(txt)
|
|
169
179
|
elif event.button.id == "deployment_link_button":
|
|
170
180
|
self.action_open_url()
|
|
181
|
+
elif event.button.id == "last_deployed_commit":
|
|
182
|
+
if self.deployment is not None:
|
|
183
|
+
self.action_open_url(
|
|
184
|
+
url=self.deployment.repo_url
|
|
185
|
+
+ "/commit/"
|
|
186
|
+
+ (self.deployment.git_sha or "")
|
|
187
|
+
)
|
|
171
188
|
|
|
172
189
|
async def _fetch_deployment(self) -> None:
|
|
173
190
|
try:
|
|
@@ -300,11 +317,15 @@ class DeploymentMonitorWidget(Widget):
|
|
|
300
317
|
return "●", red
|
|
301
318
|
return "●", gray
|
|
302
319
|
|
|
303
|
-
def action_open_url(self) -> None:
|
|
304
|
-
if not
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
320
|
+
def action_open_url(self, url: str | None = None) -> None:
|
|
321
|
+
if not url:
|
|
322
|
+
if not self.deployment or not self.deployment.apiserver_url:
|
|
323
|
+
return
|
|
324
|
+
logger.debug(f"Opening URL: {self.deployment.apiserver_url}")
|
|
325
|
+
webbrowser.open(str(self.deployment.apiserver_url))
|
|
326
|
+
else:
|
|
327
|
+
logger.debug(f"Opening URL: {url}")
|
|
328
|
+
webbrowser.open(str(url))
|
|
308
329
|
|
|
309
330
|
def _render_status_line(self) -> Text:
|
|
310
331
|
phase = self.deployment.status if self.deployment else "Unknown"
|
|
@@ -364,10 +385,15 @@ class DeploymentMonitorWidget(Widget):
|
|
|
364
385
|
ev_widget = self.query_one("#last_event_status", Static)
|
|
365
386
|
ev_details_widget = self.query_one("#last_event_details", Static)
|
|
366
387
|
deployment_link_button = self.query_one("#deployment_link_button", Button)
|
|
388
|
+
last_commit_button = self.query_one("#last_deployed_commit", Button)
|
|
367
389
|
widget.update(self._render_status_line())
|
|
368
390
|
deployment_link_button.label = (
|
|
369
391
|
f"{str(self.deployment.apiserver_url or '') if self.deployment else ''}"
|
|
370
392
|
)
|
|
393
|
+
if self.deployment:
|
|
394
|
+
last_commit_button.label = f"{(str(self.deployment.git_sha or '-'))[:7]}"
|
|
395
|
+
else:
|
|
396
|
+
last_commit_button.label = "-"
|
|
371
397
|
# Update last event line
|
|
372
398
|
ev_widget.update(self._render_last_event_status())
|
|
373
399
|
ev_details_widget.update(self._render_last_event_details())
|
|
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.19 → llamactl-0.3.21}/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.19 → llamactl-0.3.21}/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
|
|
File without changes
|
|
File without changes
|