flutter-dev 0.1.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.
- common_utils.py +261 -0
- core/__init__.py +7 -0
- core/constants.py +57 -0
- core/state.py +25 -0
- create_page.py +288 -0
- fdev.py +258 -0
- flutter_dev-0.1.0.dist-info/METADATA +411 -0
- flutter_dev-0.1.0.dist-info/RECORD +30 -0
- flutter_dev-0.1.0.dist-info/WHEEL +5 -0
- flutter_dev-0.1.0.dist-info/entry_points.txt +5 -0
- flutter_dev-0.1.0.dist-info/licenses/LICENSE +21 -0
- flutter_dev-0.1.0.dist-info/top_level.txt +9 -0
- gemini_api.py +395 -0
- git_diff_output_editor.py +34 -0
- install_legacy.py +467 -0
- managers/__init__.py +69 -0
- managers/ai.py +113 -0
- managers/app.py +541 -0
- managers/brew.py +477 -0
- managers/build.py +436 -0
- managers/datetime.py +49 -0
- managers/device.py +207 -0
- managers/doctor.py +286 -0
- managers/git.py +981 -0
- managers/git_account.py +542 -0
- managers/merge.py +165 -0
- managers/mirror.py +205 -0
- managers/project.py +138 -0
- managers/web_deploy.py +43 -0
- switch_ai.py +181 -0
managers/mirror.py
ADDED
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Mirror Manager - scrcpy and wireless ADB functions
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import shutil
|
|
7
|
+
import subprocess
|
|
8
|
+
|
|
9
|
+
from common_utils import (
|
|
10
|
+
RED, GREEN, YELLOW, BLUE, NC,
|
|
11
|
+
is_windows, is_macos, is_linux,
|
|
12
|
+
)
|
|
13
|
+
from core.constants import PATTERNS
|
|
14
|
+
from core.state import get_selected_device, set_selected_device, clear_selected_device
|
|
15
|
+
from managers.device import (
|
|
16
|
+
get_usb_devices,
|
|
17
|
+
ensure_device_connected,
|
|
18
|
+
build_adb_cmd,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def select_usb_device_for_wireless():
|
|
23
|
+
"""
|
|
24
|
+
Prompt user to select a USB device if multiple are connected.
|
|
25
|
+
Sets the selected device.
|
|
26
|
+
Returns: True if device selected/available, False if no devices
|
|
27
|
+
"""
|
|
28
|
+
devices = get_usb_devices()
|
|
29
|
+
|
|
30
|
+
if not devices:
|
|
31
|
+
return False
|
|
32
|
+
|
|
33
|
+
if len(devices) == 1:
|
|
34
|
+
# Only one USB device, auto-select it
|
|
35
|
+
set_selected_device(devices[0])
|
|
36
|
+
return True
|
|
37
|
+
|
|
38
|
+
# Multiple USB devices found, ask user to select
|
|
39
|
+
print(f"\n{YELLOW}Multiple USB devices detected:{NC}")
|
|
40
|
+
for i, device in enumerate(devices, 1):
|
|
41
|
+
print(f" {i}. {device}")
|
|
42
|
+
|
|
43
|
+
print()
|
|
44
|
+
while True:
|
|
45
|
+
try:
|
|
46
|
+
choice = input(f"Select USB device (1-{len(devices)}): ").strip()
|
|
47
|
+
index = int(choice) - 1
|
|
48
|
+
if 0 <= index < len(devices):
|
|
49
|
+
set_selected_device(devices[index])
|
|
50
|
+
print(f"{GREEN}✓ Selected: {devices[index]}{NC}\n")
|
|
51
|
+
return True
|
|
52
|
+
else:
|
|
53
|
+
print(f"{RED}Invalid choice. Please enter a number between 1 and {len(devices)}{NC}")
|
|
54
|
+
except ValueError:
|
|
55
|
+
print(f"{RED}Invalid input. Please enter a number{NC}")
|
|
56
|
+
except KeyboardInterrupt:
|
|
57
|
+
print(f"\n{YELLOW}Selection cancelled{NC}")
|
|
58
|
+
return False
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def setup_wireless_adb():
|
|
62
|
+
"""
|
|
63
|
+
Setup wireless ADB connection
|
|
64
|
+
Guides user through connecting device wirelessly
|
|
65
|
+
"""
|
|
66
|
+
print(f"{YELLOW}Setting up Wireless ADB...{NC}\n")
|
|
67
|
+
|
|
68
|
+
# Check if USB device is connected and select it
|
|
69
|
+
if not select_usb_device_for_wireless():
|
|
70
|
+
print(f"{RED}Error: No device connected via USB!{NC}")
|
|
71
|
+
print(f"{YELLOW}Please connect your device via USB first{NC}")
|
|
72
|
+
return False
|
|
73
|
+
|
|
74
|
+
selected_device = get_selected_device()
|
|
75
|
+
print(f"{GREEN}✓ Device found: {selected_device}{NC}")
|
|
76
|
+
print(f"\n{BLUE}Step 1: Getting device IP address...{NC}")
|
|
77
|
+
|
|
78
|
+
# Get device IP address
|
|
79
|
+
try:
|
|
80
|
+
result = subprocess.run(
|
|
81
|
+
build_adb_cmd(["shell", "ip", "addr", "show", "wlan0"]),
|
|
82
|
+
capture_output=True,
|
|
83
|
+
text=True,
|
|
84
|
+
encoding='utf-8',
|
|
85
|
+
errors='replace',
|
|
86
|
+
timeout=5
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
if result.returncode == 0:
|
|
90
|
+
# Extract IP address
|
|
91
|
+
match = PATTERNS['ip_address'].search(result.stdout)
|
|
92
|
+
if match:
|
|
93
|
+
device_ip = match.group(1)
|
|
94
|
+
print(f"{GREEN}Device IP: {device_ip}{NC}")
|
|
95
|
+
else:
|
|
96
|
+
print(f"{RED}Could not detect IP address{NC}")
|
|
97
|
+
device_ip = input(f"Enter device IP address manually: ")
|
|
98
|
+
else:
|
|
99
|
+
device_ip = input(f"Enter device IP address: ")
|
|
100
|
+
except Exception:
|
|
101
|
+
device_ip = input(f"Enter device IP address: ")
|
|
102
|
+
|
|
103
|
+
if not device_ip:
|
|
104
|
+
print(f"{RED}IP address required!{NC}")
|
|
105
|
+
return False
|
|
106
|
+
|
|
107
|
+
print(f"\n{BLUE}Step 2: Setting up ADB on port 5555...{NC}")
|
|
108
|
+
# Enable TCP/IP mode on port 5555
|
|
109
|
+
result = subprocess.run(
|
|
110
|
+
build_adb_cmd(["tcpip", "5555"]),
|
|
111
|
+
capture_output=True,
|
|
112
|
+
text=True,
|
|
113
|
+
encoding='utf-8',
|
|
114
|
+
errors='replace'
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
if result.returncode != 0:
|
|
118
|
+
print(f"{RED}Failed to enable TCP/IP mode{NC}")
|
|
119
|
+
print(f"{YELLOW}Error: {result.stderr}{NC}")
|
|
120
|
+
return False
|
|
121
|
+
|
|
122
|
+
print(f"{GREEN}✓ TCP/IP mode enabled{NC}")
|
|
123
|
+
print(f"\n{YELLOW}You can now disconnect the USB cable{NC}")
|
|
124
|
+
input("Press Enter after disconnecting USB cable...")
|
|
125
|
+
|
|
126
|
+
# Clear selected device since we're switching to wireless
|
|
127
|
+
clear_selected_device()
|
|
128
|
+
|
|
129
|
+
print(f"\n{BLUE}Step 3: Connecting to {device_ip}:5555...{NC}")
|
|
130
|
+
# Connect to device wirelessly (don't use device selection for connect command)
|
|
131
|
+
result = subprocess.run(
|
|
132
|
+
["adb", "connect", f"{device_ip}:5555"],
|
|
133
|
+
capture_output=True,
|
|
134
|
+
text=True,
|
|
135
|
+
encoding='utf-8',
|
|
136
|
+
errors='replace'
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
if result.returncode == 0 and "connected" in result.stdout.lower():
|
|
140
|
+
print(f"{GREEN}✓ Wireless ADB connected successfully!{NC}")
|
|
141
|
+
print(f"\n{BLUE}Device: {device_ip}:5555{NC}")
|
|
142
|
+
print(f"\n{YELLOW}To disconnect: adb disconnect{NC}")
|
|
143
|
+
print(f"{YELLOW}To reconnect via USB: adb usb{NC}")
|
|
144
|
+
return True
|
|
145
|
+
else:
|
|
146
|
+
print(f"{RED}Failed to connect wirelessly{NC}")
|
|
147
|
+
print(f"{YELLOW}Output: {result.stdout}{NC}")
|
|
148
|
+
return False
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def launch_scrcpy(always_on_top=True):
|
|
152
|
+
"""
|
|
153
|
+
Launch scrcpy with optimized settings
|
|
154
|
+
|
|
155
|
+
Args:
|
|
156
|
+
always_on_top: If True, keep the scrcpy window always on top
|
|
157
|
+
"""
|
|
158
|
+
print(f"{YELLOW}Launching scrcpy...{NC}\n")
|
|
159
|
+
|
|
160
|
+
# Check if scrcpy is installed (cross-platform)
|
|
161
|
+
scrcpy_path = shutil.which("scrcpy")
|
|
162
|
+
if not scrcpy_path:
|
|
163
|
+
print(f"{RED}Error: scrcpy not found!{NC}")
|
|
164
|
+
if is_windows():
|
|
165
|
+
print(f"{YELLOW}Install: scoop install scrcpy OR choco install scrcpy{NC}")
|
|
166
|
+
elif is_macos():
|
|
167
|
+
print(f"{YELLOW}Install: brew install scrcpy{NC}")
|
|
168
|
+
else:
|
|
169
|
+
print(f"{YELLOW}Install: sudo apt install scrcpy OR sudo snap install scrcpy{NC}")
|
|
170
|
+
return False
|
|
171
|
+
|
|
172
|
+
# Check for connected device and select if multiple
|
|
173
|
+
if not ensure_device_connected(
|
|
174
|
+
"No device connected!",
|
|
175
|
+
"Connect device via USB or use 'fdev mirror --wireless' for wireless setup"
|
|
176
|
+
):
|
|
177
|
+
return False
|
|
178
|
+
|
|
179
|
+
selected_device = get_selected_device()
|
|
180
|
+
print(f"{GREEN}✓ Device found: {selected_device}{NC}")
|
|
181
|
+
|
|
182
|
+
# Build scrcpy command with optimized settings
|
|
183
|
+
cmd = ["scrcpy", "-s", selected_device, "--no-mouse-hover", "-m", "1080", "-b", "5M"]
|
|
184
|
+
if always_on_top:
|
|
185
|
+
cmd.insert(4, "--always-on-top")
|
|
186
|
+
|
|
187
|
+
print(f"\n{BLUE}Launching scrcpy...{NC}")
|
|
188
|
+
print(f"{YELLOW}Command: {' '.join(cmd)}{NC}\n")
|
|
189
|
+
|
|
190
|
+
try:
|
|
191
|
+
# Launch scrcpy (blocking call)
|
|
192
|
+
result = subprocess.run(cmd)
|
|
193
|
+
|
|
194
|
+
if result.returncode == 0:
|
|
195
|
+
print(f"\n{GREEN}✓ scrcpy closed successfully{NC}")
|
|
196
|
+
return True
|
|
197
|
+
else:
|
|
198
|
+
print(f"\n{YELLOW}scrcpy exited with code {result.returncode}{NC}")
|
|
199
|
+
return False
|
|
200
|
+
except KeyboardInterrupt:
|
|
201
|
+
print(f"\n{YELLOW}scrcpy interrupted by user{NC}")
|
|
202
|
+
return False
|
|
203
|
+
except Exception as e:
|
|
204
|
+
print(f"{RED}Error launching scrcpy: {e}{NC}")
|
|
205
|
+
return False
|
managers/project.py
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Project Manager - Setup, cleanup, build_runner, pods functions
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import os
|
|
7
|
+
import sys
|
|
8
|
+
import subprocess
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
|
|
11
|
+
from common_utils import (
|
|
12
|
+
RED, GREEN, YELLOW,BLUE, NC, CHECKMARK,
|
|
13
|
+
timer_decorator,
|
|
14
|
+
is_windows,
|
|
15
|
+
)
|
|
16
|
+
from managers.build import run_flutter_command
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def generate_lang():
|
|
20
|
+
"""Generate localization files"""
|
|
21
|
+
# Run flutter gen-l10n to generate localization files
|
|
22
|
+
run_flutter_command(["flutter", "gen-l10n"], "Generating localizations ")
|
|
23
|
+
print(f"\n{CHECKMARK} Localizations generated successfully.")
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def run_build_runner():
|
|
27
|
+
"""Run build_runner to generate Dart code"""
|
|
28
|
+
print(f"{YELLOW}Executing build_runner...{NC} \n")
|
|
29
|
+
run_flutter_command(["dart", "run", "build_runner", "build", "--delete-conflicting-outputs"], "Running build_runner ")
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@timer_decorator
|
|
33
|
+
def full_setup():
|
|
34
|
+
"""Perform full project setup"""
|
|
35
|
+
print(f"{YELLOW}Performing full setup...{NC} \n")
|
|
36
|
+
# Clean the project
|
|
37
|
+
run_flutter_command(["flutter", "clean"], "Cleaning project... ")
|
|
38
|
+
# Upgrade dependencies
|
|
39
|
+
run_flutter_command(["flutter", "pub", "upgrade"], "Upgrading dependencies... ")
|
|
40
|
+
# Run build_runner
|
|
41
|
+
run_flutter_command(["dart", "run", "build_runner", "build", "--delete-conflicting-outputs"], "Running build_runner... ")
|
|
42
|
+
# Generate localizations
|
|
43
|
+
run_flutter_command(["flutter", "gen-l10n"], "Generating localizations... ")
|
|
44
|
+
# Refresh dependencies
|
|
45
|
+
run_flutter_command(["flutter", "pub", "upgrade"], "Refreshing dependencies... ")
|
|
46
|
+
# Analyze code
|
|
47
|
+
run_flutter_command(["flutter", "analyze"], "Analyzing code... ")
|
|
48
|
+
# Format code
|
|
49
|
+
run_flutter_command(["dart", "format", "."], "Formatting code... ")
|
|
50
|
+
print(f"\n {GREEN}✓ Full setup completed successfully. {NC}")
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def repair_cache():
|
|
54
|
+
"""Repair pub cache"""
|
|
55
|
+
print(f"{YELLOW}Repairing pub cache...{NC}\n")
|
|
56
|
+
run_flutter_command(["flutter", "pub", "cache", "repair"], "Repairing pub cache... ")
|
|
57
|
+
print(f"\n {GREEN}✓ Pub cache repaired successfully. {NC}")
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
@timer_decorator
|
|
61
|
+
def cleanup_project():
|
|
62
|
+
"""Clean up project"""
|
|
63
|
+
print(f"{YELLOW}Cleaning up project...{NC}\n")
|
|
64
|
+
run_flutter_command(["flutter", "clean"], "Cleaning project... ")
|
|
65
|
+
# Get dependencies
|
|
66
|
+
run_flutter_command(["flutter", "pub", "get"], "Getting dependencies... ")
|
|
67
|
+
|
|
68
|
+
# fix code Issues
|
|
69
|
+
run_flutter_command(["dart", "fix", "--apply"], "Fixing code issues... ")
|
|
70
|
+
|
|
71
|
+
# Format code
|
|
72
|
+
run_flutter_command(["dart", "format", "."], "Following dart guidelines... ")
|
|
73
|
+
|
|
74
|
+
# Upgrade with major version
|
|
75
|
+
run_flutter_command(["flutter", "pub", "upgrade", "--major-versions"], "Upgrading major versions... ")
|
|
76
|
+
print(f"\n{GREEN}✓ Project cleaned successfully!{NC}")
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
@timer_decorator
|
|
80
|
+
def update_pods():
|
|
81
|
+
"""Update iOS pods"""
|
|
82
|
+
if is_windows():
|
|
83
|
+
print(f"{YELLOW}iOS pods are not supported on Windows{NC}")
|
|
84
|
+
print(f"{BLUE}This command is only available on macOS and Linux{NC}")
|
|
85
|
+
return
|
|
86
|
+
|
|
87
|
+
# Check if ios directory exists
|
|
88
|
+
if not os.path.isdir("ios"):
|
|
89
|
+
print(f"{RED}Error: 'ios' directory not found. Are you in a Flutter project root?{NC}")
|
|
90
|
+
return
|
|
91
|
+
|
|
92
|
+
print(f"{YELLOW}Updating iOS pods...{NC}\n")
|
|
93
|
+
|
|
94
|
+
# Set UTF-8 encoding to prevent CocoaPods locale issues
|
|
95
|
+
env = os.environ.copy()
|
|
96
|
+
env['LANG'] = 'en_US.UTF-8'
|
|
97
|
+
env['LC_ALL'] = 'en_US.UTF-8'
|
|
98
|
+
|
|
99
|
+
# Navigate to iOS directory
|
|
100
|
+
current_dir = os.getcwd()
|
|
101
|
+
os.chdir("ios")
|
|
102
|
+
# Delete Podfile.lock
|
|
103
|
+
try:
|
|
104
|
+
os.remove("Podfile.lock")
|
|
105
|
+
# Use a dummy process for the loading animation
|
|
106
|
+
if is_windows():
|
|
107
|
+
run_flutter_command(["timeout", "/t", "1", "/nobreak", ">nul"], "Removing Podfile.lock ")
|
|
108
|
+
else:
|
|
109
|
+
run_flutter_command(["sleep", "0.1"], "Removing Podfile.lock ")
|
|
110
|
+
except FileNotFoundError:
|
|
111
|
+
pass
|
|
112
|
+
# Update pod repo (with UTF-8 env)
|
|
113
|
+
run_flutter_command(["pod", "repo", "update"], "Updating pod repository ", env=env)
|
|
114
|
+
# Install pods (with UTF-8 env)
|
|
115
|
+
run_flutter_command(["pod", "install"], "Installing pods ", env=env)
|
|
116
|
+
# Return to root directory
|
|
117
|
+
os.chdir(current_dir)
|
|
118
|
+
print(f"\n{GREEN}✓ iOS pods updated successfully!{NC}")
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def create_page(page_name):
|
|
122
|
+
"""Create page structure"""
|
|
123
|
+
print(f"{YELLOW}Creating page...{NC}\n")
|
|
124
|
+
if not page_name:
|
|
125
|
+
print(f"{RED}Error: Page name is required.{NC}")
|
|
126
|
+
print(f"Usage: {sys.argv[0]} page <page_name>")
|
|
127
|
+
sys.exit(1)
|
|
128
|
+
# Run the create_page with the page name using global path
|
|
129
|
+
try:
|
|
130
|
+
create_page_script = str(Path.home() / "scripts" / "flutter-tools" / "create_page.py")
|
|
131
|
+
subprocess.run([sys.executable, create_page_script, "page", page_name], check=True)
|
|
132
|
+
except subprocess.CalledProcessError:
|
|
133
|
+
print(f"{RED}Error: Failed to run page generator.{NC}")
|
|
134
|
+
sys.exit(1)
|
|
135
|
+
except FileNotFoundError:
|
|
136
|
+
print(f"{RED}Error: create_page.py not found.{NC}")
|
|
137
|
+
print(f"{YELLOW}Make sure create_page.py exists at ~/scripts/flutter-tools/{NC}")
|
|
138
|
+
sys.exit(1)
|
managers/web_deploy.py
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Web Deploy Manager - Build Flutter web and deploy to Firebase
|
|
4
|
+
(backend functions + frontend hosting together)
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import subprocess
|
|
8
|
+
|
|
9
|
+
from common_utils import RED, GREEN, YELLOW, BLUE, NC, timer_decorator
|
|
10
|
+
from managers.build import run_flutter_command
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@timer_decorator
|
|
14
|
+
def web_deploy():
|
|
15
|
+
"""Build Flutter web (release) and deploy functions + hosting to Firebase."""
|
|
16
|
+
print(f"{YELLOW}Building Flutter web (release)...{NC}\n")
|
|
17
|
+
|
|
18
|
+
build_success = run_flutter_command(
|
|
19
|
+
["flutter", "build", "web", "--release"],
|
|
20
|
+
"Building web... "
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
if not build_success:
|
|
24
|
+
print(f"\n{RED}✗ Web build failed!{NC}")
|
|
25
|
+
return False
|
|
26
|
+
|
|
27
|
+
print(f"\n{YELLOW}Deploying to Firebase (functions + hosting)...{NC}\n")
|
|
28
|
+
|
|
29
|
+
try:
|
|
30
|
+
result = subprocess.run(
|
|
31
|
+
["firebase", "deploy", "--only", "functions,hosting"]
|
|
32
|
+
)
|
|
33
|
+
except FileNotFoundError:
|
|
34
|
+
print(f"{RED}✗ 'firebase' CLI not found.{NC}")
|
|
35
|
+
print(f"{YELLOW}Install it with: npm install -g firebase-tools{NC}")
|
|
36
|
+
return False
|
|
37
|
+
|
|
38
|
+
if result.returncode != 0:
|
|
39
|
+
print(f"\n{RED}✗ Firebase deploy failed!{NC}")
|
|
40
|
+
return False
|
|
41
|
+
|
|
42
|
+
print(f"\n{GREEN}✓ Web deployed successfully (functions + hosting)!{NC}")
|
|
43
|
+
return True
|
switch_ai.py
ADDED
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
AI Service Switcher
|
|
4
|
+
Usage: python3 switch_ai.py [groq|mistral|sambanova|openrouter]
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import sys
|
|
8
|
+
import os
|
|
9
|
+
import subprocess
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
|
|
12
|
+
# Import common utilities
|
|
13
|
+
from common_utils import (
|
|
14
|
+
RED, GREEN, YELLOW, BLUE, NC, CHECKMARK, CROSS,
|
|
15
|
+
timer_decorator,
|
|
16
|
+
run_command_with_spinner,
|
|
17
|
+
read_env_value,
|
|
18
|
+
update_env_value,
|
|
19
|
+
get_user_shell,
|
|
20
|
+
is_windows
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
ENV_FILE = ".env"
|
|
24
|
+
|
|
25
|
+
def show_current_service():
|
|
26
|
+
"""
|
|
27
|
+
Show current AI service and usage instructions
|
|
28
|
+
"""
|
|
29
|
+
current = read_env_value("DEFAULT_AI_SERVICE")
|
|
30
|
+
if current:
|
|
31
|
+
print(f"{BLUE}📌 Current AI Service: {GREEN}{current}{NC}")
|
|
32
|
+
else:
|
|
33
|
+
print(f"{YELLOW}⚠️ DEFAULT_AI_SERVICE not found in .env{NC}")
|
|
34
|
+
|
|
35
|
+
print("")
|
|
36
|
+
print(f"{YELLOW}🔄 To switch service:{NC}")
|
|
37
|
+
print(f" {BLUE}python3 switch_ai.py groq{NC}")
|
|
38
|
+
print(f" {BLUE}python3 switch_ai.py mistral{NC}")
|
|
39
|
+
print(f" {BLUE}python3 switch_ai.py sambanova{NC}")
|
|
40
|
+
print(f" {BLUE}python3 switch_ai.py openrouter{NC}")
|
|
41
|
+
|
|
42
|
+
def run_setup():
|
|
43
|
+
"""
|
|
44
|
+
Run the legacy installer after successful AI service switch with loading spinner
|
|
45
|
+
Returns:
|
|
46
|
+
True if successful, False otherwise
|
|
47
|
+
"""
|
|
48
|
+
setup_script = Path(__file__).parent / "install_legacy.py"
|
|
49
|
+
|
|
50
|
+
if not setup_script.exists():
|
|
51
|
+
print(f"{YELLOW}⚠️ install_legacy.py not found, skipping setup{NC}")
|
|
52
|
+
return False
|
|
53
|
+
|
|
54
|
+
print("")
|
|
55
|
+
print(f"{YELLOW}🔧 Running install_legacy.py...{NC}")
|
|
56
|
+
print(f"{BLUE}{'─' * 50}{NC}")
|
|
57
|
+
|
|
58
|
+
success = run_command_with_spinner(
|
|
59
|
+
[sys.executable, str(setup_script)],
|
|
60
|
+
f"{YELLOW}Configuring environment... {NC}"
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
print(f"{BLUE}{'─' * 50}{NC}")
|
|
64
|
+
|
|
65
|
+
if success:
|
|
66
|
+
print(f"{GREEN}✅ Setup completed successfully!{NC}")
|
|
67
|
+
return True
|
|
68
|
+
else:
|
|
69
|
+
print(f"{RED}❌ Setup failed!{NC}")
|
|
70
|
+
return False
|
|
71
|
+
|
|
72
|
+
def reload_shell_config():
|
|
73
|
+
"""
|
|
74
|
+
Reload shell configuration after successful switch
|
|
75
|
+
Supports zsh, bash, fish on Unix systems
|
|
76
|
+
"""
|
|
77
|
+
if is_windows():
|
|
78
|
+
print(f"\n{YELLOW}⚠️ Windows detected - please restart your terminal{NC}")
|
|
79
|
+
return
|
|
80
|
+
|
|
81
|
+
print("")
|
|
82
|
+
print(f"{YELLOW}🔄 Reloading shell configuration...{NC}")
|
|
83
|
+
|
|
84
|
+
shell_name, config_file = get_user_shell()
|
|
85
|
+
|
|
86
|
+
try:
|
|
87
|
+
# Try to source the config file
|
|
88
|
+
subprocess.run(
|
|
89
|
+
[shell_name, "-c", f"source {config_file}"],
|
|
90
|
+
check=False,
|
|
91
|
+
stdout=subprocess.DEVNULL,
|
|
92
|
+
stderr=subprocess.DEVNULL
|
|
93
|
+
)
|
|
94
|
+
print(f"{GREEN}✅ Shell configuration reloaded!{NC}")
|
|
95
|
+
print("")
|
|
96
|
+
print(f"{BLUE}💡 If commands are not working, run manually:{NC}")
|
|
97
|
+
print(f" {GREEN}source {config_file}{NC}")
|
|
98
|
+
except Exception:
|
|
99
|
+
print(f"{YELLOW}⚠️ Please run manually to reload your shell:{NC}")
|
|
100
|
+
print(f" {GREEN}source {config_file}{NC}")
|
|
101
|
+
|
|
102
|
+
@timer_decorator
|
|
103
|
+
def switch_service(service):
|
|
104
|
+
"""
|
|
105
|
+
Switch AI service with full validation and setup
|
|
106
|
+
Parameters:
|
|
107
|
+
service: Service name to switch to
|
|
108
|
+
Returns:
|
|
109
|
+
True if successful, False otherwise
|
|
110
|
+
"""
|
|
111
|
+
# Get current service
|
|
112
|
+
current = read_env_value("DEFAULT_AI_SERVICE")
|
|
113
|
+
|
|
114
|
+
# Check if trying to switch to the same service
|
|
115
|
+
if current == service:
|
|
116
|
+
print(f"{YELLOW}⚠️ AI Service is already set to: {GREEN}{service}{NC}")
|
|
117
|
+
print(f" {BLUE}{current} → {service}{NC}")
|
|
118
|
+
print(f" {YELLOW}No changes made.{NC}")
|
|
119
|
+
return False
|
|
120
|
+
|
|
121
|
+
# Update .env file
|
|
122
|
+
print(f"{YELLOW}Switching AI service...{NC}\n")
|
|
123
|
+
|
|
124
|
+
if update_env_value("DEFAULT_AI_SERVICE", service):
|
|
125
|
+
print(f"{GREEN}✅ AI Service switched successfully!{NC}")
|
|
126
|
+
print(f" {BLUE}{current}{NC} → {GREEN}{service}{NC}")
|
|
127
|
+
|
|
128
|
+
# Run the legacy installer after successful switch
|
|
129
|
+
setup_success = run_setup()
|
|
130
|
+
|
|
131
|
+
# Reload shell configuration
|
|
132
|
+
reload_shell_config()
|
|
133
|
+
|
|
134
|
+
return setup_success
|
|
135
|
+
else:
|
|
136
|
+
print(f"{RED}❌ Failed to update .env file{NC}")
|
|
137
|
+
return False
|
|
138
|
+
|
|
139
|
+
def main():
|
|
140
|
+
"""
|
|
141
|
+
Main function to handle AI service switching
|
|
142
|
+
"""
|
|
143
|
+
print(f"{BLUE}{'=' * 50}{NC}")
|
|
144
|
+
print(f"{BLUE} AI Service Switcher{NC}")
|
|
145
|
+
print(f"{BLUE}{'=' * 50}{NC}\n")
|
|
146
|
+
|
|
147
|
+
# Check if .env file exists
|
|
148
|
+
if not os.path.exists(ENV_FILE):
|
|
149
|
+
print(f"{RED}❌ .env file not found!{NC}")
|
|
150
|
+
print(f"{YELLOW}Please create a .env file first{NC}")
|
|
151
|
+
sys.exit(1)
|
|
152
|
+
|
|
153
|
+
# If no argument provided, show current service
|
|
154
|
+
if len(sys.argv) < 2:
|
|
155
|
+
show_current_service()
|
|
156
|
+
sys.exit(0)
|
|
157
|
+
|
|
158
|
+
service = sys.argv[1].lower()
|
|
159
|
+
|
|
160
|
+
# Valid services
|
|
161
|
+
valid_services = ['groq', 'mistral', 'sambanova', 'openrouter']
|
|
162
|
+
|
|
163
|
+
if service not in valid_services:
|
|
164
|
+
print(f"{RED}❌ Invalid service: {service}{NC}")
|
|
165
|
+
print(f"{YELLOW} Valid options: {', '.join(valid_services)}{NC}")
|
|
166
|
+
sys.exit(1)
|
|
167
|
+
|
|
168
|
+
# Switch service
|
|
169
|
+
success = switch_service(service)
|
|
170
|
+
|
|
171
|
+
if success:
|
|
172
|
+
print(f"\n{GREEN}{'=' * 50}{NC}")
|
|
173
|
+
print(f"{GREEN}🎉 AI Service switch completed successfully!{NC}")
|
|
174
|
+
print(f"{GREEN}{'=' * 50}{NC}")
|
|
175
|
+
else:
|
|
176
|
+
print(f"\n{YELLOW}{'=' * 50}{NC}")
|
|
177
|
+
print(f"{YELLOW}⚠️ Service switch completed with warnings{NC}")
|
|
178
|
+
print(f"{YELLOW}{'=' * 50}{NC}")
|
|
179
|
+
|
|
180
|
+
if __name__ == "__main__":
|
|
181
|
+
main()
|