glaip-sdk 0.1.3__py3-none-any.whl ā 0.6.19__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.
- glaip_sdk/__init__.py +44 -4
- glaip_sdk/_version.py +9 -0
- glaip_sdk/agents/__init__.py +27 -0
- glaip_sdk/agents/base.py +1196 -0
- glaip_sdk/branding.py +13 -0
- glaip_sdk/cli/account_store.py +540 -0
- glaip_sdk/cli/auth.py +254 -15
- glaip_sdk/cli/commands/__init__.py +2 -2
- glaip_sdk/cli/commands/accounts.py +746 -0
- glaip_sdk/cli/commands/agents.py +213 -73
- glaip_sdk/cli/commands/common_config.py +104 -0
- glaip_sdk/cli/commands/configure.py +729 -113
- glaip_sdk/cli/commands/mcps.py +241 -72
- glaip_sdk/cli/commands/models.py +11 -5
- glaip_sdk/cli/commands/tools.py +49 -57
- glaip_sdk/cli/commands/transcripts.py +755 -0
- glaip_sdk/cli/config.py +48 -4
- glaip_sdk/cli/constants.py +38 -0
- glaip_sdk/cli/context.py +8 -0
- glaip_sdk/cli/core/__init__.py +79 -0
- glaip_sdk/cli/core/context.py +124 -0
- glaip_sdk/cli/core/output.py +851 -0
- glaip_sdk/cli/core/prompting.py +649 -0
- glaip_sdk/cli/core/rendering.py +187 -0
- glaip_sdk/cli/display.py +35 -19
- glaip_sdk/cli/hints.py +57 -0
- glaip_sdk/cli/io.py +6 -3
- glaip_sdk/cli/main.py +241 -121
- glaip_sdk/cli/masking.py +21 -33
- glaip_sdk/cli/pager.py +9 -10
- glaip_sdk/cli/parsers/__init__.py +1 -3
- glaip_sdk/cli/slash/__init__.py +0 -9
- glaip_sdk/cli/slash/accounts_controller.py +578 -0
- glaip_sdk/cli/slash/accounts_shared.py +75 -0
- glaip_sdk/cli/slash/agent_session.py +62 -21
- glaip_sdk/cli/slash/prompt.py +21 -0
- glaip_sdk/cli/slash/remote_runs_controller.py +566 -0
- glaip_sdk/cli/slash/session.py +771 -140
- glaip_sdk/cli/slash/tui/__init__.py +9 -0
- glaip_sdk/cli/slash/tui/accounts.tcss +86 -0
- glaip_sdk/cli/slash/tui/accounts_app.py +876 -0
- glaip_sdk/cli/slash/tui/background_tasks.py +72 -0
- glaip_sdk/cli/slash/tui/loading.py +58 -0
- glaip_sdk/cli/slash/tui/remote_runs_app.py +628 -0
- glaip_sdk/cli/transcript/__init__.py +12 -52
- glaip_sdk/cli/transcript/cache.py +255 -44
- glaip_sdk/cli/transcript/capture.py +27 -1
- glaip_sdk/cli/transcript/history.py +815 -0
- glaip_sdk/cli/transcript/viewer.py +72 -499
- glaip_sdk/cli/update_notifier.py +14 -5
- glaip_sdk/cli/utils.py +243 -1252
- glaip_sdk/cli/validators.py +5 -6
- glaip_sdk/client/__init__.py +2 -1
- glaip_sdk/client/_agent_payloads.py +45 -9
- glaip_sdk/client/agent_runs.py +147 -0
- glaip_sdk/client/agents.py +291 -35
- glaip_sdk/client/base.py +1 -0
- glaip_sdk/client/main.py +19 -10
- glaip_sdk/client/mcps.py +122 -12
- glaip_sdk/client/run_rendering.py +466 -89
- glaip_sdk/client/shared.py +21 -0
- glaip_sdk/client/tools.py +155 -10
- glaip_sdk/config/constants.py +11 -0
- glaip_sdk/hitl/__init__.py +15 -0
- glaip_sdk/hitl/local.py +151 -0
- glaip_sdk/mcps/__init__.py +21 -0
- glaip_sdk/mcps/base.py +345 -0
- glaip_sdk/models/__init__.py +90 -0
- glaip_sdk/models/agent.py +47 -0
- glaip_sdk/models/agent_runs.py +116 -0
- glaip_sdk/models/common.py +42 -0
- glaip_sdk/models/mcp.py +33 -0
- glaip_sdk/models/tool.py +33 -0
- glaip_sdk/payload_schemas/__init__.py +1 -13
- glaip_sdk/registry/__init__.py +55 -0
- glaip_sdk/registry/agent.py +164 -0
- glaip_sdk/registry/base.py +139 -0
- glaip_sdk/registry/mcp.py +253 -0
- glaip_sdk/registry/tool.py +232 -0
- glaip_sdk/rich_components.py +58 -2
- glaip_sdk/runner/__init__.py +59 -0
- glaip_sdk/runner/base.py +84 -0
- glaip_sdk/runner/deps.py +112 -0
- glaip_sdk/runner/langgraph.py +870 -0
- glaip_sdk/runner/mcp_adapter/__init__.py +13 -0
- glaip_sdk/runner/mcp_adapter/base_mcp_adapter.py +43 -0
- glaip_sdk/runner/mcp_adapter/langchain_mcp_adapter.py +257 -0
- glaip_sdk/runner/mcp_adapter/mcp_config_builder.py +95 -0
- glaip_sdk/runner/tool_adapter/__init__.py +18 -0
- glaip_sdk/runner/tool_adapter/base_tool_adapter.py +44 -0
- glaip_sdk/runner/tool_adapter/langchain_tool_adapter.py +219 -0
- glaip_sdk/tools/__init__.py +22 -0
- glaip_sdk/tools/base.py +435 -0
- glaip_sdk/utils/__init__.py +58 -12
- glaip_sdk/utils/a2a/__init__.py +34 -0
- glaip_sdk/utils/a2a/event_processor.py +188 -0
- glaip_sdk/utils/bundler.py +267 -0
- glaip_sdk/utils/client.py +111 -0
- glaip_sdk/utils/client_utils.py +39 -7
- glaip_sdk/utils/datetime_helpers.py +58 -0
- glaip_sdk/utils/discovery.py +78 -0
- glaip_sdk/utils/display.py +23 -15
- glaip_sdk/utils/export.py +143 -0
- glaip_sdk/utils/general.py +0 -33
- glaip_sdk/utils/import_export.py +12 -7
- glaip_sdk/utils/import_resolver.py +492 -0
- glaip_sdk/utils/instructions.py +101 -0
- glaip_sdk/utils/rendering/__init__.py +115 -1
- glaip_sdk/utils/rendering/formatting.py +5 -30
- glaip_sdk/utils/rendering/layout/__init__.py +64 -0
- glaip_sdk/utils/rendering/{renderer ā layout}/panels.py +9 -0
- glaip_sdk/utils/rendering/{renderer ā layout}/progress.py +70 -1
- glaip_sdk/utils/rendering/layout/summary.py +74 -0
- glaip_sdk/utils/rendering/layout/transcript.py +606 -0
- glaip_sdk/utils/rendering/models.py +1 -0
- glaip_sdk/utils/rendering/renderer/__init__.py +9 -47
- glaip_sdk/utils/rendering/renderer/base.py +275 -1476
- glaip_sdk/utils/rendering/renderer/debug.py +26 -20
- glaip_sdk/utils/rendering/renderer/factory.py +138 -0
- glaip_sdk/utils/rendering/renderer/stream.py +4 -12
- glaip_sdk/utils/rendering/renderer/thinking.py +273 -0
- glaip_sdk/utils/rendering/renderer/tool_panels.py +442 -0
- glaip_sdk/utils/rendering/renderer/transcript_mode.py +162 -0
- glaip_sdk/utils/rendering/state.py +204 -0
- glaip_sdk/utils/rendering/steps/__init__.py +34 -0
- glaip_sdk/utils/rendering/{steps.py ā steps/event_processor.py} +53 -440
- glaip_sdk/utils/rendering/steps/format.py +176 -0
- glaip_sdk/utils/rendering/steps/manager.py +387 -0
- glaip_sdk/utils/rendering/timing.py +36 -0
- glaip_sdk/utils/rendering/viewer/__init__.py +21 -0
- glaip_sdk/utils/rendering/viewer/presenter.py +184 -0
- glaip_sdk/utils/resource_refs.py +25 -13
- glaip_sdk/utils/runtime_config.py +425 -0
- glaip_sdk/utils/serialization.py +18 -0
- glaip_sdk/utils/sync.py +142 -0
- glaip_sdk/utils/tool_detection.py +33 -0
- glaip_sdk/utils/tool_storage_provider.py +140 -0
- glaip_sdk/utils/validation.py +16 -24
- {glaip_sdk-0.1.3.dist-info ā glaip_sdk-0.6.19.dist-info}/METADATA +56 -21
- glaip_sdk-0.6.19.dist-info/RECORD +163 -0
- {glaip_sdk-0.1.3.dist-info ā glaip_sdk-0.6.19.dist-info}/WHEEL +2 -1
- glaip_sdk-0.6.19.dist-info/entry_points.txt +2 -0
- glaip_sdk-0.6.19.dist-info/top_level.txt +1 -0
- glaip_sdk/models.py +0 -240
- glaip_sdk-0.1.3.dist-info/RECORD +0 -83
- glaip_sdk-0.1.3.dist-info/entry_points.txt +0 -3
glaip_sdk/cli/main.py
CHANGED
|
@@ -4,16 +4,13 @@ Authors:
|
|
|
4
4
|
Raymond Christopher (raymond.christopher@gdplabs.id)
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
|
-
import
|
|
7
|
+
import logging
|
|
8
8
|
import subprocess
|
|
9
9
|
import sys
|
|
10
10
|
from typing import Any
|
|
11
11
|
|
|
12
12
|
import click
|
|
13
13
|
from rich.console import Console
|
|
14
|
-
|
|
15
|
-
from glaip_sdk import Client
|
|
16
|
-
from glaip_sdk._version import __version__ as _SDK_VERSION
|
|
17
14
|
from glaip_sdk.branding import (
|
|
18
15
|
ERROR,
|
|
19
16
|
ERROR_STYLE,
|
|
@@ -26,6 +23,9 @@ from glaip_sdk.branding import (
|
|
|
26
23
|
WARNING_STYLE,
|
|
27
24
|
AIPBranding,
|
|
28
25
|
)
|
|
26
|
+
from glaip_sdk.cli.account_store import get_account_store
|
|
27
|
+
from glaip_sdk.cli.auth import resolve_credentials
|
|
28
|
+
from glaip_sdk.cli.commands.accounts import accounts_group
|
|
29
29
|
from glaip_sdk.cli.commands.agents import agents_group
|
|
30
30
|
from glaip_sdk.cli.commands.configure import (
|
|
31
31
|
config_group,
|
|
@@ -34,17 +34,49 @@ from glaip_sdk.cli.commands.configure import (
|
|
|
34
34
|
from glaip_sdk.cli.commands.mcps import mcps_group
|
|
35
35
|
from glaip_sdk.cli.commands.models import models_group
|
|
36
36
|
from glaip_sdk.cli.commands.tools import tools_group
|
|
37
|
-
from glaip_sdk.cli.commands.
|
|
37
|
+
from glaip_sdk.cli.commands.transcripts import transcripts_group
|
|
38
|
+
from glaip_sdk.cli.commands.update import _build_upgrade_command, update_command
|
|
38
39
|
from glaip_sdk.cli.config import load_config
|
|
40
|
+
from glaip_sdk.cli.hints import in_slash_mode
|
|
39
41
|
from glaip_sdk.cli.transcript import get_transcript_cache_stats
|
|
40
42
|
from glaip_sdk.cli.update_notifier import maybe_notify_update
|
|
41
|
-
from glaip_sdk.cli.utils import
|
|
43
|
+
from glaip_sdk.cli.utils import format_size, sdk_version, spinner_context, update_spinner
|
|
42
44
|
from glaip_sdk.config.constants import (
|
|
43
45
|
DEFAULT_AGENT_RUN_TIMEOUT,
|
|
44
46
|
)
|
|
45
47
|
from glaip_sdk.icons import ICON_AGENT
|
|
46
48
|
from glaip_sdk.rich_components import AIPPanel, AIPTable
|
|
47
49
|
|
|
50
|
+
Client: type[Any] | None = None
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _resolve_client_class() -> type[Any]:
|
|
54
|
+
"""Resolve the Client class lazily to avoid heavy imports at CLI startup."""
|
|
55
|
+
global Client
|
|
56
|
+
if Client is None:
|
|
57
|
+
from glaip_sdk import Client as ClientClass # noqa: PLC0415
|
|
58
|
+
|
|
59
|
+
Client = ClientClass
|
|
60
|
+
return Client
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def _suppress_chatty_loggers() -> None:
|
|
64
|
+
"""Silence noisy SDK/httpx logs for CLI output."""
|
|
65
|
+
noisy_loggers = [
|
|
66
|
+
"glaip_sdk.client",
|
|
67
|
+
"httpx",
|
|
68
|
+
"httpcore",
|
|
69
|
+
]
|
|
70
|
+
for name in noisy_loggers:
|
|
71
|
+
logger = logging.getLogger(name)
|
|
72
|
+
# Respect existing configuration: only raise level when unset,
|
|
73
|
+
# and avoid changing propagation if a custom handler is already attached.
|
|
74
|
+
if logger.level == logging.NOTSET:
|
|
75
|
+
logger.setLevel(logging.WARNING)
|
|
76
|
+
if not logger.handlers:
|
|
77
|
+
logger.propagate = False
|
|
78
|
+
|
|
79
|
+
|
|
48
80
|
# Import SlashSession for potential mocking in tests
|
|
49
81
|
try:
|
|
50
82
|
from glaip_sdk.cli.slash import SlashSession
|
|
@@ -56,35 +88,17 @@ except ImportError: # pragma: no cover - optional slash dependencies
|
|
|
56
88
|
AVAILABLE_STATUS = "ā
Available"
|
|
57
89
|
|
|
58
90
|
|
|
59
|
-
def _format_size(num: int) -> str:
|
|
60
|
-
"""Return a human-readable byte size."""
|
|
61
|
-
if num <= 0:
|
|
62
|
-
return "0B"
|
|
63
|
-
|
|
64
|
-
units = ["B", "KB", "MB", "GB", "TB"]
|
|
65
|
-
value = float(num)
|
|
66
|
-
for unit in units:
|
|
67
|
-
if value < 1024 or unit == units[-1]:
|
|
68
|
-
if value >= 100 or unit == "B":
|
|
69
|
-
return f"{value:.0f}{unit}"
|
|
70
|
-
if value >= 10:
|
|
71
|
-
return f"{value:.1f}{unit}"
|
|
72
|
-
return f"{value:.2f}{unit}"
|
|
73
|
-
value /= 1024
|
|
74
|
-
return f"{value:.1f}TB" # pragma: no cover - defensive fallback
|
|
75
|
-
|
|
76
|
-
|
|
77
91
|
@click.group(invoke_without_command=True)
|
|
78
|
-
@click.version_option(
|
|
92
|
+
@click.version_option(package_name="glaip-sdk", prog_name="aip")
|
|
79
93
|
@click.option(
|
|
80
94
|
"--api-url",
|
|
81
|
-
|
|
82
|
-
|
|
95
|
+
help="(Deprecated) AIP API URL; use profiles via --account instead",
|
|
96
|
+
hidden=True,
|
|
83
97
|
)
|
|
84
98
|
@click.option(
|
|
85
99
|
"--api-key",
|
|
86
|
-
|
|
87
|
-
|
|
100
|
+
help="(Deprecated) AIP API Key; use profiles via --account instead",
|
|
101
|
+
hidden=True,
|
|
88
102
|
)
|
|
89
103
|
@click.option("--timeout", default=30.0, help="Request timeout in seconds")
|
|
90
104
|
@click.option(
|
|
@@ -95,6 +109,12 @@ def _format_size(num: int) -> str:
|
|
|
95
109
|
help="Output view format",
|
|
96
110
|
)
|
|
97
111
|
@click.option("--no-tty", is_flag=True, help="Disable TTY renderer")
|
|
112
|
+
@click.option(
|
|
113
|
+
"--account",
|
|
114
|
+
"account_name",
|
|
115
|
+
help="Target a named account profile for this command",
|
|
116
|
+
hidden=True, # Hidden by default, shown with --help --all
|
|
117
|
+
)
|
|
98
118
|
@click.pass_context
|
|
99
119
|
def main(
|
|
100
120
|
ctx: Any,
|
|
@@ -103,6 +123,7 @@ def main(
|
|
|
103
123
|
timeout: float | None,
|
|
104
124
|
view: str | None,
|
|
105
125
|
no_tty: bool,
|
|
126
|
+
account_name: str | None,
|
|
106
127
|
) -> None:
|
|
107
128
|
r"""GL AIP SDK Command Line Interface.
|
|
108
129
|
|
|
@@ -113,9 +134,14 @@ def main(
|
|
|
113
134
|
Examples:
|
|
114
135
|
aip version # Show detailed version info
|
|
115
136
|
aip configure # Configure credentials
|
|
137
|
+
aip accounts add prod # Add account profile
|
|
138
|
+
aip accounts use staging # Switch account
|
|
116
139
|
aip agents list # List all agents
|
|
117
140
|
aip tools create my_tool.py # Create a new tool
|
|
118
141
|
aip agents run my-agent "Hello world" # Run an agent
|
|
142
|
+
|
|
143
|
+
\b
|
|
144
|
+
NEW: Store multiple accounts via 'aip accounts add' and switch with 'aip accounts use'.
|
|
119
145
|
"""
|
|
120
146
|
# Store configuration in context
|
|
121
147
|
ctx.ensure_object(dict)
|
|
@@ -123,6 +149,9 @@ def main(
|
|
|
123
149
|
ctx.obj["api_key"] = api_key
|
|
124
150
|
ctx.obj["timeout"] = timeout
|
|
125
151
|
ctx.obj["view"] = view
|
|
152
|
+
ctx.obj["account_name"] = account_name
|
|
153
|
+
|
|
154
|
+
_suppress_chatty_loggers()
|
|
126
155
|
|
|
127
156
|
ctx.obj["tty"] = not no_tty
|
|
128
157
|
|
|
@@ -135,12 +164,13 @@ def main(
|
|
|
135
164
|
|
|
136
165
|
if not ctx.resilient_parsing and ctx.obj["tty"] and not launching_slash:
|
|
137
166
|
console = Console()
|
|
138
|
-
maybe_notify_update(
|
|
139
|
-
|
|
167
|
+
preferred_console = maybe_notify_update(
|
|
168
|
+
sdk_version(),
|
|
140
169
|
console=console,
|
|
141
170
|
ctx=ctx,
|
|
142
171
|
slash_command="update",
|
|
143
172
|
)
|
|
173
|
+
ctx.obj["_preferred_console"] = preferred_console or console
|
|
144
174
|
|
|
145
175
|
if ctx.invoked_subcommand is None and not ctx.resilient_parsing:
|
|
146
176
|
if launching_slash:
|
|
@@ -153,11 +183,13 @@ def main(
|
|
|
153
183
|
|
|
154
184
|
|
|
155
185
|
# Add command groups
|
|
186
|
+
main.add_command(accounts_group)
|
|
156
187
|
main.add_command(agents_group)
|
|
157
188
|
main.add_command(config_group)
|
|
158
189
|
main.add_command(tools_group)
|
|
159
190
|
main.add_command(mcps_group)
|
|
160
191
|
main.add_command(models_group)
|
|
192
|
+
main.add_command(transcripts_group)
|
|
161
193
|
|
|
162
194
|
# Add top-level commands
|
|
163
195
|
main.add_command(configure_command)
|
|
@@ -181,27 +213,34 @@ def _should_launch_slash(ctx: click.Context) -> bool:
|
|
|
181
213
|
|
|
182
214
|
def _load_and_merge_config(ctx: click.Context) -> dict:
|
|
183
215
|
"""Load configuration from multiple sources and merge them."""
|
|
184
|
-
# Load config from file and merge with context
|
|
185
|
-
file_config = load_config()
|
|
186
216
|
context_config = ctx.obj or {}
|
|
217
|
+
account_name = context_config.get("account_name")
|
|
187
218
|
|
|
188
|
-
#
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
219
|
+
# Resolve credentials using new account store system
|
|
220
|
+
api_url, api_key, source = resolve_credentials(
|
|
221
|
+
account_name=account_name,
|
|
222
|
+
api_url=context_config.get("api_url"),
|
|
223
|
+
api_key=context_config.get("api_key"),
|
|
224
|
+
)
|
|
194
225
|
|
|
195
|
-
#
|
|
196
|
-
|
|
226
|
+
# Load other config values (timeout, etc.) from legacy config
|
|
227
|
+
legacy_config = load_config()
|
|
228
|
+
timeout = context_config.get("timeout") or legacy_config.get("timeout")
|
|
197
229
|
|
|
198
|
-
|
|
199
|
-
|
|
230
|
+
return {
|
|
231
|
+
"api_url": api_url,
|
|
232
|
+
"api_key": api_key,
|
|
233
|
+
"timeout": timeout,
|
|
234
|
+
"_source": source, # Track where credentials came from
|
|
235
|
+
}
|
|
200
236
|
|
|
201
237
|
|
|
202
238
|
def _validate_config_and_show_error(config: dict, console: Console) -> None:
|
|
203
239
|
"""Validate configuration and show error if incomplete."""
|
|
240
|
+
store = get_account_store()
|
|
241
|
+
has_accounts = bool(store.list_accounts())
|
|
204
242
|
if not config.get("api_url") or not config.get("api_key"):
|
|
243
|
+
no_accounts_hint = "" if has_accounts else "\n ⢠No accounts found; create one now to continue"
|
|
205
244
|
console.print(
|
|
206
245
|
AIPPanel(
|
|
207
246
|
f"[{ERROR_STYLE}]ā Configuration incomplete[/]\n\n"
|
|
@@ -209,13 +248,14 @@ def _validate_config_and_show_error(config: dict, console: Console) -> None:
|
|
|
209
248
|
f" ⢠API URL: {config.get('api_url', 'Not set')}\n"
|
|
210
249
|
f" ⢠API Key: {'***' + config.get('api_key', '')[-4:] if config.get('api_key') else 'Not set'}\n\n"
|
|
211
250
|
f"š” To fix this:\n"
|
|
212
|
-
f" ⢠Run 'aip
|
|
213
|
-
f" ⢠Or run 'aip
|
|
251
|
+
f" ⢠Run 'aip accounts add default' to set up credentials\n"
|
|
252
|
+
f" ⢠Or run 'aip configure' for interactive setup\n"
|
|
253
|
+
f" ⢠Or run 'aip accounts list' to see current accounts{no_accounts_hint}",
|
|
214
254
|
title="ā Configuration Error",
|
|
215
255
|
border_style=ERROR,
|
|
216
|
-
)
|
|
256
|
+
),
|
|
217
257
|
)
|
|
218
|
-
console.print(f"\n[{SUCCESS_STYLE}]ā
AIP - Ready[/] (SDK v{
|
|
258
|
+
console.print(f"\n[{SUCCESS_STYLE}]ā
AIP - Ready[/] (SDK v{sdk_version()}) - Configure to connect")
|
|
219
259
|
sys.exit(1)
|
|
220
260
|
|
|
221
261
|
|
|
@@ -223,17 +263,49 @@ def _resolve_status_console(ctx: Any) -> tuple[Console, bool]:
|
|
|
223
263
|
"""Return the console to use and whether we are in slash mode."""
|
|
224
264
|
ctx_obj = ctx.obj if isinstance(ctx.obj, dict) else None
|
|
225
265
|
console_override = ctx_obj.get("_slash_console") if ctx_obj else None
|
|
226
|
-
|
|
266
|
+
preferred_console = ctx_obj.get("_preferred_console") if ctx_obj else None
|
|
267
|
+
if preferred_console is None:
|
|
268
|
+
# In heavily mocked tests, maybe_notify_update may be patched with a return_value
|
|
269
|
+
preferred_console = getattr(maybe_notify_update, "return_value", None)
|
|
270
|
+
console = console_override or preferred_console or Console()
|
|
227
271
|
slash_mode = in_slash_mode(ctx)
|
|
228
272
|
return console, slash_mode
|
|
229
273
|
|
|
230
274
|
|
|
231
|
-
def _render_status_heading(console: Console, slash_mode: bool) ->
|
|
232
|
-
"""Print the status heading/banner.
|
|
275
|
+
def _render_status_heading(console: Console, slash_mode: bool, config: dict) -> bool:
|
|
276
|
+
"""Print the status heading/banner.
|
|
277
|
+
|
|
278
|
+
Returns True if a generic ready line was printed (to avoid duplication).
|
|
279
|
+
"""
|
|
233
280
|
del slash_mode # heading now consistent across invocation contexts
|
|
281
|
+
ready_printed = False
|
|
234
282
|
console.print(f"[{INFO_STYLE}]GL AIP status[/]")
|
|
235
|
-
console.print()
|
|
236
|
-
|
|
283
|
+
console.print("")
|
|
284
|
+
|
|
285
|
+
# Show account information
|
|
286
|
+
source = str(config.get("_source") or "unknown")
|
|
287
|
+
account_name = None
|
|
288
|
+
if source.startswith("account:") or source.startswith("active_profile:"):
|
|
289
|
+
account_name = source.split(":", 1)[1]
|
|
290
|
+
|
|
291
|
+
if account_name:
|
|
292
|
+
store = get_account_store()
|
|
293
|
+
account = store.get_account(account_name)
|
|
294
|
+
if account:
|
|
295
|
+
url = account.get("api_url", "")
|
|
296
|
+
# Format source to match spec: "active_profile" instead of "active_profile:name"
|
|
297
|
+
display_source = source.split(":")[0] if ":" in source else source
|
|
298
|
+
console.print(f"[{SUCCESS_STYLE}]Account: {account_name} (source={display_source}) Ā· API URL: {url}[/]")
|
|
299
|
+
else:
|
|
300
|
+
console.print(f"[{SUCCESS_STYLE}]ā
GL AIP ready[/] (SDK v{sdk_version()})")
|
|
301
|
+
ready_printed = True
|
|
302
|
+
elif source == "flag":
|
|
303
|
+
console.print(f"[{SUCCESS_STYLE}]Account: (source={source})[/]")
|
|
304
|
+
else:
|
|
305
|
+
console.print(f"[{SUCCESS_STYLE}]ā
GL AIP ready[/] (SDK v{sdk_version()})")
|
|
306
|
+
ready_printed = True
|
|
307
|
+
|
|
308
|
+
return ready_printed
|
|
237
309
|
|
|
238
310
|
|
|
239
311
|
def _collect_cache_summary() -> tuple[str | None, str | None]:
|
|
@@ -241,15 +313,15 @@ def _collect_cache_summary() -> tuple[str | None, str | None]:
|
|
|
241
313
|
try:
|
|
242
314
|
cache_stats = get_transcript_cache_stats()
|
|
243
315
|
except Exception:
|
|
244
|
-
return "[dim]Saved
|
|
316
|
+
return "[dim]Saved transcripts[/dim]: unavailable", None
|
|
245
317
|
|
|
246
318
|
runs_text = f"{cache_stats.entry_count} runs saved"
|
|
247
319
|
if cache_stats.total_bytes:
|
|
248
|
-
size_part = f" Ā· {
|
|
320
|
+
size_part = f" Ā· {format_size(cache_stats.total_bytes)} used"
|
|
249
321
|
else:
|
|
250
322
|
size_part = ""
|
|
251
323
|
|
|
252
|
-
cache_line = f"[dim]Saved
|
|
324
|
+
cache_line = f"[dim]Saved transcripts[/dim]: {runs_text}{size_part} Ā· {cache_stats.cache_dir}"
|
|
253
325
|
return cache_line, None
|
|
254
326
|
|
|
255
327
|
|
|
@@ -261,19 +333,38 @@ def _display_cache_summary(console: Console, slash_mode: bool, cache_line: str |
|
|
|
261
333
|
console.print(cache_note)
|
|
262
334
|
|
|
263
335
|
|
|
264
|
-
def
|
|
265
|
-
"""
|
|
266
|
-
|
|
267
|
-
|
|
336
|
+
def _safe_list_call(obj: Any, attr: str) -> list[Any]:
|
|
337
|
+
"""Call list-like client methods defensively, returning an empty list on failure."""
|
|
338
|
+
func = getattr(obj, attr, None)
|
|
339
|
+
if callable(func):
|
|
340
|
+
try:
|
|
341
|
+
return func()
|
|
342
|
+
except Exception as exc:
|
|
343
|
+
logging.getLogger(__name__).debug(
|
|
344
|
+
"Failed to call %s on %s: %s", attr, type(obj).__name__, exc, exc_info=True
|
|
345
|
+
)
|
|
346
|
+
return []
|
|
347
|
+
return []
|
|
348
|
+
|
|
349
|
+
|
|
350
|
+
def _get_client_from_config(config: dict) -> Any:
|
|
351
|
+
"""Return a Client instance built from config."""
|
|
352
|
+
client_class = _resolve_client_class()
|
|
353
|
+
return client_class(
|
|
268
354
|
api_url=config["api_url"],
|
|
269
355
|
api_key=config["api_key"],
|
|
270
356
|
timeout=config.get("timeout", 30.0),
|
|
271
357
|
)
|
|
272
358
|
|
|
273
|
-
|
|
359
|
+
|
|
360
|
+
def _create_and_test_client(config: dict, console: Console, *, compact: bool = False) -> Any:
|
|
361
|
+
"""Create client and test connection by fetching resources."""
|
|
362
|
+
client: Any = _get_client_from_config(config)
|
|
363
|
+
|
|
364
|
+
# Test connection by listing resources with a spinner where available
|
|
274
365
|
try:
|
|
275
366
|
with spinner_context(
|
|
276
|
-
None,
|
|
367
|
+
None,
|
|
277
368
|
"[bold blue]Checking GL AIP statusā¦[/bold blue]",
|
|
278
369
|
console_override=console,
|
|
279
370
|
spinner_style=INFO,
|
|
@@ -286,48 +377,21 @@ def _create_and_test_client(config: dict, console: Console, *, compact: bool = F
|
|
|
286
377
|
|
|
287
378
|
update_spinner(status_indicator, "[bold blue]Fetching MCPsā¦[/bold blue]")
|
|
288
379
|
mcps = client.list_mcps()
|
|
289
|
-
|
|
290
|
-
# Create status table
|
|
291
|
-
table = AIPTable(title="š GL AIP Status")
|
|
292
|
-
table.add_column("Resource", style=INFO, width=15)
|
|
293
|
-
table.add_column("Count", style=NEUTRAL, width=10)
|
|
294
|
-
table.add_column("Status", style=SUCCESS_STYLE, width=15)
|
|
295
|
-
|
|
296
|
-
table.add_row("Agents", str(len(agents)), AVAILABLE_STATUS)
|
|
297
|
-
table.add_row("Tools", str(len(tools)), AVAILABLE_STATUS)
|
|
298
|
-
table.add_row("MCPs", str(len(mcps)), AVAILABLE_STATUS)
|
|
299
|
-
|
|
300
|
-
if compact:
|
|
301
|
-
connection_summary = "GL AIP reachable"
|
|
302
|
-
console.print(f"[dim]⢠Base URL[/dim]: {client.api_url} ({connection_summary})")
|
|
303
|
-
console.print(f"[dim]⢠Agent timeout[/dim]: {DEFAULT_AGENT_RUN_TIMEOUT}s")
|
|
304
|
-
console.print(f"[dim]⢠Resources[/dim]: agents {len(agents)}, tools {len(tools)}, mcps {len(mcps)}")
|
|
305
|
-
else:
|
|
306
|
-
console.print( # pragma: no cover - UI display formatting
|
|
307
|
-
AIPPanel(
|
|
308
|
-
f"[{SUCCESS_STYLE}]ā
Connected to GL AIP[/]\n"
|
|
309
|
-
f"š API URL: {client.api_url}\n"
|
|
310
|
-
f"{ICON_AGENT} Agent Run Timeout: {DEFAULT_AGENT_RUN_TIMEOUT}s",
|
|
311
|
-
title="š Connection Status",
|
|
312
|
-
border_style=SUCCESS,
|
|
313
|
-
)
|
|
314
|
-
)
|
|
315
|
-
|
|
316
|
-
console.print(table) # pragma: no cover - UI display formatting
|
|
317
|
-
|
|
318
380
|
except Exception as e:
|
|
319
381
|
# Show AIP Ready status even if connection fails
|
|
320
382
|
if compact:
|
|
321
383
|
status_text = "API call failed"
|
|
322
|
-
|
|
384
|
+
api_url = getattr(client, "api_url", config.get("api_url", ""))
|
|
385
|
+
console.print(f"[dim]⢠Base URL[/dim]: {api_url} ({status_text})")
|
|
323
386
|
console.print(f"[{ERROR_STYLE}]⢠Error[/]: {e}")
|
|
324
387
|
console.print("[dim]⢠Tip[/dim]: Check network connectivity or API permissions and try again.")
|
|
325
388
|
console.print("[dim]⢠Resources[/dim]: unavailable")
|
|
326
389
|
else:
|
|
390
|
+
api_url = getattr(client, "api_url", config.get("api_url", ""))
|
|
327
391
|
console.print(
|
|
328
392
|
AIPPanel(
|
|
329
393
|
f"[{WARNING_STYLE}]ā ļø Connection established but API call failed[/]\n"
|
|
330
|
-
f"š API URL: {
|
|
394
|
+
f"š API URL: {api_url}\n"
|
|
331
395
|
f"ā Error: {e}\n\n"
|
|
332
396
|
f"š” This usually means:\n"
|
|
333
397
|
f" ⢠Network connectivity issues\n"
|
|
@@ -335,8 +399,37 @@ def _create_and_test_client(config: dict, console: Console, *, compact: bool = F
|
|
|
335
399
|
f" ⢠Backend service issues",
|
|
336
400
|
title="ā ļø Partial Connection",
|
|
337
401
|
border_style=WARNING,
|
|
338
|
-
)
|
|
402
|
+
),
|
|
339
403
|
)
|
|
404
|
+
return client
|
|
405
|
+
|
|
406
|
+
# Create status table
|
|
407
|
+
table = AIPTable(title="š GL AIP Status")
|
|
408
|
+
table.add_column("Resource", style=INFO, width=15)
|
|
409
|
+
table.add_column("Count", style=NEUTRAL, width=10)
|
|
410
|
+
table.add_column("Status", style=SUCCESS_STYLE, width=15)
|
|
411
|
+
|
|
412
|
+
table.add_row("Agents", str(len(agents)), AVAILABLE_STATUS)
|
|
413
|
+
table.add_row("Tools", str(len(tools)), AVAILABLE_STATUS)
|
|
414
|
+
table.add_row("MCPs", str(len(mcps)), AVAILABLE_STATUS)
|
|
415
|
+
|
|
416
|
+
if compact:
|
|
417
|
+
connection_summary = "GL AIP reachable"
|
|
418
|
+
console.print(f"[dim]⢠Base URL[/dim]: {client.api_url} ({connection_summary})")
|
|
419
|
+
console.print(f"[dim]⢠Agent timeout[/dim]: {DEFAULT_AGENT_RUN_TIMEOUT}s")
|
|
420
|
+
console.print(f"[dim]⢠Resources[/dim]: agents {len(agents)}, tools {len(tools)}, mcps {len(mcps)}")
|
|
421
|
+
else:
|
|
422
|
+
console.print( # pragma: no cover - UI display formatting
|
|
423
|
+
AIPPanel(
|
|
424
|
+
f"[{SUCCESS_STYLE}]ā
Connected to GL AIP[/]\n"
|
|
425
|
+
f"š API URL: {client.api_url}\n"
|
|
426
|
+
f"{ICON_AGENT} Agent Run Timeout: {DEFAULT_AGENT_RUN_TIMEOUT}s",
|
|
427
|
+
title="š Connection Status",
|
|
428
|
+
border_style=SUCCESS,
|
|
429
|
+
),
|
|
430
|
+
)
|
|
431
|
+
|
|
432
|
+
console.print(table) # pragma: no cover - UI display formatting
|
|
340
433
|
|
|
341
434
|
return client
|
|
342
435
|
|
|
@@ -354,44 +447,67 @@ def _handle_connection_error(config: dict, console: Console, error: Exception) -
|
|
|
354
447
|
f" ⢠Run 'aip config list' to check configuration",
|
|
355
448
|
title="ā Connection Error",
|
|
356
449
|
border_style=ERROR,
|
|
357
|
-
)
|
|
450
|
+
),
|
|
358
451
|
)
|
|
359
|
-
|
|
452
|
+
# Log and return; callers decide whether to exit.
|
|
360
453
|
|
|
361
454
|
|
|
362
455
|
@main.command()
|
|
456
|
+
@click.option(
|
|
457
|
+
"--account",
|
|
458
|
+
"account_name",
|
|
459
|
+
help="Target a named account profile for this command",
|
|
460
|
+
)
|
|
363
461
|
@click.pass_context
|
|
364
|
-
def status(ctx: Any) -> None:
|
|
462
|
+
def status(ctx: Any, account_name: str | None) -> None:
|
|
365
463
|
"""Show connection status and basic info."""
|
|
366
464
|
config: dict = {}
|
|
367
465
|
console: Console | None = None
|
|
368
466
|
try:
|
|
369
|
-
|
|
370
|
-
|
|
467
|
+
if account_name:
|
|
468
|
+
if ctx.obj is None:
|
|
469
|
+
ctx.obj = {}
|
|
470
|
+
ctx.obj["account_name"] = account_name
|
|
371
471
|
|
|
372
|
-
|
|
373
|
-
_display_cache_summary(console, slash_mode, cache_line, cache_note)
|
|
472
|
+
console, slash_mode = _resolve_status_console(ctx)
|
|
374
473
|
|
|
375
474
|
# Load and merge configuration
|
|
376
475
|
config = _load_and_merge_config(ctx)
|
|
377
476
|
|
|
477
|
+
ready_printed = _render_status_heading(console, slash_mode, config)
|
|
478
|
+
if not ready_printed:
|
|
479
|
+
console.print(f"[{SUCCESS_STYLE}]ā
GL AIP ready[/] (SDK v{sdk_version()})")
|
|
480
|
+
|
|
481
|
+
cache_result = _collect_cache_summary()
|
|
482
|
+
if isinstance(cache_result, tuple) and len(cache_result) == 2:
|
|
483
|
+
cache_line, cache_note = cache_result
|
|
484
|
+
else:
|
|
485
|
+
cache_line, cache_note = cache_result, None
|
|
486
|
+
_display_cache_summary(console, slash_mode, cache_line, cache_note)
|
|
487
|
+
|
|
378
488
|
# Validate configuration
|
|
379
489
|
_validate_config_and_show_error(config, console)
|
|
380
490
|
|
|
381
491
|
# Create and test client connection using unified compact layout
|
|
382
492
|
client = _create_and_test_client(config, console, compact=True)
|
|
383
|
-
client
|
|
493
|
+
close = getattr(client, "close", None)
|
|
494
|
+
if callable(close):
|
|
495
|
+
try:
|
|
496
|
+
close()
|
|
497
|
+
except Exception:
|
|
498
|
+
pass
|
|
384
499
|
|
|
385
500
|
except Exception as e:
|
|
386
|
-
# Handle any unexpected errors during the process
|
|
501
|
+
# Handle any unexpected errors during the process and exit with error code
|
|
387
502
|
fallback_console = console or Console()
|
|
388
503
|
_handle_connection_error(config or {}, fallback_console, e)
|
|
504
|
+
sys.exit(1)
|
|
389
505
|
|
|
390
506
|
|
|
391
507
|
@main.command()
|
|
392
508
|
def version() -> None:
|
|
393
509
|
"""Show version information."""
|
|
394
|
-
branding = AIPBranding.create_from_sdk(sdk_version=
|
|
510
|
+
branding = AIPBranding.create_from_sdk(sdk_version=sdk_version(), package_name="glaip-sdk")
|
|
395
511
|
branding.display_version_panel()
|
|
396
512
|
|
|
397
513
|
|
|
@@ -404,6 +520,7 @@ def version() -> None:
|
|
|
404
520
|
)
|
|
405
521
|
def update(check_only: bool, force: bool) -> None:
|
|
406
522
|
"""Update AIP SDK to the latest version from PyPI."""
|
|
523
|
+
slash_mode = in_slash_mode()
|
|
407
524
|
try:
|
|
408
525
|
console = Console()
|
|
409
526
|
|
|
@@ -413,44 +530,47 @@ def update(check_only: bool, force: bool) -> None:
|
|
|
413
530
|
"[bold blue]š Checking for updates...[/bold blue]\n\nš” To install updates, run: aip update",
|
|
414
531
|
title="š Update Check",
|
|
415
532
|
border_style="blue",
|
|
416
|
-
)
|
|
533
|
+
),
|
|
417
534
|
)
|
|
418
535
|
return
|
|
419
536
|
|
|
537
|
+
update_hint = ""
|
|
538
|
+
if not slash_mode:
|
|
539
|
+
update_hint = "\nš” Use --check-only to just check for updates"
|
|
540
|
+
|
|
420
541
|
console.print(
|
|
421
542
|
AIPPanel(
|
|
422
543
|
"[bold blue]š Updating AIP SDK...[/bold blue]\n\n"
|
|
423
|
-
"š¦ This will update the package from PyPI
|
|
424
|
-
"
|
|
544
|
+
"š¦ This will update the package from PyPI"
|
|
545
|
+
f"{update_hint}",
|
|
425
546
|
title="Update Process",
|
|
426
547
|
border_style="blue",
|
|
427
548
|
padding=(0, 1),
|
|
428
|
-
)
|
|
549
|
+
),
|
|
429
550
|
)
|
|
430
551
|
|
|
431
552
|
# Update using pip
|
|
432
553
|
try:
|
|
433
|
-
cmd =
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
"pip",
|
|
437
|
-
"install",
|
|
438
|
-
"--upgrade",
|
|
439
|
-
"glaip-sdk",
|
|
440
|
-
]
|
|
554
|
+
cmd = list(_build_upgrade_command(include_prerelease=False))
|
|
555
|
+
# Replace package name with "glaip-sdk" (main.py uses different name)
|
|
556
|
+
cmd[-1] = "glaip-sdk"
|
|
441
557
|
if force:
|
|
442
558
|
cmd.insert(5, "--force-reinstall")
|
|
443
559
|
subprocess.run(cmd, capture_output=True, text=True, check=True)
|
|
444
560
|
|
|
561
|
+
verify_hint = ""
|
|
562
|
+
if not slash_mode:
|
|
563
|
+
verify_hint = "\nš” Restart your terminal or run 'aip --version' to verify"
|
|
564
|
+
|
|
445
565
|
console.print(
|
|
446
566
|
AIPPanel(
|
|
447
567
|
f"[{SUCCESS_STYLE}]ā
Update successful![/]\n\n"
|
|
448
|
-
"š AIP SDK has been updated to the latest version
|
|
449
|
-
"
|
|
568
|
+
"š AIP SDK has been updated to the latest version"
|
|
569
|
+
f"{verify_hint}",
|
|
450
570
|
title="š Update Complete",
|
|
451
571
|
border_style=SUCCESS,
|
|
452
572
|
padding=(0, 1),
|
|
453
|
-
)
|
|
573
|
+
),
|
|
454
574
|
)
|
|
455
575
|
|
|
456
576
|
# Show new version
|
|
@@ -474,7 +594,7 @@ def update(check_only: bool, force: bool) -> None:
|
|
|
474
594
|
title="ā Update Error",
|
|
475
595
|
border_style=ERROR,
|
|
476
596
|
padding=(0, 1),
|
|
477
|
-
)
|
|
597
|
+
),
|
|
478
598
|
)
|
|
479
599
|
sys.exit(1)
|
|
480
600
|
|
|
@@ -486,10 +606,10 @@ def update(check_only: bool, force: bool) -> None:
|
|
|
486
606
|
" Then try: aip update",
|
|
487
607
|
title="ā Missing Dependency",
|
|
488
608
|
border_style=ERROR,
|
|
489
|
-
)
|
|
609
|
+
),
|
|
490
610
|
)
|
|
491
611
|
sys.exit(1)
|
|
492
612
|
|
|
493
613
|
|
|
494
614
|
if __name__ == "__main__":
|
|
495
|
-
main()
|
|
615
|
+
main() # pylint: disable=no-value-for-parameter
|