intelligence-suite 0.2.7__tar.gz → 0.2.8__tar.gz

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.
Files changed (60) hide show
  1. {intelligence_suite-0.2.7 → intelligence_suite-0.2.8}/PKG-INFO +1 -1
  2. {intelligence_suite-0.2.7 → intelligence_suite-0.2.8}/intelligence_core/config.py +4 -3
  3. {intelligence_suite-0.2.7 → intelligence_suite-0.2.8}/intelligence_suite.egg-info/PKG-INFO +1 -1
  4. {intelligence_suite-0.2.7 → intelligence_suite-0.2.8}/intelligence_suite.egg-info/SOURCES.txt +1 -0
  5. {intelligence_suite-0.2.7 → intelligence_suite-0.2.8}/intelligence_suite.egg-info/entry_points.txt +1 -0
  6. intelligence_suite-0.2.8/intelligence_ui/launcher.py +385 -0
  7. intelligence_suite-0.2.8/intelligence_ui/templates.py +547 -0
  8. {intelligence_suite-0.2.7 → intelligence_suite-0.2.8}/pyproject.toml +2 -1
  9. intelligence_suite-0.2.7/intelligence_ui/templates.py +0 -396
  10. {intelligence_suite-0.2.7 → intelligence_suite-0.2.8}/CodeIntelligence/__init__.py +0 -0
  11. {intelligence_suite-0.2.7 → intelligence_suite-0.2.8}/CodeIntelligence/embed_chunks.py +0 -0
  12. {intelligence_suite-0.2.7 → intelligence_suite-0.2.8}/CodeIntelligence/parse_repo.py +0 -0
  13. {intelligence_suite-0.2.7 → intelligence_suite-0.2.8}/CodeIntelligence/parsers/__init__.py +0 -0
  14. {intelligence_suite-0.2.7 → intelligence_suite-0.2.8}/CodeIntelligence/parsers/go_parser.py +0 -0
  15. {intelligence_suite-0.2.7 → intelligence_suite-0.2.8}/CodeIntelligence/parsers/markdown_parser.py +0 -0
  16. {intelligence_suite-0.2.7 → intelligence_suite-0.2.8}/CodeIntelligence/parsers/python_parser.py +0 -0
  17. {intelligence_suite-0.2.7 → intelligence_suite-0.2.8}/CodeIntelligence/parsers/sql_parser.py +0 -0
  18. {intelligence_suite-0.2.7 → intelligence_suite-0.2.8}/CodeIntelligence/parsers/typescript_parser.py +0 -0
  19. {intelligence_suite-0.2.7 → intelligence_suite-0.2.8}/CodeIntelligence/parsers/yaml_parser.py +0 -0
  20. {intelligence_suite-0.2.7 → intelligence_suite-0.2.8}/CodeIntelligence/rag_server.py +0 -0
  21. {intelligence_suite-0.2.7 → intelligence_suite-0.2.8}/DocIntelligence/__init__.py +0 -0
  22. {intelligence_suite-0.2.7 → intelligence_suite-0.2.8}/DocIntelligence/doc_server.py +0 -0
  23. {intelligence_suite-0.2.7 → intelligence_suite-0.2.8}/DocIntelligence/embed_docs.py +0 -0
  24. {intelligence_suite-0.2.7 → intelligence_suite-0.2.8}/DocIntelligence/ingest_docs.py +0 -0
  25. {intelligence_suite-0.2.7 → intelligence_suite-0.2.8}/DocIntelligence/parsers/__init__.py +0 -0
  26. {intelligence_suite-0.2.7 → intelligence_suite-0.2.8}/DocIntelligence/parsers/docx_parser.py +0 -0
  27. {intelligence_suite-0.2.7 → intelligence_suite-0.2.8}/DocIntelligence/parsers/markdown_parser.py +0 -0
  28. {intelligence_suite-0.2.7 → intelligence_suite-0.2.8}/DocIntelligence/parsers/pdf_parser.py +0 -0
  29. {intelligence_suite-0.2.7 → intelligence_suite-0.2.8}/DocIntelligence/parsers/txt_parser.py +0 -0
  30. {intelligence_suite-0.2.7 → intelligence_suite-0.2.8}/DocIntelligence/parsers/xlsx_parser.py +0 -0
  31. {intelligence_suite-0.2.7 → intelligence_suite-0.2.8}/LICENSE +0 -0
  32. {intelligence_suite-0.2.7 → intelligence_suite-0.2.8}/MentorIntelligence/__init__.py +0 -0
  33. {intelligence_suite-0.2.7 → intelligence_suite-0.2.8}/MentorIntelligence/content/__init__.py +0 -0
  34. {intelligence_suite-0.2.7 → intelligence_suite-0.2.8}/MentorIntelligence/content/ingest_practices.py +0 -0
  35. {intelligence_suite-0.2.7 → intelligence_suite-0.2.8}/MentorIntelligence/content/path_templates.json +0 -0
  36. {intelligence_suite-0.2.7 → intelligence_suite-0.2.8}/MentorIntelligence/mentor_server.py +0 -0
  37. {intelligence_suite-0.2.7 → intelligence_suite-0.2.8}/MentorIntelligence/orchestrator.py +0 -0
  38. {intelligence_suite-0.2.7 → intelligence_suite-0.2.8}/MentorIntelligence/path_builder.py +0 -0
  39. {intelligence_suite-0.2.7 → intelligence_suite-0.2.8}/MentorIntelligence/profile_detector.py +0 -0
  40. {intelligence_suite-0.2.7 → intelligence_suite-0.2.8}/MentorIntelligence/session_manager.py +0 -0
  41. {intelligence_suite-0.2.7 → intelligence_suite-0.2.8}/README.md +0 -0
  42. {intelligence_suite-0.2.7 → intelligence_suite-0.2.8}/intelligence_core/__init__.py +0 -0
  43. {intelligence_suite-0.2.7 → intelligence_suite-0.2.8}/intelligence_core/chunk.py +0 -0
  44. {intelligence_suite-0.2.7 → intelligence_suite-0.2.8}/intelligence_core/embedder.py +0 -0
  45. {intelligence_suite-0.2.7 → intelligence_suite-0.2.8}/intelligence_core/escalation.py +0 -0
  46. {intelligence_suite-0.2.7 → intelligence_suite-0.2.8}/intelligence_core/llm/__init__.py +0 -0
  47. {intelligence_suite-0.2.7 → intelligence_suite-0.2.8}/intelligence_core/llm/claude.py +0 -0
  48. {intelligence_suite-0.2.7 → intelligence_suite-0.2.8}/intelligence_core/llm/ollama.py +0 -0
  49. {intelligence_suite-0.2.7 → intelligence_suite-0.2.8}/intelligence_core/llm/openai_compat.py +0 -0
  50. {intelligence_suite-0.2.7 → intelligence_suite-0.2.8}/intelligence_core/llm/protocol.py +0 -0
  51. {intelligence_suite-0.2.7 → intelligence_suite-0.2.8}/intelligence_core/retriever.py +0 -0
  52. {intelligence_suite-0.2.7 → intelligence_suite-0.2.8}/intelligence_core/server_base.py +0 -0
  53. {intelligence_suite-0.2.7 → intelligence_suite-0.2.8}/intelligence_core/store.py +0 -0
  54. {intelligence_suite-0.2.7 → intelligence_suite-0.2.8}/intelligence_suite.egg-info/dependency_links.txt +0 -0
  55. {intelligence_suite-0.2.7 → intelligence_suite-0.2.8}/intelligence_suite.egg-info/requires.txt +0 -0
  56. {intelligence_suite-0.2.7 → intelligence_suite-0.2.8}/intelligence_suite.egg-info/top_level.txt +0 -0
  57. {intelligence_suite-0.2.7 → intelligence_suite-0.2.8}/intelligence_ui/__init__.py +0 -0
  58. {intelligence_suite-0.2.7 → intelligence_suite-0.2.8}/intelligence_ui/chat_app.py +0 -0
  59. {intelligence_suite-0.2.7 → intelligence_suite-0.2.8}/setup.cfg +0 -0
  60. {intelligence_suite-0.2.7 → intelligence_suite-0.2.8}/tests/test_intelligence_suite.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: intelligence-suite
