superbrain-server 1.0.7 → 1.0.11

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "superbrain-server",
3
- "version": "1.0.7",
3
+ "version": "1.0.11",
4
4
  "description": "1-Line Auto-Installer and Server Execution wrapper for SuperBrain",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -22,4 +22,4 @@
22
22
  "bin/",
23
23
  "payload/"
24
24
  ]
25
- }
25
+ }
package/payload/api.py CHANGED
@@ -289,7 +289,15 @@ class AnalysisResponse(BaseModel):
289
289
  @app.get("/")
290
290
  async def root():
291
291
  """API information and health check (no authentication required)"""
292
+
293
+ backend_id = "unknown"
294
+ backend_id_path = get_config_path("backend_id.txt")
295
+ if backend_id_path.exists():
296
+ backend_id = backend_id_path.read_text().strip()
297
+
292
298
  return {
299
+ "backendId": backend_id,
300
+
293
301
  "name": "SuperBrain Instagram Analyzer API",
294
302
  "version": "1.02",
295
303
  "status": "operational",
@@ -561,12 +569,46 @@ async def analyze_instagram(request: AnalyzeRequest, token: str = Depends(verify
561
569
  logger.warning(f"⚠️ [{shortcode}] main.py stderr:\n{stderr[:1000]}")
562
570
 
563
571
  if returncode == 2:
564
- retry_lines = [l.strip() for l in stdout.splitlines() if l.strip().startswith('?')]
565
- retry_msg = retry_lines[-1].replace('?', '').strip() if retry_lines else "API quota exhausted or rate limited. Queued for automatic retry in 24 hours."
566
- logger.info(f"? [{shortcode}] {retry_msg}")
572
+ # main.py detected quota exhaustion and queued item for retry.
573
+ # NOTE: Do NOT remove from queue here main.py already called
574
+ # queue_for_retry() which set status='retry'. Removing would lose it.
575
+ logger.info(f"⏰ [{shortcode}] Quota exhausted — queued for automatic retry")
567
576
  raise HTTPException(
568
577
  status_code=202,
569
- detail=retry_msg
578
+ detail="API quota exhausted. Your request has been queued for automatic retry in 24 hours."
579
+ )
580
+
581
+ if returncode != 0:
582
+ # Extract last meaningful error line from stdout for the error message
583
+ error_lines = [l.strip() for l in stdout.splitlines() if l.strip() and ('❌' in l or 'Error' in l or 'failed' in l.lower())]
584
+ error_detail = error_lines[-1] if error_lines else (stderr.strip()[:200] or "Analysis failed")
585
+ logger.error(f"❌ [{shortcode}] Analysis failed: {error_detail}")
586
+ logger.debug(f"[{shortcode}] stdout tail:\n{stdout[-800:]}")
587
+ raise HTTPException(
588
+ status_code=400,
589
+ detail=error_detail
590
+ )
591
+
592
+ logger.info(f"✅ [{shortcode}] Analysis complete! Fetching from database...")
593
+
594
+ # Get result from database — retry up to 4 times in case the SQLite write
595
+ # hasn't flushed yet (race condition between subprocess write and our read).
596
+ analysis = None
597
+ for _attempt in range(4):
598
+ analysis = db.check_cache(shortcode)
599
+ if analysis:
600
+ if _attempt > 0:
601
+ logger.info(f"🔄 [{shortcode}] Found in database on retry {_attempt}")
602
+ break
603
+ if _attempt < 3:
604
+ logger.warning(f"⏳ [{shortcode}] Not in DB yet (attempt {_attempt+1}/4), retrying in 1s…")
605
+ await asyncio.sleep(1)
606
+
607
+ if not analysis:
608
+ logger.error(f"❌ [{shortcode}] Not found in database after 4 attempts!")
609
+ raise HTTPException(
610
+ status_code=500,
611
+ detail="Analysis completed but result not found in database"
570
612
  )
571
613
 
572
614
  # Filter response
@@ -663,6 +663,9 @@ class ModelRouter:
663
663
  try:
664
664
  self._refresh_openrouter_models()
665
665
  except Exception as e:
666
+ if "429" in str(e) or "quota" in str(e).lower():
667
+ raise RateLimitError("Quota limit hit")
668
+ raise e
666
669
  print(f"⚠️ OpenRouter auto-refresh error: {e}")
667
670
  time.sleep(OPENROUTER_FREE_CACHE_HOURS * 3600)
668
671
 
@@ -707,6 +710,9 @@ class ModelRouter:
707
710
  resp.raise_for_status()
708
711
  all_models = resp.json().get("data", [])
709
712
  except Exception as e:
713
+ if "429" in str(e) or "quota" in str(e).lower():
714
+ raise RateLimitError("Quota limit hit")
715
+ raise e
710
716
  print(f"⚠️ OpenRouter model discovery failed: {e}")
711
717
  return
712
718
 
@@ -1100,6 +1106,9 @@ class ModelRouter:
1100
1106
  return result
1101
1107
 
1102
1108
  except Exception as e:
1109
+ if "429" in str(e) or "quota" in str(e).lower():
1110
+ raise RateLimitError("Quota limit hit")
1111
+ raise e
1103
1112
  status = 429 if "429" in str(e) else 0
1104
1113
  self._record_failure(key, str(e), status_code=status)
1105
1114
  print(f" ✗ Failed ({type(e).__name__}), trying next …", flush=True)
@@ -1144,6 +1153,9 @@ class ModelRouter:
1144
1153
  return result
1145
1154
 
1146
1155
  except Exception as e:
1156
+ if "429" in str(e) or "quota" in str(e).lower():
1157
+ raise RateLimitError("Quota limit hit")
1158
+ raise e
1147
1159
  status = 429 if "429" in str(e) else 0
1148
1160
  self._record_failure(key, str(e), status_code=status)
1149
1161
  print(f" ✗ Failed ({type(e).__name__}), trying next …", flush=True)
package/payload/start.py CHANGED
@@ -22,6 +22,7 @@ import string
22
22
  import textwrap
23
23
  import time
24
24
  import importlib
25
+ import re
25
26
  from pathlib import Path
26
27
 
27
28
  # ── Paths ─────────────────────────────────────────────────────────────────────
@@ -157,6 +158,7 @@ def ensure_runtime_dependencies():
157
158
  ("fastapi", "fastapi"),
158
159
  ("uvicorn", "uvicorn"),
159
160
  ("multipart", "python-multipart"),
161
+ ("pyngrok", "pyngrok"),
160
162
  ("instaloader", "instaloader"),
161
163
  ("segno", "segno"),
162
164
  ]
@@ -662,64 +664,41 @@ def setup_whisper():
662
664
  # ══════════════════════════════════════════════════════════════════════════════
663
665
  # Step 6 — Remote Access / Port Forwarding
664
666
  # ══════════════════════════════════════════════════════════════════════════════
665
- LOCALTUNNEL_ENABLED = BASE_DIR / "config" / "localtunnel_enabled.txt"
666
- LOCALTUNNEL_LOG = BASE_DIR / "config" / "localtunnel.log"
667
+ NGROK_ENABLED = BASE_DIR / "config" / "ngrok_enabled.txt"
668
+ NGROK_TOKEN = BASE_DIR / "config" / "ngrok_token.txt"
667
669
 
668
670
  def setup_remote_access():
669
- h1("Step 6 of 7 — Remote Access (localhost.run / Port Forwarding)")
671
+ h1("Step 6 of 7 — Remote Access (ngrok)")
670
672
 
671
673
  print(f"""
672
- The SuperBrain backend runs on {BOLD}port 5000{RESET} on your machine.
673
- Your phone needs to reach this port over the internet.
674
-
675
- You have two options:
676
-
677
- {BOLD}Option A — localhost.run (easiest + free ssh tunnel){RESET}
678
- localhost.run creates a public HTTPS URL seamlessly using SSH.
679
- No account required.
680
- Official site: {CYAN}https://localhost.run/{RESET}
681
-
682
- {BOLD}Option B — Your own port forwarding (advanced){RESET}
683
- Forward {BOLD}TCP port 5000{RESET} on your router to your machine's local IP.
684
- Then use {BOLD}http://<your-public-ip>:5000{RESET} in the mobile app.
685
- Steps:
686
- 1. Find your machine's local IP → ip addr (Linux) / ipconfig (Windows)
687
- 2. Log into your router admin panel (usually http://192.168.1.1)
688
- 3. Add a port forwarding rule: External 5000 → Internal <your-local-IP>:5000
689
- 4. Use your public IP (check https://ipify.org) in the mobile app.
690
- {YELLOW}Note: dynamic public IPs change on router restart — consider a DDNS service.{RESET}
691
-
692
- {DIM}You can also run only on your local WiFi — both phone and PC must be on
693
- the same network. Use your PC's local IP (e.g. 192.168.x.x) in the app.{RESET}
674
+ The SuperBrain backend runs locally. Your phone needs to reach it over the internet.
675
+ We recommend {BOLD}ngrok{RESET} for a secure tunnel.
676
+
677
+ Requires a free account from: {CYAN}https://dashboard.ngrok.com/signup{RESET}
678
+ Get your Authtoken at: {CYAN}https://dashboard.ngrok.com/get-started/your-authtoken{RESET}
694
679
  """)
695
680
 
696
- choice = ask_yn("Enable localhost.run on startup?", default=True)
681
+ choice = ask_yn("Enable ngrok on startup?", default=True)
697
682
  if not choice:
698
- LOCALTUNNEL_ENABLED.unlink(missing_ok=True)
699
- warn("Skipping localhost.run. Use either your own port forwarding or local WiFi.")
700
- info("Remember: set the correct server URL in the mobile app Settings.")
683
+ NGROK_ENABLED.unlink(missing_ok=True)
684
+ warn("Skipping ngrok. Local WiFi only.")
701
685
  return
702
686
 
703
- if not shutil.which("npx"):
704
- print(f"""
705
- {YELLOW}npx is not installed / not on PATH.{RESET}
706
-
707
- Install it:
708
- Linux → {CYAN}Install Node.js (includes npm + npx){RESET}
709
- macOS → {CYAN}brew install node{RESET}
710
- Windows → Install Node.js LTS from {CYAN}https://nodejs.org/{RESET}
711
-
712
- After installing, re-run {BOLD}python start.py{RESET}.
713
- """)
714
- warn("Skipping localhost.run setup.")
715
- return
716
-
717
- ok("npx binary found")
718
- LOCALTUNNEL_ENABLED.parent.mkdir(parents=True, exist_ok=True)
719
- LOCALTUNNEL_ENABLED.write_text("enabled")
720
- ok("localhost.run auto-start enabled")
721
- nl()
722
- info("localhost.run will be started automatically every time you run start.py.")
687
+ ok("ngrok auto-start enabled")
688
+ NGROK_ENABLED.parent.mkdir(parents=True, exist_ok=True)
689
+ NGROK_ENABLED.write_text("enabled")
690
+
691
+ existing_token = NGROK_TOKEN.read_text().strip() if NGROK_TOKEN.exists() else ""
692
+ print(f"\n {YELLOW}Please paste your ngrok Authtoken.{RESET}")
693
+ if existing_token:
694
+ print(f" {DIM}(Leave blank to keep existing token){RESET}")
695
+
696
+ auth_token = ask("Authtoken", default=existing_token, paste=True)
697
+ if auth_token.strip():
698
+ NGROK_TOKEN.write_text(auth_token.strip())
699
+ ok("ngrok token saved.")
700
+ else:
701
+ warn("No ngrok token provided. ngrok may disconnect. To fix, re-run setup.")
723
702
 
724
703
  # ══════════════════════════════════════════════════════════════════════════════
725
704
  # Step 6 — Access Token & Database
@@ -754,99 +733,21 @@ def setup_token_and_db():
754
733
  # ══════════════════════════════════════════════════════════════════════════════
755
734
  # Launch Backend
756
735
  # ══════════════════════════════════════════════════════════════════════════════
757
- def _extract_localhost_run_url(text: str) -> str | None:
758
- """Extract first localhost.run public URL from text."""
759
- import re
760
- lines = text.splitlines()
761
- for line in reversed(lines):
762
- if "tunneled with tls termination" in line or ".lhr.life" in line or ".localhost.run" in line:
763
- match = re.search(r'(https://[a-zA-Z0-9-]+\.(?:lhr\.life|localhost\.run))', line)
764
- if match:
765
- return match.group(1)
766
- return None
767
-
768
-
769
- def _find_localhost_run_url_from_log() -> str | None:
770
- """Read local tunnel log and return detected public URL if available."""
771
- try:
772
- if not LOCALTUNNEL_LOG.exists():
773
- return None
774
- text = LOCALTUNNEL_LOG.read_text(encoding="utf-8", errors="ignore")
775
- return _extract_localhost_run_url(text)
776
- except Exception:
777
- return None
778
-
779
-
780
- def _stop_localhost_run_processes():
781
- """Stop existing localhost.run processes so only one tunnel remains active."""
782
- try:
783
- if IS_WINDOWS:
784
- script = (
785
- "Get-CimInstance Win32_Process "
786
- "| Where-Object { $_.CommandLine -match 'nokey@localhost.run' } "
787
- "| ForEach-Object { Stop-Process -Id $_.ProcessId -Force -ErrorAction SilentlyContinue }"
788
- )
789
- subprocess.run(["powershell", "-NoProfile", "-Command", script], check=False)
790
- else:
791
- subprocess.run(["pkill", "-f", "nokey@localhost.run"], check=False)
792
- except Exception:
793
- pass
794
-
795
-
796
- def _start_localhost_run(port: int, timeout: int = 25) -> str | None:
797
- """Start localhost.run in the background via SSH and wait for the public URL."""
798
- import time as _time
799
-
800
- ssh_exec = shutil.which("ssh")
801
- if not ssh_exec:
802
- warn("SSH is required to use localhost.run. Please install OpenSSH.")
803
- return None
804
-
805
- # Clean stale ssh tunnel processes.
806
- _stop_localhost_run_processes()
807
- _time.sleep(0.8)
808
-
809
- info("Starting localhost.run SSH tunnel in background …")
736
+ def _start_ngrok(port: int) -> str | None:
810
737
  try:
811
- LOCALTUNNEL_LOG.parent.mkdir(parents=True, exist_ok=True)
812
- LOCALTUNNEL_LOG.write_text("")
813
-
814
- log_handle = open(LOCALTUNNEL_LOG, "a", encoding="utf-8", buffering=1)
815
- kwargs = {
816
- "stdout": log_handle,
817
- "stderr": subprocess.STDOUT,
818
- "stdin": subprocess.DEVNULL,
819
- "text": True,
820
- }
821
- if IS_WINDOWS:
822
- kwargs["creationflags"] = subprocess.CREATE_NEW_PROCESS_GROUP
738
+ import pyngrok
739
+ from pyngrok import ngrok
823
740
 
824
- cmd = [ssh_exec, "-o", "StrictHostKeyChecking=no", "-R", f"80:localhost:{port}", "nokey@localhost.run"]
825
- process = subprocess.Popen(cmd, **kwargs)
826
-
827
- _time.sleep(1.0)
828
- if process.poll() is not None:
829
- warn(f"Could not start localhost.run, exited with {process.returncode}")
830
- return None
831
-
741
+ token = NGROK_TOKEN.read_text().strip() if NGROK_TOKEN.exists() else None
742
+ if token:
743
+ ngrok.set_auth_token(token)
744
+
745
+ tunnel = ngrok.connect(port, bind_tls=True)
746
+ return tunnel.public_url
832
747
  except Exception as e:
833
- warn(f"Could not start localhost.run: {e}")
748
+ warn(f"Failed to start ngrok: {e}")
834
749
  return None
835
750
 
836
- # Poll log output until URL is emitted.
837
- deadline = _time.time() + timeout
838
- while _time.time() < deadline:
839
- _time.sleep(1)
840
- url = _find_localhost_run_url_from_log()
841
- if url:
842
- ok(f"localhost.run active → {GREEN}{BOLD}{url}{RESET}")
843
- return url
844
-
845
- warn("localhost.run started but URL is not available yet.")
846
- info(f"Check tunnel logs in: {LOCALTUNNEL_LOG}")
847
- return None
848
-
849
-
850
751
  def _get_windows_pids_on_port(port: int) -> list[int]:
851
752
  """Return listener PIDs on Windows using Get-NetTCPConnection when available."""
852
753
  pids: set[int] = set()
@@ -1214,26 +1115,27 @@ def launch_backend():
1214
1115
  token = TOKEN_FILE.read_text().strip() if TOKEN_FILE.exists() else "—"
1215
1116
  local_ip = _detect_local_ip()
1216
1117
 
1217
- localhost_run_enabled = bool(shutil.which("ssh"))
1218
-
1219
- localhost_run_url: str | None = None
1220
- if localhost_run_enabled:
1221
- localhost_run_url = _start_localhost_run(PORT)
1222
- else:
1223
- localhost_run_url = _find_localhost_run_url_from_log()
1224
-
1225
- if localhost_run_url:
1226
- tunnel_line = f" Public URL → {GREEN}{BOLD}{localhost_run_url}{RESET} {DIM}(localhost.run){RESET}"
1227
- tunnel_hint = f" · public → {GREEN}{localhost_run_url}{RESET}"
1228
- elif localhost_run_enabled:
1229
- tunnel_line = f" Public URL → {YELLOW}(starting — URL pending, check localtunnel.log){RESET}"
1230
- tunnel_hint = f" · public → run: {DIM}ssh -R 80:localhost:{PORT} nokey@localhost.run{RESET}"
1118
+ # ngrok startup
1119
+ public_url: str | None = None
1120
+ if NGROK_ENABLED.exists():
1121
+ info("Starting ngrok in background...")
1122
+ public_url = _start_ngrok(PORT)
1123
+
1124
+ tunnel_line = ""
1125
+ tunnel_hint = ""
1126
+
1127
+ if public_url:
1128
+ tunnel_line = f" Public URL → {GREEN}{BOLD}{public_url}{RESET} {DIM}(ngrok){RESET}"
1129
+ tunnel_hint = f" · public → {GREEN}{public_url}{RESET}"
1130
+ ok(f"ngrok active → {GREEN}{BOLD}{public_url}{RESET}")
1131
+ elif NGROK_ENABLED.exists():
1132
+ tunnel_line = f" Public URL → {YELLOW}(failed to start ngrok){RESET}"
1133
+ tunnel_hint = f" · public → run manually: {DIM}ngrok http {PORT}{RESET}"
1231
1134
  else:
1232
- tunnel_line = ""
1233
- tunnel_hint = f" · public → install OpenSSH first, then run: {DIM}ssh -R 80:localhost:{PORT} nokey@localhost.run{RESET}"
1135
+ tunnel_hint = f" · public → enable ngrok via {DIM}python start.py --reset{RESET}"
1234
1136
 
1235
1137
  # ── Generate and display QR code ──────────────────────────────────────────
1236
- qr_url = localhost_run_url if localhost_run_url else f"http://{local_ip}:{PORT}"
1138
+ qr_url = public_url if public_url else f"http://{local_ip}:{PORT}"
1237
1139
  _display_connect_qr(qr_url, token)
1238
1140
 
1239
1141
  print(f"""
@@ -1286,15 +1188,21 @@ def launch_backend_status():
1286
1188
  if TOKEN_FILE.exists():
1287
1189
  token = TOKEN_FILE.read_text(encoding="utf-8").strip()
1288
1190
 
1191
+ # Fetch ngrok status via API
1289
1192
  url = "NOT_FOUND"
1290
- log_file = BASE_DIR / "config" / "localtunnel.log"
1291
- if log_file.exists():
1292
- match = re.search(r"your url is: (https://[^\s]+)", log_file.read_text(encoding="utf-8"))
1293
- if match:
1294
- url = match.group(1)
1295
-
1193
+ try:
1194
+ import urllib.request, json
1195
+ req = urllib.request.urlopen("http://127.0.0.1:4040/api/tunnels", timeout=2)
1196
+ data = json.loads(req.read())
1197
+ for tunnel in data.get("tunnels", []):
1198
+ if tunnel.get("proto") == "https":
1199
+ url = tunnel.get("public_url")
1200
+ break
1201
+ except Exception:
1202
+ pass
1203
+
1296
1204
  if url == "NOT_FOUND":
1297
- warn("Could not find a running localhost.run URL in config/localtunnel.log.")
1205
+ warn("Could not find a running ngrok URL. Is the server running?")
1298
1206
  nl()
1299
1207
  print(" Wait 5 seconds, or run 'superbrain-server' to start the server.")
1300
1208
  return
@@ -1306,11 +1214,11 @@ def launch_backend_status():
1306
1214
  network_url = f"http://{local_ip}:5000"
1307
1215
 
1308
1216
  nl()
1309
- print(f" Local URL ? {CYAN}{local_url}{RESET}")
1310
- print(f" Network URL ? {CYAN}{network_url}{RESET}")
1311
- print(f" Public URL ? {CYAN}{url}{RESET} (localtunnel)")
1312
- print(f" API docs ? {CYAN}{local_url}/docs{RESET}")
1313
- print(f" Access Token ? {BOLD}{MAGENTA}{token}{RESET}")
1217
+ print(f" Local URL \u2192 {CYAN}{local_url}{RESET}")
1218
+ print(f" Network URL \u2192 {CYAN}{network_url}{RESET}")
1219
+ print(f" Public URL \u2192 {CYAN}{url}{RESET} (localtunnel)")
1220
+ print(f" API docs \u2192 {CYAN}{local_url}/docs{RESET}")
1221
+ print(f" Access Token \u2192 {BOLD}{MAGENTA}{token}{RESET}")
1314
1222
  nl()
1315
1223
 
1316
1224
  def main():
@@ -1339,7 +1247,7 @@ def main():
1339
1247
  3 · Configure AI provider keys + Instagram credentials
1340
1248
  4 · Set up an offline AI model via Ollama (qwen3-vl:4b)
1341
1249
  5 · Set up offline audio transcription (Whisper + ffmpeg)
1342
- 6 · Configure remote access (localhost.run or port forwarding)
1250
+ 6 · Configure remote access (localtunnel or port forwarding)
1343
1251
  7 · Generate Access Token & initialise database
1344
1252
 
1345
1253
  Press {BOLD}Enter{RESET} to accept defaults shown in [{DIM}brackets{RESET}].
package/payload/fix.py DELETED
@@ -1,66 +0,0 @@
1
- import sys, shutil
2
- path1 = 'D:/superbrain/backend/start.py'
3
- path2 = 'D:/superbrain/superbrain-cli/payload/start.py'
4
-
5
- status_code = '''
6
- def launch_backend_status():
7
- h1("SuperBrain Server Status")
8
- PORT = 5000
9
- token = TOKEN_FILE.read_text().strip() if TOKEN_FILE.exists() else "�"
10
- local_ip = _detect_local_ip()
11
-
12
- localtunnel_enabled = bool(shutil.which("npx") or shutil.which("npx.cmd"))
13
- localtunnel_url: str | None = None
14
- if localtunnel_enabled:
15
- localtunnel_url = _find_localtunnel_url_from_log()
16
-
17
- if localtunnel_url:
18
- tunnel_line = f" Public URL ? {GREEN}{BOLD}{localtunnel_url}{RESET} {DIM}(localtunnel){RESET}"
19
- tunnel_hint = f" � public ? {GREEN}{localtunnel_url}{RESET}"
20
- elif localtunnel_enabled:
21
- tunnel_line = f" Public URL ? {YELLOW}(running � URL in localtunnel.log){RESET}"
22
- tunnel_hint = f" � public ? run: {DIM}npx localtunnel --port {PORT}{RESET}"
23
- else:
24
- tunnel_line = ""
25
- tunnel_hint = f" � public ? install Node.js first, then run: {DIM}npx localtunnel --port {PORT}{RESET}"
26
-
27
- qr_url = localtunnel_url if localtunnel_url else f"http://{local_ip}:{PORT}"
28
- _display_connect_qr(qr_url, token)
29
-
30
- print(f\"\"\"
31
- {GREEN}{BOLD}Server Status{RESET}
32
-
33
- Local URL ? {CYAN}http://127.0.0.1:{PORT}{RESET}
34
- Network URL ? {CYAN}http://{local_ip}:{PORT}{RESET}
35
- {(tunnel_line + chr(10)) if tunnel_line else ''} API docs ? {CYAN}http://127.0.0.1:{PORT}/docs{RESET}
36
- Access Token ? {BOLD}{MAGENTA}{token}{RESET}
37
-
38
- {YELLOW}Mobile app setup:{RESET}
39
- {BOLD}Option A � Scan QR code:{RESET}
40
- 1. Open the app ? Settings ? tap the {BOLD}QR icon{RESET} ??
41
- 2. Scan the QR code shown above
42
-
43
- {BOLD}Option B � Manual setup:{RESET}
44
- 1. Go to app ? ? settings
45
- 2. Set {BOLD}Server URL{RESET} to:
46
- {tunnel_hint}
47
- � Same WiFi ? http://{local_ip}:{PORT}
48
- 3. Set {BOLD}Access Token{RESET} to: {BOLD}{MAGENTA}{token}{RESET}
49
- \"\"\")
50
- sys.exit(0)
51
-
52
- '''
53
-
54
- for path in [path1, path2]:
55
- with open(path, 'r', encoding='utf-8') as f:
56
- content = f.read()
57
-
58
- if 'def launch_backend_status()' not in content:
59
- content = content.replace('def main():', status_code + 'def main():')
60
-
61
- if 'status_mode = "--status" in sys.argv' not in content:
62
- content = content.replace(' reset_mode = "--reset" in sys.argv', ' status_mode = "--status" in sys.argv\\n if status_mode:\\n launch_backend_status()\\n return\\n\\n reset_mode = "--reset" in sys.argv')
63
-
64
- with open(path, 'w', encoding='utf-8') as f:
65
- f.write(content)
66
- print("Done fixing start.py!")
package/payload/fix2.py DELETED
@@ -1,63 +0,0 @@
1
- import sys, shutil
2
- path1 = 'D:/superbrain/backend/start.py'
3
- path2 = 'D:/superbrain/superbrain-cli/payload/start.py'
4
-
5
- status_code = '''
6
- def launch_backend_status():
7
- h1("SuperBrain Server Status")
8
- PORT = 5000
9
- token = TOKEN_FILE.read_text().strip() if TOKEN_FILE.exists() else "�"
10
- local_ip = _detect_local_ip()
11
-
12
- localtunnel_enabled = bool(shutil.which("npx") or shutil.which("npx.cmd"))
13
- localtunnel_url: str | None = None
14
- if localtunnel_enabled:
15
- localtunnel_url = _find_localtunnel_url_from_log()
16
-
17
- if localtunnel_url:
18
- tunnel_line = f" Public URL ? {GREEN}{BOLD}{localtunnel_url}{RESET} {DIM}(localtunnel){RESET}"
19
- tunnel_hint = f" � public ? {GREEN}{localtunnel_url}{RESET}"
20
- elif localtunnel_enabled:
21
- tunnel_line = f" Public URL ? {YELLOW}(running � URL in localtunnel.log){RESET}"
22
- tunnel_hint = f" � public ? run: {DIM}npx localtunnel --port {PORT}{RESET}"
23
- else:
24
- tunnel_line = ""
25
- tunnel_hint = f" � public ? install Node.js first, then run: {DIM}npx localtunnel --port {PORT}{RESET}"
26
-
27
- qr_url = localtunnel_url if localtunnel_url else f"http://{local_ip}:{PORT}"
28
- _display_connect_qr(qr_url, token)
29
-
30
- print(f\"\"\"
31
- {GREEN}{BOLD}Server Status{RESET}
32
-
33
- Local URL ? {CYAN}http://127.0.0.1:{PORT}{RESET}
34
- Network URL ? {CYAN}http://{local_ip}:{PORT}{RESET}
35
- {(tunnel_line + chr(10)) if tunnel_line else ''} API docs ? {CYAN}http://127.0.0.1:{PORT}/docs{RESET}
36
- Access Token ? {BOLD}{MAGENTA}{token}{RESET}
37
-
38
- {YELLOW}Mobile app setup:{RESET}
39
- {BOLD}Option A � Scan QR code:{RESET}
40
- 1. Open the app ? Settings ? tap the {BOLD}QR icon{RESET} ??
41
- 2. Scan the QR code shown above
42
-
43
- {BOLD}Option B � Manual setup:{RESET}
44
- 1. Go to app ? ? settings
45
- 2. Set {BOLD}Server URL{RESET} to:
46
- {tunnel_hint}
47
- � Same WiFi ? http://{local_ip}:{PORT}
48
- 3. Set {BOLD}Access Token{RESET} to: {BOLD}{MAGENTA}{token}{RESET}
49
- \"\"\")
50
- sys.exit(0)
51
-
52
- '''
53
-
54
- for path in [path1, path2]:
55
- with open(path, 'r', encoding='utf-8') as f:
56
- content = f.read()
57
-
58
- if 'def launch_backend_status()' not in content:
59
- content = content.replace('def main():', status_code + 'def main():')
60
-
61
- with open(path, 'w', encoding='utf-8') as f:
62
- f.write(content)
63
- print("Done fixing start.py again!")