loki-mode 6.51.0 → 6.52.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/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.51.0
6
+ # Loki Mode v6.52.0
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.51.0 | [Autonomi](https://www.autonomi.dev/) flagship product | ~260 lines core**
270
+ **v6.52.0 | [Autonomi](https://www.autonomi.dev/) flagship product | ~260 lines core**
package/VERSION CHANGED
@@ -1 +1 @@
1
- 6.51.0
1
+ 6.52.0
@@ -7,7 +7,7 @@ Modules:
7
7
  control: Session control API (start/stop/pause/resume)
8
8
  """
9
9
 
10
- __version__ = "6.51.0"
10
+ __version__ = "6.52.0"
11
11
 
12
12
  # Expose the control app for easy import
13
13
  try:
@@ -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.51.0
5
+ **Version:** v6.52.0
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.51.0'
60
+ __version__ = '6.52.0'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "loki-mode",
3
- "version": "6.51.0",
3
+ "version": "6.52.0",
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",
package/web-app/server.py CHANGED
@@ -17,6 +17,7 @@ import subprocess
17
17
  import sys
18
18
  import time
19
19
  import uuid
20
+ from contextlib import asynccontextmanager
20
21
  from pathlib import Path
21
22
  from typing import Dict, Optional
22
23
 
@@ -56,7 +57,44 @@ DIST_DIR = SCRIPT_DIR / "dist"
56
57
  # App setup
57
58
  # ---------------------------------------------------------------------------
58
59
 
59
- app = FastAPI(title="Purple Lab", docs_url=None, redoc_url=None)
60
+
61
+ @asynccontextmanager
62
+ async def lifespan(app: FastAPI):
63
+ """Manage application startup and shutdown lifecycle."""
64
+ # -- Startup --
65
+ try:
66
+ from models import init_db
67
+ db_url = os.environ.get("DATABASE_URL")
68
+ if db_url:
69
+ await init_db(db_url)
70
+ logger.info("Database initialized")
71
+ else:
72
+ logger.info("No DATABASE_URL set -- running in local mode (no auth, file-based storage)")
73
+ except ImportError:
74
+ logger.info("Database models not available -- running in local mode")
75
+ except Exception as exc:
76
+ logger.warning("Database initialization failed: %s -- falling back to local mode", exc)
77
+
78
+ yield
79
+
80
+ # -- Shutdown --
81
+ # Stop all file watchers
82
+ file_watcher.stop_all()
83
+
84
+ # Stop all dev servers
85
+ await dev_server_manager.stop_all()
86
+
87
+ # Clean up all terminal PTYs
88
+ for _sid, _pty in list(_terminal_ptys.items()):
89
+ try:
90
+ if _pty.isalive():
91
+ _pty.close(force=True)
92
+ except Exception:
93
+ pass
94
+ _terminal_ptys.clear()
95
+
96
+
97
+ app = FastAPI(title="Purple Lab", docs_url=None, redoc_url=None, lifespan=lifespan)
60
98
 
61
99
  _default_cors_origins = [
62
100
  f"http://127.0.0.1:{PORT}",
@@ -882,11 +920,14 @@ _SECRETS_FILE = SCRIPT_DIR.parent / ".loki" / "purple-lab" / "secrets.json"
882
920
 
883
921
 
884
922
  def _load_secrets() -> dict[str, str]:
885
- """Load secrets from disk."""
923
+ """Load secrets from disk, decrypting values if encryption is configured."""
924
+ from crypto import decrypt_value, encryption_available
886
925
  if _SECRETS_FILE.exists():
887
926
  try:
888
927
  data = json.loads(_SECRETS_FILE.read_text())
889
928
  if isinstance(data, dict):
929
+ if encryption_available():
930
+ return {k: decrypt_value(v) for k, v in data.items()}
890
931
  return data
891
932
  except (json.JSONDecodeError, OSError):
892
933
  pass
@@ -894,9 +935,14 @@ def _load_secrets() -> dict[str, str]:
894
935
 
895
936
 
896
937
  def _save_secrets(secrets: dict[str, str]) -> None:
897
- """Save secrets to disk. WARNING: stored in plaintext."""
938
+ """Save secrets to disk, encrypting values if PURPLE_LAB_SECRET_KEY is set."""
939
+ from crypto import encrypt_value, encryption_available
898
940
  _SECRETS_FILE.parent.mkdir(parents=True, exist_ok=True)
899
- _SECRETS_FILE.write_text(json.dumps(secrets, indent=2))
941
+ if encryption_available():
942
+ encrypted = {k: encrypt_value(v) for k, v in secrets.items()}
943
+ _SECRETS_FILE.write_text(json.dumps(encrypted, indent=2))
944
+ else:
945
+ _SECRETS_FILE.write_text(json.dumps(secrets, indent=2))
900
946
 
901
947
 
902
948
  # ---------------------------------------------------------------------------
@@ -1138,40 +1184,6 @@ async def stop_session() -> JSONResponse:
1138
1184
  return JSONResponse(content={"stopped": True, "message": "Session stopped"})
1139
1185
 
1140
1186
 
1141
- @app.on_event("startup")
1142
- async def startup_event():
1143
- """Initialize database if DATABASE_URL is configured."""
1144
- try:
1145
- from models import init_db
1146
- db_url = os.environ.get("DATABASE_URL")
1147
- if db_url:
1148
- await init_db(db_url)
1149
- logger.info("Database initialized")
1150
- else:
1151
- logger.info("No DATABASE_URL set -- running in local mode (no auth, file-based storage)")
1152
- except ImportError:
1153
- logger.info("Database models not available -- running in local mode")
1154
- except Exception as exc:
1155
- logger.warning("Database initialization failed: %s -- falling back to local mode", exc)
1156
-
1157
-
1158
- @app.on_event("shutdown")
1159
- async def shutdown_event():
1160
- """Clean up any running session, file watchers, and dev servers when Purple Lab shuts down."""
1161
- # Stop all file watchers
1162
- file_watcher.stop_all()
1163
-
1164
- # Stop all dev servers
1165
- await dev_server_manager.stop_all()
1166
-
1167
- # Clean up all terminal PTYs
1168
- for _sid, _pty in list(_terminal_ptys.items()):
1169
- try:
1170
- if _pty.isalive():
1171
- _pty.close(force=True)
1172
- except Exception:
1173
- pass
1174
- _terminal_ptys.clear()
1175
1187
 
1176
1188
  if not session.running or session.process is None:
1177
1189
  return