3
- Version: 0.2.7
3
+ Version: 0.2.8
4
4
  Summary: Modular knowledge retrieval suite: code, docs, APIs — all on-premise
5
5
  Author-email: ViciusLio <viciuslios@gmail.com>
6
6
  License-Expression: MIT
@@ -80,9 +80,10 @@ class Settings(BaseSettings):
80
80
  mi_llm_api_key: str = ""
81
81
 
82
82
  # ── Server ports (one per module, avoids conflicts when running together) ──
83
- ci_port: int = 8080 # CodeIntelligence
84
- di_port: int = 8081 # DocIntelligence
85
- mi_port: int = 8082 # MentorIntelligence
83
+ ci_port: int = 8080 # CodeIntelligence
84
+ di_port: int = 8081 # DocIntelligence
85
+ mi_port: int = 8082 # MentorIntelligence
86
+ launcher_port: int = 8079 # Launcher dashboard
86
87
 
87
88
  # ── Shared server settings ─────────────────────────────────────────────────
88
89
  api_host: str = "0.0.0.0"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: intelligence-suite
3
- Version: 0.2.7
3
+ Version: 0.2.8
4
4
  Summary: Modular knowledge retrieval suite: code, docs, APIs — all on-premise
5
5
  Author-email: ViciusLio <viciuslios@gmail.com>
