glaip-sdk 0.6.11__py3-none-any.whl → 0.6.14__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.
Files changed (156) hide show
  1. glaip_sdk/__init__.py +42 -5
  2. {glaip_sdk-0.6.11.dist-info → glaip_sdk-0.6.14.dist-info}/METADATA +31 -37
  3. glaip_sdk-0.6.14.dist-info/RECORD +12 -0
  4. {glaip_sdk-0.6.11.dist-info → glaip_sdk-0.6.14.dist-info}/WHEEL +2 -1
  5. glaip_sdk-0.6.14.dist-info/entry_points.txt +2 -0
  6. glaip_sdk-0.6.14.dist-info/top_level.txt +1 -0
  7. glaip_sdk/agents/__init__.py +0 -27
  8. glaip_sdk/agents/base.py +0 -1191
  9. glaip_sdk/cli/__init__.py +0 -9
  10. glaip_sdk/cli/account_store.py +0 -540
  11. glaip_sdk/cli/agent_config.py +0 -78
  12. glaip_sdk/cli/auth.py +0 -699
  13. glaip_sdk/cli/commands/__init__.py +0 -5
  14. glaip_sdk/cli/commands/accounts.py +0 -746
  15. glaip_sdk/cli/commands/agents.py +0 -1509
  16. glaip_sdk/cli/commands/common_config.py +0 -101
  17. glaip_sdk/cli/commands/configure.py +0 -896
  18. glaip_sdk/cli/commands/mcps.py +0 -1356
  19. glaip_sdk/cli/commands/models.py +0 -69
  20. glaip_sdk/cli/commands/tools.py +0 -576
  21. glaip_sdk/cli/commands/transcripts.py +0 -755
  22. glaip_sdk/cli/commands/update.py +0 -61
  23. glaip_sdk/cli/config.py +0 -95
  24. glaip_sdk/cli/constants.py +0 -38
  25. glaip_sdk/cli/context.py +0 -150
  26. glaip_sdk/cli/core/__init__.py +0 -79
  27. glaip_sdk/cli/core/context.py +0 -124
  28. glaip_sdk/cli/core/output.py +0 -846
  29. glaip_sdk/cli/core/prompting.py +0 -649
  30. glaip_sdk/cli/core/rendering.py +0 -187
  31. glaip_sdk/cli/display.py +0 -355
  32. glaip_sdk/cli/hints.py +0 -57
  33. glaip_sdk/cli/io.py +0 -112
  34. glaip_sdk/cli/main.py +0 -604
  35. glaip_sdk/cli/masking.py +0 -136
  36. glaip_sdk/cli/mcp_validators.py +0 -287
  37. glaip_sdk/cli/pager.py +0 -266
  38. glaip_sdk/cli/parsers/__init__.py +0 -7
  39. glaip_sdk/cli/parsers/json_input.py +0 -177
  40. glaip_sdk/cli/resolution.py +0 -67
  41. glaip_sdk/cli/rich_helpers.py +0 -27
  42. glaip_sdk/cli/slash/__init__.py +0 -15
  43. glaip_sdk/cli/slash/accounts_controller.py +0 -578
  44. glaip_sdk/cli/slash/accounts_shared.py +0 -75
  45. glaip_sdk/cli/slash/agent_session.py +0 -285
  46. glaip_sdk/cli/slash/prompt.py +0 -256
  47. glaip_sdk/cli/slash/remote_runs_controller.py +0 -566
  48. glaip_sdk/cli/slash/session.py +0 -1708
  49. glaip_sdk/cli/slash/tui/__init__.py +0 -9
  50. glaip_sdk/cli/slash/tui/accounts_app.py +0 -876
  51. glaip_sdk/cli/slash/tui/background_tasks.py +0 -72
  52. glaip_sdk/cli/slash/tui/loading.py +0 -58
  53. glaip_sdk/cli/slash/tui/remote_runs_app.py +0 -628
  54. glaip_sdk/cli/transcript/__init__.py +0 -31
  55. glaip_sdk/cli/transcript/cache.py +0 -536
  56. glaip_sdk/cli/transcript/capture.py +0 -329
  57. glaip_sdk/cli/transcript/export.py +0 -38
  58. glaip_sdk/cli/transcript/history.py +0 -815
  59. glaip_sdk/cli/transcript/launcher.py +0 -77
  60. glaip_sdk/cli/transcript/viewer.py +0 -374
  61. glaip_sdk/cli/update_notifier.py +0 -290
  62. glaip_sdk/cli/utils.py +0 -263
  63. glaip_sdk/cli/validators.py +0 -238
  64. glaip_sdk/client/__init__.py +0 -11
  65. glaip_sdk/client/_agent_payloads.py +0 -520
  66. glaip_sdk/client/agent_runs.py +0 -147
  67. glaip_sdk/client/agents.py +0 -1335
  68. glaip_sdk/client/base.py +0 -502
  69. glaip_sdk/client/main.py +0 -249
  70. glaip_sdk/client/mcps.py +0 -370
  71. glaip_sdk/client/run_rendering.py +0 -700
  72. glaip_sdk/client/shared.py +0 -21
  73. glaip_sdk/client/tools.py +0 -661
  74. glaip_sdk/client/validators.py +0 -198
  75. glaip_sdk/config/constants.py +0 -52
  76. glaip_sdk/mcps/__init__.py +0 -21
  77. glaip_sdk/mcps/base.py +0 -345
  78. glaip_sdk/models/__init__.py +0 -90
  79. glaip_sdk/models/agent.py +0 -47
  80. glaip_sdk/models/agent_runs.py +0 -116
  81. glaip_sdk/models/common.py +0 -42
  82. glaip_sdk/models/mcp.py +0 -33
  83. glaip_sdk/models/tool.py +0 -33
  84. glaip_sdk/payload_schemas/__init__.py +0 -7
  85. glaip_sdk/payload_schemas/agent.py +0 -85
  86. glaip_sdk/registry/__init__.py +0 -55
  87. glaip_sdk/registry/agent.py +0 -164
  88. glaip_sdk/registry/base.py +0 -139
  89. glaip_sdk/registry/mcp.py +0 -253
  90. glaip_sdk/registry/tool.py +0 -232
  91. glaip_sdk/runner/__init__.py +0 -59
  92. glaip_sdk/runner/base.py +0 -84
  93. glaip_sdk/runner/deps.py +0 -115
  94. glaip_sdk/runner/langgraph.py +0 -782
  95. glaip_sdk/runner/mcp_adapter/__init__.py +0 -13
  96. glaip_sdk/runner/mcp_adapter/base_mcp_adapter.py +0 -43
  97. glaip_sdk/runner/mcp_adapter/langchain_mcp_adapter.py +0 -257
  98. glaip_sdk/runner/mcp_adapter/mcp_config_builder.py +0 -95
  99. glaip_sdk/runner/tool_adapter/__init__.py +0 -18
  100. glaip_sdk/runner/tool_adapter/base_tool_adapter.py +0 -44
  101. glaip_sdk/runner/tool_adapter/langchain_tool_adapter.py +0 -219
  102. glaip_sdk/tools/__init__.py +0 -22
  103. glaip_sdk/tools/base.py +0 -435
  104. glaip_sdk/utils/__init__.py +0 -86
  105. glaip_sdk/utils/a2a/__init__.py +0 -34
  106. glaip_sdk/utils/a2a/event_processor.py +0 -188
  107. glaip_sdk/utils/agent_config.py +0 -194
  108. glaip_sdk/utils/bundler.py +0 -267
  109. glaip_sdk/utils/client.py +0 -111
  110. glaip_sdk/utils/client_utils.py +0 -486
  111. glaip_sdk/utils/datetime_helpers.py +0 -58
  112. glaip_sdk/utils/discovery.py +0 -78
  113. glaip_sdk/utils/display.py +0 -135
  114. glaip_sdk/utils/export.py +0 -143
  115. glaip_sdk/utils/general.py +0 -61
  116. glaip_sdk/utils/import_export.py +0 -168
  117. glaip_sdk/utils/import_resolver.py +0 -492
  118. glaip_sdk/utils/instructions.py +0 -101
  119. glaip_sdk/utils/rendering/__init__.py +0 -115
  120. glaip_sdk/utils/rendering/formatting.py +0 -264
  121. glaip_sdk/utils/rendering/layout/__init__.py +0 -64
  122. glaip_sdk/utils/rendering/layout/panels.py +0 -156
  123. glaip_sdk/utils/rendering/layout/progress.py +0 -202
  124. glaip_sdk/utils/rendering/layout/summary.py +0 -74
  125. glaip_sdk/utils/rendering/layout/transcript.py +0 -606
  126. glaip_sdk/utils/rendering/models.py +0 -85
  127. glaip_sdk/utils/rendering/renderer/__init__.py +0 -55
  128. glaip_sdk/utils/rendering/renderer/base.py +0 -1024
  129. glaip_sdk/utils/rendering/renderer/config.py +0 -27
  130. glaip_sdk/utils/rendering/renderer/console.py +0 -55
  131. glaip_sdk/utils/rendering/renderer/debug.py +0 -178
  132. glaip_sdk/utils/rendering/renderer/factory.py +0 -138
  133. glaip_sdk/utils/rendering/renderer/stream.py +0 -202
  134. glaip_sdk/utils/rendering/renderer/summary_window.py +0 -79
  135. glaip_sdk/utils/rendering/renderer/thinking.py +0 -273
  136. glaip_sdk/utils/rendering/renderer/toggle.py +0 -182
  137. glaip_sdk/utils/rendering/renderer/tool_panels.py +0 -442
  138. glaip_sdk/utils/rendering/renderer/transcript_mode.py +0 -162
  139. glaip_sdk/utils/rendering/state.py +0 -204
  140. glaip_sdk/utils/rendering/step_tree_state.py +0 -100
  141. glaip_sdk/utils/rendering/steps/__init__.py +0 -34
  142. glaip_sdk/utils/rendering/steps/event_processor.py +0 -778
  143. glaip_sdk/utils/rendering/steps/format.py +0 -176
  144. glaip_sdk/utils/rendering/steps/manager.py +0 -387
  145. glaip_sdk/utils/rendering/timing.py +0 -36
  146. glaip_sdk/utils/rendering/viewer/__init__.py +0 -21
  147. glaip_sdk/utils/rendering/viewer/presenter.py +0 -184
  148. glaip_sdk/utils/resource_refs.py +0 -195
  149. glaip_sdk/utils/run_renderer.py +0 -41
  150. glaip_sdk/utils/runtime_config.py +0 -425
  151. glaip_sdk/utils/serialization.py +0 -424
  152. glaip_sdk/utils/sync.py +0 -142
  153. glaip_sdk/utils/tool_detection.py +0 -33
  154. glaip_sdk/utils/validation.py +0 -264
  155. glaip_sdk-0.6.11.dist-info/RECORD +0 -159
  156. glaip_sdk-0.6.11.dist-info/entry_points.txt +0 -3
