stravinsky 0.4.18__py3-none-any.whl ā 0.4.66__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.
Potentially problematic release.
This version of stravinsky might be problematic. Click here for more details.
- mcp_bridge/__init__.py +1 -1
- mcp_bridge/auth/__init__.py +16 -6
- mcp_bridge/auth/cli.py +202 -11
- mcp_bridge/auth/oauth.py +1 -2
- mcp_bridge/auth/openai_oauth.py +4 -7
- mcp_bridge/auth/token_store.py +0 -1
- mcp_bridge/cli/__init__.py +1 -1
- mcp_bridge/cli/install_hooks.py +503 -107
- mcp_bridge/cli/session_report.py +0 -3
- mcp_bridge/config/__init__.py +2 -2
- mcp_bridge/config/hook_config.py +3 -5
- mcp_bridge/config/rate_limits.py +108 -13
- mcp_bridge/hooks/HOOKS_SETTINGS.json +17 -4
- mcp_bridge/hooks/__init__.py +14 -4
- mcp_bridge/hooks/agent_reminder.py +4 -4
- mcp_bridge/hooks/auto_slash_command.py +5 -5
- mcp_bridge/hooks/budget_optimizer.py +2 -2
- mcp_bridge/hooks/claude_limits_hook.py +114 -0
- mcp_bridge/hooks/comment_checker.py +3 -4
- mcp_bridge/hooks/compaction.py +2 -2
- mcp_bridge/hooks/context.py +2 -1
- mcp_bridge/hooks/context_monitor.py +2 -2
- mcp_bridge/hooks/delegation_policy.py +85 -0
- mcp_bridge/hooks/directory_context.py +3 -3
- mcp_bridge/hooks/edit_recovery.py +3 -2
- mcp_bridge/hooks/edit_recovery_policy.py +49 -0
- mcp_bridge/hooks/empty_message_sanitizer.py +2 -2
- mcp_bridge/hooks/events.py +160 -0
- mcp_bridge/hooks/git_noninteractive.py +4 -4
- mcp_bridge/hooks/keyword_detector.py +8 -10
- mcp_bridge/hooks/manager.py +35 -22
- mcp_bridge/hooks/notification_hook.py +13 -6
- mcp_bridge/hooks/parallel_enforcement_policy.py +67 -0
- mcp_bridge/hooks/parallel_enforcer.py +5 -5
- mcp_bridge/hooks/parallel_execution.py +22 -10
- mcp_bridge/hooks/post_tool/parallel_validation.py +103 -0
- mcp_bridge/hooks/pre_compact.py +8 -9
- mcp_bridge/hooks/pre_tool/agent_spawn_validator.py +115 -0
- mcp_bridge/hooks/preemptive_compaction.py +2 -3
- mcp_bridge/hooks/routing_notifications.py +80 -0
- mcp_bridge/hooks/rules_injector.py +11 -19
- mcp_bridge/hooks/session_idle.py +4 -4
- mcp_bridge/hooks/session_notifier.py +4 -4
- mcp_bridge/hooks/session_recovery.py +4 -5
- mcp_bridge/hooks/stravinsky_mode.py +1 -1
- mcp_bridge/hooks/subagent_stop.py +1 -3
- mcp_bridge/hooks/task_validator.py +2 -2
- mcp_bridge/hooks/tmux_manager.py +7 -8
- mcp_bridge/hooks/todo_delegation.py +4 -1
- mcp_bridge/hooks/todo_enforcer.py +180 -10
- mcp_bridge/hooks/truncation_policy.py +37 -0
- mcp_bridge/hooks/truncator.py +1 -2
- mcp_bridge/metrics/cost_tracker.py +115 -0
- mcp_bridge/native_search.py +93 -0
- mcp_bridge/native_watcher.py +118 -0
- mcp_bridge/notifications.py +3 -4
- mcp_bridge/orchestrator/enums.py +11 -0
- mcp_bridge/orchestrator/router.py +165 -0
- mcp_bridge/orchestrator/state.py +32 -0
- mcp_bridge/orchestrator/visualization.py +14 -0
- mcp_bridge/orchestrator/wisdom.py +34 -0
- mcp_bridge/prompts/__init__.py +1 -8
- mcp_bridge/prompts/dewey.py +1 -1
- mcp_bridge/prompts/planner.py +2 -4
- mcp_bridge/prompts/stravinsky.py +53 -31
- mcp_bridge/proxy/__init__.py +0 -0
- mcp_bridge/proxy/client.py +70 -0
- mcp_bridge/proxy/model_server.py +157 -0
- mcp_bridge/routing/__init__.py +43 -0
- mcp_bridge/routing/config.py +250 -0
- mcp_bridge/routing/model_tiers.py +135 -0
- mcp_bridge/routing/provider_state.py +261 -0
- mcp_bridge/routing/task_classifier.py +190 -0
- mcp_bridge/server.py +363 -34
- mcp_bridge/server_tools.py +298 -6
- mcp_bridge/tools/__init__.py +19 -8
- mcp_bridge/tools/agent_manager.py +549 -799
- mcp_bridge/tools/background_tasks.py +13 -17
- mcp_bridge/tools/code_search.py +54 -51
- mcp_bridge/tools/continuous_loop.py +0 -1
- mcp_bridge/tools/dashboard.py +19 -0
- mcp_bridge/tools/find_code.py +296 -0
- mcp_bridge/tools/init.py +1 -0
- mcp_bridge/tools/list_directory.py +42 -0
- mcp_bridge/tools/lsp/__init__.py +8 -8
- mcp_bridge/tools/lsp/manager.py +51 -28
- mcp_bridge/tools/lsp/tools.py +98 -65
- mcp_bridge/tools/model_invoke.py +1047 -152
- mcp_bridge/tools/mux_client.py +75 -0
- mcp_bridge/tools/project_context.py +1 -2
- mcp_bridge/tools/query_classifier.py +132 -49
- mcp_bridge/tools/read_file.py +84 -0
- mcp_bridge/tools/replace.py +45 -0
- mcp_bridge/tools/run_shell_command.py +38 -0
- mcp_bridge/tools/search_enhancements.py +347 -0
- mcp_bridge/tools/semantic_search.py +677 -92
- mcp_bridge/tools/session_manager.py +0 -2
- mcp_bridge/tools/skill_loader.py +0 -1
- mcp_bridge/tools/task_runner.py +5 -7
- mcp_bridge/tools/templates.py +3 -3
- mcp_bridge/tools/tool_search.py +331 -0
- mcp_bridge/tools/write_file.py +29 -0
- mcp_bridge/update_manager.py +33 -37
- mcp_bridge/update_manager_pypi.py +6 -8
- mcp_bridge/utils/cache.py +82 -0
- mcp_bridge/utils/process.py +71 -0
- mcp_bridge/utils/session_state.py +51 -0
- mcp_bridge/utils/truncation.py +76 -0
- {stravinsky-0.4.18.dist-info ā stravinsky-0.4.66.dist-info}/METADATA +84 -35
- stravinsky-0.4.66.dist-info/RECORD +198 -0
- {stravinsky-0.4.18.dist-info ā stravinsky-0.4.66.dist-info}/entry_points.txt +1 -0
- stravinsky_claude_assets/HOOKS_INTEGRATION.md +316 -0
- stravinsky_claude_assets/agents/HOOKS.md +437 -0
- stravinsky_claude_assets/agents/code-reviewer.md +210 -0
- stravinsky_claude_assets/agents/comment_checker.md +580 -0
- stravinsky_claude_assets/agents/debugger.md +254 -0
- stravinsky_claude_assets/agents/delphi.md +495 -0
- stravinsky_claude_assets/agents/dewey.md +248 -0
- stravinsky_claude_assets/agents/explore.md +1198 -0
- stravinsky_claude_assets/agents/frontend.md +472 -0
- stravinsky_claude_assets/agents/implementation-lead.md +164 -0
- stravinsky_claude_assets/agents/momus.md +464 -0
- stravinsky_claude_assets/agents/research-lead.md +141 -0
- stravinsky_claude_assets/agents/stravinsky.md +730 -0
- stravinsky_claude_assets/commands/delphi.md +9 -0
- stravinsky_claude_assets/commands/dewey.md +54 -0
- stravinsky_claude_assets/commands/git-master.md +112 -0
- stravinsky_claude_assets/commands/index.md +49 -0
- stravinsky_claude_assets/commands/publish.md +86 -0
- stravinsky_claude_assets/commands/review.md +73 -0
- stravinsky_claude_assets/commands/str/agent_cancel.md +70 -0
- stravinsky_claude_assets/commands/str/agent_list.md +56 -0
- stravinsky_claude_assets/commands/str/agent_output.md +92 -0
- stravinsky_claude_assets/commands/str/agent_progress.md +74 -0
- stravinsky_claude_assets/commands/str/agent_retry.md +94 -0
- stravinsky_claude_assets/commands/str/cancel.md +51 -0
- stravinsky_claude_assets/commands/str/clean.md +97 -0
- stravinsky_claude_assets/commands/str/continue.md +38 -0
- stravinsky_claude_assets/commands/str/index.md +199 -0
- stravinsky_claude_assets/commands/str/list_watchers.md +96 -0
- stravinsky_claude_assets/commands/str/search.md +205 -0
- stravinsky_claude_assets/commands/str/start_filewatch.md +136 -0
- stravinsky_claude_assets/commands/str/stats.md +71 -0
- stravinsky_claude_assets/commands/str/stop_filewatch.md +89 -0
- stravinsky_claude_assets/commands/str/unwatch.md +42 -0
- stravinsky_claude_assets/commands/str/watch.md +45 -0
- stravinsky_claude_assets/commands/strav.md +53 -0
- stravinsky_claude_assets/commands/stravinsky.md +292 -0
- stravinsky_claude_assets/commands/verify.md +60 -0
- stravinsky_claude_assets/commands/version.md +5 -0
- stravinsky_claude_assets/hooks/README.md +248 -0
- stravinsky_claude_assets/hooks/comment_checker.py +193 -0
- stravinsky_claude_assets/hooks/context.py +38 -0
- stravinsky_claude_assets/hooks/context_monitor.py +153 -0
- stravinsky_claude_assets/hooks/dependency_tracker.py +73 -0
- stravinsky_claude_assets/hooks/edit_recovery.py +46 -0
- stravinsky_claude_assets/hooks/execution_state_tracker.py +68 -0
- stravinsky_claude_assets/hooks/notification_hook.py +103 -0
- stravinsky_claude_assets/hooks/notification_hook_v2.py +96 -0
- stravinsky_claude_assets/hooks/parallel_execution.py +241 -0
- stravinsky_claude_assets/hooks/parallel_reinforcement.py +106 -0
- stravinsky_claude_assets/hooks/parallel_reinforcement_v2.py +112 -0
- stravinsky_claude_assets/hooks/pre_compact.py +123 -0
- stravinsky_claude_assets/hooks/ralph_loop.py +173 -0
- stravinsky_claude_assets/hooks/session_recovery.py +263 -0
- stravinsky_claude_assets/hooks/stop_hook.py +89 -0
- stravinsky_claude_assets/hooks/stravinsky_metrics.py +164 -0
- stravinsky_claude_assets/hooks/stravinsky_mode.py +146 -0
- stravinsky_claude_assets/hooks/subagent_stop.py +98 -0
- stravinsky_claude_assets/hooks/todo_continuation.py +111 -0
- stravinsky_claude_assets/hooks/todo_delegation.py +96 -0
- stravinsky_claude_assets/hooks/tool_messaging.py +281 -0
- stravinsky_claude_assets/hooks/truncator.py +23 -0
- stravinsky_claude_assets/rules/deployment_safety.md +51 -0
- stravinsky_claude_assets/rules/integration_wiring.md +89 -0
- stravinsky_claude_assets/rules/pypi_deployment.md +220 -0
- stravinsky_claude_assets/rules/stravinsky_orchestrator.md +32 -0
- stravinsky_claude_assets/settings.json +152 -0
- stravinsky_claude_assets/skills/chrome-devtools/SKILL.md +81 -0
- stravinsky_claude_assets/skills/sqlite/SKILL.md +77 -0
- stravinsky_claude_assets/skills/supabase/SKILL.md +74 -0
- stravinsky_claude_assets/task_dependencies.json +34 -0
- stravinsky-0.4.18.dist-info/RECORD +0 -88
- {stravinsky-0.4.18.dist-info ā stravinsky-0.4.66.dist-info}/WHEEL +0 -0
mcp_bridge/__init__.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "0.4.
|
|
1
|
+
__version__ = "0.4.66"
|
mcp_bridge/auth/__init__.py
CHANGED
|
@@ -1,18 +1,28 @@
|
|
|
1
1
|
# Authentication module
|
|
2
|
-
from .token_store import TokenStore, TokenData
|
|
3
2
|
from .oauth import (
|
|
4
|
-
perform_oauth_flow as gemini_oauth_flow,
|
|
5
|
-
refresh_access_token as gemini_refresh_token,
|
|
6
3
|
ANTIGRAVITY_CLIENT_ID,
|
|
7
|
-
ANTIGRAVITY_SCOPES,
|
|
8
4
|
ANTIGRAVITY_HEADERS,
|
|
5
|
+
ANTIGRAVITY_SCOPES,
|
|
6
|
+
)
|
|
7
|
+
from .oauth import (
|
|
8
|
+
perform_oauth_flow as gemini_oauth_flow,
|
|
9
|
+
)
|
|
10
|
+
from .oauth import (
|
|
11
|
+
refresh_access_token as gemini_refresh_token,
|
|
9
12
|
)
|
|
10
13
|
from .openai_oauth import (
|
|
11
|
-
perform_oauth_flow as openai_oauth_flow,
|
|
12
|
-
refresh_access_token as openai_refresh_token,
|
|
13
14
|
CLIENT_ID as OPENAI_CLIENT_ID,
|
|
15
|
+
)
|
|
16
|
+
from .openai_oauth import (
|
|
14
17
|
OPENAI_CALLBACK_PORT,
|
|
15
18
|
)
|
|
19
|
+
from .openai_oauth import (
|
|
20
|
+
perform_oauth_flow as openai_oauth_flow,
|
|
21
|
+
)
|
|
22
|
+
from .openai_oauth import (
|
|
23
|
+
refresh_access_token as openai_refresh_token,
|
|
24
|
+
)
|
|
25
|
+
from .token_store import TokenData, TokenStore
|
|
16
26
|
|
|
17
27
|
__all__ = [
|
|
18
28
|
# Token Store
|
mcp_bridge/auth/cli.py
CHANGED
|
@@ -14,16 +14,26 @@ Usage:
|
|
|
14
14
|
"""
|
|
15
15
|
|
|
16
16
|
import argparse
|
|
17
|
+
import json
|
|
17
18
|
import sys
|
|
18
19
|
import time
|
|
20
|
+
from pathlib import Path
|
|
19
21
|
|
|
20
|
-
from .token_store import TokenStore
|
|
21
22
|
from ..tools.init import bootstrap_repo
|
|
22
|
-
from .oauth import perform_oauth_flow as gemini_oauth
|
|
23
|
+
from .oauth import perform_oauth_flow as gemini_oauth
|
|
24
|
+
from .oauth import refresh_access_token as gemini_refresh
|
|
23
25
|
from .openai_oauth import (
|
|
24
26
|
perform_oauth_flow as openai_oauth,
|
|
27
|
+
)
|
|
28
|
+
from .openai_oauth import (
|
|
25
29
|
refresh_access_token as openai_refresh,
|
|
26
30
|
)
|
|
31
|
+
from .token_store import TokenStore
|
|
32
|
+
|
|
33
|
+
try:
|
|
34
|
+
from ..routing import get_provider_tracker
|
|
35
|
+
except ImportError:
|
|
36
|
+
get_provider_tracker = None # type: ignore
|
|
27
37
|
|
|
28
38
|
|
|
29
39
|
def cmd_login(provider: str, token_store: TokenStore) -> int:
|
|
@@ -34,7 +44,7 @@ def cmd_login(provider: str, token_store: TokenStore) -> int:
|
|
|
34
44
|
For OpenAI: Uses OpenAI OAuth (ChatGPT Plus/Pro subscription)
|
|
35
45
|
"""
|
|
36
46
|
if provider == "gemini":
|
|
37
|
-
print(
|
|
47
|
+
print("Starting Google OAuth for Gemini...")
|
|
38
48
|
|
|
39
49
|
try:
|
|
40
50
|
result = gemini_oauth()
|
|
@@ -57,7 +67,7 @@ def cmd_login(provider: str, token_store: TokenStore) -> int:
|
|
|
57
67
|
return 1
|
|
58
68
|
|
|
59
69
|
elif provider == "openai":
|
|
60
|
-
print(
|
|
70
|
+
print("Starting OpenAI OAuth for ChatGPT Plus/Pro...")
|
|
61
71
|
print("Note: Requires ChatGPT Plus/Pro subscription and port 1455 available")
|
|
62
72
|
|
|
63
73
|
try:
|
|
@@ -72,7 +82,7 @@ def cmd_login(provider: str, token_store: TokenStore) -> int:
|
|
|
72
82
|
expires_at=expires_at,
|
|
73
83
|
)
|
|
74
84
|
|
|
75
|
-
print(
|
|
85
|
+
print("\nā Successfully authenticated with OpenAI")
|
|
76
86
|
print(f" Token expires in: {result.expires_in // 60} minutes")
|
|
77
87
|
return 0
|
|
78
88
|
|
|
@@ -116,20 +126,156 @@ def cmd_status(token_store: TokenStore) -> int:
|
|
|
116
126
|
minutes = (remaining % 3600) // 60
|
|
117
127
|
print(f" Expires in: {hours}h {minutes}m")
|
|
118
128
|
else:
|
|
119
|
-
print(
|
|
129
|
+
print(" Token expired")
|
|
120
130
|
|
|
121
131
|
print()
|
|
122
132
|
return 0
|
|
123
133
|
|
|
124
134
|
|
|
125
135
|
def cmd_refresh(provider: str, token_store: TokenStore) -> int:
|
|
126
|
-
"""
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
136
|
+
"""Manually refresh an access token."""
|
|
137
|
+
try:
|
|
138
|
+
token = token_store.get_token(provider)
|
|
139
|
+
if not token:
|
|
140
|
+
print(f"Not logged in to {provider}. Run 'stravinsky-auth login {provider}' first.")
|
|
141
|
+
return 1
|
|
142
|
+
|
|
143
|
+
print(f"Refreshing {provider} access token...")
|
|
144
|
+
|
|
145
|
+
if provider == "gemini":
|
|
146
|
+
result = gemini_refresh(token["refresh_token"])
|
|
147
|
+
elif provider == "openai":
|
|
148
|
+
result = openai_refresh(token["refresh_token"])
|
|
149
|
+
else:
|
|
150
|
+
print(f"Refresh not supported for {provider}")
|
|
151
|
+
return 1
|
|
152
|
+
|
|
153
|
+
expires_at = int(time.time()) + result.expires_in
|
|
154
|
+
|
|
155
|
+
token_store.set_token(
|
|
156
|
+
provider=provider,
|
|
157
|
+
access_token=result.access_token,
|
|
158
|
+
refresh_token=result.refresh_token or token["refresh_token"],
|
|
159
|
+
expires_at=expires_at,
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
print(f"ā Token refreshed, expires in {result.expires_in // 60} minutes")
|
|
163
|
+
return 0
|
|
164
|
+
|
|
165
|
+
except Exception as e:
|
|
166
|
+
print(f"ā Refresh failed: {e}", file=sys.stderr)
|
|
167
|
+
return 1
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def cmd_routing_status() -> int:
|
|
171
|
+
"""Show current routing provider states."""
|
|
172
|
+
if not get_provider_tracker or not callable(get_provider_tracker):
|
|
173
|
+
print("ā Routing module not available", file=sys.stderr)
|
|
131
174
|
return 1
|
|
132
175
|
|
|
176
|
+
try:
|
|
177
|
+
tracker = get_provider_tracker()
|
|
178
|
+
status = tracker.get_status()
|
|
179
|
+
|
|
180
|
+
print("\nš Provider Routing Status\n")
|
|
181
|
+
for provider_name, state in status.items():
|
|
182
|
+
available = "ā Available" if state["available"] else "ā³ In Cooldown"
|
|
183
|
+
print(f"{provider_name.title():10} {available}")
|
|
184
|
+
|
|
185
|
+
if state["cooldown_remaining"]:
|
|
186
|
+
mins = int(state["cooldown_remaining"] / 60)
|
|
187
|
+
secs = int(state["cooldown_remaining"] % 60)
|
|
188
|
+
print(f" Cooldown: {mins}m {secs}s remaining")
|
|
189
|
+
|
|
190
|
+
print(f" Requests: {state['total_requests']}")
|
|
191
|
+
print(f" Failures: {state['total_failures']}")
|
|
192
|
+
|
|
193
|
+
if state["last_error"]:
|
|
194
|
+
print(f" Last error: {state['last_error']}")
|
|
195
|
+
|
|
196
|
+
print()
|
|
197
|
+
|
|
198
|
+
return 0
|
|
199
|
+
|
|
200
|
+
except Exception as e:
|
|
201
|
+
print(f"ā Failed to get routing status: {e}", file=sys.stderr)
|
|
202
|
+
return 1
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
def cmd_routing_reset(provider: str | None = None) -> int:
|
|
206
|
+
"""Reset provider cooldown states."""
|
|
207
|
+
if not get_provider_tracker or not callable(get_provider_tracker):
|
|
208
|
+
print("ā Routing module not available", file=sys.stderr)
|
|
209
|
+
return 1
|
|
210
|
+
|
|
211
|
+
try:
|
|
212
|
+
tracker = get_provider_tracker()
|
|
213
|
+
|
|
214
|
+
if provider:
|
|
215
|
+
tracker.reset_provider(provider)
|
|
216
|
+
print(f"ā Reset cooldown state for {provider}")
|
|
217
|
+
else:
|
|
218
|
+
tracker.reset_all()
|
|
219
|
+
print("ā Reset all provider cooldown states")
|
|
220
|
+
|
|
221
|
+
return 0
|
|
222
|
+
|
|
223
|
+
except Exception as e:
|
|
224
|
+
print(f"ā Failed to reset routing state: {e}", file=sys.stderr)
|
|
225
|
+
return 1
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
def cmd_routing_init() -> int:
|
|
229
|
+
"""Create default routing.json configuration."""
|
|
230
|
+
config_path = Path(".stravinsky/routing.json")
|
|
231
|
+
|
|
232
|
+
if config_path.exists():
|
|
233
|
+
response = input(f"{config_path} already exists. Overwrite? [y/N]: ")
|
|
234
|
+
if response.lower() != "y":
|
|
235
|
+
print("Cancelled.")
|
|
236
|
+
return 0
|
|
237
|
+
|
|
238
|
+
config_path.parent.mkdir(parents=True, exist_ok=True)
|
|
239
|
+
|
|
240
|
+
default_config = {
|
|
241
|
+
"routing": {
|
|
242
|
+
"task_routing": {
|
|
243
|
+
"code_generation": {
|
|
244
|
+
"provider": "openai",
|
|
245
|
+
"model": "gpt-5-codex",
|
|
246
|
+
"description": "Complex code generation tasks",
|
|
247
|
+
},
|
|
248
|
+
"debugging": {
|
|
249
|
+
"provider": "openai",
|
|
250
|
+
"model": "gpt-5-codex",
|
|
251
|
+
"description": "Code analysis and debugging",
|
|
252
|
+
},
|
|
253
|
+
"documentation": {
|
|
254
|
+
"provider": "gemini",
|
|
255
|
+
"model": "gemini-3-flash",
|
|
256
|
+
"description": "Documentation writing",
|
|
257
|
+
},
|
|
258
|
+
"code_search": {
|
|
259
|
+
"provider": "gemini",
|
|
260
|
+
"model": "gemini-3-flash",
|
|
261
|
+
"description": "Finding code patterns",
|
|
262
|
+
},
|
|
263
|
+
},
|
|
264
|
+
"fallback": {
|
|
265
|
+
"enabled": True,
|
|
266
|
+
"chain": ["claude", "openai", "gemini"],
|
|
267
|
+
"cooldown_seconds": 300,
|
|
268
|
+
},
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
with open(config_path, "w") as f:
|
|
273
|
+
json.dump(default_config, f, indent=2)
|
|
274
|
+
|
|
275
|
+
print(f"ā Created {config_path}")
|
|
276
|
+
print("\nEdit this file to customize routing behavior for this project.")
|
|
277
|
+
return 0
|
|
278
|
+
|
|
133
279
|
try:
|
|
134
280
|
print(f"Refreshing {provider} token...")
|
|
135
281
|
|
|
@@ -226,6 +372,41 @@ def main():
|
|
|
226
372
|
description="Creates .stravinsky/ directory structure and copies default configuration files.",
|
|
227
373
|
)
|
|
228
374
|
|
|
375
|
+
# routing command
|
|
376
|
+
routing_parser = subparsers.add_parser(
|
|
377
|
+
"routing",
|
|
378
|
+
help="Manage provider routing and fallback",
|
|
379
|
+
description="View and manage provider routing states, cooldowns, and configuration.",
|
|
380
|
+
)
|
|
381
|
+
routing_subparsers = routing_parser.add_subparsers(
|
|
382
|
+
dest="routing_command", help="Routing subcommands", metavar="SUBCOMMAND"
|
|
383
|
+
)
|
|
384
|
+
|
|
385
|
+
routing_subparsers.add_parser(
|
|
386
|
+
"status",
|
|
387
|
+
help="Show current provider routing states",
|
|
388
|
+
description="Display availability, cooldown timers, and error counts for all providers.",
|
|
389
|
+
)
|
|
390
|
+
|
|
391
|
+
routing_reset_parser = routing_subparsers.add_parser(
|
|
392
|
+
"reset",
|
|
393
|
+
help="Reset provider cooldown states",
|
|
394
|
+
description="Clear cooldown timers for one or all providers.",
|
|
395
|
+
)
|
|
396
|
+
routing_reset_parser.add_argument(
|
|
397
|
+
"provider",
|
|
398
|
+
nargs="?",
|
|
399
|
+
choices=["claude", "openai", "gemini"],
|
|
400
|
+
metavar="PROVIDER",
|
|
401
|
+
help="Provider to reset (omit to reset all)",
|
|
402
|
+
)
|
|
403
|
+
|
|
404
|
+
routing_subparsers.add_parser(
|
|
405
|
+
"init",
|
|
406
|
+
help="Create default routing.json configuration",
|
|
407
|
+
description="Generate .stravinsky/routing.json with default task-based routing rules.",
|
|
408
|
+
)
|
|
409
|
+
|
|
229
410
|
args = parser.parse_args()
|
|
230
411
|
|
|
231
412
|
if not args.command:
|
|
@@ -245,6 +426,16 @@ def main():
|
|
|
245
426
|
elif args.command == "init":
|
|
246
427
|
print(bootstrap_repo())
|
|
247
428
|
return 0
|
|
429
|
+
elif args.command == "routing":
|
|
430
|
+
if not args.routing_command:
|
|
431
|
+
routing_parser.print_help()
|
|
432
|
+
return 1
|
|
433
|
+
if args.routing_command == "status":
|
|
434
|
+
return cmd_routing_status()
|
|
435
|
+
elif args.routing_command == "reset":
|
|
436
|
+
return cmd_routing_reset(args.provider if hasattr(args, "provider") else None)
|
|
437
|
+
elif args.routing_command == "init":
|
|
438
|
+
return cmd_routing_init()
|
|
248
439
|
|
|
249
440
|
return 0
|
|
250
441
|
|
mcp_bridge/auth/oauth.py
CHANGED
|
@@ -13,13 +13,12 @@ import secrets
|
|
|
13
13
|
import threading
|
|
14
14
|
import webbrowser
|
|
15
15
|
from dataclasses import dataclass
|
|
16
|
-
from http.server import
|
|
16
|
+
from http.server import BaseHTTPRequestHandler, HTTPServer
|
|
17
17
|
from typing import Any
|
|
18
18
|
from urllib.parse import parse_qs, urlencode, urlparse
|
|
19
19
|
|
|
20
20
|
import httpx
|
|
21
21
|
|
|
22
|
-
|
|
23
22
|
# OAuth 2.0 Client Credentials (from constants.ts)
|
|
24
23
|
ANTIGRAVITY_CLIENT_ID = "1071006060591-tmhssin2h21lcre235vtolojh4g403ep.apps.googleusercontent.com"
|
|
25
24
|
ANTIGRAVITY_CLIENT_SECRET = "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"
|
mcp_bridge/auth/openai_oauth.py
CHANGED
|
@@ -9,19 +9,16 @@ Port from: https://github.com/numman-ali/opencode-openai-codex-auth/blob/main/li
|
|
|
9
9
|
|
|
10
10
|
import base64
|
|
11
11
|
import hashlib
|
|
12
|
-
import json
|
|
13
12
|
import secrets
|
|
14
13
|
import threading
|
|
15
14
|
import webbrowser
|
|
16
15
|
from dataclasses import dataclass
|
|
17
|
-
from http.server import
|
|
16
|
+
from http.server import BaseHTTPRequestHandler, HTTPServer
|
|
18
17
|
from typing import Any
|
|
19
18
|
from urllib.parse import parse_qs, urlencode, urlparse
|
|
20
|
-
import time
|
|
21
19
|
|
|
22
20
|
import httpx
|
|
23
21
|
|
|
24
|
-
|
|
25
22
|
# OAuth constants (from openai/codex via opencode-openai-codex-auth)
|
|
26
23
|
CLIENT_ID = "app_EMoamEEZ73f0CkXaXp7hrann"
|
|
27
24
|
AUTHORIZE_URL = "https://auth.openai.com/oauth/authorize" # Note: /oauth/authorize
|
|
@@ -303,8 +300,8 @@ def perform_oauth_flow(timeout: int = 300) -> TokenResult:
|
|
|
303
300
|
try:
|
|
304
301
|
auth_url, verifier, state = build_auth_url(redirect_uri)
|
|
305
302
|
|
|
306
|
-
print(
|
|
307
|
-
print(
|
|
303
|
+
print("\nš Opening browser for OpenAI authentication...")
|
|
304
|
+
print("\nIf browser doesn't open, visit:")
|
|
308
305
|
print(f"{auth_url}\n")
|
|
309
306
|
|
|
310
307
|
webbrowser.open(auth_url)
|
|
@@ -329,7 +326,7 @@ def perform_oauth_flow(timeout: int = 300) -> TokenResult:
|
|
|
329
326
|
print("š Exchanging code for tokens...")
|
|
330
327
|
tokens = exchange_code(result["code"], verifier, redirect_uri)
|
|
331
328
|
|
|
332
|
-
print(
|
|
329
|
+
print("ā Successfully authenticated with OpenAI")
|
|
333
330
|
|
|
334
331
|
return tokens
|
|
335
332
|
|
mcp_bridge/auth/token_store.py
CHANGED
mcp_bridge/cli/__init__.py
CHANGED