6
6
  License-Expression: MIT
@@ -52,5 +52,6 @@ intelligence_suite.egg-info/requires.txt
52
52
  intelligence_suite.egg-info/top_level.txt
53
53
  intelligence_ui/__init__.py
54
54
  intelligence_ui/chat_app.py
55
+ intelligence_ui/launcher.py
55
56
  intelligence_ui/templates.py
56
57
  tests/test_intelligence_suite.py
@@ -5,5 +5,6 @@ ci-serve = CodeIntelligence.rag_server:main
5
5
  di-embed = DocIntelligence.embed_docs:main
6
6
  di-ingest = DocIntelligence.ingest_docs:main
7
7
  di-serve = DocIntelligence.doc_server:main
8
+ is-launch = intelligence_ui.launcher:main
8
9
  mi-ingest = MentorIntelligence.content.ingest_practices:main
9
10
  mi-serve = MentorIntelligence.mentor_server:main
@@ -0,0 +1,385 @@
1
+ """IntelligenceSuite Launcher — start/stop/monitor all three modules from one page."""
2
+
3
+ from __future__ import annotations
4
+ import logging
5
+ import subprocess
6
+ import sys
7
+ from pathlib import Path
8
+
9
+ import httpx
10
+ import uvicorn
11
+ from fastapi import FastAPI
12
+ from fastapi.middleware.cors import CORSMiddleware
13
+ from fastapi.responses import HTMLResponse, JSONResponse
14
+
15
+ logger = logging.getLogger(__name__)
16
+
17
+ # ── Module registry ─────────────────────────────────────────────────────────
18
+ MODULES: dict[str, dict] = {
19
+ "code": {
20
+ "name": "Code Intelligence",
21
+ "description": "Ask questions about your source code in natural language",
22
+ "icon": "💻",
23
+ "port": 8080,
24
+ "color": "#6366f1",
25
+ "cli": "ci-serve",
26
+ "module": "CodeIntelligence.rag_server",
27
+ "chunks_cmd": "ci-parse",
28
+ },
29
+ "doc": {
30
+ "name": "Doc Intelligence",
31
+ "description": "Query documents, PDFs, DOCX, XLSX in any language",
32
+ "icon": "📄",
33
+ "port": 8081,
34
+ "color": "#06b6d4",
35
+ "cli": "di-serve",
36
+ "module": "DocIntelligence.doc_server",
37
+ "chunks_cmd": "di-ingest",
38
+ },
39
+ "mentor": {
40
+ "name": "Mentor Intelligence",
41
+ "description": "Adaptive onboarding with session-based learning paths",
42
+ "icon": "🎓",
43
+ "port": 8082,
44
+ "color": "#ec4899",
45
+ "cli": "mi-serve",
46
+ "module": "MentorIntelligence.mentor_server",
47
+ "chunks_cmd": "mi-ingest",
48
+ },
49
+ }
50
+
51
+ # In-memory process handles (module key → Popen)
52
+ _procs: dict[str, subprocess.Popen] = {}
53
+
54
+
55
+ # ── FastAPI app ──────────────────────────────────────────────────────────────
56
+ app = FastAPI(title="IntelligenceSuite Launcher", docs_url=None, redoc_url=None)
57
+ app.add_middleware(
58
+ CORSMiddleware,
59
+ allow_origins=["*"], allow_methods=["*"], allow_headers=["*"],
60
+ )
61
+
62
+
63
+ # ── Helpers ──────────────────────────────────────────────────────────────────
64
+ def _alive(key: str) -> bool:
65
+ """True if we started the process and it's still running."""
66
+ p = _procs.get(key)
67
+ return p is not None and p.poll() is None
68
+
69
+
70
+ def _health_url(port: int) -> str:
71
+ return f"http://localhost:{port}/health"
72
+
73
+
74
+ def _start(key: str) -> dict:
75
+ mod = MODULES[key]
76
+ if _alive(key):
77
+ return {"status": "already_running"}
78
+
79
+ # Try CLI command first, then python -m fallback
80
+ for cmd in ([mod["cli"]], [sys.executable, "-m", mod["module"]]):
81
+ try:
82
+ p = subprocess.Popen(
83
+ cmd,
84
+ stdout=subprocess.DEVNULL,
85
+ stderr=subprocess.DEVNULL,
86
+ cwd=str(Path.cwd()),
87
+ )
88
+ _procs[key] = p
89
+ return {"status": "starting", "pid": p.pid, "port": mod["port"]}
90
+ except FileNotFoundError:
91
+ continue
92
+ return {"status": "error", "detail": f"Cannot find {mod['cli']} or {mod['module']}"}
93
+
94
+
95
+ def _stop(key: str) -> dict:
96
+ p = _procs.pop(key, None)
97
+ if p is None:
98
+ return {"status": "not_managed"}
99
+ p.terminate()
100
+ return {"status": "stopped"}
101
+
102
+
103
+ # ── REST endpoints ───────────────────────────────────────────────────────────
104
+ @app.get("/api/status")
105
+ def status():
106
+ result = {}
107
+ for key, mod in MODULES.items():
108
+ running = False
109
+ chunks = 0
110
+ try:
111
+ r = httpx.get(_health_url(mod["port"]), timeout=1.5)
112
+ if r.status_code == 200:
113
+ running = True
114
+ chunks = r.json().get("chunks_indexed", 0)
115
+ except Exception:
116
+ pass
117
+ result[key] = {
118
+ "running": running,
119
+ "managed": _alive(key),
120
+ "port": mod["port"],
121
+ "chunks": chunks,
122
+ "url": f"http://localhost:{mod['port']}",
123
+ }
124
+ return JSONResponse(result)
125
+
126
+
127
+ @app.post("/api/start/{key}")
128
+ def start(key: str):
129
+ if key not in MODULES:
130
+ return JSONResponse({"error": "unknown module"}, status_code=400)
131
+ return JSONResponse(_start(key))
132
+
133
+
134
+ @app.post("/api/stop/{key}")
135
+ def stop(key: str):
136
+ if key not in MODULES:
137
+ return JSONResponse({"error": "unknown module"}, status_code=400)
138
+ return JSONResponse(_stop(key))
139
+
140
+
141
+ @app.post("/api/start-all")
142
+ def start_all():
143
+ return JSONResponse({k: _start(k) for k in MODULES})
144
+
145
+
146
+ # ── Launcher HTML ────────────────────────────────────────────────────────────
147
+ _LAUNCHER_HTML = """\
148
+ <!DOCTYPE html>
149
+ <html lang="en">
150
+ <head>
151
+ <meta charset="UTF-8">
152
+ <meta name="viewport" content="width=device-width,initial-scale=1">
153
+ <title>IntelligenceSuite — Launcher</title>
154
+ <script src="https://cdn.tailwindcss.com"></script>
155
+ <style>
156
+ body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; }
157
+ .card { transition: transform .2s, box-shadow .2s; }
158
+ .card:hover { transform: translateY(-2px); box-shadow: 0 8px 30px rgba(0,0,0,.35); }
159
+ .dot-pulse { animation: pulse 2s cubic-bezier(.4,0,.6,1) infinite; }
160
+ @keyframes pulse { 0%,100%{opacity:1} 50%{opacity:.4} }
161
+ .btn { transition: all .15s; }
162
+ </style>
163
+ </head>
164
+ <body class="bg-gray-950 text-white min-h-screen flex flex-col">
165
+
166
+ <!-- Header -->
167
+ <header class="border-b border-gray-800 px-8 py-5 flex items-center justify-between">
168
+ <div class="flex items-center gap-4">
169
+ <span class="text-3xl">🧠</span>
170
+ <div>
171
+ <h1 class="text-lg font-bold tracking-tight">IntelligenceSuite</h1>
172
+ <p class="text-xs text-gray-500">On-premise knowledge retrieval · Local AI</p>
173
+ </div>
174
+ </div>
175
+ <button onclick="startAll()"
176
+ class="btn bg-indigo-600 hover:bg-indigo-500 px-5 py-2.5 rounded-xl
177
+ text-sm font-semibold flex items-center gap-2">
178
+ ▶&nbsp; Start All
179
+ </button>
180
+ </header>
181
+
182
+ <!-- Cards grid -->
183
+ <main class="flex-1 flex items-center justify-center px-8 py-12">
184
+ <div class="grid grid-cols-1 md:grid-cols-3 gap-6 w-full max-w-5xl" id="cards">
185
+
186
+ <!-- Code Intelligence -->
187
+ <div class="card bg-gray-900 border border-gray-800 rounded-2xl p-7 flex flex-col gap-5"
188
+ id="card-code">
189
+ <div class="flex items-start justify-between">
190
+ <div class="text-4xl">💻</div>
191
+ <div class="flex items-center gap-2" id="badge-code">
192
+ <div class="w-2 h-2 rounded-full bg-gray-600 dot-pulse" id="dot-code"></div>
193
+ <span class="text-xs text-gray-500" id="label-code">checking…</span>
194
+ </div>
195
+ </div>
196
+ <div>
197
+ <h2 class="font-bold text-base mb-1" style="color:#818cf8">Code Intelligence</h2>
198
+ <p class="text-xs text-gray-400 leading-relaxed">
199
+ Ask questions about your source code in natural language. Supports Python, TS, Go, SQL, YAML.
200
+ </p>
201
+ </div>
202
+ <div class="text-xs text-gray-600 font-mono">localhost:8080</div>
203
+ <div class="text-xs text-gray-500" id="chunks-code">— chunks indexed</div>
204
+ <div class="flex gap-2 mt-auto">
205
+ <a href="http://localhost:8080" target="_blank"
206
+ class="btn flex-1 text-center bg-gray-800 hover:bg-gray-700 text-sm
207
+ py-2 rounded-lg font-medium">
208
+ Open →
209
+ </a>
210
+ <button onclick="toggle('code')" id="btn-code"
211
+ class="btn flex-1 bg-indigo-700 hover:bg-indigo-600 text-sm
212
+ py-2 rounded-lg font-medium">
213
+ Start
214
+ </button>
215
+ </div>
216
+ </div>
217
+
218
+ <!-- Doc Intelligence -->
219
+ <div class="card bg-gray-900 border border-gray-800 rounded-2xl p-7 flex flex-col gap-5"
220
+ id="card-doc">
221
+ <div class="flex items-start justify-between">
222
+ <div class="text-4xl">📄</div>
223
+ <div class="flex items-center gap-2" id="badge-doc">
224
+ <div class="w-2 h-2 rounded-full bg-gray-600 dot-pulse" id="dot-doc"></div>
225
+ <span class="text-xs text-gray-500" id="label-doc">checking…</span>
226
+ </div>
227
+ </div>
228
+ <div>
229
+ <h2 class="font-bold text-base mb-1" style="color:#22d3ee">Doc Intelligence</h2>
230
+ <p class="text-xs text-gray-400 leading-relaxed">
231
+ Query company documents in any language. PDF (with OCR fallback), DOCX, XLSX, Markdown.
232
+ </p>
233
+ </div>
234
+ <div class="text-xs text-gray-600 font-mono">localhost:8081</div>
235
+ <div class="text-xs text-gray-500" id="chunks-doc">— chunks indexed</div>
236
+ <div class="flex gap-2 mt-auto">
237
+ <a href="http://localhost:8081" target="_blank"
238
+ class="btn flex-1 text-center bg-gray-800 hover:bg-gray-700 text-sm
239
+ py-2 rounded-lg font-medium">
240
+ Open →
241
+ </a>
242
+ <button onclick="toggle('doc')" id="btn-doc"
243
+ class="btn flex-1 bg-cyan-700 hover:bg-cyan-600 text-sm
244
+ py-2 rounded-lg font-medium">
245
+ Start
246
+ </button>
247
+ </div>
248
+ </div>
249
+
250
+ <!-- Mentor Intelligence -->
251
+ <div class="card bg-gray-900 border border-gray-800 rounded-2xl p-7 flex flex-col gap-5"
252
+ id="card-mentor">
253
+ <div class="flex items-start justify-between">
254
+ <div class="text-4xl">🎓</div>
255
+ <div class="flex items-center gap-2" id="badge-mentor">
256
+ <div class="w-2 h-2 rounded-full bg-gray-600 dot-pulse" id="dot-mentor"></div>
257
+ <span class="text-xs text-gray-500" id="label-mentor">checking…</span>
258
+ </div>
259
+ </div>
260
+ <div>
261
+ <h2 class="font-bold text-base mb-1" style="color:#f472b6">Mentor Intelligence</h2>
262
+ <p class="text-xs text-gray-400 leading-relaxed">
263
+ Adaptive onboarding with profile detection, session management and cross-domain retrieval.
264
+ </p>
265
+ </div>
266
+ <div class="text-xs text-gray-600 font-mono">localhost:8082</div>
267
+ <div class="text-xs text-gray-500" id="chunks-mentor">— chunks indexed</div>
268
+ <div class="flex gap-2 mt-auto">
269
+ <a href="http://localhost:8082" target="_blank"
270
+ class="btn flex-1 text-center bg-gray-800 hover:bg-gray-700 text-sm
271
+ py-2 rounded-lg font-medium">
272
+ Open →
273
+ </a>
274
+ <button onclick="toggle('mentor')" id="btn-mentor"
275
+ class="btn flex-1 bg-pink-700 hover:bg-pink-600 text-sm
276
+ py-2 rounded-lg font-medium">
277
+ Start
278
+ </button>
279
+ </div>
280
+ </div>
281
+
282
+ </div>
283
+ </main>
284
+
285
+ <!-- Footer -->
286
+ <footer class="border-t border-gray-800 px-8 py-3 text-center text-xs text-gray-700">
287
+ IntelligenceSuite Launcher · <span id="footer-status">polling…</span>
288
+ </footer>
289
+
290
+ <script>
291
+ const MODULES = ['code', 'doc', 'mentor'];
292
+ const COLORS = { code:'bg-green-400', doc:'bg-green-400', mentor:'bg-green-400' };
293
+
294
+ async function poll() {
295
+ try {
296
+ const res = await fetch('/api/status');
297
+ const data = await res.json();
298
+ let allOk = true;
299
+ for (const [key, s] of Object.entries(data)) {
300
+ const dot = document.getElementById('dot-' + key);
301
+ const label = document.getElementById('label-' + key);
302
+ const btn = document.getElementById('btn-' + key);
303
+ const chnk = document.getElementById('chunks-' + key);
304
+
305
+ if (s.running) {
306
+ dot.className = 'w-2 h-2 rounded-full bg-green-400';
307
+ label.textContent = '● online';
308
+ label.className = 'text-xs text-green-400';
309
+ btn.textContent = 'Stop';
310
+ btn.className = btn.className.replace(/bg-\\w+-700 hover:bg-\\w+-600/,
311
+ 'bg-gray-700 hover:bg-gray-600');
312
+ chnk.textContent = s.chunks + ' chunks indexed';
313
+ } else {
314
+ dot.className = 'w-2 h-2 rounded-full bg-gray-600';
315
+ label.textContent = '○ offline';
316
+ label.className = 'text-xs text-gray-500';
317
+ const colors = { code:'bg-indigo-700 hover:bg-indigo-600',
318
+ doc:'bg-cyan-700 hover:bg-cyan-600',
319
+ mentor:'bg-pink-700 hover:bg-pink-600' };
320
+ btn.textContent = 'Start';
321
+ btn.className = 'btn flex-1 ' + colors[key] + ' text-sm py-2 rounded-lg font-medium';
322
+ chnk.textContent = '— chunks indexed';
323
+ allOk = false;
324
+ }
325
+ }
326
+ document.getElementById('footer-status').textContent =
327
+ allOk ? 'All modules online' : 'Some modules offline';
328
+ } catch(e) {
329
+ document.getElementById('footer-status').textContent = 'Launcher error: ' + e.message;
330
+ }
331
+ }
332
+
333
+ async function toggle(key) {
334
+ const btn = document.getElementById('btn-' + key);
335
+ const starting = btn.textContent.trim() === 'Start';
336
+ btn.disabled = true;
337
+ btn.textContent = starting ? 'Starting…' : 'Stopping…';
338
+ await fetch('/api/' + (starting ? 'start' : 'stop') + '/' + key, { method: 'POST' });
339
+ // Poll a few times quickly to reflect new state
340
+ for (let i = 0; i < 6; i++) {
341
+ await new Promise(r => setTimeout(r, 800));
342
+ await poll();
343
+ }
344
+ btn.disabled = false;
345
+ }
346
+
347
+ async function startAll() {
348
+ await fetch('/api/start-all', { method: 'POST' });
349
+ for (let i = 0; i < 8; i++) {
350
+ await new Promise(r => setTimeout(r, 700));
351
+ await poll();
352
+ }
353
+ }
354
+
355
+ // Initial + recurring poll
356
+ poll();
357
+ setInterval(poll, 4000);
358
+ </script>
359
+ </body>
360
+ </html>
361
+ """
362
+
363
+
364
+ @app.get("/", response_class=HTMLResponse)
365
+ def index():
366
+ return HTMLResponse(_LAUNCHER_HTML)
367
+
368
+
369
+ # ── Entry point ──────────────────────────────────────────────────────────────
370
+ def main():
371
+ from intelligence_core.config import settings
372
+ port = getattr(settings, "launcher_port", 8079)
373
+
374
+ import threading, webbrowser, time
375
+ def _open():
376
+ time.sleep(1.2)
377
+ webbrowser.open(f"http://localhost:{port}")
378
+ threading.Thread(target=_open, daemon=True).start()
379
+
380
+ print(f" 🧠 IntelligenceSuite Launcher → http://localhost:{port}")
381
+ uvicorn.run(app, host=settings.api_host, port=port, log_level="warning")
382
+
383
+
384
+ if __name__ == "__main__":
385
+ main()