loki-mode 6.26.0 → 6.26.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/SKILL.md CHANGED
@@ -3,7 +3,7 @@ name: loki-mode
3
3
  description: Multi-agent autonomous startup system. Triggers on "Loki Mode". Takes PRD to deployed product with minimal human intervention. Requires --dangerously-skip-permissions flag.
4
4
  ---
5
5
 
6
- # Loki Mode v6.26.0
6
+ # Loki Mode v6.26.2
7
7
 
8
8
  **You are an autonomous agent. You make decisions. You do not ask questions. You do not stop.**
9
9
 
@@ -267,4 +267,4 @@ The following features are documented in skill modules but not yet fully automat
267
267
  | Quality gates 3-reviewer system | Implemented (v5.35.0) | 5 specialist reviewers in `skills/quality-gates.md`; execution in run.sh |
268
268
  | Benchmarks (HumanEval, SWE-bench) | Infrastructure only | Runner scripts and datasets exist in `benchmarks/`; no published results |
269
269
 
270
- **v6.26.0 | [Autonomi](https://www.autonomi.dev/) flagship product | ~260 lines core**
270
+ **v6.26.2 | [Autonomi](https://www.autonomi.dev/) flagship product | ~260 lines core**
package/VERSION CHANGED
@@ -1 +1 @@
1
- 6.26.0
1
+ 6.26.2
package/autonomy/loki CHANGED
@@ -404,6 +404,7 @@ show_help() {
404
404
  echo " stats [flags] Session statistics (--json, --efficiency)"
405
405
  echo " logs Show recent log output"
406
406
  echo " dashboard [cmd] Dashboard server (start|stop|status|url|open)"
407
+ echo " web [cmd] Web app UI (start|stop|status) -- serves web-app/dist/"
407
408
  echo " provider [cmd] Manage AI provider (show|set|list|info)"
408
409
  echo " serve Start dashboard/API server (alias for api start)"
409
410
  echo " api [cmd] Dashboard/API server (start|stop|status)"
@@ -2905,6 +2906,138 @@ cmd_dashboard_open() {
2905
2906
  fi
2906
2907
  }
2907
2908
 
2909
+ # Web app management
2910
+ # The web app is served directly by the dashboard FastAPI server (port 57374).
2911
+ # No separate web server process is needed -- the dashboard mounts web-app/dist/
2912
+ # as static files and handles SPA catch-all routing.
2913
+
2914
+ cmd_web() {
2915
+ local subcommand="${1:-start}"
2916
+
2917
+ case "$subcommand" in
2918
+ start)
2919
+ shift || true
2920
+ cmd_web_start "$@"
2921
+ ;;
2922
+ stop)
2923
+ cmd_web_stop
2924
+ ;;
2925
+ status)
2926
+ cmd_web_status
2927
+ ;;
2928
+ --help|-h|help)
2929
+ cmd_web_help
2930
+ ;;
2931
+ *)
2932
+ # Treat unknown args as options to start (e.g., loki web --no-open)
2933
+ cmd_web_start "$subcommand" "$@"
2934
+ ;;
2935
+ esac
2936
+ }
2937
+
2938
+ cmd_web_help() {
2939
+ echo -e "${BOLD}Loki Mode Web App${NC}"
2940
+ echo ""
2941
+ echo "Usage: loki web [command] [options]"
2942
+ echo ""
2943
+ echo "Commands:"
2944
+ echo " start Start the web app (default)"
2945
+ echo " stop Stop the dashboard server (alias for 'loki dashboard stop')"
2946
+ echo " status Show dashboard server status"
2947
+ echo " help Show this help"
2948
+ echo ""
2949
+ echo "Options (for start):"
2950
+ echo " --no-open Don't open browser automatically"
2951
+ echo ""
2952
+ echo "The web app is served by the dashboard API at localhost:${DASHBOARD_DEFAULT_PORT}."
2953
+ echo "Both API endpoints and static files are served from the same origin."
2954
+ echo ""
2955
+ echo "Examples:"
2956
+ echo " loki web Start dashboard and open web app in browser"
2957
+ echo " loki web --no-open Start dashboard without opening browser"
2958
+ echo " loki web stop Stop the dashboard server"
2959
+ }
2960
+
2961
+ cmd_web_start() {
2962
+ local open_browser=true
2963
+
2964
+ # Parse arguments
2965
+ while [[ $# -gt 0 ]]; do
2966
+ case "$1" in
2967
+ --no-open)
2968
+ open_browser=false
2969
+ shift
2970
+ ;;
2971
+ --help|-h)
2972
+ cmd_web_help
2973
+ exit 0
2974
+ ;;
2975
+ *)
2976
+ echo -e "${RED}Unknown option: $1${NC}"
2977
+ echo "Run 'loki web --help' for usage."
2978
+ exit 1
2979
+ ;;
2980
+ esac
2981
+ done
2982
+
2983
+ # Check that web-app dist exists
2984
+ local web_dist="${SKILL_DIR}/web-app/dist"
2985
+ if [ ! -d "$web_dist" ] || [ ! -f "$web_dist/index.html" ]; then
2986
+ echo -e "${RED}Error: Web app not built${NC}"
2987
+ echo "Expected files at: $web_dist"
2988
+ echo ""
2989
+ echo "Build the web app first:"
2990
+ echo " cd web-app && npm install && npm run build"
2991
+ exit 1
2992
+ fi
2993
+
2994
+ # Ensure dashboard server is running (it serves both API and web app)
2995
+ local api_running=false
2996
+ if [ -f "$DASHBOARD_PID_FILE" ]; then
2997
+ local api_pid
2998
+ api_pid=$(cat "$DASHBOARD_PID_FILE" 2>/dev/null)
2999
+ if [ -n "$api_pid" ] && kill -0 "$api_pid" 2>/dev/null; then
3000
+ api_running=true
3001
+ fi
3002
+ fi
3003
+
3004
+ if [ "$api_running" = false ]; then
3005
+ echo -e "${CYAN}Starting dashboard server...${NC}"
3006
+ cmd_dashboard_start
3007
+ echo ""
3008
+ fi
3009
+
3010
+ local url="http://127.0.0.1:${DASHBOARD_DEFAULT_PORT}"
3011
+
3012
+ echo -e "${GREEN}Web app available at: $url${NC}"
3013
+ echo ""
3014
+ echo -e "Stop with: ${CYAN}loki web stop${NC} or ${CYAN}loki dashboard stop${NC}"
3015
+
3016
+ # Open browser
3017
+ if [ "$open_browser" = true ]; then
3018
+ echo ""
3019
+ if command -v open &> /dev/null; then
3020
+ open "$url"
3021
+ elif command -v xdg-open &> /dev/null; then
3022
+ xdg-open "$url"
3023
+ elif command -v start &> /dev/null; then
3024
+ start "$url"
3025
+ else
3026
+ echo "Please open in browser: $url"
3027
+ fi
3028
+ fi
3029
+ }
3030
+
3031
+ cmd_web_stop() {
3032
+ # Web app is served by the dashboard -- stopping dashboard stops the web app
3033
+ cmd_dashboard_stop
3034
+ }
3035
+
3036
+ cmd_web_status() {
3037
+ # Web app is served by the dashboard -- show dashboard status
3038
+ cmd_dashboard_status
3039
+ }
3040
+
2908
3041
  # Import GitHub issues
