codegpt-ai 1.28.1 → 1.28.2
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/chat.py +111 -67
- package/package.json +1 -1
package/chat.py
CHANGED
|
@@ -609,32 +609,27 @@ def ask_permission(action, detail=""):
|
|
|
609
609
|
|
|
610
610
|
risk_color = RISK_COLORS.get(risk, "yellow")
|
|
611
611
|
risk_icon = RISK_ICONS.get(risk, "?")
|
|
612
|
-
compact = is_compact()
|
|
613
612
|
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
),
|
|
631
|
-
title=f"[{risk_color}]Permission[/]",
|
|
632
|
-
border_style=risk_color.replace("bold ", ""), padding=(0, 2), width=tw(),
|
|
633
|
-
))
|
|
613
|
+
# Risk warnings — explain what could happen
|
|
614
|
+
risk_warnings = {
|
|
615
|
+
"CRITICAL": "This can execute code, modify your system, or expose data.",
|
|
616
|
+
"HIGH": "This accesses external services or modifies important data.",
|
|
617
|
+
"MEDIUM": "This uses resources or changes session settings.",
|
|
618
|
+
"LOW": "This is a safe operation with minimal impact.",
|
|
619
|
+
}
|
|
620
|
+
warning = risk_warnings.get(risk, "")
|
|
621
|
+
|
|
622
|
+
# Clean minimal prompt — like Claude Code
|
|
623
|
+
console.print()
|
|
624
|
+
console.print(Text.from_markup(f" [{risk_color}]{risk_icon} {action_desc}[/]"))
|
|
625
|
+
if detail:
|
|
626
|
+
console.print(Text(f" {detail[:70]}", style="dim"))
|
|
627
|
+
console.print(Text.from_markup(f" [{risk_color}]{risk} — {warning}[/]"))
|
|
628
|
+
console.print()
|
|
634
629
|
|
|
635
630
|
try:
|
|
636
631
|
answer = prompt(
|
|
637
|
-
[("class:prompt", "
|
|
632
|
+
[("class:prompt", " Allow? (y)es / (n)o / (a)lways > ")],
|
|
638
633
|
style=input_style,
|
|
639
634
|
).strip().lower()
|
|
640
635
|
except (KeyboardInterrupt, EOFError):
|
|
@@ -643,9 +638,9 @@ def ask_permission(action, detail=""):
|
|
|
643
638
|
if answer in ("a", "always"):
|
|
644
639
|
PERMISSION_ALWAYS_ALLOW.add(action)
|
|
645
640
|
save_permissions()
|
|
646
|
-
|
|
641
|
+
console.print(Text(f" ✓ Always allowed", style="green"))
|
|
647
642
|
return True
|
|
648
|
-
elif answer in ("y", "yes"):
|
|
643
|
+
elif answer in ("y", "yes", ""):
|
|
649
644
|
return True
|
|
650
645
|
else:
|
|
651
646
|
print_sys("Denied.")
|
|
@@ -1279,13 +1274,38 @@ def _print_err_panel(text):
|
|
|
1279
1274
|
|
|
1280
1275
|
|
|
1281
1276
|
def print_help():
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1277
|
+
# Group commands by category — clean minimal list
|
|
1278
|
+
categories = {
|
|
1279
|
+
"Chat": ["/new", "/save", "/load", "/delete", "/copy", "/regen", "/edit", "/history", "/clear", "/quit"],
|
|
1280
|
+
"Model": ["/model", "/modelinfo", "/params", "/temp", "/think", "/tokens", "/compact"],
|
|
1281
|
+
"AI": ["/agent", "/agents", "/all", "/vote", "/swarm", "/team", "/room", "/spectate", "/dm", "/chat-link"],
|
|
1282
|
+
"Lab": ["/lab", "/chain", "/race", "/prompts"],
|
|
1283
|
+
"Tools": ["/tools", "/bg", "/split", "/grid", "/running", "/killall"],
|
|
1284
|
+
"Connect": ["/connect", "/disconnect", "/server", "/qr", "/scan"],
|
|
1285
|
+
"Files": ["/file", "/run", "/code", "/shell", "/browse", "/open", "/export"],
|
|
1286
|
+
"Memory": ["/mem", "/train", "/pin", "/pins", "/search", "/fork", "/rate", "/tag"],
|
|
1287
|
+
"Profile": ["/profile", "/setname", "/setbio", "/persona", "/personas", "/usage"],
|
|
1288
|
+
"Skills": ["/skill", "/skills", "/auto", "/cron", "/crons"],
|
|
1289
|
+
"Comms": ["/broadcast", "/inbox", "/feed", "/monitor", "/hub"],
|
|
1290
|
+
"System": ["/github", "/weather", "/spotify", "/volume", "/bright", "/sysinfo"],
|
|
1291
|
+
"Security": ["/pin-set", "/pin-remove", "/lock", "/audit", "/security", "/permissions"],
|
|
1292
|
+
}
|
|
1293
|
+
|
|
1294
|
+
on_termux = os.path.exists("/data/data/com.termux")
|
|
1295
|
+
|
|
1296
|
+
for cat, cmds in categories.items():
|
|
1297
|
+
console.print(Text(f"\n {cat}", style="bold bright_cyan"))
|
|
1298
|
+
for cmd in cmds:
|
|
1299
|
+
desc = COMMANDS.get(cmd, "")
|
|
1300
|
+
if not desc:
|
|
1301
|
+
continue
|
|
1302
|
+
# Hide unsupported tool commands on Termux
|
|
1303
|
+
tool_name = cmd[1:]
|
|
1304
|
+
if on_termux and tool_name in AI_TOOLS and not AI_TOOLS[tool_name].get("termux", True):
|
|
1305
|
+
continue
|
|
1306
|
+
console.print(Text.from_markup(f" [bright_cyan]{cmd:<16}[/] [dim]{desc}[/]"))
|
|
1307
|
+
|
|
1308
|
+
console.print(Text("\n Type / to autocomplete. Aliases: /q /n /s /m /h /a /t /f", style="dim"))
|
|
1289
1309
|
console.print()
|
|
1290
1310
|
|
|
1291
1311
|
|
|
@@ -2443,8 +2463,24 @@ def open_url(url):
|
|
|
2443
2463
|
elif not url.startswith("http"):
|
|
2444
2464
|
url = "https://" + url
|
|
2445
2465
|
|
|
2446
|
-
|
|
2447
|
-
|
|
2466
|
+
# Platform-specific browser open
|
|
2467
|
+
try:
|
|
2468
|
+
if os.path.exists("/data/data/com.termux"):
|
|
2469
|
+
# Termux — use termux-open or am start
|
|
2470
|
+
try:
|
|
2471
|
+
subprocess.run(["termux-open-url", url], timeout=5)
|
|
2472
|
+
except FileNotFoundError:
|
|
2473
|
+
subprocess.run(["am", "start", "-a", "android.intent.action.VIEW", "-d", url], timeout=5)
|
|
2474
|
+
elif os.name == "nt":
|
|
2475
|
+
os.startfile(url)
|
|
2476
|
+
elif sys.platform == "darwin":
|
|
2477
|
+
subprocess.run(["open", url], timeout=5)
|
|
2478
|
+
else:
|
|
2479
|
+
webbrowser.open(url)
|
|
2480
|
+
print_sys(f"Opened: {url}")
|
|
2481
|
+
except Exception as e:
|
|
2482
|
+
print_err(f"Cannot open browser: {e}")
|
|
2483
|
+
print_sys(f"URL: {url}")
|
|
2448
2484
|
audit_log("OPEN_URL", url)
|
|
2449
2485
|
|
|
2450
2486
|
|
|
@@ -3913,7 +3949,7 @@ def team_chat(name1, name2, default_model, system):
|
|
|
3913
3949
|
|
|
3914
3950
|
# --- Chat Room ---
|
|
3915
3951
|
|
|
3916
|
-
def chat_room(member_names, default_model, system, user_joins=True):
|
|
3952
|
+
def chat_room(member_names, default_model, system, user_joins=True, topic=""):
|
|
3917
3953
|
"""Multi-AI chat room. User can join or spectate."""
|
|
3918
3954
|
members = [resolve_team_member(n) for n in member_names]
|
|
3919
3955
|
|
|
@@ -3999,7 +4035,8 @@ def chat_room(member_names, default_model, system, user_joins=True):
|
|
|
3999
4035
|
# Spectate mode — AIs chat with each other, user watches
|
|
4000
4036
|
try:
|
|
4001
4037
|
# Get initial topic from last arg or default
|
|
4002
|
-
|
|
4038
|
+
if not topic:
|
|
4039
|
+
topic = "Introduce yourselves and start a technical discussion."
|
|
4003
4040
|
if history:
|
|
4004
4041
|
topic = history[-1]["content"]
|
|
4005
4042
|
|
|
@@ -4253,7 +4290,7 @@ def delete_skill(name):
|
|
|
4253
4290
|
|
|
4254
4291
|
# --- Browser ---
|
|
4255
4292
|
|
|
4256
|
-
def browse_url(url):
|
|
4293
|
+
def browse_url(url, model=None):
|
|
4257
4294
|
"""Fetch a URL, extract text, and summarize it."""
|
|
4258
4295
|
if not url.startswith("http"):
|
|
4259
4296
|
url = "https://" + url
|
|
@@ -4282,7 +4319,7 @@ def browse_url(url):
|
|
|
4282
4319
|
# Ask AI to summarize
|
|
4283
4320
|
try:
|
|
4284
4321
|
ai_resp = requests.post(OLLAMA_URL, json={
|
|
4285
|
-
"model": MODEL,
|
|
4322
|
+
"model": model or MODEL,
|
|
4286
4323
|
"messages": [
|
|
4287
4324
|
{"role": "system", "content": "Summarize this web page content in 3-5 bullet points. Be concise."},
|
|
4288
4325
|
{"role": "user", "content": f"URL: {url}\n\nContent:\n{text}"},
|
|
@@ -4306,6 +4343,7 @@ def browse_url(url):
|
|
|
4306
4343
|
# --- Cron / Scheduled Tasks ---
|
|
4307
4344
|
|
|
4308
4345
|
active_crons = []
|
|
4346
|
+
cron_command_queue = [] # Thread-safe command queue for cron execution
|
|
4309
4347
|
|
|
4310
4348
|
|
|
4311
4349
|
def add_cron(interval_str, command):
|
|
@@ -4331,10 +4369,10 @@ def add_cron(interval_str, command):
|
|
|
4331
4369
|
# Check if still active
|
|
4332
4370
|
if cron_entry not in active_crons:
|
|
4333
4371
|
break
|
|
4334
|
-
print_sys(f"[cron] Running: {command}")
|
|
4335
|
-
# Execute as if user typed it
|
|
4336
4372
|
cron_entry["last_run"] = datetime.now().isoformat()
|
|
4337
4373
|
cron_entry["runs"] += 1
|
|
4374
|
+
# Queue the command for main loop to execute
|
|
4375
|
+
cron_command_queue.append(command)
|
|
4338
4376
|
|
|
4339
4377
|
cron_entry = {
|
|
4340
4378
|
"command": command,
|
|
@@ -4614,7 +4652,7 @@ def get_input():
|
|
|
4614
4652
|
# --- Main ---
|
|
4615
4653
|
|
|
4616
4654
|
def main():
|
|
4617
|
-
global last_ai_response, code_exec_count, OLLAMA_URL, sidebar_enabled
|
|
4655
|
+
global last_ai_response, code_exec_count, OLLAMA_URL, sidebar_enabled, think_mode, temperature
|
|
4618
4656
|
|
|
4619
4657
|
# CLI args mode: python chat.py --ask "question" or python chat.py --cmd "/tools"
|
|
4620
4658
|
if len(sys.argv) > 1:
|
|
@@ -4886,6 +4924,11 @@ def main():
|
|
|
4886
4924
|
audit_log("LOCKED_OUT")
|
|
4887
4925
|
break
|
|
4888
4926
|
|
|
4927
|
+
# Drain cron command queue
|
|
4928
|
+
while cron_command_queue:
|
|
4929
|
+
cron_cmd = cron_command_queue.pop(0)
|
|
4930
|
+
print_sys(f"[cron] {cron_cmd}")
|
|
4931
|
+
|
|
4889
4932
|
user_input = get_input()
|
|
4890
4933
|
if user_input is None:
|
|
4891
4934
|
cancel_all_reminders()
|
|
@@ -4952,10 +4995,10 @@ def main():
|
|
|
4952
4995
|
continue
|
|
4953
4996
|
|
|
4954
4997
|
elif cmd == "/save":
|
|
4955
|
-
if messages
|
|
4956
|
-
save_conversation(messages, model)
|
|
4957
|
-
else:
|
|
4998
|
+
if not messages:
|
|
4958
4999
|
print_sys("Nothing to save.")
|
|
5000
|
+
elif ask_permission("save_chat", "Save conversation"):
|
|
5001
|
+
save_conversation(messages, model)
|
|
4959
5002
|
continue
|
|
4960
5003
|
|
|
4961
5004
|
elif cmd == "/load":
|
|
@@ -5441,10 +5484,10 @@ def main():
|
|
|
5441
5484
|
elif sub == "clear":
|
|
5442
5485
|
mem_clear()
|
|
5443
5486
|
elif sub == "inject":
|
|
5444
|
-
# Inject memories
|
|
5487
|
+
# Inject memories as a user-role context message (not system — avoids corrupting conversation)
|
|
5445
5488
|
mem_context = get_memory_context()
|
|
5446
5489
|
if mem_context:
|
|
5447
|
-
messages.append({"role": "
|
|
5490
|
+
messages.append({"role": "user", "content": f"[Memory context for reference]:\n{mem_context}"})
|
|
5448
5491
|
print_sys("Memories injected into context.")
|
|
5449
5492
|
else:
|
|
5450
5493
|
print_sys("No memories to inject.")
|
|
@@ -5857,8 +5900,7 @@ def main():
|
|
|
5857
5900
|
|
|
5858
5901
|
if len(names) >= 2:
|
|
5859
5902
|
# Inject topic
|
|
5860
|
-
|
|
5861
|
-
h = chat_room(names, model, system, user_joins=False)
|
|
5903
|
+
h = chat_room(names, model, system, user_joins=False, topic=topic)
|
|
5862
5904
|
else:
|
|
5863
5905
|
print_sys("Need at least 2 AIs. Example: /spectate coder reviewer discuss Python")
|
|
5864
5906
|
else:
|
|
@@ -6189,7 +6231,7 @@ def main():
|
|
|
6189
6231
|
launch_cmd = [tool_bin] + tool.get("default_args", [])
|
|
6190
6232
|
if tool_args:
|
|
6191
6233
|
launch_cmd.append(tool_args)
|
|
6192
|
-
subprocess.run(launch_cmd, shell=True, cwd=project_dir, env=tool_env)
|
|
6234
|
+
subprocess.run(" ".join(launch_cmd), shell=True, cwd=project_dir, env=tool_env)
|
|
6193
6235
|
else:
|
|
6194
6236
|
tool_sandbox = Path.home() / ".codegpt" / "sandbox" / tool_key
|
|
6195
6237
|
tool_sandbox.mkdir(parents=True, exist_ok=True)
|
|
@@ -6226,7 +6268,7 @@ def main():
|
|
|
6226
6268
|
launch_cmd = [tool_bin] + tool.get("default_args", [])
|
|
6227
6269
|
if tool_args:
|
|
6228
6270
|
launch_cmd.append(tool_args)
|
|
6229
|
-
subprocess.run(launch_cmd, shell=True, cwd=str(tool_sandbox), env=tool_env)
|
|
6271
|
+
subprocess.run(" ".join(launch_cmd), shell=True, cwd=str(tool_sandbox), env=tool_env)
|
|
6230
6272
|
|
|
6231
6273
|
print_sys("Back to CodeGPT.")
|
|
6232
6274
|
audit_log(f"TOOL_EXIT", tool_key)
|
|
@@ -6343,7 +6385,7 @@ def main():
|
|
|
6343
6385
|
print_sys(f"Installed in {elapsed:.1f}s. Launching...")
|
|
6344
6386
|
audit_log(f"TOOL_INSTALL", tool_key)
|
|
6345
6387
|
launch_cmd = [found_bin] + tool.get("default_args", [])
|
|
6346
|
-
subprocess.run(launch_cmd, shell=True)
|
|
6388
|
+
subprocess.run(" ".join(launch_cmd), shell=True)
|
|
6347
6389
|
print_sys("Back to CodeGPT.")
|
|
6348
6390
|
elif tool_bin in pip_module_map:
|
|
6349
6391
|
# Try python -m fallback
|
|
@@ -6906,7 +6948,7 @@ def main():
|
|
|
6906
6948
|
elif cmd == "/browse":
|
|
6907
6949
|
url = user_input[len("/browse "):].strip()
|
|
6908
6950
|
if url and ask_permission("open_url", f"Fetch {url}"):
|
|
6909
|
-
content = browse_url(url)
|
|
6951
|
+
content = browse_url(url, model=model)
|
|
6910
6952
|
if content:
|
|
6911
6953
|
messages.append({"role": "user", "content": f"[browsed: {url}]"})
|
|
6912
6954
|
messages.append({"role": "assistant", "content": content[:500]})
|
|
@@ -6971,21 +7013,23 @@ def main():
|
|
|
6971
7013
|
save_permissions()
|
|
6972
7014
|
print_sys("All permissions reset. You'll be asked again.")
|
|
6973
7015
|
else:
|
|
6974
|
-
|
|
6975
|
-
|
|
6976
|
-
|
|
6977
|
-
|
|
6978
|
-
|
|
6979
|
-
|
|
6980
|
-
|
|
6981
|
-
|
|
6982
|
-
|
|
6983
|
-
|
|
6984
|
-
|
|
6985
|
-
|
|
6986
|
-
|
|
6987
|
-
|
|
6988
|
-
|
|
7016
|
+
console.print(Text("\n Permissions", style="bold"))
|
|
7017
|
+
console.print(Rule(style="dim", characters="─"))
|
|
7018
|
+
|
|
7019
|
+
# Group by risk level
|
|
7020
|
+
for risk_level in ["CRITICAL", "HIGH", "MEDIUM", "LOW"]:
|
|
7021
|
+
rc = RISK_COLORS.get(risk_level, "yellow")
|
|
7022
|
+
ri = RISK_ICONS.get(risk_level, "?")
|
|
7023
|
+
console.print(Text.from_markup(f"\n [{rc}]{ri} {risk_level}[/]"))
|
|
7024
|
+
for action, info in RISKY_ACTIONS.items():
|
|
7025
|
+
if isinstance(info, tuple):
|
|
7026
|
+
desc, risk = info
|
|
7027
|
+
else:
|
|
7028
|
+
desc, risk = info, "MEDIUM"
|
|
7029
|
+
if risk != risk_level:
|
|
7030
|
+
continue
|
|
7031
|
+
status = "[green]✓ allowed[/]" if action in PERMISSION_ALWAYS_ALLOW else "[dim]ask[/]"
|
|
7032
|
+
console.print(Text.from_markup(f" {action:<16} {status} [dim]{desc}[/]"))
|
|
6989
7033
|
console.print(table)
|
|
6990
7034
|
console.print(Text(" /permissions reset — revoke all", style="dim"))
|
|
6991
7035
|
console.print()
|
|
@@ -7019,7 +7063,7 @@ def main():
|
|
|
7019
7063
|
f" Log entries [bright_cyan]{audit_count}[/]\n"
|
|
7020
7064
|
f" Log file [dim]{AUDIT_FILE}[/]\n\n"
|
|
7021
7065
|
f"[bold]Storage[/]\n"
|
|
7022
|
-
f"
|
|
7066
|
+
f" PIN hash [green]SHA-256[/]\n"
|
|
7023
7067
|
f" Location [dim]{SECURITY_DIR}[/]\n"
|
|
7024
7068
|
),
|
|
7025
7069
|
border_style="bright_cyan",
|