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