2909
3042
  cmd_import() {
2910
3043
  if [ ! -d "$LOKI_DIR" ]; then
@@ -9072,6 +9205,9 @@ main() {
9072
9205
  dashboard)
9073
9206
  cmd_dashboard "$@"
9074
9207
  ;;
9208
+ web)
9209
+ cmd_web "$@"
9210
+ ;;
9075
9211
  logs)
9076
9212
  cmd_logs "$@"
9077
9213
  ;;
package/autonomy/run.sh CHANGED
@@ -9347,7 +9347,6 @@ cleanup() {
9347
9347
  if type app_runner_cleanup &>/dev/null; then
9348
9348
  app_runner_cleanup
9349
9349
  fi
9350
- stop_dashboard
9351
9350
  stop_status_monitor
9352
9351
  kill_all_registered
9353
9352
  rm -f "$loki_dir/loki.pid" 2>/dev/null
@@ -9379,7 +9378,6 @@ except (json.JSONDecodeError, OSError): pass
9379
9378
  if type app_runner_cleanup &>/dev/null; then
9380
9379
  app_runner_cleanup
9381
9380
  fi
9382
- stop_dashboard
9383
9381
  stop_status_monitor
9384
9382
  kill_all_registered
9385
9383
  rm -f "$loki_dir/loki.pid" "$loki_dir/PAUSE" 2>/dev/null
@@ -9974,7 +9972,6 @@ main() {
9974
9972
  if type app_runner_cleanup &>/dev/null; then
9975
9973
  app_runner_cleanup
9976
9974
  fi
9977
- stop_dashboard
9978
9975
  stop_status_monitor
9979
9976
  local loki_dir="${TARGET_DIR:-.}/.loki"
9980
9977
  rm -f "$loki_dir/loki.pid" 2>/dev/null
@@ -7,7 +7,7 @@ Modules:
7
7
  control: Session control API (start/stop/pause/resume)
8
8
  """
9
9
 
10
- __version__ = "6.26.0"
10
+ __version__ = "6.26.2"
11
11
 
12
12
  # Expose the control app for easy import
13
13
  try:
@@ -286,43 +286,6 @@ manager = ConnectionManager()
286
286
  start_time = datetime.now(timezone.utc)
287
287
 
288
288
 
289
- async def _orphan_watchdog():
290
- """Background task that shuts down dashboard if the parent session dies.
291
-
292
- When the session process is killed (SIGKILL), the cleanup trap never runs
293
- and the dashboard is left orphaned. This watchdog checks the session PID
294
- every 30 seconds and initiates shutdown if the session is gone.
295
- """
296
- loki_dir = _get_loki_dir()
297
- pid_file = loki_dir / "loki.pid"
298
- # Wait 60s before first check to let session fully start
299
- await asyncio.sleep(60)
300
- while True:
301
- try:
302
- if pid_file.exists():
303
- pid = int(pid_file.read_text().strip())
304
- try:
305
- os.kill(pid, 0) # Check if process exists
306
- except OSError:
307
- # Session PID is dead -- we're orphaned
308
- logger.warning(
309
- "Session PID %d is gone. Dashboard shutting down to avoid orphan.", pid
310
- )
311
- # Clean up our own PID file
312
- dash_pid = loki_dir / "dashboard" / "dashboard.pid"
313
- dash_pid.unlink(missing_ok=True)
314
- # Give a moment for any in-flight requests
315
- await asyncio.sleep(2)
316
- os._exit(0)
317
- # If PID file doesn't exist and we've been running >2 min, also shut down
318
- elif time.time() - _dashboard_start_time > 120:
319
- logger.warning("No session PID file found. Dashboard shutting down.")
320
- os._exit(0)
321
- except (ValueError, OSError):
322
- pass
323
- await asyncio.sleep(30)
324
-
325
-
326
289
  _dashboard_start_time = time.time()
327
290
 
328
291
 
@@ -332,11 +295,8 @@ async def lifespan(app: FastAPI):
332
295
  # Startup
333
296
  await init_db()
334
297
  _telemetry.send_telemetry("dashboard_start")
335
- # Start orphan watchdog
336
- watchdog_task = asyncio.create_task(_orphan_watchdog())
337
298
  yield
338
299
  # Shutdown
339
- watchdog_task.cancel()
340
300
  await close_db()
341
301
 
342
302
 
@@ -436,6 +396,26 @@ async def get_status() -> StatusResponse:
436
396
  loki_dir = _get_loki_dir()
437
397
  uptime = (datetime.now(timezone.utc) - start_time).total_seconds()
438
398
 
399
+ # Read VERSION file early (independent of .loki/ dir)
400
+ version = "unknown"
401
+ dashboard_dir = os.path.dirname(__file__)
402
+ project_root = os.path.dirname(dashboard_dir)
403
+ version_file = os.path.join(project_root, "VERSION")
404
+ if os.path.isfile(version_file):
405
+ try:
406
+ with open(version_file) as vf:
407
+ version = vf.read().strip()
408
+ except (OSError, IOError) as e:
409
+ logger.warning(f"Failed to read VERSION file: {e}")
410
+
411
+ # If .loki/ directory doesn't exist, return idle status immediately
412
+ if not loki_dir.is_dir():
413
+ return StatusResponse(
414
+ status="idle",
415
+ version=version,
416
+ uptime_seconds=uptime,
417
+ )
418
+
439
419
  # Read dashboard-state.json (written by run.sh every 2 seconds)
440
420
  state_file = loki_dir / "dashboard-state.json"
441
421
  pid_file = loki_dir / "loki.pid"
@@ -450,18 +430,6 @@ async def get_status() -> StatusResponse:
450
430
  current_task = ""
451
431
  pending_tasks = 0
452
432
  running_agents = 0
453
- version = "unknown"
454
-
455
- # Read VERSION file
456
- dashboard_dir = os.path.dirname(__file__)
457
- project_root = os.path.dirname(dashboard_dir)
458
- version_file = os.path.join(project_root, "VERSION")
459
- if os.path.isfile(version_file):
460
- try:
461
- with open(version_file) as vf:
462
- version = vf.read().strip()
463
- except (OSError, IOError) as e:
464
- logger.warning(f"Failed to read VERSION file: {e}")
465
433
 
466
434
  # Read dashboard state
467
435
  if state_file.exists():
@@ -4370,6 +4338,7 @@ PROJECT_ROOT = os.path.dirname(DASHBOARD_DIR)
4370
4338
  # Possible static file locations (in order of preference)
4371
4339
  # Resolves correctly regardless of PYTHONPATH, symlinks, or install method
4372
4340
  STATIC_LOCATIONS = [
4341
+ os.path.join(PROJECT_ROOT, "web-app", "dist"), # web-app/dist/ (web app SPA)
4373
4342
  os.path.join(DASHBOARD_DIR, "static"), # dashboard/static/ (production)
4374
4343
  os.path.join(PROJECT_ROOT, "dashboard-ui", "dist"), # dashboard-ui/dist/ (development)
4375
4344
  ]
@@ -4377,12 +4346,14 @@ STATIC_LOCATIONS = [
4377
4346
  # Add LOKI_SKILL_DIR env var fallback (set by loki CLI and run.sh)
4378
4347
  _skill_dir = os.environ.get("LOKI_SKILL_DIR", "")
4379
4348
  if _skill_dir:
4349
+ STATIC_LOCATIONS.append(os.path.join(_skill_dir, "web-app", "dist"))
4380
4350
  STATIC_LOCATIONS.append(os.path.join(_skill_dir, "dashboard", "static"))
4381
4351
  STATIC_LOCATIONS.append(os.path.join(_skill_dir, "dashboard-ui", "dist"))
4382
4352
 
4383
4353
  # Add ~/.claude/skills/loki-mode fallback (installed skill location)
4384
4354
  _home_skill = os.path.join(os.path.expanduser("~"), ".claude", "skills", "loki-mode")
4385
4355
  if os.path.isdir(_home_skill):
4356
+ STATIC_LOCATIONS.append(os.path.join(_home_skill, "web-app", "dist"))
4386
4357
  STATIC_LOCATIONS.append(os.path.join(_home_skill, "dashboard", "static"))
4387
4358
  STATIC_LOCATIONS.append(os.path.join(_home_skill, "dashboard-ui", "dist"))
4388
4359
 
@@ -4813,6 +4784,26 @@ def start_migration_phase(migration_id: str, request_body: dict):
4813
4784
  raise HTTPException(status_code=500, detail="Failed to start phase")
4814
4785
 
4815
4786
 
4787
+ # ---------------------------------------------------------------------------
4788
+ # SPA catch-all: serve index.html for any path not matched by API routes
4789
+ # or static asset mounts. This lets the web-app handle client-side routing.
4790
+ # Must be registered LAST so it never shadows an API endpoint.
4791
+ # ---------------------------------------------------------------------------
4792
+ @app.get("/{full_path:path}", include_in_schema=False)
4793
+ async def serve_spa_catchall(full_path: str):
4794
+ """Serve static files or fall back to index.html for SPA routing."""
4795
+ if STATIC_DIR:
4796
+ # Try to serve the exact file first (e.g. /vite.svg, /manifest.json)
4797
+ candidate = os.path.join(STATIC_DIR, full_path)
4798
+ if os.path.isfile(candidate):
4799
+ return FileResponse(candidate)
4800
+ # Fall back to index.html for client-side routes
4801
+ index_path = os.path.join(STATIC_DIR, "index.html")
4802
+ if os.path.isfile(index_path):
4803
+ return FileResponse(index_path, media_type="text/html")
4804
+ return Response(status_code=404)
4805
+
4806
+
4816
4807
  def run_server(host: str = None, port: int = None) -> None:
4817
4808
  """Run the dashboard server."""
4818
4809
  import uvicorn
@@ -2,7 +2,7 @@
2
2
 
3
3
  The flagship product of [Autonomi](https://www.autonomi.dev/). Complete installation instructions for all platforms and use cases.
4
4
 
5
- **Version:** v6.26.0
5
+ **Version:** v6.26.2
6
6
 
7
7
  ---
8
8
 
package/mcp/__init__.py CHANGED
@@ -57,4 +57,4 @@ try:
57
57
  except ImportError:
58
58
  __all__ = ['mcp']
59
59
 
60
- __version__ = '6.26.0'
60
+ __version__ = '6.26.2'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "loki-mode",
3
- "version": "6.26.0",
3
+ "version": "6.26.2",
4
4
  "description": "Loki Mode by Autonomi - Multi-agent autonomous startup system for Claude Code, Codex CLI, and Gemini CLI",
5
5
  "keywords": [
6
6
  "agent",
@@ -81,7 +81,8 @@
81
81
  "dashboard/requirements.txt",
82
82
  "mcp/",
83
83
  "completions/",
84
- "src/"
84
+ "src/",
85
+ "web-app/dist/"
85
86
  ],
86
87
  "scripts": {
87
88
  "postinstall": "node bin/postinstall.js",