gac 1.13.0__py3-none-any.whl → 3.6.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- gac/__version__.py +1 -1
- gac/ai.py +33 -47
- gac/ai_utils.py +66 -41
- gac/auth_cli.py +69 -0
- gac/cli.py +59 -2
- gac/config.py +9 -6
- gac/config_cli.py +26 -5
- gac/constants.py +171 -2
- gac/git.py +158 -12
- gac/init_cli.py +40 -125
- gac/language_cli.py +378 -0
- gac/main.py +827 -159
- gac/model_cli.py +374 -0
- gac/oauth/__init__.py +1 -0
- gac/oauth/claude_code.py +397 -0
- gac/preprocess.py +3 -3
- gac/prompt.py +561 -226
- gac/providers/__init__.py +46 -0
- gac/providers/azure_openai.py +97 -0
- gac/providers/claude_code.py +102 -0
- gac/providers/custom_anthropic.py +1 -1
- gac/providers/custom_openai.py +1 -2
- gac/providers/kimi_coding.py +63 -0
- gac/providers/mistral.py +38 -0
- gac/providers/moonshot.py +38 -0
- gac/providers/replicate.py +98 -0
- gac/security.py +1 -1
- gac/utils.py +242 -3
- gac/workflow_utils.py +222 -0
- {gac-1.13.0.dist-info → gac-3.6.0.dist-info}/METADATA +87 -26
- gac-3.6.0.dist-info/RECORD +53 -0
- {gac-1.13.0.dist-info → gac-3.6.0.dist-info}/WHEEL +1 -1
- gac-1.13.0.dist-info/RECORD +0 -41
- {gac-1.13.0.dist-info → gac-3.6.0.dist-info}/entry_points.txt +0 -0
- {gac-1.13.0.dist-info → gac-3.6.0.dist-info}/licenses/LICENSE +0 -0
gac/model_cli.py
ADDED
|
@@ -0,0 +1,374 @@
|
|
|
1
|
+
"""CLI for managing gac model configuration in $HOME/.gac.env."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
import click
|
|
7
|
+
import questionary
|
|
8
|
+
from dotenv import dotenv_values, load_dotenv, set_key
|
|
9
|
+
|
|
10
|
+
GAC_ENV_PATH = Path.home() / ".gac.env"
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def _should_show_rtl_warning_for_init() -> bool:
|
|
14
|
+
"""Check if RTL warning should be shown based on init's GAC_ENV_PATH.
|
|
15
|
+
|
|
16
|
+
Returns:
|
|
17
|
+
True if warning should be shown, False if user previously confirmed
|
|
18
|
+
"""
|
|
19
|
+
if GAC_ENV_PATH.exists():
|
|
20
|
+
load_dotenv(GAC_ENV_PATH)
|
|
21
|
+
rtl_confirmed = os.getenv("GAC_RTL_CONFIRMED", "false").lower() in ("true", "1", "yes", "on")
|
|
22
|
+
return not rtl_confirmed
|
|
23
|
+
return True # Show warning if no config exists
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _show_rtl_warning_for_init(language_name: str) -> bool:
|
|
27
|
+
"""Show RTL language warning for init command and save preference to GAC_ENV_PATH.
|
|
28
|
+
|
|
29
|
+
Args:
|
|
30
|
+
language_name: Name of the RTL language
|
|
31
|
+
|
|
32
|
+
Returns:
|
|
33
|
+
True if user wants to proceed, False if they cancel
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
terminal_width = 80 # Use default width
|
|
37
|
+
title = "⚠️ RTL Language Detected".center(terminal_width)
|
|
38
|
+
|
|
39
|
+
click.echo()
|
|
40
|
+
click.echo(click.style(title, fg="yellow", bold=True))
|
|
41
|
+
click.echo()
|
|
42
|
+
click.echo("Right-to-left (RTL) languages may not display correctly in gac due to terminal limitations.")
|
|
43
|
+
click.echo("However, the commit messages will work fine and should be readable in Git clients")
|
|
44
|
+
click.echo("that properly support RTL text (like most web interfaces and modern tools).\n")
|
|
45
|
+
|
|
46
|
+
proceed = questionary.confirm("Do you want to proceed anyway?").ask()
|
|
47
|
+
if proceed:
|
|
48
|
+
# Remember that user has confirmed RTL acceptance
|
|
49
|
+
set_key(str(GAC_ENV_PATH), "GAC_RTL_CONFIRMED", "true")
|
|
50
|
+
return True
|
|
51
|
+
else:
|
|
52
|
+
click.echo("RTL language setup cancelled.")
|
|
53
|
+
return False
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def _prompt_required_text(prompt: str) -> str | None:
|
|
57
|
+
"""Prompt until a non-empty string is provided or the user cancels."""
|
|
58
|
+
while True:
|
|
59
|
+
response = questionary.text(prompt).ask()
|
|
60
|
+
if response is None:
|
|
61
|
+
return None
|
|
62
|
+
value = response.strip()
|
|
63
|
+
if value:
|
|
64
|
+
return value # type: ignore[no-any-return]
|
|
65
|
+
click.echo("A value is required. Please try again.")
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def _load_existing_env() -> dict[str, str]:
|
|
69
|
+
"""Ensure the env file exists and return its current values."""
|
|
70
|
+
existing_env: dict[str, str] = {}
|
|
71
|
+
if GAC_ENV_PATH.exists():
|
|
72
|
+
click.echo(f"$HOME/.gac.env already exists at {GAC_ENV_PATH}. Values will be updated.")
|
|
73
|
+
existing_env = {k: v for k, v in dotenv_values(str(GAC_ENV_PATH)).items() if v is not None}
|
|
74
|
+
else:
|
|
75
|
+
GAC_ENV_PATH.touch()
|
|
76
|
+
click.echo(f"Created $HOME/.gac.env at {GAC_ENV_PATH}.")
|
|
77
|
+
return existing_env
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def _configure_model(existing_env: dict[str, str]) -> bool:
|
|
81
|
+
"""Run the provider/model/API key configuration flow."""
|
|
82
|
+
providers = [
|
|
83
|
+
("Anthropic", "claude-haiku-4-5"),
|
|
84
|
+
("Azure OpenAI", "gpt-5-mini"),
|
|
85
|
+
("Cerebras", "zai-glm-4.6"),
|
|
86
|
+
("Chutes", "zai-org/GLM-4.6-FP8"),
|
|
87
|
+
("Claude Code", "claude-sonnet-4-5"),
|
|
88
|
+
("Custom (Anthropic)", ""),
|
|
89
|
+
("Custom (OpenAI)", ""),
|
|
90
|
+
("DeepSeek", "deepseek-chat"),
|
|
91
|
+
("Fireworks", "accounts/fireworks/models/gpt-oss-20b"),
|
|
92
|
+
("Gemini", "gemini-2.5-flash"),
|
|
93
|
+
("Groq", "meta-llama/llama-4-maverick-17b-128e-instruct"),
|
|
94
|
+
("Kimi for Coding", "kimi-for-coding"),
|
|
95
|
+
("LM Studio", "gemma3"),
|
|
96
|
+
("MiniMax.io", "MiniMax-M2"),
|
|
97
|
+
("Mistral", "mistral-small-latest"),
|
|
98
|
+
("Moonshot AI", "kimi-k2-thinking-turbo"),
|
|
99
|
+
("Ollama", "gemma3"),
|
|
100
|
+
("OpenAI", "gpt-5-mini"),
|
|
101
|
+
("OpenRouter", "openrouter/auto"),
|
|
102
|
+
("Replicate", "openai/gpt-oss-120b"),
|
|
103
|
+
("Streamlake", ""),
|
|
104
|
+
("Synthetic.new", "hf:zai-org/GLM-4.6"),
|
|
105
|
+
("Together AI", "openai/gpt-oss-20B"),
|
|
106
|
+
("Z.AI", "glm-4.5-air"),
|
|
107
|
+
("Z.AI Coding", "glm-4.6"),
|
|
108
|
+
]
|
|
109
|
+
provider_names = [p[0] for p in providers]
|
|
110
|
+
provider = questionary.select(
|
|
111
|
+
"Select your provider:", choices=provider_names, use_shortcuts=True, use_arrow_keys=True, use_jk_keys=False
|
|
112
|
+
).ask()
|
|
113
|
+
if not provider:
|
|
114
|
+
click.echo("Provider selection cancelled. Exiting.")
|
|
115
|
+
return False
|
|
116
|
+
provider_key = provider.lower().replace(".", "").replace(" ", "-").replace("(", "").replace(")", "")
|
|
117
|
+
|
|
118
|
+
is_azure_openai = provider_key == "azure-openai"
|
|
119
|
+
is_claude_code = provider_key == "claude-code"
|
|
120
|
+
is_custom_anthropic = provider_key == "custom-anthropic"
|
|
121
|
+
is_custom_openai = provider_key == "custom-openai"
|
|
122
|
+
is_lmstudio = provider_key == "lm-studio"
|
|
123
|
+
is_ollama = provider_key == "ollama"
|
|
124
|
+
is_streamlake = provider_key == "streamlake"
|
|
125
|
+
is_zai = provider_key in ("zai", "zai-coding")
|
|
126
|
+
|
|
127
|
+
if provider_key == "minimaxio":
|
|
128
|
+
provider_key = "minimax"
|
|
129
|
+
elif provider_key == "syntheticnew":
|
|
130
|
+
provider_key = "synthetic"
|
|
131
|
+
elif provider_key == "moonshot-ai":
|
|
132
|
+
provider_key = "moonshot"
|
|
133
|
+
elif provider_key == "kimi-for-coding":
|
|
134
|
+
provider_key = "kimi-coding"
|
|
135
|
+
|
|
136
|
+
if is_streamlake:
|
|
137
|
+
endpoint_id = _prompt_required_text("Enter the Streamlake inference endpoint ID (required):")
|
|
138
|
+
if endpoint_id is None:
|
|
139
|
+
click.echo("Streamlake configuration cancelled. Exiting.")
|
|
140
|
+
return False
|
|
141
|
+
model_to_save = endpoint_id
|
|
142
|
+
else:
|
|
143
|
+
model_suggestion = dict(providers)[provider]
|
|
144
|
+
if model_suggestion == "":
|
|
145
|
+
model_prompt = "Enter the model (required):"
|
|
146
|
+
else:
|
|
147
|
+
model_prompt = f"Enter the model (default: {model_suggestion}):"
|
|
148
|
+
model = questionary.text(model_prompt, default=model_suggestion).ask()
|
|
149
|
+
if model is None:
|
|
150
|
+
click.echo("Model entry cancelled. Exiting.")
|
|
151
|
+
return False
|
|
152
|
+
model_to_save = model.strip() if model.strip() else model_suggestion
|
|
153
|
+
|
|
154
|
+
set_key(str(GAC_ENV_PATH), "GAC_MODEL", f"{provider_key}:{model_to_save}")
|
|
155
|
+
click.echo(f"Set GAC_MODEL={provider_key}:{model_to_save}")
|
|
156
|
+
|
|
157
|
+
if is_custom_anthropic:
|
|
158
|
+
base_url = _prompt_required_text("Enter the custom Anthropic-compatible base URL (required):")
|
|
159
|
+
if base_url is None:
|
|
160
|
+
click.echo("Custom Anthropic base URL entry cancelled. Exiting.")
|
|
161
|
+
return False
|
|
162
|
+
set_key(str(GAC_ENV_PATH), "CUSTOM_ANTHROPIC_BASE_URL", base_url)
|
|
163
|
+
click.echo(f"Set CUSTOM_ANTHROPIC_BASE_URL={base_url}")
|
|
164
|
+
|
|
165
|
+
api_version = questionary.text(
|
|
166
|
+
"Enter the API version (optional, press Enter for default: 2023-06-01):", default="2023-06-01"
|
|
167
|
+
).ask()
|
|
168
|
+
if api_version and api_version != "2023-06-01":
|
|
169
|
+
set_key(str(GAC_ENV_PATH), "CUSTOM_ANTHROPIC_VERSION", api_version)
|
|
170
|
+
click.echo(f"Set CUSTOM_ANTHROPIC_VERSION={api_version}")
|
|
171
|
+
elif is_azure_openai:
|
|
172
|
+
# Check for existing endpoint
|
|
173
|
+
existing_endpoint = existing_env.get("AZURE_OPENAI_ENDPOINT")
|
|
174
|
+
if existing_endpoint:
|
|
175
|
+
click.echo(f"\nAZURE_OPENAI_ENDPOINT is already configured: {existing_endpoint}")
|
|
176
|
+
endpoint_action = questionary.select(
|
|
177
|
+
"What would you like to do?",
|
|
178
|
+
choices=[
|
|
179
|
+
"Keep existing endpoint",
|
|
180
|
+
"Enter new endpoint",
|
|
181
|
+
],
|
|
182
|
+
use_shortcuts=True,
|
|
183
|
+
use_arrow_keys=True,
|
|
184
|
+
use_jk_keys=False,
|
|
185
|
+
).ask()
|
|
186
|
+
|
|
187
|
+
if endpoint_action == "Enter new endpoint":
|
|
188
|
+
endpoint = _prompt_required_text("Enter the Azure OpenAI endpoint (required):")
|
|
189
|
+
if endpoint is None:
|
|
190
|
+
click.echo("Azure OpenAI endpoint entry cancelled. Exiting.")
|
|
191
|
+
return False
|
|
192
|
+
set_key(str(GAC_ENV_PATH), "AZURE_OPENAI_ENDPOINT", endpoint)
|
|
193
|
+
click.echo(f"Set AZURE_OPENAI_ENDPOINT={endpoint}")
|
|
194
|
+
else:
|
|
195
|
+
endpoint = existing_endpoint
|
|
196
|
+
click.echo(f"Keeping existing AZURE_OPENAI_ENDPOINT={endpoint}")
|
|
197
|
+
else:
|
|
198
|
+
endpoint = _prompt_required_text("Enter the Azure OpenAI endpoint (required):")
|
|
199
|
+
if endpoint is None:
|
|
200
|
+
click.echo("Azure OpenAI endpoint entry cancelled. Exiting.")
|
|
201
|
+
return False
|
|
202
|
+
set_key(str(GAC_ENV_PATH), "AZURE_OPENAI_ENDPOINT", endpoint)
|
|
203
|
+
click.echo(f"Set AZURE_OPENAI_ENDPOINT={endpoint}")
|
|
204
|
+
|
|
205
|
+
# Check for existing API version
|
|
206
|
+
existing_api_version = existing_env.get("AZURE_OPENAI_API_VERSION")
|
|
207
|
+
if existing_api_version:
|
|
208
|
+
click.echo(f"\nAZURE_OPENAI_API_VERSION is already configured: {existing_api_version}")
|
|
209
|
+
version_action = questionary.select(
|
|
210
|
+
"What would you like to do?",
|
|
211
|
+
choices=[
|
|
212
|
+
"Keep existing version",
|
|
213
|
+
"Enter new version",
|
|
214
|
+
],
|
|
215
|
+
use_shortcuts=True,
|
|
216
|
+
use_arrow_keys=True,
|
|
217
|
+
use_jk_keys=False,
|
|
218
|
+
).ask()
|
|
219
|
+
|
|
220
|
+
if version_action == "Enter new version":
|
|
221
|
+
api_version = questionary.text(
|
|
222
|
+
"Enter the Azure OpenAI API version (required, e.g., 2025-01-01-preview):",
|
|
223
|
+
default="2025-01-01-preview",
|
|
224
|
+
).ask()
|
|
225
|
+
if api_version is None or not api_version.strip():
|
|
226
|
+
click.echo("Azure OpenAI API version entry cancelled. Exiting.")
|
|
227
|
+
return False
|
|
228
|
+
api_version = api_version.strip()
|
|
229
|
+
set_key(str(GAC_ENV_PATH), "AZURE_OPENAI_API_VERSION", api_version)
|
|
230
|
+
click.echo(f"Set AZURE_OPENAI_API_VERSION={api_version}")
|
|
231
|
+
else:
|
|
232
|
+
api_version = existing_api_version
|
|
233
|
+
click.echo(f"Keeping existing AZURE_OPENAI_API_VERSION={api_version}")
|
|
234
|
+
else:
|
|
235
|
+
api_version = questionary.text(
|
|
236
|
+
"Enter the Azure OpenAI API version (required, e.g., 2025-01-01-preview):", default="2025-01-01-preview"
|
|
237
|
+
).ask()
|
|
238
|
+
if api_version is None or not api_version.strip():
|
|
239
|
+
click.echo("Azure OpenAI API version entry cancelled. Exiting.")
|
|
240
|
+
return False
|
|
241
|
+
api_version = api_version.strip()
|
|
242
|
+
set_key(str(GAC_ENV_PATH), "AZURE_OPENAI_API_VERSION", api_version)
|
|
243
|
+
click.echo(f"Set AZURE_OPENAI_API_VERSION={api_version}")
|
|
244
|
+
elif is_custom_openai:
|
|
245
|
+
base_url = _prompt_required_text("Enter the custom OpenAI-compatible base URL (required):")
|
|
246
|
+
if base_url is None:
|
|
247
|
+
click.echo("Custom OpenAI base URL entry cancelled. Exiting.")
|
|
248
|
+
return False
|
|
249
|
+
set_key(str(GAC_ENV_PATH), "CUSTOM_OPENAI_BASE_URL", base_url)
|
|
250
|
+
click.echo(f"Set CUSTOM_OPENAI_BASE_URL={base_url}")
|
|
251
|
+
elif is_ollama:
|
|
252
|
+
url_default = "http://localhost:11434"
|
|
253
|
+
url = questionary.text(f"Enter the Ollama API URL (default: {url_default}):", default=url_default).ask()
|
|
254
|
+
if url is None:
|
|
255
|
+
click.echo("Ollama URL entry cancelled. Exiting.")
|
|
256
|
+
return False
|
|
257
|
+
url_to_save = url.strip() if url.strip() else url_default
|
|
258
|
+
set_key(str(GAC_ENV_PATH), "OLLAMA_API_URL", url_to_save)
|
|
259
|
+
click.echo(f"Set OLLAMA_API_URL={url_to_save}")
|
|
260
|
+
elif is_lmstudio:
|
|
261
|
+
url_default = "http://localhost:1234"
|
|
262
|
+
url = questionary.text(f"Enter the LM Studio API URL (default: {url_default}):", default=url_default).ask()
|
|
263
|
+
if url is None:
|
|
264
|
+
click.echo("LM Studio URL entry cancelled. Exiting.")
|
|
265
|
+
return False
|
|
266
|
+
url_to_save = url.strip() if url.strip() else url_default
|
|
267
|
+
set_key(str(GAC_ENV_PATH), "LMSTUDIO_API_URL", url_to_save)
|
|
268
|
+
click.echo(f"Set LMSTUDIO_API_URL={url_to_save}")
|
|
269
|
+
|
|
270
|
+
# Handle Claude Code OAuth separately
|
|
271
|
+
if is_claude_code:
|
|
272
|
+
from gac.oauth.claude_code import authenticate_and_save, load_stored_token
|
|
273
|
+
|
|
274
|
+
existing_token = load_stored_token()
|
|
275
|
+
if existing_token:
|
|
276
|
+
click.echo("\n✓ Claude Code access token already configured.")
|
|
277
|
+
action = questionary.select(
|
|
278
|
+
"What would you like to do?",
|
|
279
|
+
choices=[
|
|
280
|
+
"Keep existing token",
|
|
281
|
+
"Re-authenticate (get new token)",
|
|
282
|
+
],
|
|
283
|
+
use_shortcuts=True,
|
|
284
|
+
use_arrow_keys=True,
|
|
285
|
+
use_jk_keys=False,
|
|
286
|
+
).ask()
|
|
287
|
+
|
|
288
|
+
if action is None or action.startswith("Keep existing"):
|
|
289
|
+
if action is None:
|
|
290
|
+
click.echo("Claude Code configuration cancelled. Keeping existing token.")
|
|
291
|
+
else:
|
|
292
|
+
click.echo("Keeping existing Claude Code token")
|
|
293
|
+
return True
|
|
294
|
+
else:
|
|
295
|
+
click.echo("\n🔐 Starting Claude Code OAuth authentication...")
|
|
296
|
+
if not authenticate_and_save(quiet=False):
|
|
297
|
+
click.echo("❌ Claude Code authentication failed. Keeping existing token.")
|
|
298
|
+
return False
|
|
299
|
+
return True
|
|
300
|
+
else:
|
|
301
|
+
click.echo("\n🔐 Starting Claude Code OAuth authentication...")
|
|
302
|
+
click.echo(" (Your browser will open automatically)\n")
|
|
303
|
+
if not authenticate_and_save(quiet=False):
|
|
304
|
+
click.echo("\n❌ Claude Code authentication failed. Exiting.")
|
|
305
|
+
return False
|
|
306
|
+
return True
|
|
307
|
+
|
|
308
|
+
# Determine API key name based on provider
|
|
309
|
+
if is_lmstudio:
|
|
310
|
+
api_key_name = "LMSTUDIO_API_KEY"
|
|
311
|
+
elif is_zai:
|
|
312
|
+
api_key_name = "ZAI_API_KEY"
|
|
313
|
+
else:
|
|
314
|
+
api_key_name = f"{provider_key.upper().replace('-', '_')}_API_KEY"
|
|
315
|
+
|
|
316
|
+
# Check if API key already exists
|
|
317
|
+
existing_key = existing_env.get(api_key_name)
|
|
318
|
+
|
|
319
|
+
if existing_key:
|
|
320
|
+
# Key exists - offer options
|
|
321
|
+
click.echo(f"\n{api_key_name} is already configured.")
|
|
322
|
+
action = questionary.select(
|
|
323
|
+
"What would you like to do?",
|
|
324
|
+
choices=[
|
|
325
|
+
"Keep existing key",
|
|
326
|
+
"Enter new key",
|
|
327
|
+
],
|
|
328
|
+
use_shortcuts=True,
|
|
329
|
+
use_arrow_keys=True,
|
|
330
|
+
use_jk_keys=False,
|
|
331
|
+
).ask()
|
|
332
|
+
|
|
333
|
+
if action is None:
|
|
334
|
+
click.echo("API key configuration cancelled. Keeping existing key.")
|
|
335
|
+
elif action.startswith("Keep existing"):
|
|
336
|
+
click.echo(f"Keeping existing {api_key_name}")
|
|
337
|
+
elif action.startswith("Enter new"):
|
|
338
|
+
api_key = questionary.password("Enter your new API key (input hidden):").ask()
|
|
339
|
+
if api_key and api_key.strip():
|
|
340
|
+
set_key(str(GAC_ENV_PATH), api_key_name, api_key)
|
|
341
|
+
click.echo(f"Updated {api_key_name} (hidden)")
|
|
342
|
+
else:
|
|
343
|
+
click.echo(f"No key entered. Keeping existing {api_key_name}")
|
|
344
|
+
else:
|
|
345
|
+
# No existing key - prompt for new one
|
|
346
|
+
api_key_prompt = "Enter your API key (input hidden, can be set later):"
|
|
347
|
+
if is_ollama or is_lmstudio:
|
|
348
|
+
click.echo(
|
|
349
|
+
"This provider typically runs locally. API keys are optional unless your instance requires authentication."
|
|
350
|
+
)
|
|
351
|
+
api_key_prompt = "Enter your API key (optional, press Enter to skip):"
|
|
352
|
+
|
|
353
|
+
api_key = questionary.password(api_key_prompt).ask()
|
|
354
|
+
if api_key and api_key.strip():
|
|
355
|
+
set_key(str(GAC_ENV_PATH), api_key_name, api_key)
|
|
356
|
+
click.echo(f"Set {api_key_name} (hidden)")
|
|
357
|
+
elif is_ollama or is_lmstudio:
|
|
358
|
+
click.echo("Skipping API key. You can add one later if needed.")
|
|
359
|
+
else:
|
|
360
|
+
click.echo("No API key entered. You can add one later by editing ~/.gac.env")
|
|
361
|
+
|
|
362
|
+
return True
|
|
363
|
+
|
|
364
|
+
|
|
365
|
+
@click.command()
|
|
366
|
+
def model() -> None:
|
|
367
|
+
"""Interactively update provider/model/API key without language prompts."""
|
|
368
|
+
click.echo("Welcome to gac model configuration!\n")
|
|
369
|
+
|
|
370
|
+
existing_env = _load_existing_env()
|
|
371
|
+
if not _configure_model(existing_env):
|
|
372
|
+
return
|
|
373
|
+
|
|
374
|
+
click.echo(f"\nModel configuration complete. You can edit {GAC_ENV_PATH} to update values later.")
|
gac/oauth/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""OAuth authentication utilities for GAC."""
|