glaip_sdk/cli/auth.py DELETED
@@ -1,699 +0,0 @@
1
- """Authentication export helpers for MCP CLI commands and credential resolution.
2
-
3
- This module provides utilities for preparing authentication data for export,
4
- including interactive secret capture and placeholder generation.
5
-
6
- It also provides credential resolution for the AIP CLI, supporting multiple
7
- account profiles and environment variable overrides.
8
-
9
- Authors:
10
- Raymond Christopher (raymond.christopher@gdplabs.id)
11
- """
12
-
13
- import os
14
- from collections.abc import Callable, Iterable, Mapping
15
- from typing import Any
16
-
17
- import click
18
- from rich.console import Console
19
-
20
- from glaip_sdk.branding import HINT_PREFIX_STYLE, WARNING_STYLE
21
- from glaip_sdk.cli.account_store import AccountNotFoundError, AccountStoreError, get_account_store
22
- from glaip_sdk.cli.hints import format_command_hint
23
- from glaip_sdk.cli.utils import command_hint
24
-
25
-
26
- def prepare_authentication_export(
27
- auth: dict[str, Any] | None,
28
- *,
29
- prompt_for_secrets: bool,
30
- placeholder: str,
31
- console: Console,
32
- ) -> dict[str, Any] | None:
33
- """Prepare authentication data for export with secret handling.
34
-
35
- This function processes authentication objects from MCP resources and prepares
36
- them for export. It handles secret capture (interactive or placeholder mode),
37
- reconstructs proper authentication structures from helper metadata, and ensures
38
- helper metadata doesn't leak into the final export.
39
-
40
- Args:
41
- auth: Authentication dictionary from an MCP resource. May contain helper
42
- metadata like ``header_keys`` that should be consumed and removed.
43
- prompt_for_secrets: If True, interactively prompt for missing secrets.
44
- If False, use ``placeholder`` automatically.
45
- placeholder: Placeholder text to use for missing secrets when not prompting.
46
- console: Rich ``Console`` instance for user interaction and warnings.
47
-
48
- Returns:
49
- A prepared authentication dictionary ready for export, or ``None`` if
50
- ``auth`` is ``None``.
51
-
52
- Notes:
53
- - Helper metadata (for example, ``header_keys``) is consumed to rebuild
54
- structures but never appears in the final output.
55
- - When ``prompt_for_secrets`` is False and stdin is not a TTY, a warning is
56
- logged.
57
- - Empty user input during prompts defaults to the placeholder value.
58
- """
59
- if auth is None:
60
- return None
61
-
62
- auth_type = auth.get("type")
63
-
64
- # Handle no-auth case
65
- if auth_type == "no-auth":
66
- return {"type": "no-auth"}
67
-
68
- # Handle bearer-token authentication
69
- if auth_type == "bearer-token":
70
- return _prepare_bearer_token_auth(auth, prompt_for_secrets, placeholder, console)
71
-
72
- # Handle api-key authentication
73
- if auth_type == "api-key":
74
- return _prepare_api_key_auth(auth, prompt_for_secrets, placeholder, console)
75
-
76
- # Handle custom-header authentication
77
- if auth_type == "custom-header":
78
- return _prepare_custom_header_auth(auth, prompt_for_secrets, placeholder, console)
79
-
80
- # Unknown auth type - return as-is but strip helper metadata
81
- result = auth.copy()
82
- result.pop("header_keys", None)
83
- return result
84
-
85
-
86
- def _get_token_value(prompt_for_secrets: bool, placeholder: str, console: Console) -> str:
87
- """Get bearer token value either by prompting or using a placeholder.
88
-
89
- Args:
90
- prompt_for_secrets: If True, prompt for the token value.
91
- placeholder: Placeholder to use when not prompting or when input is empty.
92
- console: Rich ``Console`` used to display informational messages.
93
-
94
- Returns:
95
- The token string, either provided by the user or the placeholder.
96
- """
97
- if prompt_for_secrets:
98
- return _prompt_secret_with_placeholder(
99
- console,
100
- warning_message="Bearer token is missing or redacted. Please provide the token.",
101
- prompt_message="Bearer token (leave blank for placeholder)",
102
- placeholder=placeholder,
103
- tip_cli_command="configure",
104
- tip_slash_command="configure",
105
- )
106
-
107
- if not click.get_text_stream("stdin").isatty():
108
- console.print(f"[{WARNING_STYLE}]⚠️ Non-interactive mode: using placeholder for bearer token[/]")
109
- return placeholder
110
-
111
-
112
- def _normalize_header_keys(
113
- header_keys: Iterable[str] | str | None,
114
- *,
115
- default: Iterable[str] | None = None,
116
- ) -> list[str]:
117
- """Normalize header_keys to a list, handling strings and None safely."""
118
- if header_keys is None:
119
- return list(default or [])
120
- if isinstance(header_keys, str):
121
- return [header_keys] if header_keys else list(default or [])
122
- try:
123
- return list(header_keys)
124
- except TypeError:
125
- raise click.ClickException(
126
- f"Invalid header_keys type: expected string or iterable, got {type(header_keys).__name__}"
127
- ) from None
128
-
129
-
130
- def _build_bearer_headers(auth: dict[str, Any], token_value: str) -> dict[str, str]:
131
- """Build headers for bearer token authentication.
132
-
133
- Args:
134
- auth: Original authentication dictionary which may include ``header_keys``.
135
- token_value: The token value to embed into the headers.
136
-
137
- Returns:
138
- A dictionary of HTTP headers including the Authorization header when
139
- applicable.
140
- """
141
- default_header_keys = ["Authorization"]
142
- has_header_keys = "header_keys" in auth
143
- header_keys_raw = auth.get("header_keys") if has_header_keys else default_header_keys
144
- header_keys = _normalize_header_keys(header_keys_raw, default=None if has_header_keys else default_header_keys)
145
- headers = {}
146
- for key in header_keys:
147
- # Prepend "Bearer " if this is Authorization header
148
- if key.lower() == "authorization":
149
- headers[key] = f"Bearer {token_value}"
150
- else:
151
- headers[key] = token_value
152
- return headers
153
-
154
-
155
- def _prepare_bearer_token_auth(
156
- auth: dict[str, Any],
157
- prompt_for_secrets: bool,
158
- placeholder: str,
159
- console: Console,
160
- ) -> dict[str, Any]:
161
- """Prepare bearer-token authentication for export.
162
-
163
- Args:
164
- auth: Original authentication dictionary.
165
- prompt_for_secrets: Whether to prompt for secrets.
166
- placeholder: Placeholder value for secrets.
167
- console: Rich ``Console`` for interaction.
168
-
169
- Returns:
170
- A prepared ``bearer-token`` authentication dictionary.
171
- """
172
- # Check if token exists and is not masked
173
- token = auth.get("token")
174
- has_valid_token = token and token not in (None, "", "***", "REDACTED")
175
-
176
- # If we have a valid token, use it
177
- if has_valid_token:
178
- return {"type": "bearer-token", "token": token}
179
-
180
- # Get token value (prompt or placeholder)
181
- token_value = _get_token_value(prompt_for_secrets, placeholder, console)
182
-
183
- # Check if original had headers structure
184
- if "headers" in auth or "header_keys" in auth:
185
- headers = _build_bearer_headers(auth, token_value)
186
- return {"type": "bearer-token", "headers": headers}
187
-
188
- # Use token field structure
189
- return {"type": "bearer-token", "token": token_value}
190
-
191
-
192
- def _extract_api_key_name(auth: dict[str, Any]) -> str | None:
193
- """Extract the API key name from an authentication dictionary.
194
-
195
- Args:
196
- auth: Authentication dictionary that may contain ``key`` or ``header_keys``.
197
-
198
- Returns:
199
- The API key name if available, otherwise ``None``.
200
- """
201
- key_name = auth.get("key")
202
- if not key_name and "header_keys" in auth:
203
- header_keys = _normalize_header_keys(auth["header_keys"])
204
- if header_keys:
205
- key_name = header_keys[0]
206
- return key_name
207
-
208
-
209
- def _get_api_key_value(
210
- key_name: str | None,
211
- prompt_for_secrets: bool,
212
- placeholder: str,
213
- console: Console,
214
- ) -> str:
215
- """Get API key value either by prompting or using a placeholder.
216
-
217
- Args:
218
- key_name: The name of the API key; used in prompt messages.
219
- prompt_for_secrets: If True, prompt for the API key value.
220
- placeholder: Placeholder to use when not prompting or when input is empty.
221
- console: Rich ``Console`` used to display informational messages.
222
-
223
- Returns:
224
- The API key value, either provided by the user or the placeholder.
225
- """
226
- if prompt_for_secrets:
227
- return _prompt_secret_with_placeholder(
228
- console,
229
- warning_message=f"API key value for '{key_name}' is missing or redacted.",
230
- prompt_message=f"API key value for '{key_name}' (leave blank for placeholder)",
231
- placeholder=placeholder,
232
- tip_cli_command="configure api-key",
233
- tip_slash_command="configure",
234
- )
235
-
236
- if not click.get_text_stream("stdin").isatty():
237
- console.print(f"[{WARNING_STYLE}]⚠️ Non-interactive mode: using placeholder for API key '{key_name}'[/]")
238
- return placeholder
239
-
240
-
241
- def _build_api_key_headers(auth: dict[str, Any], key_name: str | None, key_value: str) -> dict[str, str]:
242
- """Build headers for API key authentication.
243
-
244
- Args:
245
- auth: Original authentication dictionary which may include ``header_keys``.
246
- key_name: The header key name if present.
247
- key_value: The API key value to populate.
248
-
249
- Returns:
250
- A dictionary of HTTP headers for API key authentication.
251
- """
252
- default_header_keys = [key_name] if key_name else []
253
- has_header_keys = "header_keys" in auth
254
- header_keys_raw = auth.get("header_keys") if has_header_keys else default_header_keys
255
- header_keys_list = _normalize_header_keys(header_keys_raw, default=None if has_header_keys else default_header_keys)
256
- filtered_keys = [k for k in header_keys_list if k]
257
- return dict.fromkeys(filtered_keys, key_value)
258
-
259
-
260
- def _prepare_api_key_auth(
261
- auth: dict[str, Any],
262
- prompt_for_secrets: bool,
263
- placeholder: str,
264
- console: Console,
265
- ) -> dict[str, Any]:
266
- """Prepare api-key authentication for export.
267
-
268
- Args:
269
- auth: Original authentication dictionary.
270
- prompt_for_secrets: Whether to prompt for secrets.
271
- placeholder: Placeholder value for secrets.
272
- console: Rich ``Console`` for interaction.
273
-
274
- Returns:
275
- A prepared ``api-key`` authentication dictionary.
276
- """
277
- # Extract key name and value
278
- key_name = _extract_api_key_name(auth)
279
- key_value = auth.get("value")
280
-
281
- # Check if we have a valid value
282
- has_valid_value = key_value and key_value not in (None, "", "***", "REDACTED")
283
-
284
- # Capture or use placeholder for value
285
- if not has_valid_value:
286
- key_value = _get_api_key_value(key_name, prompt_for_secrets, placeholder, console)
287
-
288
- # Check if original had headers structure
289
- if "headers" in auth or "header_keys" in auth:
290
- headers = _build_api_key_headers(auth, key_name, key_value)
291
- return {"type": "api-key", "headers": headers}
292
-
293
- # Use key/value field structure
294
- return {"type": "api-key", "key": key_name, "value": key_value}
295
-
296
-
297
- def _prepare_custom_header_auth(
298
- auth: dict[str, Any],
299
- prompt_for_secrets: bool,
300
- placeholder: str,
301
- console: Console,
302
- ) -> dict[str, Any]:
303
- """Prepare custom-header authentication for export.
304
-
305
- Args:
306
- auth: Original authentication dictionary.
307
- prompt_for_secrets: Whether to prompt for header values.
308
- placeholder: Placeholder value when not prompting or input is empty.
309
- console: Rich ``Console`` for interaction.
310
-
311
- Returns:
312
- A prepared ``custom-header`` authentication dictionary.
313
- """
314
- existing_headers: dict[str, Any] = auth.get("headers", {})
315
- header_names = _extract_header_names(existing_headers, auth.get("header_keys", []))
316
-
317
- if not header_names:
318
- return {"type": "custom-header", "headers": {}}
319
-
320
- headers = _build_custom_headers(
321
- existing_headers=existing_headers,
322
- header_names=header_names,
323
- prompt_for_secrets=prompt_for_secrets,
324
- placeholder=placeholder,
325
- console=console,
326
- )
327
-
328
- return {"type": "custom-header", "headers": headers}
329
-
330
-
331
- def _extract_header_names(existing_headers: Mapping[str, Any] | None, header_keys: Iterable[str] | None) -> list[str]:
332
- """Extract the list of header names to process.
333
-
334
- Args:
335
- existing_headers: Existing headers mapping from the auth object.
336
- header_keys: Optional helper metadata listing header names.
337
-
338
- Returns:
339
- A list of header names to process.
340
- """
341
- if existing_headers:
342
- return list(existing_headers.keys())
343
- return _normalize_header_keys(header_keys)
344
-
345
-
346
- def _is_valid_secret(value: Any) -> bool:
347
- """Determine whether a secret value is present and not masked.
348
-
349
- Args:
350
- value: The value to test.
351
-
352
- Returns:
353
- True if the value is non-empty and not one of the masked placeholders.
354
- """
355
- return bool(value) and value not in (None, "", "***", "REDACTED")
356
-
357
-
358
- def _prompt_or_placeholder(
359
- name: str,
360
- prompt_for_secrets: bool,
361
- placeholder: str,
362
- console: Console,
363
- ) -> str:
364
- """Prompt for a header value or return the placeholder when not prompting.
365
-
366
- Args:
367
- name: Header name used in prompt messages.
368
- prompt_for_secrets: If True, prompt for the value interactively.
369
- placeholder: Placeholder value used when not prompting or empty input.
370
- console: Rich ``Console`` instance for user-facing messages.
371
-
372
- Returns:
373
- The provided value or the placeholder.
374
- """
375
- if prompt_for_secrets:
376
- return _prompt_secret_with_placeholder(
377
- console,
378
- warning_message=f"Header '{name}' is missing or redacted.",
379
- prompt_message=f"Value for header '{name}' (leave blank for placeholder)",
380
- placeholder=placeholder,
381
- tip_cli_command="configure",
382
- tip_slash_command="configure",
383
- )
384
-
385
- if not click.get_text_stream("stdin").isatty():
386
- console.print(f"[{WARNING_STYLE}]⚠️ Non-interactive mode: using placeholder for header '{name}'[/]")
387
- return placeholder
388
-
389
-
390
- def _build_custom_headers(
391
- *,
392
- existing_headers: Mapping[str, Any],
393
- header_names: Iterable[str],
394
- prompt_for_secrets: bool,
395
- placeholder: str,
396
- console: Console,
397
- ) -> dict[str, str]:
398
- """Build a headers mapping for custom-header authentication.
399
-
400
- Args:
401
- existing_headers: Existing headers mapping from the auth object.
402
- header_names: Header names to process.
403
- prompt_for_secrets: Whether to prompt for missing values.
404
- placeholder: Placeholder to use for missing or masked values.
405
- console: Rich ``Console`` used for prompt/warning messages.
406
-
407
- Returns:
408
- A dictionary mapping header names to resolved values.
409
- """
410
- headers: dict[str, str] = {}
411
- for name in header_names:
412
- existing_value = existing_headers.get(name)
413
- if _is_valid_secret(existing_value):
414
- headers[name] = str(existing_value)
415
- continue
416
-
417
- headers[name] = _prompt_or_placeholder(
418
- name=name,
419
- prompt_for_secrets=prompt_for_secrets,
420
- placeholder=placeholder,
421
- console=console,
422
- )
423
-
424
- return headers
425
-
426
-
427
- def _prompt_secret_with_placeholder(
428
- console: Console,
429
- *,
430
- warning_message: str,
431
- prompt_message: str,
432
- placeholder: str,
433
- tip_cli_command: str | None = "configure",
434
- tip_slash_command: str | None = "configure",
435
- mask_input: bool = True,
436
- retry_limit: int = 1,
437
- ) -> str:
438
- """Prompt for a secret value with masking, retries, and placeholder fallback.
439
-
440
- Args:
441
- console: Rich console used to render messaging.
442
- warning_message: Message shown before prompting (rendered with warning style).
443
- prompt_message: The message passed to :func:`click.prompt`.
444
- placeholder: Placeholder value inserted when the user skips input.
445
- tip_cli_command: CLI command (without ``aip`` prefix) used to build hints.
446
- tip_slash_command: Slash command counterpart used in hints.
447
- mask_input: Whether to hide user input while typing.
448
- retry_limit: Number of additional attempts when the user submits empty input.
449
-
450
- Returns:
451
- The value entered by the user or the provided placeholder.
452
- """
453
- console.print(f"[{WARNING_STYLE}]{warning_message}[/]")
454
-
455
- tip = command_hint(tip_cli_command, tip_slash_command)
456
- if tip:
457
- console.print(
458
- f"[{HINT_PREFIX_STYLE}]Tip:[/] use {format_command_hint(tip) or tip} later "
459
- "if you want to update these credentials."
460
- )
461
-
462
- attempts = 0
463
- while attempts <= retry_limit: # pragma: no cover
464
- response = click.prompt(
465
- prompt_message,
466
- default="",
467
- show_default=False,
468
- hide_input=mask_input,
469
- )
470
- value = response.strip()
471
- if value:
472
- return value
473
-
474
- if attempts < retry_limit:
475
- console.print(
476
- f"[{WARNING_STYLE}]No value entered. Enter a value or press Enter again to use the placeholder.[/]"
477
- )
478
- attempts += 1
479
- continue
480
-
481
- console.print("[dim]Using placeholder value.[/dim]")
482
- return placeholder
483
-
484
- # This line is unreachable as the loop always returns
485
- # return placeholder
486
-
487
-
488
- # ----------------------------- Credential Resolution ----------------------------- #
489
-
490
-
491
- def resolve_api_url_from_context(
492
- ctx: Any,
493
- *,
494
- get_api_url: Callable[[Any], str | None] | None = None,
495
- get_account_name: Callable[[Any], str | None] | None = None,
496
- ) -> str | None:
497
- """Resolve API URL from context using account store (CLI/palette ignores env creds).
498
-
499
- Helper function to extract API URL from various context formats.
500
- Used by transcript capture and slash session to avoid code duplication.
501
-
502
- Args:
503
- ctx: Context object (can be dict, click.Context, or any object with attributes).
504
- get_api_url: Optional function to extract api_url from context.
505
- If None, tries ctx.obj.get("api_url") or ctx.get("api_url").
506
- get_account_name: Optional function to extract account_name from context.
507
- If None, tries ctx.obj.get("account_name") or ctx.get("account_name").
508
-
509
- Returns:
510
- Resolved API URL or None.
511
- """
512
- api_url = None
513
- account_name = None
514
-
515
- if get_api_url:
516
- api_url = get_api_url(ctx)
517
- elif isinstance(ctx, dict):
518
- api_url = ctx.get("api_url")
519
- elif hasattr(ctx, "obj") and isinstance(ctx.obj, dict):
520
- api_url = ctx.obj.get("api_url")
521
-
522
- if get_account_name:
523
- account_name = get_account_name(ctx)
524
- elif isinstance(ctx, dict):
525
- account_name = ctx.get("account_name")
526
- elif hasattr(ctx, "obj") and isinstance(ctx.obj, dict):
527
- account_name = ctx.obj.get("account_name")
528
-
529
- resolved_url, _, _ = resolve_credentials(
530
- account_name=account_name,
531
- api_url=api_url,
532
- api_key=None,
533
- ignore_env_creds=True,
534
- )
535
- return resolved_url
536
-
537
-
538
- def _resolve_account_name(account_name: str | None) -> str | None:
539
- """Resolve account name from parameter (env var removed for CLI/palette)."""
540
- return account_name
541
-
542
-
543
- def _validate_account_exists(account_name: str | None, store: Any) -> None:
544
- """Validate that the specified account exists.
545
-
546
- Raises:
547
- AccountNotFoundError: If account_name is specified but account doesn't exist.
548
- """
549
- if account_name:
550
- account = store.get_account(account_name)
551
- if not account:
552
- raise AccountNotFoundError(
553
- f"Account '{account_name}' not found. Run 'aip accounts list' to see available accounts."
554
- )
555
-
556
-
557
- def _merge_credentials(
558
- api_url: str | None,
559
- api_key: str | None,
560
- profile_url: str | None,
561
- profile_key: str | None,
562
- ignore_env_creds: bool,
563
- ) -> tuple[str | None, str | None]:
564
- """Merge credentials from multiple sources.
565
-
566
- Args:
567
- api_url: Explicit API URL override.
568
- api_key: Explicit API key override.
569
- profile_url: Profile API URL.
570
- profile_key: Profile API key.
571
- ignore_env_creds: If True, ignore env vars.
572
-
573
- Returns:
574
- Tuple of (final_url, final_key).
575
- """
576
- if not ignore_env_creds:
577
- env_url = os.getenv("AIP_API_URL")
578
- env_key = os.getenv("AIP_API_KEY")
579
- final_url = api_url or env_url or profile_url
580
- final_key = api_key or env_key or profile_key
581
- else:
582
- final_url = api_url or profile_url
583
- final_key = api_key or profile_key
584
- return final_url, final_key
585
-
586
-
587
- def _determine_source(
588
- api_url: str | None,
589
- api_key: str | None,
590
- account_name: str | None,
591
- store: Any,
592
- ) -> str:
593
- """Determine the source of credentials.
594
-
595
- Returns:
596
- Source string describing where credentials came from.
597
- """
598
- if api_url or api_key:
599
- return "flag"
600
- if account_name:
601
- return f"account:{account_name}"
602
- active = store.get_active_account()
603
- return f"active_profile:{active}" if active else "none"
604
-
605
-
606
- _ENV_WARNING_EMITTED = False
607
-
608
-
609
- def _maybe_warn_env_creds_ignored(ignore_env_creds: bool) -> None:
610
- """Emit a one-time warning when env credentials are present but ignored."""
611
- global _ENV_WARNING_EMITTED
612
-
613
- if _ENV_WARNING_EMITTED or not ignore_env_creds:
614
- return
615
-
616
- if os.getenv("AIP_API_URL") or os.getenv("AIP_API_KEY"):
617
- click.echo(
618
- "Warning: CLI ignores AIP_API_URL/AIP_API_KEY; use account profiles via 'aip accounts add/use'. "
619
- "Python SDK callers can opt in with ignore_env_creds=False.",
620
- err=True,
621
- )
622
- _ENV_WARNING_EMITTED = True
623
-
624
-
625
- def resolve_credentials(
626
- account_name: str | None = None,
627
- api_url: str | None = None,
628
- api_key: str | None = None,
629
- *,
630
- ignore_env_creds: bool = True,
631
- ) -> tuple[str | None, str | None, str]:
632
- """Resolve credentials from multiple sources with precedence.
633
-
634
- For CLI/palette: ignores raw credential env vars (AIP_API_URL/AIP_API_KEY),
635
- and only uses explicit account selection (no AIP_ACCOUNT env). Python SDK can use
636
- ignore_env_creds=False to honor env vars if needed.
637
-
638
- Precedence order (CLI/palette):
639
- 1. Explicit parameters (api_url, api_key)
640
- 2. Account profile (from account_name or active_account)
641
-
642
- Args:
643
- account_name: Account name to use, or None for active account.
644
- api_url: Explicit API URL override.
645
- api_key: Explicit API key override.
646
- ignore_env_creds: If True (default), ignore AIP_API_URL/AIP_API_KEY env vars.
647
-
648
- Returns:
649
- Tuple of (api_url, api_key, source) where source describes where
650
- credentials came from (e.g., "flag", "active_profile", "account:name").
651
-
652
- Raises:
653
- click.ClickException: If a requested account does not exist.
654
- """
655
- _maybe_warn_env_creds_ignored(ignore_env_creds)
656
-
657
- # 1. Explicit parameters take highest precedence
658
- if api_url and api_key:
659
- return api_url, api_key, "flag"
660
-
661
- # 2. Account profile resolution
662
- account_name = _resolve_account_name(account_name)
663
- store = get_account_store()
664
- try:
665
- _validate_account_exists(account_name, store)
666
- except AccountNotFoundError as exc:
667
- raise click.ClickException(str(exc)) from exc
668
-
669
- try:
670
- profile_url, profile_key = store.get_credentials(account_name)
671
- except AccountStoreError:
672
- profile_url, profile_key = None, None
673
-
674
- final_url, final_key = _merge_credentials(api_url, api_key, profile_url, profile_key, ignore_env_creds)
675
- source = _determine_source(api_url, api_key, account_name, store)
676
-
677
- return final_url, final_key, source
678
-
679
-
680
- def get_credentials(
681
- account_name: str | None = None,
682
- api_url: str | None = None,
683
- api_key: str | None = None,
684
- ) -> tuple[str | None, str | None]:
685
- """Get credentials for CLI commands (backward compatible wrapper).
686
-
687
- This function maintains backward compatibility with existing code that
688
- expects (url, key) tuple. For source information, use resolve_credentials.
689
-
690
- Args:
691
- account_name: Account name to use, or None for active account.
692
- api_url: Explicit API URL override.
693
- api_key: Explicit API key override.
694
-
695
- Returns:
696
- Tuple of (api_url, api_key).
697
- """
698
- url, key, _ = resolve_credentials(account_name, api_url, api_key)
699
- return url, key
@@ -1,5 +0,0 @@
1
- """CLI commands package exports."""
2
-
3
- from glaip_sdk.cli.commands import agents, configure, mcps, models, tools, transcripts, update
4
-
5
- __all__ = ["agents", "configure", "mcps", "models", "tools", "transcripts", "update"]