codegpt-ai 1.0.0 → 1.2.0
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.
- package/README.md +210 -0
- package/ai_cli/__pycache__/__init__.cpython-313.pyc +0 -0
- package/ai_cli/__pycache__/__main__.cpython-313.pyc +0 -0
- package/ai_cli/__pycache__/doctor.cpython-313.pyc +0 -0
- package/ai_cli/__pycache__/updater.cpython-313.pyc +0 -0
- package/ai_cli/updater.py +48 -2
- package/bin/ai.js +19 -18
- package/bin/chat.js +361 -0
- package/chat.py +36 -6
- package/package.json +8 -4
- package/.claude/settings.local.json +0 -7
- package/.github/workflows/release.yml +0 -40
- package/ai.spec +0 -81
- package/app.py +0 -958
- package/bot.py +0 -1453
- package/build.ps1 +0 -22
- package/codex-instructions.md +0 -11
- package/install-termux.sh +0 -158
- package/install.ps1 +0 -89
- package/mobile.py +0 -422
- package/pyproject.toml +0 -33
- package/requirements.txt +0 -12
- package/run.py +0 -157
- package/server.py +0 -335
- package/uninstall.ps1 +0 -30
- package/web.py +0 -728
package/mobile.py
DELETED
|
@@ -1,422 +0,0 @@
|
|
|
1
|
-
"""CodeGPT Mobile — Flet app for Android + Desktop. Connects to Ollama."""
|
|
2
|
-
|
|
3
|
-
import json
|
|
4
|
-
import time
|
|
5
|
-
import threading
|
|
6
|
-
from pathlib import Path
|
|
7
|
-
|
|
8
|
-
import flet as ft
|
|
9
|
-
import requests
|
|
10
|
-
|
|
11
|
-
# --- Config ---
|
|
12
|
-
|
|
13
|
-
DEFAULT_SERVER = "http://192.168.1.237:5050" # CodeGPT backend
|
|
14
|
-
DEFAULT_MODEL = "" # Empty = use server default
|
|
15
|
-
CONFIG_DIR = Path.home() / ".codegpt"
|
|
16
|
-
MOBILE_CONFIG = CONFIG_DIR / "mobile_config.json"
|
|
17
|
-
|
|
18
|
-
SYSTEM_PROMPT = """You are an AI modeled after a highly technical, system-focused developer mindset.
|
|
19
|
-
Be direct, concise, and dense with information. No fluff, no filler, no emojis.
|
|
20
|
-
Give conclusions first, then minimal necessary explanation.
|
|
21
|
-
Focus on: AI, coding, automation, cybersecurity, system design.
|
|
22
|
-
Blunt but intelligent. Slightly dark tone is acceptable.
|
|
23
|
-
Keep responses concise for mobile reading."""
|
|
24
|
-
|
|
25
|
-
PERSONAS = {
|
|
26
|
-
"Default": SYSTEM_PROMPT,
|
|
27
|
-
"Hacker": "You are a cybersecurity expert. Technical jargon, CVEs, attack vectors. Defensive security only. Be concise for mobile.",
|
|
28
|
-
"Teacher": "You are a patient programming teacher. Step by step, analogies, examples. Adapt to the student. Concise for mobile.",
|
|
29
|
-
"Roast": "You are a brutally sarcastic code reviewer. Roast bad code then give the fix. Dark humor. Concise for mobile.",
|
|
30
|
-
"Minimal": "Shortest possible answer. One line if possible. Code only, no commentary.",
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
def load_config():
|
|
35
|
-
CONFIG_DIR.mkdir(parents=True, exist_ok=True)
|
|
36
|
-
if MOBILE_CONFIG.exists():
|
|
37
|
-
try:
|
|
38
|
-
return json.loads(MOBILE_CONFIG.read_text())
|
|
39
|
-
except Exception:
|
|
40
|
-
pass
|
|
41
|
-
return {"server": DEFAULT_SERVER, "model": DEFAULT_MODEL, "persona": "Default"}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
def save_config(config):
|
|
45
|
-
CONFIG_DIR.mkdir(parents=True, exist_ok=True)
|
|
46
|
-
MOBILE_CONFIG.write_text(json.dumps(config, indent=2))
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
# --- Main App ---
|
|
50
|
-
|
|
51
|
-
def main(page: ft.Page):
|
|
52
|
-
# Theme
|
|
53
|
-
page.title = "CodeGPT"
|
|
54
|
-
page.theme_mode = ft.ThemeMode.DARK
|
|
55
|
-
page.theme = ft.Theme(
|
|
56
|
-
color_scheme_seed=ft.Colors.CYAN,
|
|
57
|
-
font_family="Roboto",
|
|
58
|
-
)
|
|
59
|
-
page.padding = 0
|
|
60
|
-
|
|
61
|
-
# State
|
|
62
|
-
config = load_config()
|
|
63
|
-
messages = []
|
|
64
|
-
is_streaming = [False]
|
|
65
|
-
|
|
66
|
-
# --- UI Components ---
|
|
67
|
-
|
|
68
|
-
chat_list = ft.ListView(
|
|
69
|
-
expand=True,
|
|
70
|
-
spacing=8,
|
|
71
|
-
padding=ft.padding.symmetric(horizontal=12, vertical=8),
|
|
72
|
-
auto_scroll=True,
|
|
73
|
-
)
|
|
74
|
-
|
|
75
|
-
input_field = ft.TextField(
|
|
76
|
-
hint_text="Message CodeGPT...",
|
|
77
|
-
border_radius=24,
|
|
78
|
-
filled=True,
|
|
79
|
-
expand=True,
|
|
80
|
-
on_submit=lambda e: send_message(e),
|
|
81
|
-
text_size=14,
|
|
82
|
-
content_padding=ft.padding.symmetric(horizontal=16, vertical=10),
|
|
83
|
-
)
|
|
84
|
-
|
|
85
|
-
status_text = ft.Text(
|
|
86
|
-
f"{config['model']} | 0 msgs",
|
|
87
|
-
size=11,
|
|
88
|
-
color=ft.Colors.with_opacity(0.5, ft.Colors.WHITE),
|
|
89
|
-
)
|
|
90
|
-
|
|
91
|
-
def update_status():
|
|
92
|
-
status_text.value = f"{config['model']} | {len(messages)} msgs | {config.get('persona', 'Default')}"
|
|
93
|
-
try:
|
|
94
|
-
page.update()
|
|
95
|
-
except Exception:
|
|
96
|
-
pass
|
|
97
|
-
|
|
98
|
-
# --- Message Bubbles ---
|
|
99
|
-
|
|
100
|
-
def add_user_bubble(text):
|
|
101
|
-
chat_list.controls.append(
|
|
102
|
-
ft.Container(
|
|
103
|
-
content=ft.Column([
|
|
104
|
-
ft.Text("You", size=11, color=ft.Colors.CYAN, weight=ft.FontWeight.BOLD),
|
|
105
|
-
ft.Text(text, size=14, color=ft.Colors.WHITE, selectable=True),
|
|
106
|
-
], spacing=4),
|
|
107
|
-
padding=ft.padding.all(12),
|
|
108
|
-
border_radius=ft.border_radius.only(
|
|
109
|
-
top_left=16, top_right=16, bottom_left=16, bottom_right=4,
|
|
110
|
-
),
|
|
111
|
-
bgcolor=ft.Colors.with_opacity(0.15, ft.Colors.CYAN),
|
|
112
|
-
margin=ft.margin.only(left=48, bottom=4),
|
|
113
|
-
)
|
|
114
|
-
)
|
|
115
|
-
|
|
116
|
-
def add_ai_bubble(text, stats=""):
|
|
117
|
-
bubble = ft.Container(
|
|
118
|
-
content=ft.Column([
|
|
119
|
-
ft.Text("AI", size=11, color=ft.Colors.GREEN, weight=ft.FontWeight.BOLD),
|
|
120
|
-
ft.Markdown(
|
|
121
|
-
text,
|
|
122
|
-
selectable=True,
|
|
123
|
-
extension_set=ft.MarkdownExtensionSet.GITHUB_WEB,
|
|
124
|
-
code_theme=ft.MarkdownCodeTheme.MONOKAI,
|
|
125
|
-
on_tap_link=lambda e: page.launch_url(e.data),
|
|
126
|
-
),
|
|
127
|
-
ft.Text(stats, size=10, color=ft.Colors.with_opacity(0.4, ft.Colors.WHITE)) if stats else ft.Container(),
|
|
128
|
-
], spacing=4),
|
|
129
|
-
padding=ft.padding.all(12),
|
|
130
|
-
border_radius=ft.border_radius.only(
|
|
131
|
-
top_left=16, top_right=16, bottom_left=4, bottom_right=16,
|
|
132
|
-
),
|
|
133
|
-
bgcolor=ft.Colors.with_opacity(0.1, ft.Colors.GREEN),
|
|
134
|
-
margin=ft.margin.only(right=48, bottom=4),
|
|
135
|
-
key="ai_latest",
|
|
136
|
-
)
|
|
137
|
-
chat_list.controls.append(bubble)
|
|
138
|
-
|
|
139
|
-
def add_thinking_bubble():
|
|
140
|
-
chat_list.controls.append(
|
|
141
|
-
ft.Container(
|
|
142
|
-
content=ft.Row([
|
|
143
|
-
ft.ProgressRing(width=16, height=16, stroke_width=2, color=ft.Colors.GREEN),
|
|
144
|
-
ft.Text("Thinking...", size=13, color=ft.Colors.with_opacity(0.5, ft.Colors.WHITE), italic=True),
|
|
145
|
-
], spacing=8),
|
|
146
|
-
padding=ft.padding.all(12),
|
|
147
|
-
border_radius=16,
|
|
148
|
-
bgcolor=ft.Colors.with_opacity(0.05, ft.Colors.GREEN),
|
|
149
|
-
margin=ft.margin.only(right=48, bottom=4),
|
|
150
|
-
key="thinking",
|
|
151
|
-
)
|
|
152
|
-
)
|
|
153
|
-
|
|
154
|
-
def remove_thinking():
|
|
155
|
-
chat_list.controls[:] = [c for c in chat_list.controls if getattr(c, 'key', None) != "thinking"]
|
|
156
|
-
|
|
157
|
-
# --- Send Message ---
|
|
158
|
-
|
|
159
|
-
def send_message(e):
|
|
160
|
-
text = input_field.value.strip()
|
|
161
|
-
if not text or is_streaming[0]:
|
|
162
|
-
return
|
|
163
|
-
|
|
164
|
-
input_field.value = ""
|
|
165
|
-
add_user_bubble(text)
|
|
166
|
-
add_thinking_bubble()
|
|
167
|
-
page.update()
|
|
168
|
-
|
|
169
|
-
messages.append({"role": "user", "content": text})
|
|
170
|
-
is_streaming[0] = True
|
|
171
|
-
|
|
172
|
-
def do_request():
|
|
173
|
-
server = config.get("server", DEFAULT_SERVER)
|
|
174
|
-
model = config.get("model", DEFAULT_MODEL)
|
|
175
|
-
persona = config.get("persona", "Default")
|
|
176
|
-
system = PERSONAS.get(persona, SYSTEM_PROMPT)
|
|
177
|
-
|
|
178
|
-
ollama_messages = [{"role": "system", "content": system}]
|
|
179
|
-
ollama_messages.extend(messages)
|
|
180
|
-
|
|
181
|
-
try:
|
|
182
|
-
response = requests.post(
|
|
183
|
-
f"{server}/chat",
|
|
184
|
-
json={
|
|
185
|
-
"messages": messages,
|
|
186
|
-
"model": model,
|
|
187
|
-
"persona": persona,
|
|
188
|
-
},
|
|
189
|
-
stream=True,
|
|
190
|
-
timeout=120,
|
|
191
|
-
)
|
|
192
|
-
response.raise_for_status()
|
|
193
|
-
|
|
194
|
-
full = []
|
|
195
|
-
stats = ""
|
|
196
|
-
last_update = 0
|
|
197
|
-
|
|
198
|
-
for line in response.iter_lines():
|
|
199
|
-
if not line:
|
|
200
|
-
continue
|
|
201
|
-
try:
|
|
202
|
-
chunk = json.loads(line)
|
|
203
|
-
except json.JSONDecodeError:
|
|
204
|
-
continue
|
|
205
|
-
|
|
206
|
-
content = chunk.get("content", "")
|
|
207
|
-
if content:
|
|
208
|
-
full.append(content)
|
|
209
|
-
|
|
210
|
-
now = time.time()
|
|
211
|
-
if now - last_update >= 0.5:
|
|
212
|
-
remove_thinking()
|
|
213
|
-
chat_list.controls[:] = [
|
|
214
|
-
c for c in chat_list.controls
|
|
215
|
-
if getattr(c, 'key', None) != "ai_latest"
|
|
216
|
-
]
|
|
217
|
-
add_ai_bubble("".join(full), "streaming...")
|
|
218
|
-
try:
|
|
219
|
-
page.update()
|
|
220
|
-
except Exception:
|
|
221
|
-
pass
|
|
222
|
-
last_update = now
|
|
223
|
-
|
|
224
|
-
if chunk.get("done"):
|
|
225
|
-
stats = chunk.get("stats", "")
|
|
226
|
-
if not stats:
|
|
227
|
-
tokens = chunk.get("tokens", {})
|
|
228
|
-
provider = chunk.get("provider", "")
|
|
229
|
-
out_tok = tokens.get("output", 0)
|
|
230
|
-
stats = f"{out_tok} tok | {provider}"
|
|
231
|
-
|
|
232
|
-
final_text = "".join(full)
|
|
233
|
-
messages.append({"role": "assistant", "content": final_text})
|
|
234
|
-
|
|
235
|
-
# Final render
|
|
236
|
-
remove_thinking()
|
|
237
|
-
chat_list.controls[:] = [
|
|
238
|
-
c for c in chat_list.controls
|
|
239
|
-
if getattr(c, 'key', None) != "ai_latest"
|
|
240
|
-
]
|
|
241
|
-
add_ai_bubble(final_text, stats)
|
|
242
|
-
|
|
243
|
-
except requests.ConnectionError:
|
|
244
|
-
remove_thinking()
|
|
245
|
-
add_ai_bubble("Cannot connect to Ollama.\nCheck server IP in settings.", "error")
|
|
246
|
-
if messages and messages[-1]["role"] == "user":
|
|
247
|
-
messages.pop()
|
|
248
|
-
except requests.Timeout:
|
|
249
|
-
remove_thinking()
|
|
250
|
-
add_ai_bubble("Request timed out.", "error")
|
|
251
|
-
if messages and messages[-1]["role"] == "user":
|
|
252
|
-
messages.pop()
|
|
253
|
-
except Exception as ex:
|
|
254
|
-
remove_thinking()
|
|
255
|
-
add_ai_bubble(f"Error: {ex}", "error")
|
|
256
|
-
if messages and messages[-1]["role"] == "user":
|
|
257
|
-
messages.pop()
|
|
258
|
-
finally:
|
|
259
|
-
is_streaming[0] = False
|
|
260
|
-
update_status()
|
|
261
|
-
try:
|
|
262
|
-
page.update()
|
|
263
|
-
except Exception:
|
|
264
|
-
pass
|
|
265
|
-
|
|
266
|
-
threading.Thread(target=do_request, daemon=True).start()
|
|
267
|
-
|
|
268
|
-
# --- Settings Dialog ---
|
|
269
|
-
|
|
270
|
-
def open_settings(e):
|
|
271
|
-
server_field = ft.TextField(
|
|
272
|
-
value=config.get("server", DEFAULT_SERVER),
|
|
273
|
-
label="Ollama Server URL",
|
|
274
|
-
border_radius=12,
|
|
275
|
-
text_size=14,
|
|
276
|
-
)
|
|
277
|
-
model_field = ft.TextField(
|
|
278
|
-
value=config.get("model", DEFAULT_MODEL),
|
|
279
|
-
label="Model",
|
|
280
|
-
border_radius=12,
|
|
281
|
-
text_size=14,
|
|
282
|
-
)
|
|
283
|
-
persona_dropdown = ft.Dropdown(
|
|
284
|
-
value=config.get("persona", "Default"),
|
|
285
|
-
label="Persona",
|
|
286
|
-
options=[ft.dropdown.Option(p) for p in PERSONAS],
|
|
287
|
-
border_radius=12,
|
|
288
|
-
text_size=14,
|
|
289
|
-
)
|
|
290
|
-
|
|
291
|
-
def save_settings(e):
|
|
292
|
-
config["server"] = server_field.value.strip().rstrip("/")
|
|
293
|
-
config["model"] = model_field.value.strip()
|
|
294
|
-
config["persona"] = persona_dropdown.value
|
|
295
|
-
save_config(config)
|
|
296
|
-
update_status()
|
|
297
|
-
dlg.open = False
|
|
298
|
-
page.update()
|
|
299
|
-
page.open(ft.SnackBar(ft.Text("Settings saved"), duration=1500))
|
|
300
|
-
|
|
301
|
-
def test_connection(e):
|
|
302
|
-
server = server_field.value.strip().rstrip("/")
|
|
303
|
-
try:
|
|
304
|
-
resp = requests.get(f"{server}/health", timeout=5)
|
|
305
|
-
data = resp.json()
|
|
306
|
-
provider = data.get("provider", "?")
|
|
307
|
-
model = data.get("model", "?")
|
|
308
|
-
page.open(ft.SnackBar(
|
|
309
|
-
ft.Text(f"Connected. Provider: {provider}, Model: {model}"),
|
|
310
|
-
duration=3000,
|
|
311
|
-
))
|
|
312
|
-
except Exception as ex:
|
|
313
|
-
page.open(ft.SnackBar(ft.Text(f"Failed: {ex}"), duration=3000))
|
|
314
|
-
|
|
315
|
-
dlg = ft.AlertDialog(
|
|
316
|
-
title=ft.Text("Settings"),
|
|
317
|
-
content=ft.Container(
|
|
318
|
-
content=ft.Column([
|
|
319
|
-
server_field,
|
|
320
|
-
ft.ElevatedButton("Test Connection", on_click=test_connection, icon=ft.Icons.WIFI),
|
|
321
|
-
model_field,
|
|
322
|
-
persona_dropdown,
|
|
323
|
-
], spacing=16, tight=True),
|
|
324
|
-
width=320,
|
|
325
|
-
padding=ft.padding.only(top=8),
|
|
326
|
-
),
|
|
327
|
-
actions=[
|
|
328
|
-
ft.TextButton("Cancel", on_click=lambda e: setattr(dlg, 'open', False) or page.update()),
|
|
329
|
-
ft.ElevatedButton("Save", on_click=save_settings),
|
|
330
|
-
],
|
|
331
|
-
)
|
|
332
|
-
page.open(dlg)
|
|
333
|
-
|
|
334
|
-
# --- New Chat ---
|
|
335
|
-
|
|
336
|
-
def new_chat(e):
|
|
337
|
-
messages.clear()
|
|
338
|
-
chat_list.controls.clear()
|
|
339
|
-
|
|
340
|
-
# Welcome
|
|
341
|
-
chat_list.controls.append(
|
|
342
|
-
ft.Container(
|
|
343
|
-
content=ft.Column([
|
|
344
|
-
ft.Text("CodeGPT", size=28, weight=ft.FontWeight.BOLD, color=ft.Colors.CYAN,
|
|
345
|
-
text_align=ft.TextAlign.CENTER),
|
|
346
|
-
ft.Text("Local AI assistant on your phone.", size=14,
|
|
347
|
-
color=ft.Colors.with_opacity(0.5, ft.Colors.WHITE),
|
|
348
|
-
text_align=ft.TextAlign.CENTER),
|
|
349
|
-
ft.Divider(height=20, color=ft.Colors.TRANSPARENT),
|
|
350
|
-
*[
|
|
351
|
-
ft.OutlinedButton(
|
|
352
|
-
text=s,
|
|
353
|
-
on_click=lambda e, txt=s: (setattr(input_field, 'value', txt), send_message(e)),
|
|
354
|
-
style=ft.ButtonStyle(shape=ft.RoundedRectangleBorder(radius=12)),
|
|
355
|
-
)
|
|
356
|
-
for s in [
|
|
357
|
-
"Explain TCP/IP",
|
|
358
|
-
"Python CPU monitor script",
|
|
359
|
-
"OWASP top 10",
|
|
360
|
-
"Design a REST API",
|
|
361
|
-
]
|
|
362
|
-
],
|
|
363
|
-
], horizontal_alignment=ft.CrossAxisAlignment.CENTER, spacing=8),
|
|
364
|
-
padding=ft.padding.all(32),
|
|
365
|
-
alignment=ft.alignment.center,
|
|
366
|
-
)
|
|
367
|
-
)
|
|
368
|
-
update_status()
|
|
369
|
-
page.update()
|
|
370
|
-
|
|
371
|
-
# --- App Bar ---
|
|
372
|
-
|
|
373
|
-
page.appbar = ft.AppBar(
|
|
374
|
-
leading=ft.Icon(ft.Icons.TERMINAL, color=ft.Colors.CYAN),
|
|
375
|
-
title=ft.Text("CodeGPT", weight=ft.FontWeight.BOLD),
|
|
376
|
-
center_title=False,
|
|
377
|
-
bgcolor=ft.Colors.with_opacity(0.9, ft.Colors.BLACK),
|
|
378
|
-
actions=[
|
|
379
|
-
ft.IconButton(ft.Icons.ADD_COMMENT, on_click=new_chat, tooltip="New Chat"),
|
|
380
|
-
ft.IconButton(ft.Icons.SETTINGS, on_click=open_settings, tooltip="Settings"),
|
|
381
|
-
],
|
|
382
|
-
)
|
|
383
|
-
|
|
384
|
-
# --- Bottom Input Bar ---
|
|
385
|
-
|
|
386
|
-
send_btn = ft.IconButton(
|
|
387
|
-
ft.Icons.SEND_ROUNDED,
|
|
388
|
-
icon_color=ft.Colors.CYAN,
|
|
389
|
-
on_click=send_message,
|
|
390
|
-
tooltip="Send",
|
|
391
|
-
)
|
|
392
|
-
|
|
393
|
-
input_bar = ft.Container(
|
|
394
|
-
content=ft.Row([input_field, send_btn], spacing=8),
|
|
395
|
-
padding=ft.padding.symmetric(horizontal=12, vertical=8),
|
|
396
|
-
bgcolor=ft.Colors.with_opacity(0.95, ft.Colors.BLACK),
|
|
397
|
-
border=ft.border.only(top=ft.BorderSide(1, ft.Colors.with_opacity(0.1, ft.Colors.WHITE))),
|
|
398
|
-
)
|
|
399
|
-
|
|
400
|
-
# --- Status Bar ---
|
|
401
|
-
|
|
402
|
-
status_bar = ft.Container(
|
|
403
|
-
content=status_text,
|
|
404
|
-
padding=ft.padding.symmetric(horizontal=16, vertical=4),
|
|
405
|
-
bgcolor=ft.Colors.with_opacity(0.95, ft.Colors.BLACK),
|
|
406
|
-
)
|
|
407
|
-
|
|
408
|
-
# --- Layout ---
|
|
409
|
-
|
|
410
|
-
page.add(
|
|
411
|
-
ft.Column([
|
|
412
|
-
ft.Container(content=chat_list, expand=True),
|
|
413
|
-
status_bar,
|
|
414
|
-
input_bar,
|
|
415
|
-
], expand=True, spacing=0),
|
|
416
|
-
)
|
|
417
|
-
|
|
418
|
-
# Show welcome
|
|
419
|
-
new_chat(None)
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
ft.app(target=main)
|
package/pyproject.toml
DELETED
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
[build-system]
|
|
2
|
-
requires = ["setuptools>=68.0", "wheel"]
|
|
3
|
-
build-backend = "setuptools.build_meta"
|
|
4
|
-
|
|
5
|
-
[project]
|
|
6
|
-
name = "codegpt"
|
|
7
|
-
version = "1.0.0"
|
|
8
|
-
description = "Local AI Assistant Hub — CLI, agents, training, 15+ tool integrations"
|
|
9
|
-
readme = "CLAUDE.md"
|
|
10
|
-
license = {text = "MIT"}
|
|
11
|
-
requires-python = ">=3.10"
|
|
12
|
-
authors = [{name = "ArukuX"}]
|
|
13
|
-
|
|
14
|
-
dependencies = [
|
|
15
|
-
"requests>=2.31.0",
|
|
16
|
-
"rich>=13.0.0",
|
|
17
|
-
"prompt-toolkit>=3.0.0",
|
|
18
|
-
]
|
|
19
|
-
|
|
20
|
-
[project.optional-dependencies]
|
|
21
|
-
full = [
|
|
22
|
-
"textual>=0.40.0",
|
|
23
|
-
"python-telegram-bot>=20.0",
|
|
24
|
-
"flask>=3.0.0",
|
|
25
|
-
"groq>=0.4.0",
|
|
26
|
-
"SpeechRecognition>=3.10.0",
|
|
27
|
-
]
|
|
28
|
-
|
|
29
|
-
[project.scripts]
|
|
30
|
-
ai = "ai_cli.__main__:main"
|
|
31
|
-
|
|
32
|
-
[tool.setuptools.packages.find]
|
|
33
|
-
include = ["ai_cli*"]
|
package/requirements.txt
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
requests>=2.31.0
|
|
2
|
-
rich>=13.0.0
|
|
3
|
-
prompt_toolkit>=3.0.0
|
|
4
|
-
textual>=1.0.0
|
|
5
|
-
python-telegram-bot>=21.0
|
|
6
|
-
SpeechRecognition>=3.10.0
|
|
7
|
-
# PyAudio - install separately for voice input:
|
|
8
|
-
# pipwin install pyaudio OR
|
|
9
|
-
# pip install pipwin && pipwin install pyaudio
|
|
10
|
-
flask>=3.0.0
|
|
11
|
-
groq>=0.4.0
|
|
12
|
-
flet>=0.20.0
|
package/run.py
DELETED
|
@@ -1,157 +0,0 @@
|
|
|
1
|
-
"""CodeGPT Launcher
|
|
2
|
-
|
|
3
|
-
Usage:
|
|
4
|
-
python run.py Start CLI chat (default)
|
|
5
|
-
python run.py chat Start CLI chat
|
|
6
|
-
python run.py tui Start TUI with sidebar
|
|
7
|
-
python run.py bot Start Telegram bot
|
|
8
|
-
python run.py server Start backend server
|
|
9
|
-
python run.py mobile Start mobile app (desktop preview)
|
|
10
|
-
python run.py apk Build Android APK
|
|
11
|
-
"""
|
|
12
|
-
|
|
13
|
-
import os
|
|
14
|
-
import sys
|
|
15
|
-
import json
|
|
16
|
-
import subprocess
|
|
17
|
-
from pathlib import Path
|
|
18
|
-
|
|
19
|
-
CONFIG_DIR = Path.home() / ".codegpt"
|
|
20
|
-
CONFIG_FILE = CONFIG_DIR / "config.json"
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
def load_config():
|
|
24
|
-
CONFIG_DIR.mkdir(parents=True, exist_ok=True)
|
|
25
|
-
if CONFIG_FILE.exists():
|
|
26
|
-
try:
|
|
27
|
-
return json.loads(CONFIG_FILE.read_text())
|
|
28
|
-
except Exception:
|
|
29
|
-
pass
|
|
30
|
-
return {}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
def save_config(config):
|
|
34
|
-
CONFIG_DIR.mkdir(parents=True, exist_ok=True)
|
|
35
|
-
CONFIG_FILE.write_text(json.dumps(config, indent=2))
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
def run_script(name):
|
|
39
|
-
script = Path(__file__).parent / name
|
|
40
|
-
subprocess.run([sys.executable, str(script)])
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
def run_bot(token=None):
|
|
44
|
-
config = load_config()
|
|
45
|
-
bot_token = token or os.environ.get("CODEGPT_BOT_TOKEN") or config.get("bot_token")
|
|
46
|
-
|
|
47
|
-
if not bot_token:
|
|
48
|
-
print(" No token. Get one from @BotFather on Telegram.")
|
|
49
|
-
try:
|
|
50
|
-
bot_token = input(" Paste token > ").strip()
|
|
51
|
-
except (KeyboardInterrupt, EOFError):
|
|
52
|
-
return
|
|
53
|
-
if not bot_token:
|
|
54
|
-
return
|
|
55
|
-
try:
|
|
56
|
-
if input(" Save for later? (y/n) > ").strip().lower() == "y":
|
|
57
|
-
config["bot_token"] = bot_token
|
|
58
|
-
save_config(config)
|
|
59
|
-
print(" Saved.\n")
|
|
60
|
-
except (KeyboardInterrupt, EOFError):
|
|
61
|
-
pass
|
|
62
|
-
|
|
63
|
-
os.environ["CODEGPT_BOT_TOKEN"] = bot_token
|
|
64
|
-
run_script("bot.py")
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
def run_server():
|
|
68
|
-
config = load_config()
|
|
69
|
-
groq_key = os.environ.get("GROQ_API_KEY") or config.get("groq_api_key")
|
|
70
|
-
|
|
71
|
-
if not groq_key:
|
|
72
|
-
print(" No Groq API key found.")
|
|
73
|
-
print(" Get a free one at: https://console.groq.com/keys\n")
|
|
74
|
-
print(" Without it, the server will use local Ollama.\n")
|
|
75
|
-
try:
|
|
76
|
-
groq_key = input(" Paste Groq key (or Enter to skip) > ").strip()
|
|
77
|
-
except (KeyboardInterrupt, EOFError):
|
|
78
|
-
groq_key = ""
|
|
79
|
-
|
|
80
|
-
if groq_key:
|
|
81
|
-
try:
|
|
82
|
-
if input(" Save for later? (y/n) > ").strip().lower() == "y":
|
|
83
|
-
config["groq_api_key"] = groq_key
|
|
84
|
-
save_config(config)
|
|
85
|
-
print(" Saved.\n")
|
|
86
|
-
except (KeyboardInterrupt, EOFError):
|
|
87
|
-
pass
|
|
88
|
-
|
|
89
|
-
if groq_key:
|
|
90
|
-
os.environ["GROQ_API_KEY"] = groq_key
|
|
91
|
-
|
|
92
|
-
run_script("server.py")
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
def build_apk():
|
|
96
|
-
project = Path(__file__).parent
|
|
97
|
-
print(" Building APK with Flet...\n")
|
|
98
|
-
result = subprocess.run(
|
|
99
|
-
["flet", "build", "apk", "--project", str(project), "--module-name", "mobile"],
|
|
100
|
-
cwd=str(project),
|
|
101
|
-
)
|
|
102
|
-
if result.returncode == 0:
|
|
103
|
-
# Copy APK to desktop
|
|
104
|
-
apk_path = project / "build" / "apk" / "app-release.apk"
|
|
105
|
-
desktop = Path.home() / "Desktop"
|
|
106
|
-
if not desktop.exists():
|
|
107
|
-
desktop = Path.home() / "OneDrive" / "Desktop"
|
|
108
|
-
if apk_path.exists():
|
|
109
|
-
import shutil
|
|
110
|
-
dest = desktop / "CodeGPT.apk"
|
|
111
|
-
shutil.copy2(str(apk_path), str(dest))
|
|
112
|
-
print(f"\n APK copied to: {dest}")
|
|
113
|
-
else:
|
|
114
|
-
print(f"\n APK built. Check: {project / 'build'}")
|
|
115
|
-
else:
|
|
116
|
-
print("\n Build failed. Make sure Flet is installed: pip install flet")
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
def main():
|
|
120
|
-
args = sys.argv[1:]
|
|
121
|
-
|
|
122
|
-
if not args or args[0].lower() in ("chat", "cli"):
|
|
123
|
-
run_script("chat.py")
|
|
124
|
-
elif args[0].lower() in ("tui", "ui", "app"):
|
|
125
|
-
run_script("app.py")
|
|
126
|
-
elif args[0].lower() in ("bot", "telegram"):
|
|
127
|
-
token = None
|
|
128
|
-
if "--token" in args:
|
|
129
|
-
idx = args.index("--token")
|
|
130
|
-
if idx + 1 < len(args):
|
|
131
|
-
token = args[idx + 1]
|
|
132
|
-
if "--save-token" in args:
|
|
133
|
-
idx = args.index("--save-token")
|
|
134
|
-
if idx + 1 < len(args):
|
|
135
|
-
token = args[idx + 1]
|
|
136
|
-
config = load_config()
|
|
137
|
-
config["bot_token"] = token
|
|
138
|
-
save_config(config)
|
|
139
|
-
print(" Token saved.")
|
|
140
|
-
run_bot(token)
|
|
141
|
-
elif args[0].lower() in ("server", "backend", "api"):
|
|
142
|
-
run_server()
|
|
143
|
-
elif args[0].lower() in ("mobile", "phone"):
|
|
144
|
-
run_script("mobile.py")
|
|
145
|
-
elif args[0].lower() in ("web",):
|
|
146
|
-
run_script("web.py")
|
|
147
|
-
elif args[0].lower() in ("apk", "build"):
|
|
148
|
-
build_apk()
|
|
149
|
-
elif args[0].lower() in ("help", "--help", "-h"):
|
|
150
|
-
print(__doc__)
|
|
151
|
-
else:
|
|
152
|
-
print(f" Unknown: {args[0]}")
|
|
153
|
-
print(" Use: chat, tui, bot, server, mobile, or apk")
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
if __name__ == "__main__":
|
|
157
|
-
main()
|