superqode 0.1.5__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.
- superqode/__init__.py +33 -0
- superqode/acp/__init__.py +23 -0
- superqode/acp/client.py +913 -0
- superqode/acp/permission_screen.py +457 -0
- superqode/acp/types.py +480 -0
- superqode/acp_discovery.py +856 -0
- superqode/agent/__init__.py +22 -0
- superqode/agent/edit_strategies.py +334 -0
- superqode/agent/loop.py +892 -0
- superqode/agent/qe_report_templates.py +39 -0
- superqode/agent/system_prompts.py +353 -0
- superqode/agent_output.py +721 -0
- superqode/agent_stream.py +953 -0
- superqode/agents/__init__.py +59 -0
- superqode/agents/acp_registry.py +305 -0
- superqode/agents/client.py +249 -0
- superqode/agents/data/augmentcode.com.toml +51 -0
- superqode/agents/data/cagent.dev.toml +51 -0
- superqode/agents/data/claude.com.toml +60 -0
- superqode/agents/data/codeassistant.dev.toml +51 -0
- superqode/agents/data/codex.openai.com.toml +57 -0
- superqode/agents/data/fastagent.ai.toml +66 -0
- superqode/agents/data/geminicli.com.toml +77 -0
- superqode/agents/data/goose.block.xyz.toml +54 -0
- superqode/agents/data/junie.jetbrains.com.toml +56 -0
- superqode/agents/data/kimi.moonshot.cn.toml +57 -0
- superqode/agents/data/llmlingagent.dev.toml +51 -0
- superqode/agents/data/molt.bot.toml +49 -0
- superqode/agents/data/opencode.ai.toml +60 -0
- superqode/agents/data/stakpak.dev.toml +51 -0
- superqode/agents/data/vtcode.dev.toml +51 -0
- superqode/agents/discovery.py +266 -0
- superqode/agents/messaging.py +160 -0
- superqode/agents/persona.py +166 -0
- superqode/agents/registry.py +421 -0
- superqode/agents/schema.py +72 -0
- superqode/agents/unified.py +367 -0
- superqode/app/__init__.py +111 -0
- superqode/app/constants.py +314 -0
- superqode/app/css.py +366 -0
- superqode/app/models.py +118 -0
- superqode/app/suggester.py +125 -0
- superqode/app/widgets.py +1591 -0
- superqode/app_enhanced.py +399 -0
- superqode/app_main.py +17187 -0
- superqode/approval.py +312 -0
- superqode/atomic.py +296 -0
- superqode/commands/__init__.py +1 -0
- superqode/commands/acp.py +965 -0
- superqode/commands/agents.py +180 -0
- superqode/commands/auth.py +278 -0
- superqode/commands/config.py +374 -0
- superqode/commands/init.py +826 -0
- superqode/commands/providers.py +819 -0
- superqode/commands/qe.py +1145 -0
- superqode/commands/roles.py +380 -0
- superqode/commands/serve.py +172 -0
- superqode/commands/suggestions.py +127 -0
- superqode/commands/superqe.py +460 -0
- superqode/config/__init__.py +51 -0
- superqode/config/loader.py +812 -0
- superqode/config/schema.py +498 -0
- superqode/core/__init__.py +111 -0
- superqode/core/roles.py +281 -0
- superqode/danger.py +386 -0
- superqode/data/superqode-template.yaml +1522 -0
- superqode/design_system.py +1080 -0
- superqode/dialogs/__init__.py +6 -0
- superqode/dialogs/base.py +39 -0
- superqode/dialogs/model.py +130 -0
- superqode/dialogs/provider.py +870 -0
- superqode/diff_view.py +919 -0
- superqode/enterprise.py +21 -0
- superqode/evaluation/__init__.py +25 -0
- superqode/evaluation/adapters.py +93 -0
- superqode/evaluation/behaviors.py +89 -0
- superqode/evaluation/engine.py +209 -0
- superqode/evaluation/scenarios.py +96 -0
- superqode/execution/__init__.py +36 -0
- superqode/execution/linter.py +538 -0
- superqode/execution/modes.py +347 -0
- superqode/execution/resolver.py +283 -0
- superqode/execution/runner.py +642 -0
- superqode/file_explorer.py +811 -0
- superqode/file_viewer.py +471 -0
- superqode/flash.py +183 -0
- superqode/guidance/__init__.py +58 -0
- superqode/guidance/config.py +203 -0
- superqode/guidance/prompts.py +71 -0
- superqode/harness/__init__.py +54 -0
- superqode/harness/accelerator.py +291 -0
- superqode/harness/config.py +319 -0
- superqode/harness/validator.py +147 -0
- superqode/history.py +279 -0
- superqode/integrations/superopt_runner.py +124 -0
- superqode/logging/__init__.py +49 -0
- superqode/logging/adapters.py +219 -0
- superqode/logging/formatter.py +923 -0
- superqode/logging/integration.py +341 -0
- superqode/logging/sinks.py +170 -0
- superqode/logging/unified_log.py +417 -0
- superqode/lsp/__init__.py +26 -0
- superqode/lsp/client.py +544 -0
- superqode/main.py +1069 -0
- superqode/mcp/__init__.py +89 -0
- superqode/mcp/auth_storage.py +380 -0
- superqode/mcp/client.py +1236 -0
- superqode/mcp/config.py +319 -0
- superqode/mcp/integration.py +337 -0
- superqode/mcp/oauth.py +436 -0
- superqode/mcp/oauth_callback.py +385 -0
- superqode/mcp/types.py +290 -0
- superqode/memory/__init__.py +31 -0
- superqode/memory/feedback.py +342 -0
- superqode/memory/store.py +522 -0
- superqode/notifications.py +369 -0
- superqode/optimization/__init__.py +5 -0
- superqode/optimization/config.py +33 -0
- superqode/permissions/__init__.py +25 -0
- superqode/permissions/rules.py +488 -0
- superqode/plan.py +323 -0
- superqode/providers/__init__.py +33 -0
- superqode/providers/gateway/__init__.py +165 -0
- superqode/providers/gateway/base.py +228 -0
- superqode/providers/gateway/litellm_gateway.py +1170 -0
- superqode/providers/gateway/openresponses_gateway.py +436 -0
- superqode/providers/health.py +297 -0
- superqode/providers/huggingface/__init__.py +74 -0
- superqode/providers/huggingface/downloader.py +472 -0
- superqode/providers/huggingface/endpoints.py +442 -0
- superqode/providers/huggingface/hub.py +531 -0
- superqode/providers/huggingface/inference.py +394 -0
- superqode/providers/huggingface/transformers_runner.py +516 -0
- superqode/providers/local/__init__.py +100 -0
- superqode/providers/local/base.py +438 -0
- superqode/providers/local/discovery.py +418 -0
- superqode/providers/local/lmstudio.py +256 -0
- superqode/providers/local/mlx.py +457 -0
- superqode/providers/local/ollama.py +486 -0
- superqode/providers/local/sglang.py +268 -0
- superqode/providers/local/tgi.py +260 -0
- superqode/providers/local/tool_support.py +477 -0
- superqode/providers/local/vllm.py +258 -0
- superqode/providers/manager.py +1338 -0
- superqode/providers/models.py +1016 -0
- superqode/providers/models_dev.py +578 -0
- superqode/providers/openresponses/__init__.py +87 -0
- superqode/providers/openresponses/converters/__init__.py +17 -0
- superqode/providers/openresponses/converters/messages.py +343 -0
- superqode/providers/openresponses/converters/tools.py +268 -0
- superqode/providers/openresponses/schema/__init__.py +56 -0
- superqode/providers/openresponses/schema/models.py +585 -0
- superqode/providers/openresponses/streaming/__init__.py +5 -0
- superqode/providers/openresponses/streaming/parser.py +338 -0
- superqode/providers/openresponses/tools/__init__.py +21 -0
- superqode/providers/openresponses/tools/apply_patch.py +352 -0
- superqode/providers/openresponses/tools/code_interpreter.py +290 -0
- superqode/providers/openresponses/tools/file_search.py +333 -0
- superqode/providers/openresponses/tools/mcp_adapter.py +252 -0
- superqode/providers/registry.py +716 -0
- superqode/providers/usage.py +332 -0
- superqode/pure_mode.py +384 -0
- superqode/qr/__init__.py +23 -0
- superqode/qr/dashboard.py +781 -0
- superqode/qr/generator.py +1018 -0
- superqode/qr/templates.py +135 -0
- superqode/safety/__init__.py +41 -0
- superqode/safety/sandbox.py +413 -0
- superqode/safety/warnings.py +256 -0
- superqode/server/__init__.py +33 -0
- superqode/server/lsp_server.py +775 -0
- superqode/server/web.py +250 -0
- superqode/session/__init__.py +25 -0
- superqode/session/persistence.py +580 -0
- superqode/session/sharing.py +477 -0
- superqode/session.py +475 -0
- superqode/sidebar.py +2991 -0
- superqode/stream_view.py +648 -0
- superqode/styles/__init__.py +3 -0
- superqode/superqe/__init__.py +184 -0
- superqode/superqe/acp_runner.py +1064 -0
- superqode/superqe/constitution/__init__.py +62 -0
- superqode/superqe/constitution/evaluator.py +308 -0
- superqode/superqe/constitution/loader.py +432 -0
- superqode/superqe/constitution/schema.py +250 -0
- superqode/superqe/events.py +591 -0
- superqode/superqe/frameworks/__init__.py +65 -0
- superqode/superqe/frameworks/base.py +234 -0
- superqode/superqe/frameworks/e2e.py +263 -0
- superqode/superqe/frameworks/executor.py +237 -0
- superqode/superqe/frameworks/javascript.py +409 -0
- superqode/superqe/frameworks/python.py +373 -0
- superqode/superqe/frameworks/registry.py +92 -0
- superqode/superqe/mcp_tools/__init__.py +47 -0
- superqode/superqe/mcp_tools/core_tools.py +418 -0
- superqode/superqe/mcp_tools/registry.py +230 -0
- superqode/superqe/mcp_tools/testing_tools.py +167 -0
- superqode/superqe/noise.py +89 -0
- superqode/superqe/orchestrator.py +778 -0
- superqode/superqe/roles.py +609 -0
- superqode/superqe/session.py +713 -0
- superqode/superqe/skills/__init__.py +57 -0
- superqode/superqe/skills/base.py +106 -0
- superqode/superqe/skills/core_skills.py +899 -0
- superqode/superqe/skills/registry.py +90 -0
- superqode/superqe/verifier.py +101 -0
- superqode/superqe_cli.py +76 -0
- superqode/tool_call.py +358 -0
- superqode/tools/__init__.py +93 -0
- superqode/tools/agent_tools.py +496 -0
- superqode/tools/base.py +324 -0
- superqode/tools/batch_tool.py +133 -0
- superqode/tools/diagnostics.py +311 -0
- superqode/tools/edit_tools.py +653 -0
- superqode/tools/enhanced_base.py +515 -0
- superqode/tools/file_tools.py +269 -0
- superqode/tools/file_tracking.py +45 -0
- superqode/tools/lsp_tools.py +610 -0
- superqode/tools/network_tools.py +350 -0
- superqode/tools/permissions.py +400 -0
- superqode/tools/question_tool.py +324 -0
- superqode/tools/search_tools.py +598 -0
- superqode/tools/shell_tools.py +259 -0
- superqode/tools/todo_tools.py +121 -0
- superqode/tools/validation.py +80 -0
- superqode/tools/web_tools.py +639 -0
- superqode/tui.py +1152 -0
- superqode/tui_integration.py +875 -0
- superqode/tui_widgets/__init__.py +27 -0
- superqode/tui_widgets/widgets/__init__.py +18 -0
- superqode/tui_widgets/widgets/progress.py +185 -0
- superqode/tui_widgets/widgets/tool_display.py +188 -0
- superqode/undo_manager.py +574 -0
- superqode/utils/__init__.py +5 -0
- superqode/utils/error_handling.py +323 -0
- superqode/utils/fuzzy.py +257 -0
- superqode/widgets/__init__.py +477 -0
- superqode/widgets/agent_collab.py +390 -0
- superqode/widgets/agent_store.py +936 -0
- superqode/widgets/agent_switcher.py +395 -0
- superqode/widgets/animation_manager.py +284 -0
- superqode/widgets/code_context.py +356 -0
- superqode/widgets/command_palette.py +412 -0
- superqode/widgets/connection_status.py +537 -0
- superqode/widgets/conversation_history.py +470 -0
- superqode/widgets/diff_indicator.py +155 -0
- superqode/widgets/enhanced_status_bar.py +385 -0
- superqode/widgets/enhanced_toast.py +476 -0
- superqode/widgets/file_browser.py +809 -0
- superqode/widgets/file_reference.py +585 -0
- superqode/widgets/issue_timeline.py +340 -0
- superqode/widgets/leader_key.py +264 -0
- superqode/widgets/mode_switcher.py +445 -0
- superqode/widgets/model_picker.py +234 -0
- superqode/widgets/permission_preview.py +1205 -0
- superqode/widgets/prompt.py +358 -0
- superqode/widgets/provider_connect.py +725 -0
- superqode/widgets/pty_shell.py +587 -0
- superqode/widgets/qe_dashboard.py +321 -0
- superqode/widgets/resizable_sidebar.py +377 -0
- superqode/widgets/response_changes.py +218 -0
- superqode/widgets/response_display.py +528 -0
- superqode/widgets/rich_tool_display.py +613 -0
- superqode/widgets/sidebar_panels.py +1180 -0
- superqode/widgets/slash_complete.py +356 -0
- superqode/widgets/split_view.py +612 -0
- superqode/widgets/status_bar.py +273 -0
- superqode/widgets/superqode_display.py +786 -0
- superqode/widgets/thinking_display.py +815 -0
- superqode/widgets/throbber.py +87 -0
- superqode/widgets/toast.py +206 -0
- superqode/widgets/unified_output.py +1073 -0
- superqode/workspace/__init__.py +75 -0
- superqode/workspace/artifacts.py +472 -0
- superqode/workspace/coordinator.py +353 -0
- superqode/workspace/diff_tracker.py +429 -0
- superqode/workspace/git_guard.py +373 -0
- superqode/workspace/git_snapshot.py +526 -0
- superqode/workspace/manager.py +750 -0
- superqode/workspace/snapshot.py +357 -0
- superqode/workspace/watcher.py +535 -0
- superqode/workspace/worktree.py +440 -0
- superqode-0.1.5.dist-info/METADATA +204 -0
- superqode-0.1.5.dist-info/RECORD +288 -0
- superqode-0.1.5.dist-info/WHEEL +5 -0
- superqode-0.1.5.dist-info/entry_points.txt +3 -0
- superqode-0.1.5.dist-info/licenses/LICENSE +648 -0
- superqode-0.1.5.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Agent CLI commands for SuperQode.
|
|
3
|
+
|
|
4
|
+
Commands for listing and showing ACP/External agents.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import click
|
|
8
|
+
from rich.console import Console
|
|
9
|
+
from rich.table import Table
|
|
10
|
+
from rich.panel import Panel
|
|
11
|
+
|
|
12
|
+
from ..agents.registry import (
|
|
13
|
+
AGENTS,
|
|
14
|
+
AgentProtocol,
|
|
15
|
+
AgentStatus,
|
|
16
|
+
get_supported_agents,
|
|
17
|
+
get_acp_agents,
|
|
18
|
+
get_external_agents,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
console = Console()
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@click.group()
|
|
26
|
+
def agents():
|
|
27
|
+
"""Manage coding agents (ACP mode)."""
|
|
28
|
+
pass
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@agents.command("list")
|
|
32
|
+
@click.option(
|
|
33
|
+
"--protocol",
|
|
34
|
+
type=click.Choice(["acp", "external"]),
|
|
35
|
+
help="Filter by protocol",
|
|
36
|
+
)
|
|
37
|
+
@click.option(
|
|
38
|
+
"--supported",
|
|
39
|
+
is_flag=True,
|
|
40
|
+
help="Show only supported agents",
|
|
41
|
+
)
|
|
42
|
+
def list_agents(protocol: str, supported: bool):
|
|
43
|
+
"""List available coding agents."""
|
|
44
|
+
|
|
45
|
+
# Filter agents
|
|
46
|
+
filtered = dict(AGENTS)
|
|
47
|
+
|
|
48
|
+
if protocol:
|
|
49
|
+
proto = AgentProtocol.ACP if protocol == "acp" else AgentProtocol.EXTERNAL
|
|
50
|
+
filtered = {k: v for k, v in filtered.items() if v.protocol == proto}
|
|
51
|
+
|
|
52
|
+
if supported:
|
|
53
|
+
filtered = {k: v for k, v in filtered.items() if v.status == AgentStatus.SUPPORTED}
|
|
54
|
+
|
|
55
|
+
# Build table
|
|
56
|
+
table = Table(title="Coding Agents", show_header=True, header_style="bold cyan")
|
|
57
|
+
table.add_column("Agent", style="white")
|
|
58
|
+
table.add_column("Name", style="white")
|
|
59
|
+
table.add_column("Protocol", style="dim")
|
|
60
|
+
table.add_column("Status", style="white")
|
|
61
|
+
table.add_column("Description", style="dim", max_width=40)
|
|
62
|
+
|
|
63
|
+
# Sort by status (supported first) then name
|
|
64
|
+
sorted_agents = sorted(filtered.items(), key=lambda x: (x[1].status.value, x[0]))
|
|
65
|
+
|
|
66
|
+
for agent_id, agent_def in sorted_agents:
|
|
67
|
+
status_map = {
|
|
68
|
+
AgentStatus.SUPPORTED: "[green]✅ Supported[/green]",
|
|
69
|
+
AgentStatus.COMING_SOON: "[yellow]🔜 Coming Soon[/yellow]",
|
|
70
|
+
AgentStatus.EXPERIMENTAL: "[blue]🧪 Experimental[/blue]",
|
|
71
|
+
}
|
|
72
|
+
status = status_map.get(agent_def.status, agent_def.status.value)
|
|
73
|
+
|
|
74
|
+
protocol_str = agent_def.protocol.value.upper()
|
|
75
|
+
|
|
76
|
+
table.add_row(
|
|
77
|
+
agent_id,
|
|
78
|
+
agent_def.name,
|
|
79
|
+
protocol_str,
|
|
80
|
+
status,
|
|
81
|
+
agent_def.description[:40] + "..."
|
|
82
|
+
if len(agent_def.description) > 40
|
|
83
|
+
else agent_def.description,
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
console.print(table)
|
|
87
|
+
|
|
88
|
+
# Summary
|
|
89
|
+
supported_count = sum(1 for v in filtered.values() if v.status == AgentStatus.SUPPORTED)
|
|
90
|
+
console.print(f"\n[dim]Total: {len(filtered)} agents, {supported_count} supported[/dim]")
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
@agents.command("show")
|
|
94
|
+
@click.argument("agent_id")
|
|
95
|
+
def show_agent(agent_id: str):
|
|
96
|
+
"""Show details for a specific agent."""
|
|
97
|
+
|
|
98
|
+
agent_def = AGENTS.get(agent_id)
|
|
99
|
+
|
|
100
|
+
if not agent_def:
|
|
101
|
+
console.print(f"[red]Error: Agent '{agent_id}' not found[/red]")
|
|
102
|
+
console.print("\nAvailable agents:")
|
|
103
|
+
for aid in sorted(AGENTS.keys()):
|
|
104
|
+
console.print(f" • {aid}")
|
|
105
|
+
return
|
|
106
|
+
|
|
107
|
+
# Build info panel
|
|
108
|
+
status_map = {
|
|
109
|
+
AgentStatus.SUPPORTED: "[green]✅ Supported[/green]",
|
|
110
|
+
AgentStatus.COMING_SOON: "[yellow]🔜 Coming Soon[/yellow]",
|
|
111
|
+
AgentStatus.EXPERIMENTAL: "[blue]🧪 Experimental[/blue]",
|
|
112
|
+
}
|
|
113
|
+
status = status_map.get(agent_def.status, agent_def.status.value)
|
|
114
|
+
|
|
115
|
+
info_lines = [
|
|
116
|
+
f"[bold]Agent:[/bold] {agent_def.name}",
|
|
117
|
+
f"[bold]ID:[/bold] {agent_id}",
|
|
118
|
+
f"[bold]Protocol:[/bold] {agent_def.protocol.value.upper()}",
|
|
119
|
+
f"[bold]Status:[/bold] {status}",
|
|
120
|
+
f"[bold]Connection:[/bold] {agent_def.connection_type}",
|
|
121
|
+
"",
|
|
122
|
+
f"[bold]Description:[/bold]",
|
|
123
|
+
f" {agent_def.description}",
|
|
124
|
+
"",
|
|
125
|
+
]
|
|
126
|
+
|
|
127
|
+
# Capabilities
|
|
128
|
+
if agent_def.capabilities:
|
|
129
|
+
info_lines.append("[bold]Capabilities:[/bold]")
|
|
130
|
+
for cap in agent_def.capabilities:
|
|
131
|
+
info_lines.append(f" • {cap}")
|
|
132
|
+
info_lines.append("")
|
|
133
|
+
|
|
134
|
+
# Auth info
|
|
135
|
+
info_lines.append("[bold]Authentication:[/bold]")
|
|
136
|
+
info_lines.append(f" {agent_def.auth_info}")
|
|
137
|
+
info_lines.append("")
|
|
138
|
+
|
|
139
|
+
# Setup
|
|
140
|
+
info_lines.append("[bold]Setup:[/bold]")
|
|
141
|
+
info_lines.append(f" {agent_def.setup_command}")
|
|
142
|
+
info_lines.append("")
|
|
143
|
+
|
|
144
|
+
# Command
|
|
145
|
+
if agent_def.command:
|
|
146
|
+
info_lines.append(f"[bold]Command:[/bold] {agent_def.command}")
|
|
147
|
+
info_lines.append("")
|
|
148
|
+
|
|
149
|
+
# Docs
|
|
150
|
+
info_lines.append(f"[bold]Documentation:[/bold] {agent_def.docs_url}")
|
|
151
|
+
|
|
152
|
+
panel = Panel(
|
|
153
|
+
"\n".join(info_lines),
|
|
154
|
+
title=f"Agent: {agent_def.name}",
|
|
155
|
+
border_style="cyan",
|
|
156
|
+
)
|
|
157
|
+
console.print(panel)
|
|
158
|
+
|
|
159
|
+
# Usage example
|
|
160
|
+
if agent_def.status == AgentStatus.SUPPORTED:
|
|
161
|
+
console.print("\n[bold]Usage in superqode.yaml:[/bold]")
|
|
162
|
+
console.print(f"""
|
|
163
|
+
[dim]team:
|
|
164
|
+
dev:
|
|
165
|
+
roles:
|
|
166
|
+
my-role:
|
|
167
|
+
mode: "acp"
|
|
168
|
+
agent: "{agent_id}"
|
|
169
|
+
agent_config:
|
|
170
|
+
provider: "anthropic"
|
|
171
|
+
model: "claude-sonnet-4-20250514"
|
|
172
|
+
job_description: |
|
|
173
|
+
Your job description here...[/dim]
|
|
174
|
+
""")
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
# Register with main CLI
|
|
178
|
+
def register_commands(cli):
|
|
179
|
+
"""Register agent commands with the main CLI."""
|
|
180
|
+
cli.add_command(agents)
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Auth CLI commands for SuperQode.
|
|
3
|
+
|
|
4
|
+
Commands for showing authentication information and security details.
|
|
5
|
+
SuperQode NEVER stores API keys - this shows where keys are stored
|
|
6
|
+
and who controls them.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import os
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
|
|
12
|
+
import click
|
|
13
|
+
from rich.console import Console
|
|
14
|
+
from rich.table import Table
|
|
15
|
+
from rich.panel import Panel
|
|
16
|
+
|
|
17
|
+
from ..providers.registry import PROVIDERS, ProviderCategory
|
|
18
|
+
from ..agents.registry import AGENTS, AgentStatus
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
console = Console()
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@click.group()
|
|
25
|
+
def auth():
|
|
26
|
+
"""Show authentication and security information."""
|
|
27
|
+
pass
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@auth.command("info")
|
|
31
|
+
def auth_info():
|
|
32
|
+
"""Show comprehensive auth information."""
|
|
33
|
+
|
|
34
|
+
# Header
|
|
35
|
+
console.print(
|
|
36
|
+
Panel(
|
|
37
|
+
"[bold]🔒 SECURITY PRINCIPLE:[/bold] SuperQode [bold red]NEVER[/bold red] stores your API keys.\n\n"
|
|
38
|
+
"All credentials are read from YOUR environment at runtime.\n"
|
|
39
|
+
"You control where and how your keys are stored.",
|
|
40
|
+
title="SuperQode Auth Information",
|
|
41
|
+
border_style="cyan",
|
|
42
|
+
)
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
console.print()
|
|
46
|
+
|
|
47
|
+
# BYOK Section
|
|
48
|
+
console.print("[bold cyan]═══ BYOK MODE (Direct LLM) ═══[/bold cyan]")
|
|
49
|
+
console.print()
|
|
50
|
+
console.print("Your API keys are read from YOUR environment variables:")
|
|
51
|
+
console.print()
|
|
52
|
+
|
|
53
|
+
# Build provider status table
|
|
54
|
+
table = Table(show_header=True, header_style="bold")
|
|
55
|
+
table.add_column("Provider", style="white")
|
|
56
|
+
table.add_column("Env Variable", style="dim")
|
|
57
|
+
table.add_column("Status", style="white")
|
|
58
|
+
table.add_column("Source", style="dim")
|
|
59
|
+
|
|
60
|
+
# Check common providers
|
|
61
|
+
priority_providers = [
|
|
62
|
+
"anthropic",
|
|
63
|
+
"openai",
|
|
64
|
+
"google",
|
|
65
|
+
"xai",
|
|
66
|
+
"deepseek",
|
|
67
|
+
"groq",
|
|
68
|
+
"openrouter",
|
|
69
|
+
"ollama",
|
|
70
|
+
"zhipu",
|
|
71
|
+
"alibaba",
|
|
72
|
+
]
|
|
73
|
+
|
|
74
|
+
for provider_id in priority_providers:
|
|
75
|
+
provider_def = PROVIDERS.get(provider_id)
|
|
76
|
+
if not provider_def:
|
|
77
|
+
continue
|
|
78
|
+
|
|
79
|
+
# Check env vars
|
|
80
|
+
configured = False
|
|
81
|
+
configured_var = None
|
|
82
|
+
|
|
83
|
+
for env_var in provider_def.env_vars:
|
|
84
|
+
if os.environ.get(env_var):
|
|
85
|
+
configured = True
|
|
86
|
+
configured_var = env_var
|
|
87
|
+
break
|
|
88
|
+
|
|
89
|
+
if not provider_def.env_vars:
|
|
90
|
+
# Local provider
|
|
91
|
+
status = "[blue]🏠 Local[/blue]"
|
|
92
|
+
source = provider_def.default_base_url or "localhost"
|
|
93
|
+
elif configured:
|
|
94
|
+
status = "[green]✅ Set[/green]"
|
|
95
|
+
source = _detect_env_source(configured_var)
|
|
96
|
+
else:
|
|
97
|
+
status = "[red]❌ Not set[/red]"
|
|
98
|
+
source = "-"
|
|
99
|
+
|
|
100
|
+
env_var_display = provider_def.env_vars[0] if provider_def.env_vars else "(none)"
|
|
101
|
+
|
|
102
|
+
table.add_row(
|
|
103
|
+
provider_id,
|
|
104
|
+
env_var_display,
|
|
105
|
+
status,
|
|
106
|
+
source,
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
console.print(table)
|
|
110
|
+
console.print()
|
|
111
|
+
console.print("[dim]💡 Keys are read at runtime, never stored by SuperQode[/dim]")
|
|
112
|
+
console.print()
|
|
113
|
+
|
|
114
|
+
# ACP Section
|
|
115
|
+
console.print("[bold cyan]═══ ACP MODE (Coding Agents) ═══[/bold cyan]")
|
|
116
|
+
console.print()
|
|
117
|
+
console.print("Agent authentication is managed by each agent, not SuperQode:")
|
|
118
|
+
console.print()
|
|
119
|
+
|
|
120
|
+
# Build agent status table
|
|
121
|
+
agent_table = Table(show_header=True, header_style="bold")
|
|
122
|
+
agent_table.add_column("Agent", style="white")
|
|
123
|
+
agent_table.add_column("Auth Location", style="dim")
|
|
124
|
+
agent_table.add_column("Status", style="white")
|
|
125
|
+
|
|
126
|
+
for agent_id, agent_def in AGENTS.items():
|
|
127
|
+
if agent_def.status != AgentStatus.SUPPORTED:
|
|
128
|
+
continue
|
|
129
|
+
|
|
130
|
+
# Check if agent auth exists
|
|
131
|
+
auth_exists = _check_agent_auth(agent_id)
|
|
132
|
+
status = "[green]✅ Configured[/green]" if auth_exists else "[yellow]⚠️ Check agent[/yellow]"
|
|
133
|
+
|
|
134
|
+
agent_table.add_row(
|
|
135
|
+
agent_id,
|
|
136
|
+
agent_def.auth_info,
|
|
137
|
+
status,
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
console.print(agent_table)
|
|
141
|
+
console.print()
|
|
142
|
+
console.print("[dim]💡 Agent auth is managed by the agent itself, not SuperQode[/dim]")
|
|
143
|
+
console.print("[dim]💡 Run the agent directly to configure: e.g., 'opencode' → /connect[/dim]")
|
|
144
|
+
console.print()
|
|
145
|
+
|
|
146
|
+
# Data Flow Section
|
|
147
|
+
console.print("[bold cyan]═══ DATA FLOW ═══[/bold cyan]")
|
|
148
|
+
console.print()
|
|
149
|
+
console.print("[bold]BYOK:[/bold] You → SuperQode → LiteLLM → Provider API")
|
|
150
|
+
console.print("[bold]ACP:[/bold] You → SuperQode → Agent (e.g., opencode) → Provider API")
|
|
151
|
+
console.print()
|
|
152
|
+
console.print("[dim]SuperQode is a pass-through orchestrator. Your data goes directly[/dim]")
|
|
153
|
+
console.print("[dim]to the LLM provider or agent. We don't intercept or store anything.[/dim]")
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
@auth.command("check")
|
|
157
|
+
@click.argument("provider_or_agent")
|
|
158
|
+
def auth_check(provider_or_agent: str):
|
|
159
|
+
"""Check auth status for a specific provider or agent."""
|
|
160
|
+
|
|
161
|
+
# Check if it's a provider
|
|
162
|
+
provider_def = PROVIDERS.get(provider_or_agent)
|
|
163
|
+
if provider_def:
|
|
164
|
+
_check_provider_auth(provider_or_agent, provider_def)
|
|
165
|
+
return
|
|
166
|
+
|
|
167
|
+
# Check if it's an agent
|
|
168
|
+
agent_def = AGENTS.get(provider_or_agent)
|
|
169
|
+
if agent_def:
|
|
170
|
+
_check_agent_auth_detailed(provider_or_agent, agent_def)
|
|
171
|
+
return
|
|
172
|
+
|
|
173
|
+
console.print(f"[red]Error: '{provider_or_agent}' not found as provider or agent[/red]")
|
|
174
|
+
console.print(
|
|
175
|
+
"\nUse 'superqode providers list' or 'superqode agents list' to see available options."
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def _detect_env_source(env_var: str) -> str:
|
|
180
|
+
"""Try to detect where an env var is set."""
|
|
181
|
+
# This is a best-effort detection
|
|
182
|
+
home = Path.home()
|
|
183
|
+
|
|
184
|
+
# Check common shell config files
|
|
185
|
+
shell_files = [
|
|
186
|
+
home / ".zshrc",
|
|
187
|
+
home / ".bashrc",
|
|
188
|
+
home / ".bash_profile",
|
|
189
|
+
home / ".profile",
|
|
190
|
+
]
|
|
191
|
+
|
|
192
|
+
for shell_file in shell_files:
|
|
193
|
+
if shell_file.exists():
|
|
194
|
+
try:
|
|
195
|
+
content = shell_file.read_text()
|
|
196
|
+
if env_var in content:
|
|
197
|
+
return f"~/{shell_file.name}"
|
|
198
|
+
except Exception:
|
|
199
|
+
pass
|
|
200
|
+
|
|
201
|
+
# Check .env file in current directory
|
|
202
|
+
env_file = Path.cwd() / ".env"
|
|
203
|
+
if env_file.exists():
|
|
204
|
+
try:
|
|
205
|
+
content = env_file.read_text()
|
|
206
|
+
if env_var in content:
|
|
207
|
+
return ".env"
|
|
208
|
+
except Exception:
|
|
209
|
+
pass
|
|
210
|
+
|
|
211
|
+
return "environment"
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
def _check_agent_auth(agent_id: str) -> bool:
|
|
215
|
+
"""Check if agent auth exists."""
|
|
216
|
+
if agent_id == "opencode":
|
|
217
|
+
auth_file = Path.home() / ".local" / "share" / "opencode" / "auth.json"
|
|
218
|
+
return auth_file.exists()
|
|
219
|
+
return False
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
def _check_provider_auth(provider_id: str, provider_def):
|
|
223
|
+
"""Check and display provider auth status."""
|
|
224
|
+
console.print(f"\n[bold]Provider: {provider_def.name}[/bold]")
|
|
225
|
+
console.print()
|
|
226
|
+
|
|
227
|
+
if not provider_def.env_vars:
|
|
228
|
+
console.print("[blue]🏠 Local provider - no API key required[/blue]")
|
|
229
|
+
if provider_def.default_base_url:
|
|
230
|
+
console.print(f"Default URL: {provider_def.default_base_url}")
|
|
231
|
+
return
|
|
232
|
+
|
|
233
|
+
configured = False
|
|
234
|
+
for env_var in provider_def.env_vars:
|
|
235
|
+
value = os.environ.get(env_var)
|
|
236
|
+
if value:
|
|
237
|
+
configured = True
|
|
238
|
+
# Mask the key
|
|
239
|
+
masked = value[:8] + "..." + value[-4:] if len(value) > 12 else "***"
|
|
240
|
+
source = _detect_env_source(env_var)
|
|
241
|
+
console.print(f"[green]✅ {env_var}[/green] = {masked}")
|
|
242
|
+
console.print(f" Source: {source}")
|
|
243
|
+
else:
|
|
244
|
+
console.print(f"[red]❌ {env_var}[/red] = (not set)")
|
|
245
|
+
|
|
246
|
+
if not configured:
|
|
247
|
+
console.print()
|
|
248
|
+
console.print("[yellow]To configure:[/yellow]")
|
|
249
|
+
console.print(f' export {provider_def.env_vars[0]}="your-api-key"')
|
|
250
|
+
console.print(f"\n Get your key at: {provider_def.docs_url}")
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
def _check_agent_auth_detailed(agent_id: str, agent_def):
|
|
254
|
+
"""Check and display agent auth status."""
|
|
255
|
+
console.print(f"\n[bold]Agent: {agent_def.name}[/bold]")
|
|
256
|
+
console.print()
|
|
257
|
+
|
|
258
|
+
console.print(f"[bold]Auth managed by:[/bold] {agent_def.name} (not SuperQode)")
|
|
259
|
+
console.print(f"[bold]Auth location:[/bold] {agent_def.auth_info}")
|
|
260
|
+
console.print()
|
|
261
|
+
|
|
262
|
+
if agent_id == "opencode":
|
|
263
|
+
auth_file = Path.home() / ".local" / "share" / "opencode" / "auth.json"
|
|
264
|
+
if auth_file.exists():
|
|
265
|
+
console.print(f"[green]✅ Auth file exists:[/green] {auth_file}")
|
|
266
|
+
else:
|
|
267
|
+
console.print(f"[yellow]⚠️ Auth file not found:[/yellow] {auth_file}")
|
|
268
|
+
console.print()
|
|
269
|
+
console.print("[yellow]To configure:[/yellow]")
|
|
270
|
+
console.print(f" {agent_def.setup_command}")
|
|
271
|
+
else:
|
|
272
|
+
console.print(f"[dim]Setup: {agent_def.setup_command}[/dim]")
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
# Register with main CLI
|
|
276
|
+
def register_commands(cli):
|
|
277
|
+
"""Register auth commands with the main CLI."""
|
|
278
|
+
cli.add_command(auth)
|