glaip-sdk 0.0.7__py3-none-any.whl ā 0.6.5b6__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 +6 -3
- glaip_sdk/_version.py +12 -5
- glaip_sdk/agents/__init__.py +27 -0
- glaip_sdk/agents/base.py +1126 -0
- glaip_sdk/branding.py +79 -15
- glaip_sdk/cli/account_store.py +540 -0
- glaip_sdk/cli/agent_config.py +2 -6
- glaip_sdk/cli/auth.py +699 -0
- glaip_sdk/cli/commands/__init__.py +2 -2
- glaip_sdk/cli/commands/accounts.py +746 -0
- glaip_sdk/cli/commands/agents.py +503 -183
- glaip_sdk/cli/commands/common_config.py +101 -0
- glaip_sdk/cli/commands/configure.py +774 -137
- glaip_sdk/cli/commands/mcps.py +1124 -181
- glaip_sdk/cli/commands/models.py +25 -10
- glaip_sdk/cli/commands/tools.py +144 -92
- glaip_sdk/cli/commands/transcripts.py +755 -0
- glaip_sdk/cli/commands/update.py +61 -0
- glaip_sdk/cli/config.py +95 -0
- glaip_sdk/cli/constants.py +38 -0
- glaip_sdk/cli/context.py +150 -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 +143 -53
- glaip_sdk/cli/hints.py +57 -0
- glaip_sdk/cli/io.py +24 -18
- glaip_sdk/cli/main.py +420 -145
- glaip_sdk/cli/masking.py +136 -0
- glaip_sdk/cli/mcp_validators.py +287 -0
- glaip_sdk/cli/pager.py +266 -0
- glaip_sdk/cli/parsers/__init__.py +7 -0
- glaip_sdk/cli/parsers/json_input.py +177 -0
- glaip_sdk/cli/resolution.py +28 -21
- glaip_sdk/cli/rich_helpers.py +27 -0
- glaip_sdk/cli/slash/__init__.py +15 -0
- glaip_sdk/cli/slash/accounts_controller.py +500 -0
- glaip_sdk/cli/slash/accounts_shared.py +75 -0
- glaip_sdk/cli/slash/agent_session.py +282 -0
- glaip_sdk/cli/slash/prompt.py +245 -0
- glaip_sdk/cli/slash/remote_runs_controller.py +566 -0
- glaip_sdk/cli/slash/session.py +1679 -0
- 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 +872 -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 +31 -0
- glaip_sdk/cli/transcript/cache.py +536 -0
- glaip_sdk/cli/transcript/capture.py +329 -0
- glaip_sdk/cli/transcript/export.py +38 -0
- glaip_sdk/cli/transcript/history.py +815 -0
- glaip_sdk/cli/transcript/launcher.py +77 -0
- glaip_sdk/cli/transcript/viewer.py +372 -0
- glaip_sdk/cli/update_notifier.py +290 -0
- glaip_sdk/cli/utils.py +247 -1238
- glaip_sdk/cli/validators.py +16 -18
- glaip_sdk/client/__init__.py +2 -1
- glaip_sdk/client/_agent_payloads.py +520 -0
- glaip_sdk/client/agent_runs.py +147 -0
- glaip_sdk/client/agents.py +940 -574
- glaip_sdk/client/base.py +163 -48
- glaip_sdk/client/main.py +35 -12
- glaip_sdk/client/mcps.py +126 -18
- glaip_sdk/client/run_rendering.py +415 -0
- glaip_sdk/client/shared.py +21 -0
- glaip_sdk/client/tools.py +195 -37
- glaip_sdk/client/validators.py +20 -48
- glaip_sdk/config/constants.py +15 -5
- glaip_sdk/exceptions.py +16 -9
- glaip_sdk/icons.py +25 -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 +7 -0
- glaip_sdk/payload_schemas/agent.py +85 -0
- 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 +231 -0
- glaip_sdk/rich_components.py +98 -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 +597 -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 +158 -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 +177 -0
- glaip_sdk/tools/__init__.py +22 -0
- glaip_sdk/tools/base.py +435 -0
- glaip_sdk/utils/__init__.py +59 -13
- glaip_sdk/utils/a2a/__init__.py +34 -0
- glaip_sdk/utils/a2a/event_processor.py +188 -0
- glaip_sdk/utils/agent_config.py +53 -40
- glaip_sdk/utils/bundler.py +267 -0
- glaip_sdk/utils/client.py +111 -0
- glaip_sdk/utils/client_utils.py +58 -26
- glaip_sdk/utils/datetime_helpers.py +58 -0
- glaip_sdk/utils/discovery.py +78 -0
- glaip_sdk/utils/display.py +65 -32
- glaip_sdk/utils/export.py +143 -0
- glaip_sdk/utils/general.py +1 -36
- glaip_sdk/utils/import_export.py +20 -25
- 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 +85 -43
- glaip_sdk/utils/rendering/layout/__init__.py +64 -0
- glaip_sdk/utils/rendering/{renderer ā layout}/panels.py +51 -19
- glaip_sdk/utils/rendering/layout/progress.py +202 -0
- glaip_sdk/utils/rendering/layout/summary.py +74 -0
- glaip_sdk/utils/rendering/layout/transcript.py +606 -0
- glaip_sdk/utils/rendering/models.py +39 -7
- glaip_sdk/utils/rendering/renderer/__init__.py +9 -51
- glaip_sdk/utils/rendering/renderer/base.py +672 -759
- glaip_sdk/utils/rendering/renderer/config.py +4 -10
- glaip_sdk/utils/rendering/renderer/debug.py +75 -22
- glaip_sdk/utils/rendering/renderer/factory.py +138 -0
- glaip_sdk/utils/rendering/renderer/stream.py +13 -54
- 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 +182 -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/step_tree_state.py +100 -0
- glaip_sdk/utils/rendering/steps/__init__.py +34 -0
- glaip_sdk/utils/rendering/steps/event_processor.py +778 -0
- 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 +422 -0
- glaip_sdk/utils/serialization.py +184 -51
- glaip_sdk/utils/sync.py +142 -0
- glaip_sdk/utils/tool_detection.py +33 -0
- glaip_sdk/utils/validation.py +21 -30
- {glaip_sdk-0.0.7.dist-info ā glaip_sdk-0.6.5b6.dist-info}/METADATA +58 -12
- glaip_sdk-0.6.5b6.dist-info/RECORD +159 -0
- {glaip_sdk-0.0.7.dist-info ā glaip_sdk-0.6.5b6.dist-info}/WHEEL +1 -1
- glaip_sdk/models.py +0 -250
- glaip_sdk/utils/rendering/renderer/progress.py +0 -118
- glaip_sdk/utils/rendering/steps.py +0 -232
- glaip_sdk/utils/rich_utils.py +0 -29
- glaip_sdk-0.0.7.dist-info/RECORD +0 -55
- {glaip_sdk-0.0.7.dist-info ā glaip_sdk-0.6.5b6.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,25 +13,83 @@ import click
|
|
|
13
13
|
from rich.console import Console
|
|
14
14
|
|
|
15
15
|
from glaip_sdk import Client
|
|
16
|
-
from glaip_sdk.
|
|
17
|
-
|
|
16
|
+
from glaip_sdk.branding import (
|
|
17
|
+
ERROR,
|
|
18
|
+
ERROR_STYLE,
|
|
19
|
+
INFO,
|
|
20
|
+
INFO_STYLE,
|
|
21
|
+
NEUTRAL,
|
|
22
|
+
SUCCESS,
|
|
23
|
+
SUCCESS_STYLE,
|
|
24
|
+
WARNING,
|
|
25
|
+
WARNING_STYLE,
|
|
26
|
+
AIPBranding,
|
|
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
|
|
18
31
|
from glaip_sdk.cli.commands.agents import agents_group
|
|
19
32
|
from glaip_sdk.cli.commands.configure import (
|
|
20
33
|
config_group,
|
|
21
34
|
configure_command,
|
|
22
|
-
load_config,
|
|
23
35
|
)
|
|
24
36
|
from glaip_sdk.cli.commands.mcps import mcps_group
|
|
25
37
|
from glaip_sdk.cli.commands.models import models_group
|
|
26
38
|
from glaip_sdk.cli.commands.tools import tools_group
|
|
27
|
-
from glaip_sdk.
|
|
39
|
+
from glaip_sdk.cli.commands.transcripts import transcripts_group
|
|
40
|
+
from glaip_sdk.cli.commands.update import _build_upgrade_command, update_command
|
|
41
|
+
from glaip_sdk.cli.config import load_config
|
|
42
|
+
from glaip_sdk.cli.hints import in_slash_mode
|
|
43
|
+
from glaip_sdk.cli.transcript import get_transcript_cache_stats
|
|
44
|
+
from glaip_sdk.cli.update_notifier import maybe_notify_update
|
|
45
|
+
from glaip_sdk.cli.utils import format_size, sdk_version, spinner_context, update_spinner
|
|
46
|
+
from glaip_sdk.config.constants import (
|
|
47
|
+
DEFAULT_AGENT_RUN_TIMEOUT,
|
|
48
|
+
)
|
|
49
|
+
from glaip_sdk.icons import ICON_AGENT
|
|
28
50
|
from glaip_sdk.rich_components import AIPPanel, AIPTable
|
|
29
51
|
|
|
30
52
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
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
|
+
|
|
70
|
+
# Import SlashSession for potential mocking in tests
|
|
71
|
+
try:
|
|
72
|
+
from glaip_sdk.cli.slash import SlashSession
|
|
73
|
+
except ImportError: # pragma: no cover - optional slash dependencies
|
|
74
|
+
# Slash dependencies might not be available in all environments
|
|
75
|
+
SlashSession = None
|
|
76
|
+
|
|
77
|
+
# Constants
|
|
78
|
+
AVAILABLE_STATUS = "ā
Available"
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
@click.group(invoke_without_command=True)
|
|
82
|
+
@click.version_option(package_name="glaip-sdk", prog_name="aip")
|
|
83
|
+
@click.option(
|
|
84
|
+
"--api-url",
|
|
85
|
+
help="(Deprecated) AIP API URL; use profiles via --account instead",
|
|
86
|
+
hidden=True,
|
|
87
|
+
)
|
|
88
|
+
@click.option(
|
|
89
|
+
"--api-key",
|
|
90
|
+
help="(Deprecated) AIP API Key; use profiles via --account instead",
|
|
91
|
+
hidden=True,
|
|
92
|
+
)
|
|
35
93
|
@click.option("--timeout", default=30.0, help="Request timeout in seconds")
|
|
36
94
|
@click.option(
|
|
37
95
|
"--view",
|
|
@@ -41,6 +99,12 @@ from glaip_sdk.rich_components import AIPPanel, AIPTable
|
|
|
41
99
|
help="Output view format",
|
|
42
100
|
)
|
|
43
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
|
+
)
|
|
44
108
|
@click.pass_context
|
|
45
109
|
def main(
|
|
46
110
|
ctx: Any,
|
|
@@ -49,187 +113,395 @@ def main(
|
|
|
49
113
|
timeout: float | None,
|
|
50
114
|
view: str | None,
|
|
51
115
|
no_tty: bool,
|
|
116
|
+
account_name: str | None,
|
|
52
117
|
) -> None:
|
|
53
|
-
"""GL AIP SDK Command Line Interface.
|
|
118
|
+
r"""GL AIP SDK Command Line Interface.
|
|
54
119
|
|
|
55
120
|
A comprehensive CLI for managing GL AIP resources including
|
|
56
121
|
agents, tools, MCPs, and more.
|
|
57
122
|
|
|
123
|
+
\b
|
|
58
124
|
Examples:
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
125
|
+
aip version # Show detailed version info
|
|
126
|
+
aip configure # Configure credentials
|
|
127
|
+
aip accounts add prod # Add account profile
|
|
128
|
+
aip accounts use staging # Switch account
|
|
129
|
+
aip agents list # List all agents
|
|
130
|
+
aip tools create my_tool.py # Create a new tool
|
|
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'.
|
|
64
135
|
"""
|
|
65
|
-
|
|
66
136
|
# Store configuration in context
|
|
67
137
|
ctx.ensure_object(dict)
|
|
68
138
|
ctx.obj["api_url"] = api_url
|
|
69
139
|
ctx.obj["api_key"] = api_key
|
|
70
140
|
ctx.obj["timeout"] = timeout
|
|
71
141
|
ctx.obj["view"] = view
|
|
142
|
+
ctx.obj["account_name"] = account_name
|
|
143
|
+
|
|
144
|
+
_suppress_chatty_loggers()
|
|
72
145
|
|
|
73
146
|
ctx.obj["tty"] = not no_tty
|
|
74
147
|
|
|
148
|
+
launching_slash = (
|
|
149
|
+
ctx.invoked_subcommand is None
|
|
150
|
+
and not ctx.resilient_parsing
|
|
151
|
+
and _should_launch_slash(ctx)
|
|
152
|
+
and SlashSession is not None
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
if not ctx.resilient_parsing and ctx.obj["tty"] and not launching_slash:
|
|
156
|
+
console = Console()
|
|
157
|
+
preferred_console = maybe_notify_update(
|
|
158
|
+
sdk_version(),
|
|
159
|
+
console=console,
|
|
160
|
+
ctx=ctx,
|
|
161
|
+
slash_command="update",
|
|
162
|
+
)
|
|
163
|
+
ctx.obj["_preferred_console"] = preferred_console or console
|
|
164
|
+
|
|
165
|
+
if ctx.invoked_subcommand is None and not ctx.resilient_parsing:
|
|
166
|
+
if launching_slash:
|
|
167
|
+
session = SlashSession(ctx)
|
|
168
|
+
session.run()
|
|
169
|
+
ctx.exit()
|
|
170
|
+
else:
|
|
171
|
+
click.echo(ctx.get_help())
|
|
172
|
+
ctx.exit()
|
|
173
|
+
|
|
75
174
|
|
|
76
175
|
# Add command groups
|
|
176
|
+
main.add_command(accounts_group)
|
|
77
177
|
main.add_command(agents_group)
|
|
78
178
|
main.add_command(config_group)
|
|
79
179
|
main.add_command(tools_group)
|
|
80
180
|
main.add_command(mcps_group)
|
|
81
181
|
main.add_command(models_group)
|
|
182
|
+
main.add_command(transcripts_group)
|
|
82
183
|
|
|
83
184
|
# Add top-level commands
|
|
84
185
|
main.add_command(configure_command)
|
|
186
|
+
main.add_command(update_command)
|
|
85
187
|
|
|
86
188
|
|
|
87
189
|
# Tip: `--version` is provided by click.version_option above.
|
|
88
190
|
|
|
89
191
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
try:
|
|
96
|
-
console = Console()
|
|
192
|
+
def _should_launch_slash(ctx: click.Context) -> bool:
|
|
193
|
+
"""Determine whether to open the command palette automatically."""
|
|
194
|
+
ctx_obj = ctx.obj or {}
|
|
195
|
+
if not bool(ctx_obj.get("tty", True)):
|
|
196
|
+
return False
|
|
97
197
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
198
|
+
if not (sys.stdin.isatty() and sys.stdout.isatty()):
|
|
199
|
+
return False
|
|
200
|
+
|
|
201
|
+
return True
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
def _load_and_merge_config(ctx: click.Context) -> dict:
|
|
205
|
+
"""Load configuration from multiple sources and merge them."""
|
|
206
|
+
context_config = ctx.obj or {}
|
|
207
|
+
account_name = context_config.get("account_name")
|
|
208
|
+
|
|
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
|
+
)
|
|
215
|
+
|
|
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")
|
|
219
|
+
|
|
220
|
+
return {
|
|
221
|
+
"api_url": api_url,
|
|
222
|
+
"api_key": api_key,
|
|
223
|
+
"timeout": timeout,
|
|
224
|
+
"_source": source, # Track where credentials came from
|
|
225
|
+
}
|
|
103
226
|
|
|
104
|
-
|
|
227
|
+
|
|
228
|
+
def _validate_config_and_show_error(config: dict, console: Console) -> None:
|
|
229
|
+
"""Validate configuration and show error if incomplete."""
|
|
230
|
+
store = get_account_store()
|
|
231
|
+
has_accounts = bool(store.list_accounts())
|
|
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"
|
|
105
234
|
console.print(
|
|
106
|
-
|
|
235
|
+
AIPPanel(
|
|
236
|
+
f"[{ERROR_STYLE}]ā Configuration incomplete[/]\n\n"
|
|
237
|
+
f"š Current config:\n"
|
|
238
|
+
f" ⢠API URL: {config.get('api_url', 'Not set')}\n"
|
|
239
|
+
f" ⢠API Key: {'***' + config.get('api_key', '')[-4:] if config.get('api_key') else 'Not set'}\n\n"
|
|
240
|
+
f"š” To fix this:\n"
|
|
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}",
|
|
244
|
+
title="ā Configuration Error",
|
|
245
|
+
border_style=ERROR,
|
|
246
|
+
),
|
|
107
247
|
)
|
|
248
|
+
console.print(f"\n[{SUCCESS_STYLE}]ā
AIP - Ready[/] (SDK v{sdk_version()}) - Configure to connect")
|
|
249
|
+
sys.exit(1)
|
|
108
250
|
|
|
109
|
-
# Load config from file and merge with context
|
|
110
|
-
file_config = load_config()
|
|
111
|
-
context_config = ctx.obj or {}
|
|
112
251
|
|
|
113
|
-
|
|
252
|
+
def _resolve_status_console(ctx: Any) -> tuple[Console, bool]:
|
|
253
|
+
"""Return the console to use and whether we are in slash mode."""
|
|
254
|
+
ctx_obj = ctx.obj if isinstance(ctx.obj, dict) else None
|
|
255
|
+
console_override = ctx_obj.get("_slash_console") if ctx_obj else None
|
|
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()
|
|
261
|
+
slash_mode = in_slash_mode(ctx)
|
|
262
|
+
return console, slash_mode
|
|
114
263
|
|
|
115
|
-
env_config = {}
|
|
116
|
-
if os.getenv("AIP_API_URL"):
|
|
117
|
-
env_config["api_url"] = os.getenv("AIP_API_URL")
|
|
118
|
-
if os.getenv("AIP_API_KEY"):
|
|
119
|
-
env_config["api_key"] = os.getenv("AIP_API_KEY")
|
|
120
264
|
|
|
121
|
-
|
|
122
|
-
|
|
265
|
+
def _render_status_heading(console: Console, slash_mode: bool, config: dict) -> bool:
|
|
266
|
+
"""Print the status heading/banner.
|
|
123
267
|
|
|
124
|
-
|
|
125
|
-
|
|
268
|
+
Returns True if a generic ready line was printed (to avoid duplication).
|
|
269
|
+
"""
|
|
270
|
+
del slash_mode # heading now consistent across invocation contexts
|
|
271
|
+
ready_printed = False
|
|
272
|
+
console.print(f"[{INFO_STYLE}]GL AIP status[/]")
|
|
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
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
def _collect_cache_summary() -> tuple[str | None, str | None]:
|
|
302
|
+
"""Collect transcript cache summary and optional note."""
|
|
303
|
+
try:
|
|
304
|
+
cache_stats = get_transcript_cache_stats()
|
|
305
|
+
except Exception:
|
|
306
|
+
return "[dim]Saved transcripts[/dim]: unavailable", None
|
|
126
307
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
f" ⢠API URL: {config.get('api_url', 'Not set')}\n"
|
|
133
|
-
f" ⢠API Key: {'***' + config.get('api_key', '')[-4:] if config.get('api_key') else 'Not set'}\n\n"
|
|
134
|
-
f"š” To fix this:\n"
|
|
135
|
-
f" ⢠Run 'aip configure' to set up credentials\n"
|
|
136
|
-
f" ⢠Or run 'aip config list' to see current config",
|
|
137
|
-
title="ā Configuration Error",
|
|
138
|
-
border_style="red",
|
|
139
|
-
)
|
|
140
|
-
)
|
|
141
|
-
console.print(
|
|
142
|
-
f"\n[bold green]ā
AIP - Ready[/bold green] (SDK v{_SDK_VERSION}) - Configure to connect"
|
|
143
|
-
)
|
|
144
|
-
sys.exit(1)
|
|
308
|
+
runs_text = f"{cache_stats.entry_count} runs saved"
|
|
309
|
+
if cache_stats.total_bytes:
|
|
310
|
+
size_part = f" Ā· {format_size(cache_stats.total_bytes)} used"
|
|
311
|
+
else:
|
|
312
|
+
size_part = ""
|
|
145
313
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
api_url=config["api_url"],
|
|
149
|
-
api_key=config["api_key"],
|
|
150
|
-
timeout=config.get("timeout", 30.0),
|
|
151
|
-
)
|
|
314
|
+
cache_line = f"[dim]Saved transcripts[/dim]: {runs_text}{size_part} Ā· {cache_stats.cache_dir}"
|
|
315
|
+
return cache_line, None
|
|
152
316
|
|
|
153
|
-
# Test connection by listing resources
|
|
154
|
-
try:
|
|
155
|
-
agents = client.list_agents()
|
|
156
|
-
tools = client.list_tools()
|
|
157
|
-
mcps = client.list_mcps()
|
|
158
317
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
318
|
+
def _display_cache_summary(console: Console, slash_mode: bool, cache_line: str | None, cache_note: str | None) -> None:
|
|
319
|
+
"""Render the cache summary details."""
|
|
320
|
+
if cache_line:
|
|
321
|
+
console.print(cache_line)
|
|
322
|
+
if cache_note and not slash_mode:
|
|
323
|
+
console.print(cache_note)
|
|
164
324
|
|
|
165
|
-
table.add_row("Agents", str(len(agents)), "ā
Available")
|
|
166
|
-
table.add_row("Tools", str(len(tools)), "ā
Available")
|
|
167
|
-
table.add_row("MCPs", str(len(mcps)), "ā
Available")
|
|
168
325
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
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
|
|
177
335
|
)
|
|
336
|
+
return []
|
|
337
|
+
return []
|
|
178
338
|
|
|
179
|
-
console.print(table)
|
|
180
339
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
340
|
+
def _get_client_from_config(config: dict) -> Any:
|
|
341
|
+
"""Return a Client instance built from config."""
|
|
342
|
+
return Client(
|
|
343
|
+
api_url=config["api_url"],
|
|
344
|
+
api_key=config["api_key"],
|
|
345
|
+
timeout=config.get("timeout", 30.0),
|
|
346
|
+
)
|
|
347
|
+
|
|
186
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
|
|
354
|
+
try:
|
|
355
|
+
with spinner_context(
|
|
356
|
+
None,
|
|
357
|
+
"[bold blue]Checking GL AIP statusā¦[/bold blue]",
|
|
358
|
+
console_override=console,
|
|
359
|
+
spinner_style=INFO,
|
|
360
|
+
) as status_indicator:
|
|
361
|
+
update_spinner(status_indicator, "[bold blue]Fetching agentsā¦[/bold blue]")
|
|
362
|
+
agents = client.list_agents()
|
|
363
|
+
|
|
364
|
+
update_spinner(status_indicator, "[bold blue]Fetching toolsā¦[/bold blue]")
|
|
365
|
+
tools = client.list_tools()
|
|
366
|
+
|
|
367
|
+
update_spinner(status_indicator, "[bold blue]Fetching MCPsā¦[/bold blue]")
|
|
368
|
+
mcps = client.list_mcps()
|
|
369
|
+
except Exception as e:
|
|
370
|
+
# Show AIP Ready status even if connection fails
|
|
371
|
+
if compact:
|
|
372
|
+
status_text = "API call failed"
|
|
373
|
+
api_url = getattr(client, "api_url", config.get("api_url", ""))
|
|
374
|
+
console.print(f"[dim]⢠Base URL[/dim]: {api_url} ({status_text})")
|
|
375
|
+
console.print(f"[{ERROR_STYLE}]⢠Error[/]: {e}")
|
|
376
|
+
console.print("[dim]⢠Tip[/dim]: Check network connectivity or API permissions and try again.")
|
|
377
|
+
console.print("[dim]⢠Resources[/dim]: unavailable")
|
|
378
|
+
else:
|
|
379
|
+
api_url = getattr(client, "api_url", config.get("api_url", ""))
|
|
187
380
|
console.print(
|
|
188
381
|
AIPPanel(
|
|
189
|
-
f"[
|
|
190
|
-
f"š API URL: {
|
|
382
|
+
f"[{WARNING_STYLE}]ā ļø Connection established but API call failed[/]\n"
|
|
383
|
+
f"š API URL: {api_url}\n"
|
|
191
384
|
f"ā Error: {e}\n\n"
|
|
192
385
|
f"š” This usually means:\n"
|
|
193
386
|
f" ⢠Network connectivity issues\n"
|
|
194
387
|
f" ⢠API permissions problems\n"
|
|
195
388
|
f" ⢠Backend service issues",
|
|
196
389
|
title="ā ļø Partial Connection",
|
|
197
|
-
border_style=
|
|
198
|
-
)
|
|
390
|
+
border_style=WARNING,
|
|
391
|
+
),
|
|
199
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
|
+
)
|
|
200
420
|
|
|
201
|
-
|
|
421
|
+
console.print(table) # pragma: no cover - UI display formatting
|
|
422
|
+
|
|
423
|
+
return client
|
|
424
|
+
|
|
425
|
+
|
|
426
|
+
def _handle_connection_error(config: dict, console: Console, error: Exception) -> None:
|
|
427
|
+
"""Handle connection errors and show troubleshooting information."""
|
|
428
|
+
console.print(
|
|
429
|
+
AIPPanel(
|
|
430
|
+
f"[{ERROR_STYLE}]ā Connection failed[/]\n\n"
|
|
431
|
+
f"š Error: {error}\n\n"
|
|
432
|
+
f"š” Troubleshooting steps:\n"
|
|
433
|
+
f" ⢠Verify your API URL and key are correct\n"
|
|
434
|
+
f" ⢠Check network connectivity to {config.get('api_url', 'your API')}\n"
|
|
435
|
+
f" ⢠Run 'aip configure' to update credentials\n"
|
|
436
|
+
f" ⢠Run 'aip config list' to check configuration",
|
|
437
|
+
title="ā Connection Error",
|
|
438
|
+
border_style=ERROR,
|
|
439
|
+
),
|
|
440
|
+
)
|
|
441
|
+
# Log and return; callers decide whether to exit.
|
|
442
|
+
|
|
443
|
+
|
|
444
|
+
@main.command()
|
|
445
|
+
@click.option(
|
|
446
|
+
"--account",
|
|
447
|
+
"account_name",
|
|
448
|
+
help="Target a named account profile for this command",
|
|
449
|
+
)
|
|
450
|
+
@click.pass_context
|
|
451
|
+
def status(ctx: Any, account_name: str | None) -> None:
|
|
452
|
+
"""Show connection status and basic info."""
|
|
453
|
+
config: dict = {}
|
|
454
|
+
console: Console | None = None
|
|
455
|
+
try:
|
|
456
|
+
if account_name:
|
|
457
|
+
if ctx.obj is None:
|
|
458
|
+
ctx.obj = {}
|
|
459
|
+
ctx.obj["account_name"] = account_name
|
|
460
|
+
|
|
461
|
+
console, slash_mode = _resolve_status_console(ctx)
|
|
462
|
+
|
|
463
|
+
# Load and merge configuration
|
|
464
|
+
config = _load_and_merge_config(ctx)
|
|
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
|
+
|
|
477
|
+
# Validate configuration
|
|
478
|
+
_validate_config_and_show_error(config, console)
|
|
479
|
+
|
|
480
|
+
# Create and test client connection using unified compact layout
|
|
481
|
+
client = _create_and_test_client(config, console, compact=True)
|
|
482
|
+
close = getattr(client, "close", None)
|
|
483
|
+
if callable(close):
|
|
484
|
+
try:
|
|
485
|
+
close()
|
|
486
|
+
except Exception:
|
|
487
|
+
pass
|
|
202
488
|
|
|
203
489
|
except Exception as e:
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
f"š Error: {e}\n\n"
|
|
208
|
-
f"š” Troubleshooting steps:\n"
|
|
209
|
-
f" ⢠Run 'aip config list' to check configuration\n"
|
|
210
|
-
f" ⢠Run 'aip configure' to update credentials\n"
|
|
211
|
-
f" ⢠Verify your API URL and key are correct\n"
|
|
212
|
-
f" ⢠Check network connectivity to {config.get('api_url', 'your API')}",
|
|
213
|
-
title="ā Connection Error",
|
|
214
|
-
border_style="red",
|
|
215
|
-
)
|
|
216
|
-
)
|
|
490
|
+
# Handle any unexpected errors during the process and exit with error code
|
|
491
|
+
fallback_console = console or Console()
|
|
492
|
+
_handle_connection_error(config or {}, fallback_console, e)
|
|
217
493
|
sys.exit(1)
|
|
218
494
|
|
|
219
495
|
|
|
220
496
|
@main.command()
|
|
221
497
|
def version() -> None:
|
|
222
498
|
"""Show version information."""
|
|
223
|
-
branding = AIPBranding.create_from_sdk(
|
|
224
|
-
sdk_version=_SDK_VERSION, package_name="glaip-sdk"
|
|
225
|
-
)
|
|
499
|
+
branding = AIPBranding.create_from_sdk(sdk_version=sdk_version(), package_name="glaip-sdk")
|
|
226
500
|
branding.display_version_panel()
|
|
227
501
|
|
|
228
502
|
|
|
229
503
|
@main.command()
|
|
230
|
-
@click.option(
|
|
231
|
-
"--check-only", is_flag=True, help="Only check for updates without installing"
|
|
232
|
-
)
|
|
504
|
+
@click.option("--check-only", is_flag=True, help="Only check for updates without installing")
|
|
233
505
|
@click.option(
|
|
234
506
|
"--force",
|
|
235
507
|
is_flag=True,
|
|
@@ -237,54 +509,57 @@ def version() -> None:
|
|
|
237
509
|
)
|
|
238
510
|
def update(check_only: bool, force: bool) -> None:
|
|
239
511
|
"""Update AIP SDK to the latest version from PyPI."""
|
|
512
|
+
slash_mode = in_slash_mode()
|
|
240
513
|
try:
|
|
241
514
|
console = Console()
|
|
242
515
|
|
|
243
516
|
if check_only:
|
|
244
517
|
console.print(
|
|
245
518
|
AIPPanel(
|
|
246
|
-
"[bold blue]š Checking for updates...[/bold blue]\n\n"
|
|
247
|
-
"š” To install updates, run: aip update",
|
|
519
|
+
"[bold blue]š Checking for updates...[/bold blue]\n\nš” To install updates, run: aip update",
|
|
248
520
|
title="š Update Check",
|
|
249
521
|
border_style="blue",
|
|
250
|
-
)
|
|
522
|
+
),
|
|
251
523
|
)
|
|
252
524
|
return
|
|
253
525
|
|
|
526
|
+
update_hint = ""
|
|
527
|
+
if not slash_mode:
|
|
528
|
+
update_hint = "\nš” Use --check-only to just check for updates"
|
|
529
|
+
|
|
254
530
|
console.print(
|
|
255
531
|
AIPPanel(
|
|
256
532
|
"[bold blue]š Updating AIP SDK...[/bold blue]\n\n"
|
|
257
|
-
"š¦ This will update the package from PyPI
|
|
258
|
-
"
|
|
259
|
-
title="
|
|
533
|
+
"š¦ This will update the package from PyPI"
|
|
534
|
+
f"{update_hint}",
|
|
535
|
+
title="Update Process",
|
|
260
536
|
border_style="blue",
|
|
261
537
|
padding=(0, 1),
|
|
262
|
-
)
|
|
538
|
+
),
|
|
263
539
|
)
|
|
264
540
|
|
|
265
541
|
# Update using pip
|
|
266
542
|
try:
|
|
267
|
-
cmd =
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
"pip",
|
|
271
|
-
"install",
|
|
272
|
-
"--upgrade",
|
|
273
|
-
"glaip-sdk",
|
|
274
|
-
]
|
|
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"
|
|
275
546
|
if force:
|
|
276
547
|
cmd.insert(5, "--force-reinstall")
|
|
277
548
|
subprocess.run(cmd, capture_output=True, text=True, check=True)
|
|
278
549
|
|
|
550
|
+
verify_hint = ""
|
|
551
|
+
if not slash_mode:
|
|
552
|
+
verify_hint = "\nš” Restart your terminal or run 'aip --version' to verify"
|
|
553
|
+
|
|
279
554
|
console.print(
|
|
280
555
|
AIPPanel(
|
|
281
|
-
"[
|
|
282
|
-
"š AIP SDK has been updated to the latest version
|
|
283
|
-
"
|
|
556
|
+
f"[{SUCCESS_STYLE}]ā
Update successful![/]\n\n"
|
|
557
|
+
"š AIP SDK has been updated to the latest version"
|
|
558
|
+
f"{verify_hint}",
|
|
284
559
|
title="š Update Complete",
|
|
285
|
-
border_style=
|
|
560
|
+
border_style=SUCCESS,
|
|
286
561
|
padding=(0, 1),
|
|
287
|
-
)
|
|
562
|
+
),
|
|
288
563
|
)
|
|
289
564
|
|
|
290
565
|
# Show new version
|
|
@@ -299,31 +574,31 @@ def update(check_only: bool, force: bool) -> None:
|
|
|
299
574
|
except subprocess.CalledProcessError as e:
|
|
300
575
|
console.print(
|
|
301
576
|
AIPPanel(
|
|
302
|
-
f"[
|
|
577
|
+
f"[{ERROR_STYLE}]ā Update failed[/]\n\n"
|
|
303
578
|
f"š Error: {e.stderr}\n\n"
|
|
304
579
|
"š” Troubleshooting:\n"
|
|
305
580
|
" ⢠Check your internet connection\n"
|
|
306
581
|
" ⢠Try running: pip install --upgrade glaip-sdk\n"
|
|
307
582
|
" ⢠Check if you have write permissions",
|
|
308
583
|
title="ā Update Error",
|
|
309
|
-
border_style=
|
|
584
|
+
border_style=ERROR,
|
|
310
585
|
padding=(0, 1),
|
|
311
|
-
)
|
|
586
|
+
),
|
|
312
587
|
)
|
|
313
588
|
sys.exit(1)
|
|
314
589
|
|
|
315
590
|
except ImportError:
|
|
316
591
|
console.print(
|
|
317
592
|
AIPPanel(
|
|
318
|
-
"[
|
|
593
|
+
f"[{ERROR_STYLE}]ā Rich library not available[/]\n\n"
|
|
319
594
|
"š” Install rich: pip install rich\n"
|
|
320
595
|
" Then try: aip update",
|
|
321
596
|
title="ā Missing Dependency",
|
|
322
|
-
border_style=
|
|
323
|
-
)
|
|
597
|
+
border_style=ERROR,
|
|
598
|
+
),
|
|
324
599
|
)
|
|
325
600
|
sys.exit(1)
|
|
326
601
|
|
|
327
602
|
|
|
328
603
|
if __name__ == "__main__":
|
|
329
|
-
main()
|
|
604
|
+
main() # pylint: disable=no-value-for-parameter
|