loki-mode 9.50.1 → 9.50.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 +2 -2
- package/VERSION +1 -1
- package/dashboard/__init__.py +1 -1
- package/docs/INSTALLATION.md +1 -1
- package/loki-ts/dist/loki.js +2 -2
- package/mcp/__init__.py +1 -1
- package/package.json +1 -1
- package/plugins/loki-mode/.claude-plugin/plugin.json +1 -1
- package/web-app/auth.py +27 -0
package/SKILL.md
CHANGED
|
@@ -3,7 +3,7 @@ name: loki-mode
|
|
|
3
3
|
description: Autonomous spec-driven build system with a built-in trust layer. It does not call work done until it is verified (RARV-C closure loop, 8 quality gates, completion council, verified-completion evidence gate). Triggers on "Loki Mode". Takes a spec (PRD, GitHub issue, OpenAPI doc, etc.) to deployed product with minimal human intervention. Provider-agnostic. Requires --dangerously-skip-permissions flag.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
# Loki Mode v9.50.
|
|
6
|
+
# Loki Mode v9.50.2
|
|
7
7
|
|
|
8
8
|
**You are an autonomous agent. You make decisions. You do not ask questions. You do not stop.**
|
|
9
9
|
|
|
@@ -470,4 +470,4 @@ See `CHANGELOG.md` entries [7.5.7], [7.5.8], [7.5.13] for the per-fix list and r
|
|
|
470
470
|
|
|
471
471
|
---
|
|
472
472
|
|
|
473
|
-
**v9.50.
|
|
473
|
+
**v9.50.2 | [Autonomi](https://www.autonomi.dev/) flagship product | ~410 lines core**
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
9.50.
|
|
1
|
+
9.50.2
|
package/dashboard/__init__.py
CHANGED
package/docs/INSTALLATION.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
The flagship product of [Autonomi](https://www.autonomi.dev/). Loki Mode is a spec-driven autonomous builder with a built-in trust layer that takes any spec to a deployed product and verifies completion with evidence (quality gates plus a completion council), not just a "done" claim. Complete installation instructions for all platforms and use cases.
|
|
4
4
|
|
|
5
|
-
**Version:** v9.50.
|
|
5
|
+
**Version:** v9.50.2
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
package/loki-ts/dist/loki.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
var ht=Object.create;var{getPrototypeOf:gt,defineProperty:jG,getOwnPropertyNames:mt}=Object;var ut=Object.prototype.hasOwnProperty;function dt($){return this[$]}var pt,ct,lt=($,X,Q)=>{var z=$!=null&&typeof $==="object";if(z){var Z=X?pt??=new WeakMap:ct??=new WeakMap,K=Z.get($);if(K)return K}Q=$!=null?ht(gt($)):{};let J=X||!$||!$.__esModule?jG(Q,"default",{value:$,enumerable:!0}):Q;for(let q of mt($))if(!ut.call(J,q))jG(J,q,{get:dt.bind($,q),enumerable:!0});if(z)Z.set($,J);return J};var zq=($,X)=>()=>(X||$((X={exports:{}}).exports,X),X.exports);var it=($)=>$;function at($,X){this[$]=it.bind(null,X)}var B1=($,X)=>{for(var Q in X)jG($,Q,{get:X[Q],enumerable:!0,configurable:!0,set:at.bind(X,Q)})};var s=($,X)=>()=>($&&(X=$($=0)),X);var w5=import.meta.require;var HR={};B1(HR,{lokiDir:()=>h0,homeLokiDir:()=>GQ,findRepoRootForVersion:()=>AG,REPO_ROOT:()=>L1});import{resolve as h2,dirname as LG}from"path";import{fileURLToPath as ot}from"url";import{existsSync as Zq}from"fs";import{homedir as st}from"os";function nt(){let $=UR;for(let X=0;X<6;X++){if(Zq(h2($,"VERSION"))&&Zq(h2($,"autonomy/run.sh")))return $;let Q=LG($);if(Q===$)break;$=Q}return h2(UR,"..","..","..")}function AG($){let X=$;for(let Q=0;Q<6;Q++){if(Zq(h2(X,"VERSION"))&&Zq(h2(X,"autonomy/run.sh")))return X;let z=LG(X);if(z===X)break;X=z}return h2($,"..","..","..")}function h0(){return process.env.LOKI_DIR??h2(process.cwd(),".loki")}function GQ(){return h2(st(),".loki")}var UR,L1;var k1=s(()=>{UR=LG(ot(import.meta.url));L1=nt()});import{readFileSync as rt}from"fs";import{resolve as tt,dirname as et}from"path";import{fileURLToPath as $e}from"url";function j9(){if(n3!==null)return n3;let $="9.50.
|
|
2
|
+
var ht=Object.create;var{getPrototypeOf:gt,defineProperty:jG,getOwnPropertyNames:mt}=Object;var ut=Object.prototype.hasOwnProperty;function dt($){return this[$]}var pt,ct,lt=($,X,Q)=>{var z=$!=null&&typeof $==="object";if(z){var Z=X?pt??=new WeakMap:ct??=new WeakMap,K=Z.get($);if(K)return K}Q=$!=null?ht(gt($)):{};let J=X||!$||!$.__esModule?jG(Q,"default",{value:$,enumerable:!0}):Q;for(let q of mt($))if(!ut.call(J,q))jG(J,q,{get:dt.bind($,q),enumerable:!0});if(z)Z.set($,J);return J};var zq=($,X)=>()=>(X||$((X={exports:{}}).exports,X),X.exports);var it=($)=>$;function at($,X){this[$]=it.bind(null,X)}var B1=($,X)=>{for(var Q in X)jG($,Q,{get:X[Q],enumerable:!0,configurable:!0,set:at.bind(X,Q)})};var s=($,X)=>()=>($&&(X=$($=0)),X);var w5=import.meta.require;var HR={};B1(HR,{lokiDir:()=>h0,homeLokiDir:()=>GQ,findRepoRootForVersion:()=>AG,REPO_ROOT:()=>L1});import{resolve as h2,dirname as LG}from"path";import{fileURLToPath as ot}from"url";import{existsSync as Zq}from"fs";import{homedir as st}from"os";function nt(){let $=UR;for(let X=0;X<6;X++){if(Zq(h2($,"VERSION"))&&Zq(h2($,"autonomy/run.sh")))return $;let Q=LG($);if(Q===$)break;$=Q}return h2(UR,"..","..","..")}function AG($){let X=$;for(let Q=0;Q<6;Q++){if(Zq(h2(X,"VERSION"))&&Zq(h2(X,"autonomy/run.sh")))return X;let z=LG(X);if(z===X)break;X=z}return h2($,"..","..","..")}function h0(){return process.env.LOKI_DIR??h2(process.cwd(),".loki")}function GQ(){return h2(st(),".loki")}var UR,L1;var k1=s(()=>{UR=LG(ot(import.meta.url));L1=nt()});import{readFileSync as rt}from"fs";import{resolve as tt,dirname as et}from"path";import{fileURLToPath as $e}from"url";function j9(){if(n3!==null)return n3;let $="9.50.2";if(typeof $==="string"&&$.length>0)return n3=$,n3;try{let X=et($e(import.meta.url)),Q=AG(X);n3=rt(tt(Q,"VERSION"),"utf-8").trim()}catch{n3="unknown"}return n3}var n3=null;var Kq=s(()=>{k1()});var jR={};B1(jR,{runOrThrow:()=>Me,run:()=>$1,readStreamCapped:()=>Jq,commandVersion:()=>je,commandExists:()=>g5,ShellError:()=>TG,MAX_STDOUT_BYTES:()=>OR});async function Jq($,X=OR){let Q=$.getReader(),z=new TextDecoder,Z="",K=0;try{while(K<X){let{done:J,value:q}=await Q.read();if(J)break;if(!q)continue;if(K+=q.byteLength,K>X){let V=q.byteLength-(K-X);Z+=z.decode(q.subarray(0,V),{stream:!0});break}Z+=z.decode(q,{stream:!0})}Z+=z.decode()}finally{try{await Q.cancel()}catch{}Q.releaseLock()}return Z}async function $1($,X={}){let Q=Bun.spawn({cmd:[...$],stdout:"pipe",stderr:"pipe",env:X.env?{...process.env,...X.env}:process.env,cwd:X.cwd}),z,Z;if(X.timeoutMs&&X.timeoutMs>0)z=setTimeout(()=>{try{Q.kill("SIGTERM")}catch{}Z=setTimeout(()=>{try{Q.kill("SIGKILL")}catch{}},2000)},X.timeoutMs);try{let[K,J,q]=await Promise.all([Jq(Q.stdout),new Response(Q.stderr).text(),Q.exited]);return{stdout:K,stderr:J,exitCode:q}}finally{if(z)clearTimeout(z);if(Z)clearTimeout(Z)}}async function Me($,X={}){let Q=await $1($,X);if(Q.exitCode!==0)throw new TG(`command failed (${Q.exitCode}): ${$.join(" ")}`,Q.exitCode,Q.stdout,Q.stderr);return Q}async function g5($){let X=Oe($),Q=await $1(["sh","-c",`command -v ${X}`],{timeoutMs:5000});if(Q.exitCode===0)return Q.stdout.trim()||null;return null}function Oe($){if(!/^[A-Za-z0-9._/-]+$/.test($))throw Error(`refused to shell-escape suspect token: ${$}`);return $}async function je($,X="--version"){if(!await g5($))return null;let z=await $1([$,X],{timeoutMs:5000});if(z.exitCode!==0)return null;return((z.stdout||z.stderr).split(/\r?\n/)[0]?.trim()??"")||null}var OR=16777216,TG;var y8=s(()=>{TG=class TG extends Error{message;exitCode;stdout;stderr;constructor($,X,Q,z){super($);this.message=$;this.exitCode=X;this.stdout=Q;this.stderr=z;this.name="ShellError"}}});function g2($){return Le?"":$}var Le,p0,$5,q1,F61,A1,f1,m5,r;var t7=s(()=>{Le=(process.env.NO_COLOR??"").length>0;p0=g2("\x1B[0;31m"),$5=g2("\x1B[0;32m"),q1=g2("\x1B[1;33m"),F61=g2("\x1B[0;34m"),A1=g2("\x1B[0;36m"),f1=g2("\x1B[1m"),m5=g2("\x1B[2m"),r=g2("\x1B[0m")});import{existsSync as Ee}from"fs";async function Z2(){if(BQ!==void 0)return BQ;let $="/opt/homebrew/bin/python3.12";if(Ee($))return BQ=$,$;let X=await g5("python3.12");if(X)return BQ=X,X;let Q=await g5("python3");return BQ=Q,Q}async function I4($,X={}){let Q=await Z2();if(!Q)return{stdout:"",stderr:"python3 not found",exitCode:127};return $1([Q,"-c",$],X)}var BQ;var m2=s(()=>{y8()});var hR={};B1(hR,{runStatus:()=>$00});import{existsSync as u5,readFileSync as C9,readdirSync as xR,statSync as kR}from"fs";import{resolve as A5,basename as le}from"path";import{homedir as ie}from"os";function SR($){let X=Math.trunc($);if(X>=1e6)return`${(Math.trunc(X/1e6*10)/10).toFixed(1)}M`;if(X>=1000)return`${(Math.trunc(X/1000*10)/10).toFixed(1)}K`;return String(X)}function yR($,X,Q){if(X===0)return null;let z=Math.trunc($*100/X),Z=Math.trunc($*Vq/X);if(Z>Vq)Z=Vq;let K=Vq-Z,J=$5;if(z>=80)J=p0;else if(z>=50)J=q1;let q="=".repeat(Math.max(0,Z))+" ".repeat(Math.max(0,K)),V=SR($),Y=SR(X);return` ${f1}${Q}${r} ${J}[${q}]${r} ${z}% (${V} / ${Y})`}async function oe(){if(await g5("jq"))return!0;return process.stdout.write(`${p0}Error: jq is required but not installed.${r}
|
|
3
3
|
`),process.stdout.write(`Install with:
|
|
4
4
|
`),process.stdout.write(` brew install jq (macOS)
|
|
5
5
|
`),process.stdout.write(` apt install jq (Debian/Ubuntu)
|
|
@@ -1337,4 +1337,4 @@ Set LOKI_LEGACY_BASH=1 to force the bash CLI for every command.
|
|
|
1337
1337
|
`),2}case"start":{let{runStart:z}=await Promise.resolve().then(() => (ft(),bt));return z(Q)}default:return process.stderr.write(`Unknown command: ${X}
|
|
1338
1338
|
`),process.stderr.write(_t),2}}PR();process.on("SIGINT",()=>process.exit(130));process.on("SIGTERM",()=>process.exit(143));var J61=await K61(Bun.argv.slice(2));process.exit(J61);
|
|
1339
1339
|
|
|
1340
|
-
//# debugId=
|
|
1340
|
+
//# debugId=DCDDCB6951FA867C7A41715C9E3207BA
|
package/mcp/__init__.py
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "loki-mode",
|
|
3
3
|
"mcpName": "io.github.asklokesh/loki-mode",
|
|
4
|
-
"version": "9.50.
|
|
4
|
+
"version": "9.50.2",
|
|
5
5
|
"description": "Loki Mode by Autonomi. Autonomous spec-to-product system: takes a PRD, GitHub issue, OpenAPI/JSON/YAML, or one-line brief to a deployed app via the RARV-C closure loop with 8 quality gates. Provider-agnostic (Claude Code, OpenAI Codex, Cline, Aider, opencode).",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"agent",
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json",
|
|
3
3
|
"name": "loki-mode",
|
|
4
4
|
"displayName": "Loki Mode",
|
|
5
|
-
"version": "9.50.
|
|
5
|
+
"version": "9.50.2",
|
|
6
6
|
"description": "Autonomous spec-to-product build system with a built-in trust layer (RARV-C closure loop, 8 quality gates, completion council). Ships Loki's spec-hardening, drift-detection, and deterministic PR verification commands plus the Loki MCP server.",
|
|
7
7
|
"author": {
|
|
8
8
|
"name": "Autonomi",
|
package/web-app/auth.py
CHANGED
|
@@ -31,7 +31,34 @@ logger = logging.getLogger("purple-lab.auth")
|
|
|
31
31
|
# Config from environment variables
|
|
32
32
|
# ---------------------------------------------------------------------------
|
|
33
33
|
|
|
34
|
+
# Placeholder values that ship in this repository. A non-empty placeholder used
|
|
35
|
+
# to pass the `if not SECRET_KEY` check below and become the live HS256 signing
|
|
36
|
+
# AND verification key -- and the same secret derives the Fernet key for secrets
|
|
37
|
+
# at rest (crypto.py:31), so one placeholder compromises both cryptosystems.
|
|
38
|
+
#
|
|
39
|
+
# web-app/deploy/k8s/purple-lab/secret.yaml ships
|
|
40
|
+
# PURPLE_LAB_SECRET_KEY: "CHANGE_ME_TO_RANDOM_64_CHAR_HEX". That value is public,
|
|
41
|
+
# so an operator who applies the manifest unedited hands anyone the ability to
|
|
42
|
+
# mint a token for any subject. Refusing it is the only safe direction: an
|
|
43
|
+
# ephemeral key breaks sessions across restarts, which is visible and
|
|
44
|
+
# recoverable, whereas a public signing key is a silent full auth bypass.
|
|
45
|
+
_PLACEHOLDER_SECRETS = frozenset({
|
|
46
|
+
"CHANGE_ME_TO_RANDOM_64_CHAR_HEX",
|
|
47
|
+
"CHANGE_ME",
|
|
48
|
+
"changeme",
|
|
49
|
+
"REPLACE_ME",
|
|
50
|
+
})
|
|
51
|
+
|
|
34
52
|
SECRET_KEY = os.environ.get("PURPLE_LAB_SECRET_KEY", "")
|
|
53
|
+
if SECRET_KEY and SECRET_KEY.strip() in _PLACEHOLDER_SECRETS:
|
|
54
|
+
logger.critical(
|
|
55
|
+
"PURPLE_LAB_SECRET_KEY is set to the placeholder value that ships in "
|
|
56
|
+
"web-app/deploy/k8s/purple-lab/secret.yaml. That value is public, so "
|
|
57
|
+
"anyone could mint a valid token. REFUSING it and generating an "
|
|
58
|
+
"ephemeral key instead; sessions will not survive a restart until you "
|
|
59
|
+
"set a real secret (openssl rand -hex 32)."
|
|
60
|
+
)
|
|
61
|
+
SECRET_KEY = ""
|
|
35
62
|
if not SECRET_KEY:
|
|
36
63
|
SECRET_KEY = secrets.token_hex(32)
|
|
37
64
|
logger.warning(
|