general-augment-cli 0.1.0__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.
- general_augment_cli-0.1.0.dist-info/METADATA +180 -0
- general_augment_cli-0.1.0.dist-info/RECORD +42 -0
- general_augment_cli-0.1.0.dist-info/WHEEL +4 -0
- general_augment_cli-0.1.0.dist-info/entry_points.txt +2 -0
- platform_cli/__init__.py +5 -0
- platform_cli/branding.py +27 -0
- platform_cli/client.py +179 -0
- platform_cli/commands/__init__.py +1 -0
- platform_cli/commands/approvals.py +150 -0
- platform_cli/commands/auth.py +96 -0
- platform_cli/commands/billing.py +143 -0
- platform_cli/commands/channels.py +212 -0
- platform_cli/commands/deploy.py +72 -0
- platform_cli/commands/dev.py +38 -0
- platform_cli/commands/doctor.py +170 -0
- platform_cli/commands/identity.py +433 -0
- platform_cli/commands/init.py +55 -0
- platform_cli/commands/integrate.py +94 -0
- platform_cli/commands/keys.py +116 -0
- platform_cli/commands/logs.py +43 -0
- platform_cli/commands/mcp.py +258 -0
- platform_cli/commands/memory.py +316 -0
- platform_cli/commands/mock.py +30 -0
- platform_cli/commands/model_providers.py +226 -0
- platform_cli/commands/observability.py +174 -0
- platform_cli/commands/onboarding.py +72 -0
- platform_cli/commands/projects.py +302 -0
- platform_cli/commands/skills.py +116 -0
- platform_cli/commands/smoke.py +280 -0
- platform_cli/commands/status.py +49 -0
- platform_cli/commands/tools.py +179 -0
- platform_cli/commands/users.py +150 -0
- platform_cli/commands/validate.py +96 -0
- platform_cli/commands/verify.py +648 -0
- platform_cli/config.py +114 -0
- platform_cli/errors.py +103 -0
- platform_cli/local_mock.py +1392 -0
- platform_cli/main.py +130 -0
- platform_cli/openapi.py +1048 -0
- platform_cli/output.py +47 -0
- platform_cli/readiness.py +176 -0
- platform_cli/runtime.py +22 -0
platform_cli/config.py
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
"""Local CLI configuration stored in ~/.genaug/config.yaml."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
from dataclasses import dataclass
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import Any
|
|
9
|
+
|
|
10
|
+
import yaml
|
|
11
|
+
from pydantic import BaseModel, Field
|
|
12
|
+
|
|
13
|
+
DEFAULT_BASE_URL = "https://api.generalaugment.com"
|
|
14
|
+
CONFIG_PATH_ENV = "GENAUG_CLI_CONFIG"
|
|
15
|
+
CONFIG_DIR = ".genaug"
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@dataclass(frozen=True)
|
|
19
|
+
class ConfigPaths:
|
|
20
|
+
"""Resolved config paths for reading existing auth and writing new auth."""
|
|
21
|
+
|
|
22
|
+
load_path: Path
|
|
23
|
+
save_path: Path
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class CLIConfig(BaseModel):
|
|
27
|
+
"""Persisted CLI configuration."""
|
|
28
|
+
|
|
29
|
+
base_url: str = DEFAULT_BASE_URL
|
|
30
|
+
api_key: str | None = None
|
|
31
|
+
active_project: str | None = None
|
|
32
|
+
profile: str = "default"
|
|
33
|
+
metadata: dict[str, Any] = Field(default_factory=dict)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def default_config_path() -> Path:
|
|
37
|
+
"""Return the preferred config path."""
|
|
38
|
+
override = _config_path_override()
|
|
39
|
+
if override:
|
|
40
|
+
return override
|
|
41
|
+
return Path.home() / CONFIG_DIR / "config.yaml"
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def resolve_config_paths(path: Path | None = None) -> ConfigPaths:
|
|
45
|
+
"""Resolve read and write paths."""
|
|
46
|
+
if path:
|
|
47
|
+
resolved = path.expanduser()
|
|
48
|
+
return ConfigPaths(load_path=resolved, save_path=resolved)
|
|
49
|
+
override = _config_path_override()
|
|
50
|
+
if override:
|
|
51
|
+
return ConfigPaths(load_path=override, save_path=override)
|
|
52
|
+
preferred = default_config_path()
|
|
53
|
+
return ConfigPaths(load_path=preferred, save_path=preferred)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def load_config(path: Path | None = None) -> CLIConfig:
|
|
57
|
+
"""Load CLI config from disk or return defaults."""
|
|
58
|
+
resolved = path or default_config_path()
|
|
59
|
+
if not resolved.exists():
|
|
60
|
+
return CLIConfig()
|
|
61
|
+
payload = yaml.safe_load(resolved.read_text(encoding="utf-8")) or {}
|
|
62
|
+
if not isinstance(payload, dict):
|
|
63
|
+
return CLIConfig()
|
|
64
|
+
return CLIConfig.model_validate(payload)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def save_config(config: CLIConfig, path: Path | None = None) -> Path:
|
|
68
|
+
"""Write CLI config and return the path."""
|
|
69
|
+
resolved = path or default_config_path()
|
|
70
|
+
resolved.parent.mkdir(parents=True, exist_ok=True)
|
|
71
|
+
resolved.write_text(
|
|
72
|
+
yaml.safe_dump(config.model_dump(), sort_keys=False),
|
|
73
|
+
encoding="utf-8",
|
|
74
|
+
)
|
|
75
|
+
resolved.chmod(0o600)
|
|
76
|
+
return resolved
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def clear_config(path: Path | None = None) -> None:
|
|
80
|
+
"""Remove the persisted config file when it exists."""
|
|
81
|
+
resolved = path or default_config_path()
|
|
82
|
+
if resolved.exists():
|
|
83
|
+
resolved.unlink()
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def apply_runtime_overrides(
|
|
87
|
+
config: CLIConfig,
|
|
88
|
+
*,
|
|
89
|
+
base_url: str | None = None,
|
|
90
|
+
api_key: str | None = None,
|
|
91
|
+
) -> CLIConfig:
|
|
92
|
+
"""Apply CLI flags and environment variables without mutating the stored config."""
|
|
93
|
+
return config.model_copy(
|
|
94
|
+
update={
|
|
95
|
+
"base_url": (
|
|
96
|
+
base_url
|
|
97
|
+
or os.getenv("GENAUG_ADMIN_BASE_URL")
|
|
98
|
+
or os.getenv("GENAUG_API_BASE_URL")
|
|
99
|
+
or config.base_url
|
|
100
|
+
),
|
|
101
|
+
"api_key": (
|
|
102
|
+
api_key
|
|
103
|
+
or os.getenv("GENAUG_ADMIN_API_KEY")
|
|
104
|
+
or os.getenv("GENAUG_API_KEY")
|
|
105
|
+
or config.api_key
|
|
106
|
+
),
|
|
107
|
+
}
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def _config_path_override() -> Path | None:
|
|
112
|
+
"""Return a configured path."""
|
|
113
|
+
override = os.getenv(CONFIG_PATH_ENV)
|
|
114
|
+
return Path(override).expanduser() if override else None
|
platform_cli/errors.py
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
"""CLI-specific error types and formatting."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from collections.abc import Mapping
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
import click
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class CLIError(click.ClickException):
|
|
12
|
+
"""Base CLI error with a user-facing message."""
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class APIError(CLIError):
|
|
16
|
+
"""Raised when the platform API returns an error."""
|
|
17
|
+
|
|
18
|
+
def __init__(
|
|
19
|
+
self,
|
|
20
|
+
status_code: int,
|
|
21
|
+
detail: Any,
|
|
22
|
+
headers: Mapping[str, str] | None = None,
|
|
23
|
+
) -> None:
|
|
24
|
+
"""Create an API error."""
|
|
25
|
+
self.status_code = status_code
|
|
26
|
+
self.detail = detail
|
|
27
|
+
self.headers = dict(headers or {})
|
|
28
|
+
super().__init__(helpful_api_error(status_code, detail, self.headers))
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def helpful_api_error(
|
|
32
|
+
status_code: int,
|
|
33
|
+
detail: Any,
|
|
34
|
+
headers: Mapping[str, str] | None = None,
|
|
35
|
+
) -> str:
|
|
36
|
+
"""Return actionable API error text."""
|
|
37
|
+
if status_code == 401:
|
|
38
|
+
return "Your API key may be invalid. Run genaug auth login to re-authenticate."
|
|
39
|
+
if status_code == 403:
|
|
40
|
+
return f"You do not have access to this resource. API said: {_detail_message(detail)}"
|
|
41
|
+
if status_code == 404:
|
|
42
|
+
return (
|
|
43
|
+
"That resource was not found. Check the project or tool name. "
|
|
44
|
+
f"API said: {_detail_message(detail)}"
|
|
45
|
+
)
|
|
46
|
+
if status_code == 429:
|
|
47
|
+
return _rate_limit_message(detail, headers or {})
|
|
48
|
+
if status_code >= 500:
|
|
49
|
+
return (
|
|
50
|
+
"The platform API is having trouble. Retry shortly. "
|
|
51
|
+
f"API said: {_detail_message(detail)}"
|
|
52
|
+
)
|
|
53
|
+
return f"Platform API returned {status_code}: {_detail_message(detail)}"
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def _rate_limit_message(detail: Any, headers: Mapping[str, str]) -> str:
|
|
57
|
+
"""Return a specific rate-limit explanation when the API provides one."""
|
|
58
|
+
reason = _detail_value(detail, "reason") or _detail_value(detail, "code")
|
|
59
|
+
retry_after = _header_value(headers, "Retry-After") or _detail_value(detail, "retry_after")
|
|
60
|
+
message = _detail_value(detail, "message")
|
|
61
|
+
parts = ["You are being rate limited."]
|
|
62
|
+
if retry_after:
|
|
63
|
+
parts.append(f"Retry after {retry_after} seconds.")
|
|
64
|
+
else:
|
|
65
|
+
parts.append("Wait a minute and retry the command.")
|
|
66
|
+
if reason:
|
|
67
|
+
parts.append(f"Reason: {reason}.")
|
|
68
|
+
if message and message != reason:
|
|
69
|
+
parts.append(f"API said: {message}")
|
|
70
|
+
return " ".join(parts)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def _detail_value(detail: Any, key: str) -> str | None:
|
|
74
|
+
"""Find a stable value in API error payloads."""
|
|
75
|
+
if not isinstance(detail, dict):
|
|
76
|
+
return None
|
|
77
|
+
candidates = [detail]
|
|
78
|
+
for nested_key in ("detail", "error"):
|
|
79
|
+
nested = detail.get(nested_key)
|
|
80
|
+
if isinstance(nested, dict):
|
|
81
|
+
candidates.append(nested)
|
|
82
|
+
for candidate in candidates:
|
|
83
|
+
value = candidate.get(key)
|
|
84
|
+
if value not in (None, ""):
|
|
85
|
+
return str(value)
|
|
86
|
+
return None
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def _header_value(headers: Mapping[str, str], key: str) -> str | None:
|
|
90
|
+
"""Return a header value without depending on header casing."""
|
|
91
|
+
for header_key, value in headers.items():
|
|
92
|
+
if header_key.lower() == key.lower():
|
|
93
|
+
return value
|
|
94
|
+
return None
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def _detail_message(detail: Any) -> str:
|
|
98
|
+
"""Return a readable API detail string."""
|
|
99
|
+
if isinstance(detail, dict):
|
|
100
|
+
message = _detail_value(detail, "message")
|
|
101
|
+
if message:
|
|
102
|
+
return message
|
|
103
|
+
return str(detail)
|