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 +2 -2
- package/VERSION +1 -1
- package/dashboard/__init__.py +1 -1
- package/docs/INSTALLATION.md +1 -1
- package/mcp/__init__.py +1 -1
- package/package.json +1 -1
- package/web-app/server.py +50 -38
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.
|
|
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.
|
|
270
|
+
**v6.52.0 | [Autonomi](https://www.autonomi.dev/) flagship product | ~260 lines core**
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
6.
|
|
1
|
+
6.52.0
|
package/dashboard/__init__.py
CHANGED
package/docs/INSTALLATION.md
CHANGED
package/mcp/__init__.py
CHANGED
package/package.json
CHANGED
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|