loki-mode 11.31.1 → 11.32.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.
Files changed (67) hide show
  1. package/SKILL.md +2 -2
  2. package/VERSION +1 -1
  3. package/autonomy/sandbox.sh +220 -2
  4. package/dashboard/__init__.py +1 -1
  5. package/docs/CLI-REFERENCE.md +1 -1
  6. package/docs/INSTALLATION.md +1 -1
  7. package/loki-ts/dist/loki.js +2 -2
  8. package/mcp/__init__.py +1 -1
  9. package/package.json +2 -2
  10. package/plugins/loki-mode/.claude-plugin/plugin.json +1 -1
  11. package/web-app/dist/assets/{AdminPage-A5UQEQzo.js → AdminPage-OCc-aouC.js} +1 -1
  12. package/web-app/dist/assets/{Avatar-xQ1gSPVB.js → Avatar-P8CySSFH.js} +1 -1
  13. package/web-app/dist/assets/{Badge-dQyhI3UH.js → Badge-Btxjz38F.js} +1 -1
  14. package/web-app/dist/assets/{Button-BehU-7tC.js → Button-BKE7kijL.js} +1 -1
  15. package/web-app/dist/assets/{CockpitPage-BPLFrfno.js → CockpitPage-B_0dgMaz.js} +1 -1
  16. package/web-app/dist/assets/{ComparePage-Dc7ecTbH.js → ComparePage-XMl2oBun.js} +1 -1
  17. package/web-app/dist/assets/{ErrorBoundary-P4CkwBs3.js → ErrorBoundary-oVL59njg.js} +1 -1
  18. package/web-app/dist/assets/{EvidenceReceiptPanel-CCC_Wc2o.js → EvidenceReceiptPanel-Ceif5573.js} +1 -1
  19. package/web-app/dist/assets/{GitHubIssuesPanel-Bi3c1_CN.js → GitHubIssuesPanel-jnlLMV-j.js} +1 -1
  20. package/web-app/dist/assets/{GitHubPRsPanel-A7D7tuaQ.js → GitHubPRsPanel-DcXxfrgp.js} +1 -1
  21. package/web-app/dist/assets/{HomePage-Cj7XatdG.js → HomePage-B0_1Ubd8.js} +2 -2
  22. package/web-app/dist/assets/{LoginPage-lFQFWzTA.js → LoginPage-s-Q1WGev.js} +1 -1
  23. package/web-app/dist/assets/{MagicPage-sHngqPOB.js → MagicPage-CexVGatU.js} +1 -1
  24. package/web-app/dist/assets/{MetricsPage-vmk3uj53.js → MetricsPage-DyvYyZkW.js} +1 -1
  25. package/web-app/dist/assets/{NotFoundPage-B37nihqx.js → NotFoundPage-CtLA5FFt.js} +1 -1
  26. package/web-app/dist/assets/{ProjectPage-TrK9SBhV.js → ProjectPage-B5cKA8WR.js} +3 -3
  27. package/web-app/dist/assets/{ProjectsPage-BoKOqxu4.js → ProjectsPage-CgVb0y2u.js} +1 -1
  28. package/web-app/dist/assets/{SettingsPage-Dfyhp6nl.js → SettingsPage-D3lfPSvp.js} +1 -1
  29. package/web-app/dist/assets/{ShowcasePage-DOCeqHxj.js → ShowcasePage-CEumy6-I.js} +1 -1
  30. package/web-app/dist/assets/{SystemSettingsPage-idLDGVpd.js → SystemSettingsPage-DZ-FEPPh.js} +1 -1
  31. package/web-app/dist/assets/{TeamsPage-CG0cXstx.js → TeamsPage-Bf6TUaQj.js} +1 -1
  32. package/web-app/dist/assets/{TemplatesPage-cThU88IX.js → TemplatesPage-B-3-HIP5.js} +1 -1
  33. package/web-app/dist/assets/{TerminalOutput-CZcfr298.js → TerminalOutput-Cla9B3Ef.js} +1 -1
  34. package/web-app/dist/assets/{activity-JwnQAJzU.js → activity-BPaXjKEf.js} +1 -1
  35. package/web-app/dist/assets/{bell-EMBOci2m.js → bell-BIhXhta4.js} +1 -1
  36. package/web-app/dist/assets/{bot-Dwg7eFlL.js → bot-BREtMbFp.js} +1 -1
  37. package/web-app/dist/assets/{check-DNNlHviH.js → check-BBDkYdTy.js} +1 -1
  38. package/web-app/dist/assets/{chevron-left-BJ2VldHk.js → chevron-left-MJKoMYpu.js} +1 -1
  39. package/web-app/dist/assets/{circle-alert-0P4HhIzB.js → circle-alert-Bk6g0LfO.js} +1 -1
  40. package/web-app/dist/assets/{clock-DxxclmlE.js → clock-DphpX3yF.js} +1 -1
  41. package/web-app/dist/assets/{cloud-COohLeUE.js → cloud-CwIuA-VW.js} +1 -1
  42. package/web-app/dist/assets/{code-xml-BJ25iVx3.js → code-xml-DPGK4IbU.js} +1 -1
  43. package/web-app/dist/assets/{database-Dvy7Bbcf.js → database-D1oOHoQg.js} +1 -1
  44. package/web-app/dist/assets/{dollar-sign-qUbMFHU4.js → dollar-sign-8w0iMGof.js} +1 -1
  45. package/web-app/dist/assets/{file-code-corner-BHhWk-yB.js → file-code-corner-Do_wBHbQ.js} +1 -1
  46. package/web-app/dist/assets/{file-plus-CXDzSmIV.js → file-plus-yM8hYAJy.js} +1 -1
  47. package/web-app/dist/assets/{globe--nSuGAqH.js → globe-CvMW8-NT.js} +1 -1
  48. package/web-app/dist/assets/{hammer-CyPoRSsX.js → hammer-BgKbsMMT.js} +1 -1
  49. package/web-app/dist/assets/{index-sCyA5kT1.js → index-D_gr9DjQ.js} +3 -3
  50. package/web-app/dist/assets/{layers-D8YjFUHc.js → layers-kRENi5Kn.js} +1 -1
  51. package/web-app/dist/assets/{loader-circle-ZB7xFqz2.js → loader-circle-D1LGSlkG.js} +1 -1
  52. package/web-app/dist/assets/{lock-D1UH6v-O.js → lock-AWRWK-Aa.js} +1 -1
  53. package/web-app/dist/assets/{package-sDqydd_4.js → package-CGXLDs1G.js} +1 -1
  54. package/web-app/dist/assets/{plus-ClTe2QX5.js → plus-BXNCq6zK.js} +1 -1
  55. package/web-app/dist/assets/{refresh-cw-D54nCHRH.js → refresh-cw-D1La5GDI.js} +1 -1
  56. package/web-app/dist/assets/{rotate-ccw-CFJoICwQ.js → rotate-ccw-Dlm3A4yL.js} +1 -1
  57. package/web-app/dist/assets/{scroll-text-D_5t4SMl.js → scroll-text-BVziFaFm.js} +1 -1
  58. package/web-app/dist/assets/{server-cUdSs3Jw.js → server-C1QO24zQ.js} +1 -1
  59. package/web-app/dist/assets/{shield-alert-o3-54oNJ.js → shield-alert-DgWjn5rX.js} +1 -1
  60. package/web-app/dist/assets/{thumbs-up-Bj5vm_ge.js → thumbs-up-B2v87stN.js} +1 -1
  61. package/web-app/dist/assets/{trash-2-D4yWbHl5.js → trash-2-BTk6wQb5.js} +1 -1
  62. package/web-app/dist/assets/{trending-down-DStUnNZ3.js → trending-down-YLEdCvay.js} +1 -1
  63. package/web-app/dist/assets/{trending-up-CL4ZFYFb.js → trending-up-B1v8JIc8.js} +1 -1
  64. package/web-app/dist/assets/{upload-Dem0HZRa.js → upload-iTjkpxy-.js} +1 -1
  65. package/web-app/dist/assets/{usePolling-14ezEt6p.js → usePolling-DaJm-uw5.js} +1 -1
  66. package/web-app/dist/assets/{user-DuS3X15u.js → user-DQDpD4Cu.js} +1 -1
  67. package/web-app/dist/index.html +1 -1
package/SKILL.md CHANGED
@@ -3,7 +3,7 @@ name: loki-mode
3
3
  description: An autonomous software factory that knows what it is supposed to deliver, and proves it did. Use when the user says Loki Mode or asks to build, fix or verify software autonomously.
4
4
  ---
5
5
 
6
- # Loki Mode v11.31.1
6
+ # Loki Mode v11.32.0
7
7
 
8
8
  **Current entry points (Loki 10 engine):** `loki "<task>"`, `loki owner/repo#N` and `loki quick "<task>"` run the Loki 10 engine and end with a signed receipt (`loki verify`). `loki backlog owner/repo --all` runs many issues in parallel. Guide: `docs/v10/GUIDE.md`. The skill instructions below, and `loki start`, describe the previous bash loop. PRD-file starts (`loki start ./prd.md`) and the opencode provider still run on it until FC38-SWEEP ports them; the legacy-engine command and the LOKI_ENGINE switch remain removed. Prefer `loki owner/repo#N` for issues.
9
9
 
@@ -496,4 +496,4 @@ See `CHANGELOG.md` entries [7.5.7], [7.5.8], [7.5.13] for the per-fix list and r
496
496
 
497
497
  ---
498
498
 
499
- **v11.31.1 | [Autonomi](https://www.autonomi.dev/) flagship product | core skill**
499
+ **v11.32.0 | [Autonomi](https://www.autonomi.dev/) flagship product | core skill**
package/VERSION CHANGED
@@ -1 +1 @@
1
- 11.31.1
1
+ 11.32.0
@@ -12,6 +12,8 @@
12
12
  # Environment Variables:
13
13
  # LOKI_SANDBOX_IMAGE - Docker image to use (default: loki-mode:sandbox)
14
14
  # LOKI_SANDBOX_NETWORK - Network mode: bridge, none, host (default: bridge)
15
+ # LOKI_SANDBOX_ALLOW_HOSTS - Opt-in egress allowlist (comma-separated hostnames);
16
+ # blocks link-local and private ranges (default: unset)
15
17
  # LOKI_SANDBOX_CPUS - CPU limit (default: 2)
16
18
  # LOKI_SANDBOX_MEMORY - Memory limit (default: 4g)
17
19
  # LOKI_SANDBOX_READONLY - Mount project as read-only (default: false)
@@ -261,6 +263,7 @@ warn_missing_api_keys() {
261
263
  cleanup_container() {
262
264
  log_warn "Interrupted - cleaning up container..."
263
265
  docker rm -f "$CONTAINER_NAME" 2>/dev/null || true
266
+ _sandbox_egress_down
264
267
  exit 130
265
268
  }
266
269
 
@@ -1083,6 +1086,196 @@ except: pass
1083
1086
  fi
1084
1087
  }
1085
1088
 
1089
+ #-------------------------------------------------------------------------------
1090
+ # Opt-in egress allowlist (LOKI_SANDBOX_ALLOW_HOSTS, comma-separated hostnames).
1091
+ # Unset: nothing below runs and the docker run argv is unchanged. Set: the sandbox
1092
+ # joins an --internal network (no route out) and reaches the world only through a
1093
+ # filtering proxy sidecar that permits the listed hosts and refuses any target
1094
+ # resolving to link-local, loopback or private ranges.
1095
+ #-------------------------------------------------------------------------------
1096
+ SANDBOX_ALLOW_HOSTS="${LOKI_SANDBOX_ALLOW_HOSTS:-}"
1097
+ EGRESS_PROXY_PORT=3128
1098
+ EGRESS_BLOCKED_CIDRS="169.254.0.0/16 fe80::/10 fec0::/10 100.64.0.0/10 10.0.0.0/8 172.16.0.0/12 192.168.0.0/16 127.0.0.0/8 ::1/128 fc00::/7"
1099
+
1100
+ # Print the validated allowlist one host per line. Rejects IP literals and
1101
+ # anything that is not a plain hostname, so a literal metadata IP can never be
1102
+ # allowlisted. Returns 1 on an invalid entry or an empty list.
1103
+ _sandbox_egress_hosts() {
1104
+ local raw="${1:-$SANDBOX_ALLOW_HOSTS}" h count=0
1105
+ local -a parts
1106
+ IFS=',' read -ra parts <<< "$raw"
1107
+ for h in "${parts[@]}"; do
1108
+ h="${h//[[:space:]]/}"
1109
+ [[ -z "$h" ]] && continue
1110
+ if [[ ! "$h" =~ ^[A-Za-z0-9]([A-Za-z0-9-]*[A-Za-z0-9])?(\.[A-Za-z0-9]([A-Za-z0-9-]*[A-Za-z0-9])?)+$ ]] \
1111
+ || [[ "$h" =~ ^[0-9.]+$ ]]; then
1112
+ log_error "LOKI_SANDBOX_ALLOW_HOSTS: invalid host (hostnames only, no IPs): $h"
1113
+ return 1
1114
+ fi
1115
+ printf '%s\n' "$h" | tr '[:upper:]' '[:lower:]'
1116
+ count=$((count+1))
1117
+ done
1118
+ [[ $count -gt 0 ]]
1119
+ }
1120
+
1121
+ # Filtering proxy (CONNECT and plain HTTP). Decision function is check_target so
1122
+ # tests can exercise it with an injected resolver; no network needed.
1123
+ _sandbox_egress_proxy_source() {
1124
+ cat <<'PYEOF'
1125
+ import ipaddress, os, socket, sys, threading
1126
+
1127
+ ALLOW = {h.strip().lower() for h in os.environ.get("ALLOW_HOSTS", "").split(",") if h.strip()}
1128
+ BLOCKED = [ipaddress.ip_network(c) for c in os.environ.get("BLOCKED_CIDRS", "").split()]
1129
+
1130
+
1131
+ def ip_blocked(ip):
1132
+ a = ipaddress.ip_address(ip)
1133
+ if getattr(a, "ipv4_mapped", None):
1134
+ a = a.ipv4_mapped
1135
+ return (not a.is_global or getattr(a, "is_site_local", False) or a.is_private or a.is_link_local or a.is_loopback or a.is_unspecified
1136
+ or a.is_multicast or a.is_reserved or any(a in n for n in BLOCKED))
1137
+
1138
+
1139
+ def check_target(host, resolve=None, port=443):
1140
+ """Return (ok, reason). Allowed only if host is listed, port is 80 or 443 and every resolved IP is global."""
1141
+ if port not in (80, 443):
1142
+ return False, "port not allowed"
1143
+ host = host.strip().lower().rstrip(".")
1144
+ if host not in ALLOW:
1145
+ return False, "host not in allowlist"
1146
+ resolve = resolve or (lambda h: [i[4][0] for i in socket.getaddrinfo(h, None)])
1147
+ try:
1148
+ ips = resolve(host)
1149
+ except Exception:
1150
+ return False, "resolution failed"
1151
+ if not ips:
1152
+ return False, "no address"
1153
+ for ip in ips:
1154
+ if ip_blocked(ip):
1155
+ return False, "resolves to blocked range " + ip
1156
+ return True, ips[0]
1157
+
1158
+
1159
+ def pipe(a, b):
1160
+ try:
1161
+ while True:
1162
+ d = a.recv(65536)
1163
+ if not d:
1164
+ break
1165
+ b.sendall(d)
1166
+ except OSError:
1167
+ pass
1168
+ finally:
1169
+ for s in (a, b):
1170
+ try:
1171
+ s.shutdown(socket.SHUT_RDWR)
1172
+ except OSError:
1173
+ pass
1174
+
1175
+
1176
+ def handle(c):
1177
+ try:
1178
+ buf = b""
1179
+ while b"\r\n\r\n" not in buf and len(buf) < 65536:
1180
+ d = c.recv(4096)
1181
+ if not d:
1182
+ return
1183
+ buf += d
1184
+ head, _, rest = buf.partition(b"\r\n\r\n")
1185
+ line = head.split(b"\r\n")[0].decode("latin1").split()
1186
+ if len(line) < 3:
1187
+ return
1188
+ method, target = line[0], line[1]
1189
+ if method.upper() == "CONNECT":
1190
+ host, _, port = target.rpartition(":")
1191
+ port = int(port or 443)
1192
+ rest_out = b""
1193
+ else:
1194
+ u = target.split("://", 1)[-1].split("/", 1)[0]
1195
+ host, _, port = u.partition(":")
1196
+ port = int(port or 80)
1197
+ rest_out = buf
1198
+ ok, info = check_target(host, port=port)
1199
+ if not ok:
1200
+ c.sendall(b"HTTP/1.1 403 Forbidden\r\nContent-Length: 0\r\n\r\n")
1201
+ return
1202
+ up = socket.create_connection((info, port), timeout=20)
1203
+ if method.upper() == "CONNECT":
1204
+ c.sendall(b"HTTP/1.1 200 Connection established\r\n\r\n")
1205
+ else:
1206
+ up.sendall(rest_out)
1207
+ t = threading.Thread(target=pipe, args=(up, c), daemon=True)
1208
+ t.start()
1209
+ pipe(c, up)
1210
+ except Exception:
1211
+ pass
1212
+ finally:
1213
+ c.close()
1214
+
1215
+
1216
+ def main():
1217
+ s = socket.socket()
1218
+ s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
1219
+ s.bind(("0.0.0.0", int(os.environ.get("PROXY_PORT", "3128"))))
1220
+ s.listen(128)
1221
+ while True:
1222
+ conn, _ = s.accept()
1223
+ threading.Thread(target=handle, args=(conn,), daemon=True).start()
1224
+
1225
+
1226
+ if __name__ == "__main__":
1227
+ main()
1228
+ PYEOF
1229
+ }
1230
+
1231
+ # Echo the docker run argv fragments the sandbox container needs when the
1232
+ # allowlist is on: one token per line (network override first, then proxy env).
1233
+ _sandbox_egress_container_args() {
1234
+ local net="$1" proxy="http://${CONTAINER_NAME}-egress:${EGRESS_PROXY_PORT}"
1235
+ printf '%s\n' "--network=$net"
1236
+ local v
1237
+ for v in HTTP_PROXY HTTPS_PROXY http_proxy https_proxy; do
1238
+ printf '%s\n' "--env" "$v=$proxy"
1239
+ done
1240
+ printf '%s\n' "--env" "NO_PROXY=localhost,127.0.0.1"
1241
+ }
1242
+
1243
+ # Echo the docker run argv for the proxy sidecar (test-visible, no side effects).
1244
+ _sandbox_egress_proxy_args() {
1245
+ local script="$1" hosts="$2"
1246
+ printf '%s\n' run --detach --name "${CONTAINER_NAME}-egress" \
1247
+ --network=bridge --cap-drop=ALL --security-opt=no-new-privileges:true \
1248
+ --read-only --pids-limit=128 --memory=256m \
1249
+ --env "ALLOW_HOSTS=$hosts" --env "BLOCKED_CIDRS=$EGRESS_BLOCKED_CIDRS" \
1250
+ --env "PROXY_PORT=$EGRESS_PROXY_PORT" \
1251
+ --volume "$script:/egress-proxy.py:ro" \
1252
+ --entrypoint python3 "$SANDBOX_IMAGE" /egress-proxy.py
1253
+ }
1254
+
1255
+ # Create the internal network and proxy sidecar. Echoes the internal network name.
1256
+ _sandbox_egress_up() {
1257
+ local hosts="$1" net="${CONTAINER_NAME}-internal" script
1258
+ local dir="${LOKI_RUN_TMP:-${TMPDIR:-/tmp}/loki-egress-${CONTAINER_NAME}}"
1259
+ mkdir -p "$dir" && chmod 700 "$dir" || return 1
1260
+ script="$dir/egress-proxy.py"
1261
+ _sandbox_egress_proxy_source > "$script"
1262
+ docker rm -f "${CONTAINER_NAME}-egress" >/dev/null 2>&1 || true
1263
+ docker network rm "$net" >/dev/null 2>&1 || true
1264
+ docker network create --internal "$net" >/dev/null || return 1
1265
+ local -a pargs=()
1266
+ while IFS= read -r line; do pargs+=("$line"); done < <(_sandbox_egress_proxy_args "$script" "$(echo "$hosts" | paste -sd, -)")
1267
+ docker "${pargs[@]}" >/dev/null || return 1
1268
+ docker network connect --alias "${CONTAINER_NAME}-egress" "$net" "${CONTAINER_NAME}-egress" >/dev/null || return 1
1269
+ printf '%s\n' "$net"
1270
+ }
1271
+
1272
+ _sandbox_egress_down() {
1273
+ local fb="${TMPDIR:-/tmp}/loki-egress-${CONTAINER_NAME}"
1274
+ if [[ -z "${LOKI_RUN_TMP:-}" && -d "$fb" && ! -L "$fb" ]]; then rm -rf -- "$fb"; fi
1275
+ docker rm -f "${CONTAINER_NAME}-egress" >/dev/null 2>&1 || true
1276
+ docker network rm "${CONTAINER_NAME}-internal" >/dev/null 2>&1 || true
1277
+ }
1278
+
1086
1279
  start_sandbox() {
1087
1280
  # Parse arguments: extract flags before positional args
1088
1281
  local prd_path=""
@@ -1158,6 +1351,22 @@ start_sandbox() {
1158
1351
  # Clean up any stopped container with same name
1159
1352
  docker rm -f "$CONTAINER_NAME" 2>/dev/null || true
1160
1353
 
1354
+ local network_arg="--network=$SANDBOX_NETWORK" egress_args=() egress_ok=1
1355
+ if [[ -n "$SANDBOX_ALLOW_HOSTS" ]]; then
1356
+ local egress_hosts egress_net
1357
+ egress_hosts=$(_sandbox_egress_hosts) || return 1
1358
+ if [[ "$SANDBOX_NETWORK" != "bridge" ]]; then
1359
+ log_error "LOKI_SANDBOX_ALLOW_HOSTS requires LOKI_SANDBOX_NETWORK=bridge (got $SANDBOX_NETWORK)"
1360
+ return 1
1361
+ fi
1362
+ egress_net=$(_sandbox_egress_up "$egress_hosts") || { _sandbox_egress_down; log_error "Failed to start egress proxy"; return 1; }
1363
+ # Any return after this point (refusal, failed docker run) must tear the proxy and network down.
1364
+ egress_ok=0
1365
+ trap '[[ "${egress_ok:-1}" == 1 ]] || _sandbox_egress_down; trap - RETURN' RETURN
1366
+ while IFS= read -r line; do egress_args+=("$line"); done < <(_sandbox_egress_container_args "$egress_net")
1367
+ network_arg="${egress_args[0]}"
1368
+ fi
1369
+
1161
1370
  log_info "Starting sandbox container..."
1162
1371
  log_info " Image: $SANDBOX_IMAGE"
1163
1372
  log_info " Project: $PROJECT_DIR"
@@ -1188,8 +1397,12 @@ start_sandbox() {
1188
1397
  # and should not be able to change UID/GID, which would undermine isolation
1189
1398
 
1190
1399
  # Network
1191
- "--network=$SANDBOX_NETWORK"
1400
+ "$network_arg"
1192
1401
  )
1402
+ if [[ ${#egress_args[@]} -gt 1 ]]; then
1403
+ docker_args+=("${egress_args[@]:1}")
1404
+ log_info " Egress: allowlist only ($(echo "$egress_hosts" | paste -sd, -)); private and link-local ranges blocked"
1405
+ fi
1193
1406
 
1194
1407
  # Add seccomp profile if available
1195
1408
  local seccomp_profile="$SKILL_DIR/autonomy/seccomp-sandbox.json"
@@ -1393,7 +1606,8 @@ start_sandbox() {
1393
1606
 
1394
1607
  # Run container
1395
1608
  local container_id
1396
- container_id=$(docker "${docker_args[@]}")
1609
+ container_id=$(docker "${docker_args[@]}") || { log_error "docker run failed"; return 1; }
1610
+ egress_ok=1
1397
1611
 
1398
1612
  log_success "Sandbox started: ${container_id:0:12}"
1399
1613
  log_info ""
@@ -1435,6 +1649,7 @@ stop_sandbox() {
1435
1649
  while [ $waited -lt 10 ]; do
1436
1650
  if ! docker ps --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; then
1437
1651
  log_success "Sandbox stopped gracefully"
1652
+ _sandbox_egress_down
1438
1653
  # Remove the temp state dir now that the container has stopped.
1439
1654
  if [[ -n "$_state_dir" ]] && [[ -d "$_state_dir" ]]; then
1440
1655
  rm -rf -- "$_state_dir" 2>/dev/null || true
@@ -1455,8 +1670,10 @@ stop_sandbox() {
1455
1670
  rm -rf -- "$_state_dir" 2>/dev/null || true
1456
1671
  fi
1457
1672
 
1673
+ _sandbox_egress_down
1458
1674
  log_success "Sandbox stopped"
1459
1675
  else
1676
+ _sandbox_egress_down
1460
1677
  log_warn "No running sandbox found"
1461
1678
  fi
1462
1679
  }
@@ -1880,6 +2097,7 @@ show_help() {
1880
2097
  echo "Environment Variables:"
1881
2098
  echo " LOKI_SANDBOX_IMAGE Docker image (default: loki-mode:sandbox)"
1882
2099
  echo " LOKI_SANDBOX_NETWORK Network mode: bridge, none, host (default: bridge)"
2100
+ echo " LOKI_SANDBOX_ALLOW_HOSTS Egress allowlist, comma-separated hostnames (default: unset)"
1883
2101
  echo " LOKI_SANDBOX_CPUS CPU limit (default: 2)"
1884
2102
  echo " LOKI_SANDBOX_MEMORY Memory limit (default: 4g)"
1885
2103
  echo " LOKI_SANDBOX_READONLY Mount project read-only (default: false)"
@@ -7,6 +7,6 @@ Modules:
7
7
  control: Session control helpers (status, atomic writes, origin check)
8
8
  """
9
9
 
10
- __version__ = "11.31.1"
10
+ __version__ = "11.32.0"
11
11
 
12
12
  __all__ = ["__version__"]
@@ -1,6 +1,6 @@
1
1
  # CLI reference
2
2
 
3
- Generated from `loki-ts/src/cli/registry.ts` by `scripts/generate-stale-zero.sh` for v11.31.1. Do not edit by hand.
3
+ Generated from `loki-ts/src/cli/registry.ts` by `scripts/generate-stale-zero.sh` for v11.32.0. Do not edit by hand.
4
4
  Hidden and legacy-only commands are omitted; see `docs/v10/CLI-MODERN.md` for the full inventory.
5
5
 
6
6
  | Command | Description |
@@ -2,7 +2,7 @@
2
2
 
3
3
  Loki Mode, by [Autonomi](https://www.autonomi.dev/), turns a spec or an issue into a verified pull request with a receipt you can check. This page covers installing it, a first run, and the options around it.
4
4
 
5
- **Version:** v11.31.1
5
+ **Version:** v11.32.0
6
6
 
7
7
  **Engine note:** `loki start` is the primary command. `loki start`, `loki "<task>"`, `loki owner/repo#N` and `loki quick` all run the Loki 10 engine, whatever the flag order or input form (PRD file, issue ref, task or one-word brief). The previous loop runs only on `--engine legacy`, `LOKI_ENGINE=legacy`, or for one call when a flag Loki 10 cannot honor is present. See the [user guide](v10/GUIDE.md) and the [5-minute quickstart](QUICKSTART.md).
8
8
 
@@ -1,5 +1,5 @@
1
1
  // @bun
2
- var pb0=Object.create;var{getPrototypeOf:cb0,defineProperty:OK,getOwnPropertyNames:xi,getOwnPropertyDescriptor:db0}=Object,Si=Object.prototype.hasOwnProperty;function bi(Z){return this[Z]}var lb0,ab0,ib0=(Z,$,J)=>{var X=Z!=null&&typeof Z==="object";if(X){var Y=$?lb0??=new WeakMap:ab0??=new WeakMap,Q=Y.get(Z);if(Q)return Q}J=Z!=null?pb0(cb0(Z)):{};let z=$||!Z||!Z.__esModule?OK(J,"default",{value:Z,enumerable:!0}):J;for(let W of xi(Z))if(!Si.call(z,W))OK(z,W,{get:bi.bind(Z,W),enumerable:!0});if(X)Y.set(Z,z);return z},ob0=(Z)=>{var $=(ki??=new WeakMap).get(Z),J;if($)return $;if($=OK({},"__esModule",{value:!0}),Z&&typeof Z==="object"||typeof Z==="function"){for(var X of xi(Z))if(!Si.call($,X))OK($,X,{get:bi.bind(Z,X),enumerable:!(J=db0(Z,X))||J.enumerable})}return ki.set(Z,$),$},ki,gN=(Z,$)=>()=>($||Z(($={exports:{}}).exports,$),$.exports);var nb0=(Z)=>Z;function sb0(Z,$){this[Z]=nb0.bind(null,$)}var A0=(Z,$)=>{for(var J in $)OK(Z,J,{get:$[J],enumerable:!0,configurable:!0,set:sb0.bind($,J)})};var S=(Z,$)=>()=>(Z&&($=Z(Z=0)),$);var s9=import.meta.require;var vi={};A0(vi,{lokiDir:()=>P0,homeLokiDir:()=>B2,findRepoRootForVersion:()=>AK,REPO_ROOT:()=>F0});import{resolve as AZ,dirname as pR}from"path";import{fileURLToPath as rb0}from"url";import{existsSync as mN}from"fs";import{homedir as tb0}from"os";function eb0(){let Z=yi;for(let $=0;$<6;$++){if(mN(AZ(Z,"VERSION"))&&mN(AZ(Z,"autonomy/run.sh")))return Z;let J=pR(Z);if(J===Z)break;Z=J}return AZ(yi,"..","..","..")}function AK(Z){let $=Z;for(let J=0;J<6;J++){if(mN(AZ($,"VERSION"))&&mN(AZ($,"autonomy/run.sh")))return $;let X=pR($);if(X===$)break;$=X}return AZ(Z,"..","..","..")}function P0(){return process.env.LOKI_DIR??AZ(process.cwd(),".loki")}function B2(){return AZ(tb0(),".loki")}var yi,F0;var n0=S(()=>{yi=pR(rb0(import.meta.url));F0=eb0()});import{readFileSync as Zy0}from"fs";import{resolve as $y0,dirname as Jy0}from"path";import{fileURLToPath as Xy0}from"url";function q6(){if(D1!==null)return D1;let Z="11.31.1";if(typeof Z==="string"&&Z.length>0)return D1=Z,D1;try{let $=Jy0(Xy0(import.meta.url)),J=AK($);D1=Zy0($y0(J,"VERSION"),"utf-8").trim()}catch{D1="unknown"}return D1}var D1=null;var LZ=S(()=>{n0()});import{mkdirSync as dR,readFileSync as uN,realpathSync as Yy0,statSync as Qy0,writeFileSync as lR}from"fs";import{dirname as XY,join as cR,resolve as hi}from"path";function Gy0(){return hi(B2(),"cache","update-check.json")}function jZ(Z){let $=/^(\d+)\.(\d+)\.(\d+)$/.exec(Z.trim());if(!$)return null;return[Number($[1]),Number($[2]),Number($[3])]}function LK(Z,$){let J=jZ(Z),X=jZ($);if(!J||!X)return!1;let[Y,Q,z]=J,[W,K,V]=X;if(Y!==W)return Y>W;if(Q!==K)return Q>K;return z>V}function fi(){let Z=(process.env.LOKI_NO_UPDATE_CHECK??"").trim().toLowerCase();if(Z==="1"||Z==="true"||Z==="yes")return!0;if(process.env.CI)return!0;if(!process.stdout.isTTY)return!0;return!1}function gi(Z){try{let $=uN(Z,"utf-8"),J=JSON.parse($);if(typeof J.checkedAt==="number"&&typeof J.latest==="string"&&jZ(J.latest)!==null)return{checkedAt:J.checkedAt,latest:J.latest}}catch{}return null}function Hy0(Z){try{let $=JSON.parse(uN(Z,"utf-8")).failedAt;if(typeof $==="number")return $}catch{}return null}function qy0(Z,$){let J=gi(Z);try{dR(XY(Z),{recursive:!0}),lR(Z,JSON.stringify({...J??{},failedAt:$}),"utf-8")}catch{}}function Uy0(Z,$){try{dR(XY(Z),{recursive:!0}),lR(Z,JSON.stringify($),"utf-8")}catch{}}async function By0(){try{let Z=await fetch(zy0,{signal:AbortSignal.timeout(Ky0),headers:{accept:"application/json"}});if(!Z.ok)return null;let $=await Z.json();if(typeof $.version==="string"&&jZ($.version)!==null)return $.version}catch{}return null}async function Ny0(Z=Date.now(),$=By0,J=Gy0()){let X=gi(J);if(X&&Z-X.checkedAt<Wy0)return X.latest;let Y=Hy0(J);if(Y!==null&&Z-Y>=0&&Z-Y<Vy0)return null;let Q=await $();if(Q===null)return qy0(J,Z),null;return Uy0(J,{checkedAt:Z,latest:Q}),Q}function My0(Z,$=process.env){if(jZ(Z)===null)return null;let X=$.PATH;if(!X)return null;let Y=new Set;for(let Q of X.split(":")){if(!Q)continue;let z=cR(Q,"loki"),W;try{Qy0(z),W=Yy0(z)}catch{continue}if(Y.has(W))continue;Y.add(W);try{let K=cR(XY(XY(W)),"package.json"),V=JSON.parse(uN(K,"utf8")).version;if(typeof V!=="string")continue;if(LK(V,Z))return{path:z,version:V}}catch{continue}}return null}function Oy0(){return hi(B2(),"cache","last-seen-version.json")}function Ay0(Z,$={}){try{if(fi())return!1;if(jZ(Z)===null)return!1;let J=$.seenFile??Oy0(),X=null;try{let Q=JSON.parse(uN(J,"utf-8")).version;if(typeof Q==="string"&&jZ(Q)!==null)X=Q}catch{}if(X!==null&&!LK(Z,X))return!1;try{dR(XY(J),{recursive:!0}),lR(J,JSON.stringify({version:Z}),"utf-8")}catch{return!1}if(X===null)return!1;return($.write??((Q)=>process.stderr.write(Q)))(`Loki Mode upgraded ${X} -> ${Z}. What is new: https://github.com/asklokesh/loki-mode/releases/tag/v${Z}
2
+ var pb0=Object.create;var{getPrototypeOf:cb0,defineProperty:OK,getOwnPropertyNames:xi,getOwnPropertyDescriptor:db0}=Object,Si=Object.prototype.hasOwnProperty;function bi(Z){return this[Z]}var lb0,ab0,ib0=(Z,$,J)=>{var X=Z!=null&&typeof Z==="object";if(X){var Y=$?lb0??=new WeakMap:ab0??=new WeakMap,Q=Y.get(Z);if(Q)return Q}J=Z!=null?pb0(cb0(Z)):{};let z=$||!Z||!Z.__esModule?OK(J,"default",{value:Z,enumerable:!0}):J;for(let W of xi(Z))if(!Si.call(z,W))OK(z,W,{get:bi.bind(Z,W),enumerable:!0});if(X)Y.set(Z,z);return z},ob0=(Z)=>{var $=(ki??=new WeakMap).get(Z),J;if($)return $;if($=OK({},"__esModule",{value:!0}),Z&&typeof Z==="object"||typeof Z==="function"){for(var X of xi(Z))if(!Si.call($,X))OK($,X,{get:bi.bind(Z,X),enumerable:!(J=db0(Z,X))||J.enumerable})}return ki.set(Z,$),$},ki,gN=(Z,$)=>()=>($||Z(($={exports:{}}).exports,$),$.exports);var nb0=(Z)=>Z;function sb0(Z,$){this[Z]=nb0.bind(null,$)}var A0=(Z,$)=>{for(var J in $)OK(Z,J,{get:$[J],enumerable:!0,configurable:!0,set:sb0.bind($,J)})};var S=(Z,$)=>()=>(Z&&($=Z(Z=0)),$);var s9=import.meta.require;var vi={};A0(vi,{lokiDir:()=>P0,homeLokiDir:()=>B2,findRepoRootForVersion:()=>AK,REPO_ROOT:()=>F0});import{resolve as AZ,dirname as pR}from"path";import{fileURLToPath as rb0}from"url";import{existsSync as mN}from"fs";import{homedir as tb0}from"os";function eb0(){let Z=yi;for(let $=0;$<6;$++){if(mN(AZ(Z,"VERSION"))&&mN(AZ(Z,"autonomy/run.sh")))return Z;let J=pR(Z);if(J===Z)break;Z=J}return AZ(yi,"..","..","..")}function AK(Z){let $=Z;for(let J=0;J<6;J++){if(mN(AZ($,"VERSION"))&&mN(AZ($,"autonomy/run.sh")))return $;let X=pR($);if(X===$)break;$=X}return AZ(Z,"..","..","..")}function P0(){return process.env.LOKI_DIR??AZ(process.cwd(),".loki")}function B2(){return AZ(tb0(),".loki")}var yi,F0;var n0=S(()=>{yi=pR(rb0(import.meta.url));F0=eb0()});import{readFileSync as Zy0}from"fs";import{resolve as $y0,dirname as Jy0}from"path";import{fileURLToPath as Xy0}from"url";function q6(){if(D1!==null)return D1;let Z="11.32.0";if(typeof Z==="string"&&Z.length>0)return D1=Z,D1;try{let $=Jy0(Xy0(import.meta.url)),J=AK($);D1=Zy0($y0(J,"VERSION"),"utf-8").trim()}catch{D1="unknown"}return D1}var D1=null;var LZ=S(()=>{n0()});import{mkdirSync as dR,readFileSync as uN,realpathSync as Yy0,statSync as Qy0,writeFileSync as lR}from"fs";import{dirname as XY,join as cR,resolve as hi}from"path";function Gy0(){return hi(B2(),"cache","update-check.json")}function jZ(Z){let $=/^(\d+)\.(\d+)\.(\d+)$/.exec(Z.trim());if(!$)return null;return[Number($[1]),Number($[2]),Number($[3])]}function LK(Z,$){let J=jZ(Z),X=jZ($);if(!J||!X)return!1;let[Y,Q,z]=J,[W,K,V]=X;if(Y!==W)return Y>W;if(Q!==K)return Q>K;return z>V}function fi(){let Z=(process.env.LOKI_NO_UPDATE_CHECK??"").trim().toLowerCase();if(Z==="1"||Z==="true"||Z==="yes")return!0;if(process.env.CI)return!0;if(!process.stdout.isTTY)return!0;return!1}function gi(Z){try{let $=uN(Z,"utf-8"),J=JSON.parse($);if(typeof J.checkedAt==="number"&&typeof J.latest==="string"&&jZ(J.latest)!==null)return{checkedAt:J.checkedAt,latest:J.latest}}catch{}return null}function Hy0(Z){try{let $=JSON.parse(uN(Z,"utf-8")).failedAt;if(typeof $==="number")return $}catch{}return null}function qy0(Z,$){let J=gi(Z);try{dR(XY(Z),{recursive:!0}),lR(Z,JSON.stringify({...J??{},failedAt:$}),"utf-8")}catch{}}function Uy0(Z,$){try{dR(XY(Z),{recursive:!0}),lR(Z,JSON.stringify($),"utf-8")}catch{}}async function By0(){try{let Z=await fetch(zy0,{signal:AbortSignal.timeout(Ky0),headers:{accept:"application/json"}});if(!Z.ok)return null;let $=await Z.json();if(typeof $.version==="string"&&jZ($.version)!==null)return $.version}catch{}return null}async function Ny0(Z=Date.now(),$=By0,J=Gy0()){let X=gi(J);if(X&&Z-X.checkedAt<Wy0)return X.latest;let Y=Hy0(J);if(Y!==null&&Z-Y>=0&&Z-Y<Vy0)return null;let Q=await $();if(Q===null)return qy0(J,Z),null;return Uy0(J,{checkedAt:Z,latest:Q}),Q}function My0(Z,$=process.env){if(jZ(Z)===null)return null;let X=$.PATH;if(!X)return null;let Y=new Set;for(let Q of X.split(":")){if(!Q)continue;let z=cR(Q,"loki"),W;try{Qy0(z),W=Yy0(z)}catch{continue}if(Y.has(W))continue;Y.add(W);try{let K=cR(XY(XY(W)),"package.json"),V=JSON.parse(uN(K,"utf8")).version;if(typeof V!=="string")continue;if(LK(V,Z))return{path:z,version:V}}catch{continue}}return null}function Oy0(){return hi(B2(),"cache","last-seen-version.json")}function Ay0(Z,$={}){try{if(fi())return!1;if(jZ(Z)===null)return!1;let J=$.seenFile??Oy0(),X=null;try{let Q=JSON.parse(uN(J,"utf-8")).version;if(typeof Q==="string"&&jZ(Q)!==null)X=Q}catch{}if(X!==null&&!LK(Z,X))return!1;try{dR(XY(J),{recursive:!0}),lR(J,JSON.stringify({version:Z}),"utf-8")}catch{return!1}if(X===null)return!1;return($.write??((Q)=>process.stderr.write(Q)))(`Loki Mode upgraded ${X} -> ${Z}. What is new: https://github.com/asklokesh/loki-mode/releases/tag/v${Z}
3
3
  `),!0}catch{return!1}}function Ly0(Z){let $=Z.replace(/\\/g,"/");if($.includes("/Cellar/"))return"brew upgrade loki-mode";if(/\/pnpm\//.test($)||$.includes("/.pnpm/"))return"pnpm add -g loki-mode (or npm i -g loki-mode)";if($.includes("/yarn/global/")||$.includes("/.yarn/"))return"yarn global add loki-mode (or npm i -g loki-mode)";if($.includes("/.bun/"))return"bun install -g loki-mode (or npm i -g loki-mode)";if($.includes("/lib/node_modules/"))return"npm i -g loki-mode (or bun install -g loki-mode)";if($.includes("/homebrew/"))return"brew upgrade loki-mode";return"bun install -g loki-mode (or npm i -g loki-mode)"}async function mi(Z,$={}){try{if(fi())return;if(jZ(Z)===null)return;let J=$.write??((Q)=>process.stderr.write(Q));Ay0(Z,{write:J,...$.cacheFile?{seenFile:cR(XY($.cacheFile),"last-seen-version.json")}:{}});let X=My0(Z,$.env??process.env);if(X!==null){J(`Loki Mode ${X.version} is installed but not the one running (you are running ${Z}).
4
4
  Another loki earlier on PATH is winning. Newer copy: ${X.path}
5
5
  Run \`which -a loki\` to see the order, then remove or re-point the earlier entry.
@@ -3238,4 +3238,4 @@ Unknown commands exit 2 with a suggestion; run 'loki help' for the full list.
3238
3238
  `);{let{suggestCommand:X}=await Promise.resolve().then(() => (t4(),Ao)),Y=$===void 0?void 0:X($);if(Y)process.stderr.write(`Did you mean 'loki ${Y}'?
3239
3239
  `)}return process.stderr.write(yb0),2}}uF0();var vb0=()=>globalThis[Symbol.for("loki.ownsStopSignals")]===!0;process.on("SIGINT",()=>{if(!vb0())process.exit(130)});process.on("SIGTERM",()=>{if(!vb0())process.exit(143)});var Jq9=await $q9(Bun.argv.slice(2));process.exit(Jq9);
3240
3240
 
3241
- //# debugId=248392EECAF364271812EE3846FE7E27
3241
+ //# debugId=097F6B5069B7BD2F2BE5278EF34F11FC
package/mcp/__init__.py CHANGED
@@ -75,4 +75,4 @@ try:
75
75
  except ImportError:
76
76
  __all__ = ['mcp']
77
77
 
78
- __version__ = '11.31.1'
78
+ __version__ = '11.32.0'
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": "11.31.1",
4
+ "version": "11.32.0",
5
5
  "description": "An autonomous software factory that knows what it is supposed to deliver, and proves it did.",
6
6
  "keywords": [
7
7
  "agent",
@@ -160,5 +160,5 @@
160
160
  "dependencies": {
161
161
  "@resvg/resvg-wasm": "^2.6.2"
162
162
  },
163
- "gitHead": "2d7d6a84ca9f8ff8b403530374792bcaad9dd592"
163
+ "gitHead": "137a1518a4636d9fa33a622174d95326ff3874a3"
164
164
  }
@@ -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": "11.31.1",
5
+ "version": "11.32.0",
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",
@@ -1,4 +1,4 @@
1
- import{c as L,r as x,j as e,w as re,Z as ne,m as le,h as ie,p as P,F as ye,U as I,X as ce,L as de,x as oe,M as Ae,T as Z,S as we,l as xe,k as me,y as Ce,E as Fe,a0 as Se,a as U,a2 as he}from"./index-sCyA5kT1.js";import{B as C}from"./Button-BehU-7tC.js";import{T as $e}from"./trending-up-CL4ZFYFb.js";import{C as z}from"./clock-DxxclmlE.js";import{A as Be,U as ue,M as De}from"./Avatar-xQ1gSPVB.js";import{A as J}from"./activity-JwnQAJzU.js";import{R as pe}from"./refresh-cw-D54nCHRH.js";import{G as ze}from"./globe--nSuGAqH.js";import{C as Le}from"./chevron-left-BJ2VldHk.js";import{C as je}from"./check-DNNlHviH.js";import{D as Y}from"./dollar-sign-qUbMFHU4.js";import{U as Te}from"./user-DuS3X15u.js";import{D as Me}from"./database-Dvy7Bbcf.js";import{L as Ue}from"./lock-D1UH6v-O.js";import{S as Re}from"./scroll-text-D_5t4SMl.js";import{S as Ie}from"./shield-alert-o3-54oNJ.js";/**
1
+ import{c as L,r as x,j as e,w as re,Z as ne,m as le,h as ie,p as P,F as ye,U as I,X as ce,L as de,x as oe,M as Ae,T as Z,S as we,l as xe,k as me,y as Ce,E as Fe,a0 as Se,a as U,a2 as he}from"./index-D_gr9DjQ.js";import{B as C}from"./Button-BKE7kijL.js";import{T as $e}from"./trending-up-B1v8JIc8.js";import{C as z}from"./clock-DphpX3yF.js";import{A as Be,U as ue,M as De}from"./Avatar-P8CySSFH.js";import{A as J}from"./activity-BPaXjKEf.js";import{R as pe}from"./refresh-cw-D1La5GDI.js";import{G as ze}from"./globe-CvMW8-NT.js";import{C as Le}from"./chevron-left-MJKoMYpu.js";import{C as je}from"./check-BBDkYdTy.js";import{D as Y}from"./dollar-sign-8w0iMGof.js";import{U as Te}from"./user-DQDpD4Cu.js";import{D as Me}from"./database-D1oOHoQg.js";import{L as Ue}from"./lock-AWRWK-Aa.js";import{S as Re}from"./scroll-text-BVziFaFm.js";import{S as Ie}from"./shield-alert-DgWjn5rX.js";/**
2
2
  * @license lucide-react v0.577.0 - ISC
3
3
  *
4
4
  * This source code is licensed under the ISC license.
@@ -1,4 +1,4 @@
1
- import{c as l,j as s}from"./index-sCyA5kT1.js";/**
1
+ import{c as l,j as s}from"./index-D_gr9DjQ.js";/**
2
2
  * @license lucide-react v0.577.0 - ISC
3
3
  *
4
4
  * This source code is licensed under the ISC license.
@@ -1 +1 @@
1
- import{j as e}from"./index-sCyA5kT1.js";import{C as r}from"./clock-DxxclmlE.js";import{C as a}from"./circle-alert-0P4HhIzB.js";import{C as l}from"./check-DNNlHviH.js";const i={completed:"bg-[#1FC5A8]/10 text-[#1FC5A8]",running:"bg-[#553DE9]/10 text-[#553DE9]",failed:"bg-[#C45B5B]/10 text-[#C45B5B]",started:"bg-[#D4A03C]/10 text-[#D4A03C]",empty:"bg-[#F8F4F0] text-[#6B6960]",version:"bg-[#553DE9]/10 text-[#553DE9]"};function o({status:t}){switch(t){case"completed":return e.jsx(l,{size:12});case"running":return e.jsxs("span",{className:"relative flex h-2 w-2",children:[e.jsx("span",{className:"animate-ping motion-reduce:animate-none absolute inline-flex h-full w-full rounded-full bg-current opacity-75"}),e.jsx("span",{className:"relative inline-flex rounded-full h-2 w-2 bg-current"})]});case"failed":return e.jsx(a,{size:12});case"started":return e.jsx(r,{size:12});default:return null}}function f({status:t,children:n,className:s=""}){return e.jsxs("span",{className:["inline-flex items-center gap-1 rounded-btn px-2.5 py-0.5 text-xs font-semibold",i[t],s].filter(Boolean).join(" "),children:[e.jsx(o,{status:t}),n]})}export{f as B};
1
+ import{j as e}from"./index-D_gr9DjQ.js";import{C as r}from"./clock-DphpX3yF.js";import{C as a}from"./circle-alert-Bk6g0LfO.js";import{C as l}from"./check-BBDkYdTy.js";const i={completed:"bg-[#1FC5A8]/10 text-[#1FC5A8]",running:"bg-[#553DE9]/10 text-[#553DE9]",failed:"bg-[#C45B5B]/10 text-[#C45B5B]",started:"bg-[#D4A03C]/10 text-[#D4A03C]",empty:"bg-[#F8F4F0] text-[#6B6960]",version:"bg-[#553DE9]/10 text-[#553DE9]"};function o({status:t}){switch(t){case"completed":return e.jsx(l,{size:12});case"running":return e.jsxs("span",{className:"relative flex h-2 w-2",children:[e.jsx("span",{className:"animate-ping motion-reduce:animate-none absolute inline-flex h-full w-full rounded-full bg-current opacity-75"}),e.jsx("span",{className:"relative inline-flex rounded-full h-2 w-2 bg-current"})]});case"failed":return e.jsx(a,{size:12});case"started":return e.jsx(r,{size:12});default:return null}}function f({status:t,children:n,className:s=""}){return e.jsxs("span",{className:["inline-flex items-center gap-1 rounded-btn px-2.5 py-0.5 text-xs font-semibold",i[t],s].filter(Boolean).join(" "),children:[e.jsx(o,{status:t}),n]})}export{f as B};
@@ -1 +1 @@
1
- import{r as p,j as t}from"./index-sCyA5kT1.js";const m={primary:"bg-[#553DE9] text-white hover:bg-[#4432c4] shadow-button rounded-btn",secondary:"border border-[#553DE9] text-[#553DE9] hover:bg-[#E8E4FD] bg-transparent rounded-btn",ghost:"text-[#36342E] hover:bg-[#F8F4F0] rounded-btn",danger:"bg-[#C45B5B]/10 text-[#C45B5B] border border-[#C45B5B]/20 hover:bg-[#C45B5B]/20 rounded-btn"},b={sm:"px-3 py-1.5 text-xs",md:"px-4 py-2 text-sm",lg:"px-6 py-3 text-base"},u={sm:14,md:16,lg:18};function h({size:e}){return t.jsxs("svg",{className:"animate-spin",width:e,height:e,viewBox:"0 0 24 24",fill:"none",children:[t.jsx("circle",{className:"opacity-25",cx:"12",cy:"12",r:"10",stroke:"currentColor",strokeWidth:"4"}),t.jsx("path",{className:"opacity-75",fill:"currentColor",d:"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"})]})}const B=p.forwardRef(({variant:e="primary",size:o="md",icon:n,iconRight:i,loading:r=!1,disabled:a,className:c="",children:l,...x},d)=>{const s=u[o];return t.jsxs("button",{ref:d,disabled:a||r,className:["inline-flex items-center justify-center gap-2 font-medium transition-colors",m[e],b[o],(a||r)&&"opacity-60 cursor-not-allowed",c].filter(Boolean).join(" "),...x,children:[r?t.jsx(h,{size:s}):n?t.jsx(n,{size:s}):null,l,i&&!r&&t.jsx(i,{size:s})]})});B.displayName="Button";export{B};
1
+ import{r as p,j as t}from"./index-D_gr9DjQ.js";const m={primary:"bg-[#553DE9] text-white hover:bg-[#4432c4] shadow-button rounded-btn",secondary:"border border-[#553DE9] text-[#553DE9] hover:bg-[#E8E4FD] bg-transparent rounded-btn",ghost:"text-[#36342E] hover:bg-[#F8F4F0] rounded-btn",danger:"bg-[#C45B5B]/10 text-[#C45B5B] border border-[#C45B5B]/20 hover:bg-[#C45B5B]/20 rounded-btn"},b={sm:"px-3 py-1.5 text-xs",md:"px-4 py-2 text-sm",lg:"px-6 py-3 text-base"},u={sm:14,md:16,lg:18};function h({size:e}){return t.jsxs("svg",{className:"animate-spin",width:e,height:e,viewBox:"0 0 24 24",fill:"none",children:[t.jsx("circle",{className:"opacity-25",cx:"12",cy:"12",r:"10",stroke:"currentColor",strokeWidth:"4"}),t.jsx("path",{className:"opacity-75",fill:"currentColor",d:"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"})]})}const B=p.forwardRef(({variant:e="primary",size:o="md",icon:n,iconRight:i,loading:r=!1,disabled:a,className:c="",children:l,...x},d)=>{const s=u[o];return t.jsxs("button",{ref:d,disabled:a||r,className:["inline-flex items-center justify-center gap-2 font-medium transition-colors",m[e],b[o],(a||r)&&"opacity-60 cursor-not-allowed",c].filter(Boolean).join(" "),...x,children:[r?t.jsx(h,{size:s}):n?t.jsx(n,{size:s}):null,l,i&&!r&&t.jsx(i,{size:s})]})});B.displayName="Button";export{B};
@@ -1,4 +1,4 @@
1
- import{c as $,r as l,j as e,G as we,Q as Se,F as Ce,h as Re,T as U,Z as Ee,a as S,V as Me,S as $e,Y as Te,X as Pe,P as ie,i as Ae,g as ze,C as Ie,l as Le,N as I,J as _e,K as De,O as Fe,u as Oe}from"./index-sCyA5kT1.js";import{P as K,S as He,E as Be}from"./ErrorBoundary-P4CkwBs3.js";import{C as qe,F as Ge,M as Ue,H as Ke,a as We,G as Ve,T as Qe}from"./thumbs-up-Bj5vm_ge.js";import{C as oe}from"./check-DNNlHviH.js";import{C as Ye}from"./clock-DxxclmlE.js";import{D as Xe}from"./dollar-sign-qUbMFHU4.js";import{U as Ze}from"./upload-Dem0HZRa.js";import{L as V}from"./loader-circle-ZB7xFqz2.js";/**
1
+ import{c as $,r as l,j as e,G as we,Q as Se,F as Ce,h as Re,T as U,Z as Ee,a as S,V as Me,S as $e,Y as Te,X as Pe,P as ie,i as Ae,g as ze,C as Ie,l as Le,N as I,J as _e,K as De,O as Fe,u as Oe}from"./index-D_gr9DjQ.js";import{P as K,S as He,E as Be}from"./ErrorBoundary-oVL59njg.js";import{C as qe,F as Ge,M as Ue,H as Ke,a as We,G as Ve,T as Qe}from"./thumbs-up-B2v87stN.js";import{C as oe}from"./check-BBDkYdTy.js";import{C as Ye}from"./clock-DphpX3yF.js";import{D as Xe}from"./dollar-sign-8w0iMGof.js";import{U as Ze}from"./upload-iTjkpxy-.js";import{L as V}from"./loader-circle-D1LGSlkG.js";/**
2
2
  * @license lucide-react v0.577.0 - ISC
3
3
  *
4
4
  * This source code is licensed under the ISC license.
@@ -1 +1 @@
1
- import{r as o,j as e}from"./index-sCyA5kT1.js";function f({value:r,prefix:t="",suffix:s=""}){const[u,n]=o.useState(0),c=o.useRef(0);return o.useEffect(()=>{const a=c.current,h=r-a;if(h===0)return;const i=600,d=performance.now();function l(b){const v=b-d,m=Math.min(v/i,1),N=1-Math.pow(1-m,3),p=Math.round(a+h*N);n(p),c.current=p,m<1&&requestAnimationFrame(l)}requestAnimationFrame(l)},[r]),e.jsxs("span",{className:"tabular-nums",children:[t,u.toLocaleString(),s]})}function g(){const[r,t]=o.useState(5),[s,u]=o.useState(30),[n,c]=o.useState(75),a=.4,i=Math.round(r*s*a*4.33),d=Math.round(i*n);return e.jsxs("div",{className:"bg-white border border-[#ECEAE3] rounded-xl p-6 shadow-sm",children:[e.jsx("h3",{className:"text-lg font-bold text-[#36342E] mb-1",children:"ROI Calculator"}),e.jsx("p",{className:"text-sm text-[#6B6960] mb-6",children:"Estimate how much your team could save with autonomous development."}),e.jsxs("div",{className:"space-y-5",children:[e.jsxs("div",{children:[e.jsxs("div",{className:"flex items-center justify-between mb-2",children:[e.jsx("label",{className:"text-sm font-medium text-[#36342E]",children:"Team size"}),e.jsxs("span",{className:"text-sm font-semibold text-[#553DE9] tabular-nums",children:[r," developers"]})]}),e.jsx("input",{type:"range",min:1,max:50,value:r,onChange:l=>t(Number(l.target.value)),className:"w-full h-2 bg-[#ECEAE3] rounded-full appearance-none cursor-pointer accent-[#553DE9]"}),e.jsxs("div",{className:"flex justify-between text-xs text-[#939084] mt-1",children:[e.jsx("span",{children:"1"}),e.jsx("span",{children:"50"})]})]}),e.jsxs("div",{children:[e.jsxs("div",{className:"flex items-center justify-between mb-2",children:[e.jsx("label",{className:"text-sm font-medium text-[#36342E]",children:"Hours coding per week"}),e.jsxs("span",{className:"text-sm font-semibold text-[#553DE9] tabular-nums",children:[s,"h"]})]}),e.jsx("input",{type:"range",min:5,max:60,value:s,onChange:l=>u(Number(l.target.value)),className:"w-full h-2 bg-[#ECEAE3] rounded-full appearance-none cursor-pointer accent-[#553DE9]"}),e.jsxs("div",{className:"flex justify-between text-xs text-[#939084] mt-1",children:[e.jsx("span",{children:"5h"}),e.jsx("span",{children:"60h"})]})]}),e.jsxs("div",{children:[e.jsxs("div",{className:"flex items-center justify-between mb-2",children:[e.jsx("label",{className:"text-sm font-medium text-[#36342E]",children:"Average hourly rate"}),e.jsxs("span",{className:"text-sm font-semibold text-[#553DE9] tabular-nums",children:["$",n,"/hr"]})]}),e.jsx("input",{type:"range",min:25,max:250,step:5,value:n,onChange:l=>c(Number(l.target.value)),className:"w-full h-2 bg-[#ECEAE3] rounded-full appearance-none cursor-pointer accent-[#553DE9]"}),e.jsxs("div",{className:"flex justify-between text-xs text-[#939084] mt-1",children:[e.jsx("span",{children:"$25"}),e.jsx("span",{children:"$250"})]})]})]}),e.jsxs("div",{className:"mt-6 pt-6 border-t border-[#ECEAE3]",children:[e.jsxs("div",{className:"grid grid-cols-2 gap-4 text-center",children:[e.jsxs("div",{children:[e.jsx("div",{className:"text-2xl font-bold text-[#553DE9]",children:e.jsx(f,{value:i,suffix:"h"})}),e.jsx("div",{className:"text-xs text-[#6B6960] mt-1",children:"Est. hours saved / month"})]}),e.jsxs("div",{children:[e.jsx("div",{className:"text-2xl font-bold text-[#1FC5A8]",children:e.jsx(f,{value:d,prefix:"$"})}),e.jsx("div",{className:"text-xs text-[#6B6960] mt-1",children:"Est. cost saved / month"})]})]}),e.jsxs("div",{className:"mt-4 p-3 rounded-lg bg-[#553DE9]/5 border border-[#553DE9]/10",children:[e.jsxs("p",{className:"text-sm text-[#36342E] text-center",children:["If Loki Mode saved ",Math.round(a*100),"% of coding time, your team could save"," ",e.jsxs("span",{className:"font-bold text-[#553DE9]",children:[i.toLocaleString()," hours"]})," ","and"," ",e.jsxs("span",{className:"font-bold text-[#1FC5A8]",children:["$",d.toLocaleString()]})," ","per month."]}),e.jsxs("p",{className:"mt-2 text-xs text-[#6B6960] text-center",children:["The ",Math.round(a*100),"% is an assumption, not a measurement. Model and API spend is not included."]})]})]})]})}const j=[{feature:"Claude (Opus/Sonnet/Haiku)",category:"AI Models",lokiMode:!0,boltNew:!1,replit:!1,lovable:!0},{feature:"GPT-4o / Codex",category:"AI Models",lokiMode:!0,boltNew:!0,replit:!0,lovable:!1},{feature:"5-provider auto-failover",category:"AI Models",lokiMode:!0,boltNew:!1,replit:!1,lovable:!1},{feature:"8 automated quality gates",category:"Quality",lokiMode:!0,boltNew:!1,replit:!1,lovable:!1},{feature:"Blind 3-reviewer code review",category:"Quality",lokiMode:!0,boltNew:!1,replit:!1,lovable:!1},{feature:"Anti-sycophancy checks",category:"Quality",lokiMode:!0,boltNew:!1,replit:!1,lovable:!1},{feature:"Automated test generation",category:"Quality",lokiMode:!0,boltNew:!1,replit:"Partial",lovable:!1},{feature:"Docker container generation",category:"Deployment",lokiMode:!0,boltNew:!1,replit:!0,lovable:!1},{feature:"CI/CD pipeline generation",category:"Deployment",lokiMode:!0,boltNew:!1,replit:!1,lovable:!1},{feature:"One-click cloud deploy",category:"Deployment",lokiMode:"Planned",boltNew:!0,replit:!0,lovable:!0},{feature:"Self-hosted (your keys)",category:"Cost",lokiMode:!0,boltNew:!1,replit:!1,lovable:!1},{feature:"Free tier available",category:"Cost",lokiMode:"Source-available (BUSL-1.1)",boltNew:!0,replit:!0,lovable:!0},{feature:"No vendor lock-in",category:"Cost",lokiMode:!0,boltNew:!1,replit:!1,lovable:!1},{feature:"SSO / OIDC authentication",category:"Enterprise",lokiMode:!0,boltNew:!1,replit:!0,lovable:!1},{feature:"RBAC access control",category:"Enterprise",lokiMode:!0,boltNew:!1,replit:!0,lovable:!1},{feature:"Audit logging",category:"Enterprise",lokiMode:!0,boltNew:!1,replit:!0,lovable:!1},{feature:"Air-gapped deployment",category:"Enterprise",lokiMode:!0,boltNew:!1,replit:!1,lovable:!1}];function y(){return e.jsx("svg",{width:"18",height:"18",viewBox:"0 0 24 24",fill:"none",className:"text-[#1FC5A8]",children:e.jsx("path",{d:"M20 6L9 17l-5-5",stroke:"currentColor",strokeWidth:"2.5",strokeLinecap:"round",strokeLinejoin:"round"})})}function w(){return e.jsx("svg",{width:"18",height:"18",viewBox:"0 0 24 24",fill:"none",className:"text-[#C45B5B]/40",children:e.jsx("path",{d:"M18 6L6 18M6 6l12 12",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round"})})}function x({value:r}){return typeof r=="string"?e.jsx("span",{className:"text-xs text-[#6B6960] font-medium",children:r}):r?e.jsx(y,{}):e.jsx(w,{})}const k=[{title:"Full Autonomy",description:"Describe what you want. Loki Mode handles planning, coding, testing, and verification automatically.",icon:e.jsx("svg",{width:"24",height:"24",viewBox:"0 0 24 24",fill:"none",className:"text-[#553DE9]",children:e.jsx("path",{d:"M13 2L3 14h9l-1 8 10-12h-9l1-8z",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round"})})},{title:"Production Quality",description:"8 quality gates, blind code review, and anti-sycophancy checks ensure code that actually works.",icon:e.jsx("svg",{width:"24",height:"24",viewBox:"0 0 24 24",fill:"none",className:"text-[#1FC5A8]",children:e.jsx("path",{d:"M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round"})})},{title:"Your Infrastructure",description:"Self-hosted, source-available (BUSL-1.1), no data leaves your network. Use your own API keys across 5 providers.",icon:e.jsxs("svg",{width:"24",height:"24",viewBox:"0 0 24 24",fill:"none",className:"text-[#2F71E3]",children:[e.jsx("rect",{x:"2",y:"2",width:"20",height:"8",rx:"2",stroke:"currentColor",strokeWidth:"2"}),e.jsx("rect",{x:"2",y:"14",width:"20",height:"8",rx:"2",stroke:"currentColor",strokeWidth:"2"}),e.jsx("circle",{cx:"6",cy:"6",r:"1",fill:"currentColor"}),e.jsx("circle",{cx:"6",cy:"18",r:"1",fill:"currentColor"})]})},{title:"Built for Teams",description:"Enterprise auth, RBAC, audit logs, and parallel workflows for teams of any size.",icon:e.jsxs("svg",{width:"24",height:"24",viewBox:"0 0 24 24",fill:"none",className:"text-[#E93D82]",children:[e.jsx("path",{d:"M17 21v-2a4 4 0 00-4-4H5a4 4 0 00-4 4v2",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round"}),e.jsx("circle",{cx:"9",cy:"7",r:"4",stroke:"currentColor",strokeWidth:"2"}),e.jsx("path",{d:"M23 21v-2a4 4 0 00-3-3.87M16 3.13a4 4 0 010 7.75",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round"})]})}];function C(){const r=[...new Set(j.map(t=>t.category))];return e.jsx("div",{className:"min-h-screen bg-[#FAF9F6]",children:e.jsxs("div",{className:"max-w-6xl mx-auto px-6 py-8",children:[e.jsxs("div",{className:"mb-8",children:[e.jsx("h1",{className:"text-2xl font-bold text-[#36342E]",children:"Compare"}),e.jsx("p",{className:"text-[#6B6960] mt-1",children:"See how Loki Mode stacks up against other AI development platforms."})]}),e.jsx("div",{className:"bg-white border border-[#ECEAE3] rounded-xl overflow-hidden shadow-sm",children:e.jsx("div",{className:"overflow-x-auto",children:e.jsxs("table",{className:"w-full text-sm",children:[e.jsx("thead",{children:e.jsxs("tr",{className:"border-b border-[#ECEAE3]",children:[e.jsx("th",{className:"text-left px-4 py-3 font-medium text-[#6B6960] w-[240px]",children:"Feature"}),e.jsx("th",{className:"text-center px-4 py-3 font-bold text-[#553DE9] bg-[#553DE9]/5 w-[130px]",children:"Loki Mode"}),e.jsx("th",{className:"text-center px-4 py-3 font-medium text-[#6B6960] w-[130px]",children:"bolt.new"}),e.jsx("th",{className:"text-center px-4 py-3 font-medium text-[#6B6960] w-[130px]",children:"Replit"}),e.jsx("th",{className:"text-center px-4 py-3 font-medium text-[#6B6960] w-[130px]",children:"Lovable"})]})}),e.jsx("tbody",{children:r.map(t=>e.jsxs(e.Fragment,{children:[e.jsx("tr",{className:"bg-[#FAF9F6]",children:e.jsx("td",{colSpan:5,className:"px-4 py-2 text-xs font-bold text-[#939084] uppercase tracking-wider",children:t})},`cat-${t}`),j.filter(s=>s.category===t).map(s=>e.jsxs("tr",{className:"border-b border-[#ECEAE3] last:border-b-0",children:[e.jsx("td",{className:"px-4 py-2.5 text-[#36342E]",children:s.feature}),e.jsx("td",{className:"px-4 py-2.5 text-center bg-[#553DE9]/5",children:e.jsx("div",{className:"flex justify-center",children:e.jsx(x,{value:s.lokiMode})})}),e.jsx("td",{className:"px-4 py-2.5 text-center",children:e.jsx("div",{className:"flex justify-center",children:e.jsx(x,{value:s.boltNew})})}),e.jsx("td",{className:"px-4 py-2.5 text-center",children:e.jsx("div",{className:"flex justify-center",children:e.jsx(x,{value:s.replit})})}),e.jsx("td",{className:"px-4 py-2.5 text-center",children:e.jsx("div",{className:"flex justify-center",children:e.jsx(x,{value:s.lovable})})})]},s.feature))]}))})]})})}),e.jsxs("div",{className:"mt-12",children:[e.jsx("h2",{className:"text-xl font-bold text-[#36342E] mb-6",children:"Why developers choose Loki Mode"}),e.jsx("div",{className:"grid grid-cols-1 md:grid-cols-2 gap-5",children:k.map(t=>e.jsxs("div",{className:"bg-white border border-[#ECEAE3] rounded-xl p-5 shadow-sm hover:shadow-md transition-shadow",children:[e.jsx("div",{className:"mb-3",children:t.icon}),e.jsx("h3",{className:"text-base font-bold text-[#36342E] mb-1",children:t.title}),e.jsx("p",{className:"text-sm text-[#6B6960]",children:t.description})]},t.title))})]}),e.jsx("div",{className:"mt-12 max-w-xl mx-auto",children:e.jsx(g,{})})]})})}export{C as default};
1
+ import{r as o,j as e}from"./index-D_gr9DjQ.js";function f({value:r,prefix:t="",suffix:s=""}){const[u,n]=o.useState(0),c=o.useRef(0);return o.useEffect(()=>{const a=c.current,h=r-a;if(h===0)return;const i=600,d=performance.now();function l(b){const v=b-d,m=Math.min(v/i,1),N=1-Math.pow(1-m,3),p=Math.round(a+h*N);n(p),c.current=p,m<1&&requestAnimationFrame(l)}requestAnimationFrame(l)},[r]),e.jsxs("span",{className:"tabular-nums",children:[t,u.toLocaleString(),s]})}function g(){const[r,t]=o.useState(5),[s,u]=o.useState(30),[n,c]=o.useState(75),a=.4,i=Math.round(r*s*a*4.33),d=Math.round(i*n);return e.jsxs("div",{className:"bg-white border border-[#ECEAE3] rounded-xl p-6 shadow-sm",children:[e.jsx("h3",{className:"text-lg font-bold text-[#36342E] mb-1",children:"ROI Calculator"}),e.jsx("p",{className:"text-sm text-[#6B6960] mb-6",children:"Estimate how much your team could save with autonomous development."}),e.jsxs("div",{className:"space-y-5",children:[e.jsxs("div",{children:[e.jsxs("div",{className:"flex items-center justify-between mb-2",children:[e.jsx("label",{className:"text-sm font-medium text-[#36342E]",children:"Team size"}),e.jsxs("span",{className:"text-sm font-semibold text-[#553DE9] tabular-nums",children:[r," developers"]})]}),e.jsx("input",{type:"range",min:1,max:50,value:r,onChange:l=>t(Number(l.target.value)),className:"w-full h-2 bg-[#ECEAE3] rounded-full appearance-none cursor-pointer accent-[#553DE9]"}),e.jsxs("div",{className:"flex justify-between text-xs text-[#939084] mt-1",children:[e.jsx("span",{children:"1"}),e.jsx("span",{children:"50"})]})]}),e.jsxs("div",{children:[e.jsxs("div",{className:"flex items-center justify-between mb-2",children:[e.jsx("label",{className:"text-sm font-medium text-[#36342E]",children:"Hours coding per week"}),e.jsxs("span",{className:"text-sm font-semibold text-[#553DE9] tabular-nums",children:[s,"h"]})]}),e.jsx("input",{type:"range",min:5,max:60,value:s,onChange:l=>u(Number(l.target.value)),className:"w-full h-2 bg-[#ECEAE3] rounded-full appearance-none cursor-pointer accent-[#553DE9]"}),e.jsxs("div",{className:"flex justify-between text-xs text-[#939084] mt-1",children:[e.jsx("span",{children:"5h"}),e.jsx("span",{children:"60h"})]})]}),e.jsxs("div",{children:[e.jsxs("div",{className:"flex items-center justify-between mb-2",children:[e.jsx("label",{className:"text-sm font-medium text-[#36342E]",children:"Average hourly rate"}),e.jsxs("span",{className:"text-sm font-semibold text-[#553DE9] tabular-nums",children:["$",n,"/hr"]})]}),e.jsx("input",{type:"range",min:25,max:250,step:5,value:n,onChange:l=>c(Number(l.target.value)),className:"w-full h-2 bg-[#ECEAE3] rounded-full appearance-none cursor-pointer accent-[#553DE9]"}),e.jsxs("div",{className:"flex justify-between text-xs text-[#939084] mt-1",children:[e.jsx("span",{children:"$25"}),e.jsx("span",{children:"$250"})]})]})]}),e.jsxs("div",{className:"mt-6 pt-6 border-t border-[#ECEAE3]",children:[e.jsxs("div",{className:"grid grid-cols-2 gap-4 text-center",children:[e.jsxs("div",{children:[e.jsx("div",{className:"text-2xl font-bold text-[#553DE9]",children:e.jsx(f,{value:i,suffix:"h"})}),e.jsx("div",{className:"text-xs text-[#6B6960] mt-1",children:"Est. hours saved / month"})]}),e.jsxs("div",{children:[e.jsx("div",{className:"text-2xl font-bold text-[#1FC5A8]",children:e.jsx(f,{value:d,prefix:"$"})}),e.jsx("div",{className:"text-xs text-[#6B6960] mt-1",children:"Est. cost saved / month"})]})]}),e.jsxs("div",{className:"mt-4 p-3 rounded-lg bg-[#553DE9]/5 border border-[#553DE9]/10",children:[e.jsxs("p",{className:"text-sm text-[#36342E] text-center",children:["If Loki Mode saved ",Math.round(a*100),"% of coding time, your team could save"," ",e.jsxs("span",{className:"font-bold text-[#553DE9]",children:[i.toLocaleString()," hours"]})," ","and"," ",e.jsxs("span",{className:"font-bold text-[#1FC5A8]",children:["$",d.toLocaleString()]})," ","per month."]}),e.jsxs("p",{className:"mt-2 text-xs text-[#6B6960] text-center",children:["The ",Math.round(a*100),"% is an assumption, not a measurement. Model and API spend is not included."]})]})]})]})}const j=[{feature:"Claude (Opus/Sonnet/Haiku)",category:"AI Models",lokiMode:!0,boltNew:!1,replit:!1,lovable:!0},{feature:"GPT-4o / Codex",category:"AI Models",lokiMode:!0,boltNew:!0,replit:!0,lovable:!1},{feature:"5-provider auto-failover",category:"AI Models",lokiMode:!0,boltNew:!1,replit:!1,lovable:!1},{feature:"8 automated quality gates",category:"Quality",lokiMode:!0,boltNew:!1,replit:!1,lovable:!1},{feature:"Blind 3-reviewer code review",category:"Quality",lokiMode:!0,boltNew:!1,replit:!1,lovable:!1},{feature:"Anti-sycophancy checks",category:"Quality",lokiMode:!0,boltNew:!1,replit:!1,lovable:!1},{feature:"Automated test generation",category:"Quality",lokiMode:!0,boltNew:!1,replit:"Partial",lovable:!1},{feature:"Docker container generation",category:"Deployment",lokiMode:!0,boltNew:!1,replit:!0,lovable:!1},{feature:"CI/CD pipeline generation",category:"Deployment",lokiMode:!0,boltNew:!1,replit:!1,lovable:!1},{feature:"One-click cloud deploy",category:"Deployment",lokiMode:"Planned",boltNew:!0,replit:!0,lovable:!0},{feature:"Self-hosted (your keys)",category:"Cost",lokiMode:!0,boltNew:!1,replit:!1,lovable:!1},{feature:"Free tier available",category:"Cost",lokiMode:"Source-available (BUSL-1.1)",boltNew:!0,replit:!0,lovable:!0},{feature:"No vendor lock-in",category:"Cost",lokiMode:!0,boltNew:!1,replit:!1,lovable:!1},{feature:"SSO / OIDC authentication",category:"Enterprise",lokiMode:!0,boltNew:!1,replit:!0,lovable:!1},{feature:"RBAC access control",category:"Enterprise",lokiMode:!0,boltNew:!1,replit:!0,lovable:!1},{feature:"Audit logging",category:"Enterprise",lokiMode:!0,boltNew:!1,replit:!0,lovable:!1},{feature:"Air-gapped deployment",category:"Enterprise",lokiMode:!0,boltNew:!1,replit:!1,lovable:!1}];function y(){return e.jsx("svg",{width:"18",height:"18",viewBox:"0 0 24 24",fill:"none",className:"text-[#1FC5A8]",children:e.jsx("path",{d:"M20 6L9 17l-5-5",stroke:"currentColor",strokeWidth:"2.5",strokeLinecap:"round",strokeLinejoin:"round"})})}function w(){return e.jsx("svg",{width:"18",height:"18",viewBox:"0 0 24 24",fill:"none",className:"text-[#C45B5B]/40",children:e.jsx("path",{d:"M18 6L6 18M6 6l12 12",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round"})})}function x({value:r}){return typeof r=="string"?e.jsx("span",{className:"text-xs text-[#6B6960] font-medium",children:r}):r?e.jsx(y,{}):e.jsx(w,{})}const k=[{title:"Full Autonomy",description:"Describe what you want. Loki Mode handles planning, coding, testing, and verification automatically.",icon:e.jsx("svg",{width:"24",height:"24",viewBox:"0 0 24 24",fill:"none",className:"text-[#553DE9]",children:e.jsx("path",{d:"M13 2L3 14h9l-1 8 10-12h-9l1-8z",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round"})})},{title:"Production Quality",description:"8 quality gates, blind code review, and anti-sycophancy checks ensure code that actually works.",icon:e.jsx("svg",{width:"24",height:"24",viewBox:"0 0 24 24",fill:"none",className:"text-[#1FC5A8]",children:e.jsx("path",{d:"M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round"})})},{title:"Your Infrastructure",description:"Self-hosted, source-available (BUSL-1.1), no data leaves your network. Use your own API keys across 5 providers.",icon:e.jsxs("svg",{width:"24",height:"24",viewBox:"0 0 24 24",fill:"none",className:"text-[#2F71E3]",children:[e.jsx("rect",{x:"2",y:"2",width:"20",height:"8",rx:"2",stroke:"currentColor",strokeWidth:"2"}),e.jsx("rect",{x:"2",y:"14",width:"20",height:"8",rx:"2",stroke:"currentColor",strokeWidth:"2"}),e.jsx("circle",{cx:"6",cy:"6",r:"1",fill:"currentColor"}),e.jsx("circle",{cx:"6",cy:"18",r:"1",fill:"currentColor"})]})},{title:"Built for Teams",description:"Enterprise auth, RBAC, audit logs, and parallel workflows for teams of any size.",icon:e.jsxs("svg",{width:"24",height:"24",viewBox:"0 0 24 24",fill:"none",className:"text-[#E93D82]",children:[e.jsx("path",{d:"M17 21v-2a4 4 0 00-4-4H5a4 4 0 00-4 4v2",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round"}),e.jsx("circle",{cx:"9",cy:"7",r:"4",stroke:"currentColor",strokeWidth:"2"}),e.jsx("path",{d:"M23 21v-2a4 4 0 00-3-3.87M16 3.13a4 4 0 010 7.75",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round"})]})}];function C(){const r=[...new Set(j.map(t=>t.category))];return e.jsx("div",{className:"min-h-screen bg-[#FAF9F6]",children:e.jsxs("div",{className:"max-w-6xl mx-auto px-6 py-8",children:[e.jsxs("div",{className:"mb-8",children:[e.jsx("h1",{className:"text-2xl font-bold text-[#36342E]",children:"Compare"}),e.jsx("p",{className:"text-[#6B6960] mt-1",children:"See how Loki Mode stacks up against other AI development platforms."})]}),e.jsx("div",{className:"bg-white border border-[#ECEAE3] rounded-xl overflow-hidden shadow-sm",children:e.jsx("div",{className:"overflow-x-auto",children:e.jsxs("table",{className:"w-full text-sm",children:[e.jsx("thead",{children:e.jsxs("tr",{className:"border-b border-[#ECEAE3]",children:[e.jsx("th",{className:"text-left px-4 py-3 font-medium text-[#6B6960] w-[240px]",children:"Feature"}),e.jsx("th",{className:"text-center px-4 py-3 font-bold text-[#553DE9] bg-[#553DE9]/5 w-[130px]",children:"Loki Mode"}),e.jsx("th",{className:"text-center px-4 py-3 font-medium text-[#6B6960] w-[130px]",children:"bolt.new"}),e.jsx("th",{className:"text-center px-4 py-3 font-medium text-[#6B6960] w-[130px]",children:"Replit"}),e.jsx("th",{className:"text-center px-4 py-3 font-medium text-[#6B6960] w-[130px]",children:"Lovable"})]})}),e.jsx("tbody",{children:r.map(t=>e.jsxs(e.Fragment,{children:[e.jsx("tr",{className:"bg-[#FAF9F6]",children:e.jsx("td",{colSpan:5,className:"px-4 py-2 text-xs font-bold text-[#939084] uppercase tracking-wider",children:t})},`cat-${t}`),j.filter(s=>s.category===t).map(s=>e.jsxs("tr",{className:"border-b border-[#ECEAE3] last:border-b-0",children:[e.jsx("td",{className:"px-4 py-2.5 text-[#36342E]",children:s.feature}),e.jsx("td",{className:"px-4 py-2.5 text-center bg-[#553DE9]/5",children:e.jsx("div",{className:"flex justify-center",children:e.jsx(x,{value:s.lokiMode})})}),e.jsx("td",{className:"px-4 py-2.5 text-center",children:e.jsx("div",{className:"flex justify-center",children:e.jsx(x,{value:s.boltNew})})}),e.jsx("td",{className:"px-4 py-2.5 text-center",children:e.jsx("div",{className:"flex justify-center",children:e.jsx(x,{value:s.replit})})}),e.jsx("td",{className:"px-4 py-2.5 text-center",children:e.jsx("div",{className:"flex justify-center",children:e.jsx(x,{value:s.lovable})})})]},s.feature))]}))})]})})}),e.jsxs("div",{className:"mt-12",children:[e.jsx("h2",{className:"text-xl font-bold text-[#36342E] mb-6",children:"Why developers choose Loki Mode"}),e.jsx("div",{className:"grid grid-cols-1 md:grid-cols-2 gap-5",children:k.map(t=>e.jsxs("div",{className:"bg-white border border-[#ECEAE3] rounded-xl p-5 shadow-sm hover:shadow-md transition-shadow",children:[e.jsx("div",{className:"mb-3",children:t.icon}),e.jsx("h3",{className:"text-base font-bold text-[#36342E] mb-1",children:t.title}),e.jsx("p",{className:"text-sm text-[#6B6960]",children:t.description})]},t.title))})]}),e.jsx("div",{className:"mt-12 max-w-xl mx-auto",children:e.jsx(g,{})})]})})}export{C as default};
@@ -1,4 +1,4 @@
1
- import{c as o,r as a,j as e,T as n,h as i,p as c}from"./index-sCyA5kT1.js";/**
1
+ import{c as o,r as a,j as e,T as n,h as i,p as c}from"./index-D_gr9DjQ.js";/**
2
2
  * @license lucide-react v0.577.0 - ISC
3
3
  *
4
4
  * This source code is licensed under the ISC license.
@@ -1 +1 @@
1
- import{r as l,a as x,j as e}from"./index-sCyA5kT1.js";const N={verified:{badge:"bg-success/10 text-success border-success/20",dot:"bg-success",label:"Verified",proven:"The server recomputed the integrity hash and it matches, and the gpg signature verified against its keyring.",notProven:"That the code is correct, or that the recorded diff still matches your repo. A receipt records which checks ran, not that the result is right."},unsigned:{badge:"bg-warning/10 text-warning border-warning/20",dot:"bg-warning",label:"Unsigned",proven:"The server recomputed the integrity hash and it matches these receipt bytes.",notProven:"Who produced it. An unsigned receipt is forgeable: whoever controls the builder can rewrite the facts and recompute the hash."},unchecked:{badge:"bg-warning/10 text-warning border-warning/20",dot:"bg-warning",label:"Not checked",proven:"The server recomputed the integrity hash and it matches.",notProven:"Provenance. A signature is present but was not evaluated here, which is not the same as a bad signature."},tampered:{badge:"bg-danger/10 text-danger border-danger/20",dot:"bg-danger",label:"Tampered",proven:"Nothing. The server recomputed the integrity hash and it does not match the contents.",notProven:"Anything at all. This receipt was edited after it was written. Do not trust this build."},failed:{badge:"bg-danger/10 text-danger border-danger/20",dot:"bg-danger",label:"Failed verification",proven:"Nothing. A verification check ran on the server and said no.",notProven:"Anything the receipt claims. Read the reason below."},not_verified:{badge:"border-muted/20 text-muted",dot:"bg-muted/40",label:"Not verified here",proven:"Nothing. No verification result was computed for this receipt here.",notProven:"Integrity or provenance. Run the command below to check it yourself."}};function w(t){const n=t==null?void 0:t.integrity_check,r=t==null?void 0:t.verification;return n?n.status==="tampered"?"tampered":n.status==="failed"?"failed":n.status!=="verified"?"not_verified":n.gpg_ok===!0?"verified":r!=null&&r.attestation||r!=null&&r.gpg_signature?"unchecked":"unsigned":"not_verified"}const v=[{key:"verified",label:"Verified",bar:"bg-success",text:"text-success"},{key:"with_gaps",label:"With gaps",bar:"bg-warning",text:"text-warning"},{key:"not_verified",label:"Not verified",bar:"bg-danger",text:"text-danger"},{key:"unknown",label:"Unknown",bar:"bg-muted/40",text:"text-muted"}];function y({summary:t}){const n=t.total_receipts;return n?e.jsxs("div",{className:"space-y-2",children:[e.jsx("div",{className:"flex h-1.5 rounded-full overflow-hidden bg-muted/10",children:v.map(({key:r,bar:s})=>{const i=t[r];return i?e.jsx("div",{className:s,style:{width:`${i/n*100}%`},title:`${i} ${r}`},r):null})}),e.jsx("div",{className:"flex flex-wrap gap-x-4 gap-y-1 text-xs",children:v.map(({key:r,label:s,text:i})=>{const c=t[r];return c?e.jsxs("span",{className:i,children:[e.jsx("span",{className:"font-semibold",children:c})," ",s.toLowerCase()]},r):null})}),e.jsxs("p",{className:"text-muted text-xs",children:["Recorded verdicts across ",n," receipt",n===1?"":"s",", not re-verified here."]})]}):null}function u({label:t,value:n}){return e.jsxs("div",{className:"flex items-baseline gap-2 text-xs",children:[e.jsx("span",{className:"text-muted w-28 flex-shrink-0",children:t}),e.jsx("span",{className:"font-mono truncate",title:n,children:n})]})}function k({proof:t}){var m,p,g,f,b;const[n,r]=l.useState(!1),[s,i]=l.useState(null),[c,d]=l.useState(null);l.useEffect(()=>{if(!n||s||c)return;let o=!1;return x.getProof(t.run_id).then(h=>{o||i(h)}).catch(h=>{o||d((h==null?void 0:h.message)||"could not load this receipt")}),()=>{o=!0}},[n,t.run_id,s,c]);const a=N[w(s)],j=t.generated_at?new Date(t.generated_at).toLocaleString():"unknown";return e.jsxs("div",{className:`border rounded-card overflow-hidden ${s?a.badge:"border-muted/20"}`,children:[e.jsxs("button",{type:"button",className:"w-full flex items-center gap-3 px-3 py-2.5 text-left",onClick:()=>r(!n),"aria-expanded":n,children:[e.jsx("span",{className:`w-2 h-2 rounded-full flex-shrink-0 ${s?a.dot:"bg-muted/40"}`}),e.jsx("span",{className:"text-sm font-medium flex-1 truncate",children:t.headline?`Recorded: ${t.headline}`:t.run_id}),t.final_verdict&&e.jsxs("span",{className:"text-xs font-mono uppercase tracking-wider flex-shrink-0 text-muted",children:["council ",t.final_verdict]}),e.jsx("span",{className:"text-muted text-xs flex-shrink-0",children:n?"−":"+"})]}),n&&e.jsxs("div",{className:"px-3 pb-3 space-y-3 border-t border-current/10 pt-3",children:[c&&e.jsxs("p",{className:"text-xs text-danger",children:["Could not load this receipt: ",c]}),!c&&!s&&e.jsx("p",{className:"text-xs text-muted",children:"Loading receipt…"}),s&&e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"space-y-1",children:[e.jsx(u,{label:"Run",value:s.run_id}),e.jsx(u,{label:"Generated",value:j}),e.jsx(u,{label:"Loki version",value:s.loki_version||"unknown"}),e.jsx(u,{label:"Cost",value:typeof((m=s.cost)==null?void 0:m.usd)=="number"?`${s.cost.cost_partial===!0?"at least ":""}$${s.cost.usd.toFixed(2)}`:"unknown"}),e.jsx(u,{label:"Files changed",value:typeof((p=s.files_changed)==null?void 0:p.count)=="number"?String(s.files_changed.count):"unknown"}),((g=s.verification)==null?void 0:g.hash)&&e.jsx(u,{label:"Recorded hash",value:s.verification.hash})]}),e.jsxs("div",{className:`rounded-card border px-3 py-2 ${a.badge}`,children:[e.jsx("p",{className:"text-xs font-semibold uppercase tracking-wider mb-1",children:a.label}),e.jsxs("p",{className:"text-xs mb-1",children:[e.jsx("span",{className:"font-semibold",children:"Proven: "}),a.proven]}),e.jsxs("p",{className:"text-xs opacity-80",children:[e.jsx("span",{className:"font-semibold",children:"Not proven: "}),a.notProven]}),(((f=s.integrity_check)==null?void 0:f.reasons)||[]).slice(0,2).map(o=>e.jsx("p",{className:"text-xs opacity-80 mt-1",children:o},o))]}),e.jsxs("p",{className:"text-xs text-muted",children:["Verify it yourself (also re-derives the diff against your repo):"," ",e.jsxs("code",{className:"font-mono",children:["loki proof verify ",s.run_id,(b=s.verification)!=null&&b.attestation?" --jwks <url|file>":""]})]}),t.pr_url&&e.jsx("a",{href:t.pr_url,target:"_blank",rel:"noreferrer noopener",className:"text-xs text-primary underline",children:"View the pull request"})]})]})]})}function P(){const[t,n]=l.useState(null),[r,s]=l.useState(null),[i,c]=l.useState(null);return l.useEffect(()=>{let d=!1;return x.listProofs().then(a=>{d||n(a.proofs||[])}).catch(a=>{d||c((a==null?void 0:a.message)||"could not load receipts")}),x.proofsSummary().then(a=>{d||s(a)}).catch(()=>{}),()=>{d=!0}},[]),e.jsxs("section",{className:"space-y-3",children:[e.jsxs("div",{children:[e.jsx("h2",{className:"text-sm font-semibold",children:"Evidence Receipts"}),e.jsx("p",{className:"text-muted text-xs mt-1",children:"What each build actually checked, recorded so you can verify it yourself."})]}),i&&e.jsxs("p",{className:"text-xs text-danger",children:["Could not load receipts: ",i]}),!i&&t===null&&e.jsx("p",{className:"text-xs text-muted",children:"Loading…"}),r&&e.jsx(y,{summary:r}),t!==null&&t.length===0&&e.jsxs("p",{className:"text-xs text-muted",children:["No receipts yet. One is written at the end of each run (set ",e.jsx("code",{className:"font-mono",children:"LOKI_PROOF=0"})," to opt out)."]}),e.jsx("div",{className:"space-y-2",children:t==null?void 0:t.map(d=>e.jsx(k,{proof:d},d.run_id))})]})}export{P as E};
1
+ import{r as l,a as x,j as e}from"./index-D_gr9DjQ.js";const N={verified:{badge:"bg-success/10 text-success border-success/20",dot:"bg-success",label:"Verified",proven:"The server recomputed the integrity hash and it matches, and the gpg signature verified against its keyring.",notProven:"That the code is correct, or that the recorded diff still matches your repo. A receipt records which checks ran, not that the result is right."},unsigned:{badge:"bg-warning/10 text-warning border-warning/20",dot:"bg-warning",label:"Unsigned",proven:"The server recomputed the integrity hash and it matches these receipt bytes.",notProven:"Who produced it. An unsigned receipt is forgeable: whoever controls the builder can rewrite the facts and recompute the hash."},unchecked:{badge:"bg-warning/10 text-warning border-warning/20",dot:"bg-warning",label:"Not checked",proven:"The server recomputed the integrity hash and it matches.",notProven:"Provenance. A signature is present but was not evaluated here, which is not the same as a bad signature."},tampered:{badge:"bg-danger/10 text-danger border-danger/20",dot:"bg-danger",label:"Tampered",proven:"Nothing. The server recomputed the integrity hash and it does not match the contents.",notProven:"Anything at all. This receipt was edited after it was written. Do not trust this build."},failed:{badge:"bg-danger/10 text-danger border-danger/20",dot:"bg-danger",label:"Failed verification",proven:"Nothing. A verification check ran on the server and said no.",notProven:"Anything the receipt claims. Read the reason below."},not_verified:{badge:"border-muted/20 text-muted",dot:"bg-muted/40",label:"Not verified here",proven:"Nothing. No verification result was computed for this receipt here.",notProven:"Integrity or provenance. Run the command below to check it yourself."}};function w(t){const n=t==null?void 0:t.integrity_check,r=t==null?void 0:t.verification;return n?n.status==="tampered"?"tampered":n.status==="failed"?"failed":n.status!=="verified"?"not_verified":n.gpg_ok===!0?"verified":r!=null&&r.attestation||r!=null&&r.gpg_signature?"unchecked":"unsigned":"not_verified"}const v=[{key:"verified",label:"Verified",bar:"bg-success",text:"text-success"},{key:"with_gaps",label:"With gaps",bar:"bg-warning",text:"text-warning"},{key:"not_verified",label:"Not verified",bar:"bg-danger",text:"text-danger"},{key:"unknown",label:"Unknown",bar:"bg-muted/40",text:"text-muted"}];function y({summary:t}){const n=t.total_receipts;return n?e.jsxs("div",{className:"space-y-2",children:[e.jsx("div",{className:"flex h-1.5 rounded-full overflow-hidden bg-muted/10",children:v.map(({key:r,bar:s})=>{const i=t[r];return i?e.jsx("div",{className:s,style:{width:`${i/n*100}%`},title:`${i} ${r}`},r):null})}),e.jsx("div",{className:"flex flex-wrap gap-x-4 gap-y-1 text-xs",children:v.map(({key:r,label:s,text:i})=>{const c=t[r];return c?e.jsxs("span",{className:i,children:[e.jsx("span",{className:"font-semibold",children:c})," ",s.toLowerCase()]},r):null})}),e.jsxs("p",{className:"text-muted text-xs",children:["Recorded verdicts across ",n," receipt",n===1?"":"s",", not re-verified here."]})]}):null}function u({label:t,value:n}){return e.jsxs("div",{className:"flex items-baseline gap-2 text-xs",children:[e.jsx("span",{className:"text-muted w-28 flex-shrink-0",children:t}),e.jsx("span",{className:"font-mono truncate",title:n,children:n})]})}function k({proof:t}){var m,p,g,f,b;const[n,r]=l.useState(!1),[s,i]=l.useState(null),[c,d]=l.useState(null);l.useEffect(()=>{if(!n||s||c)return;let o=!1;return x.getProof(t.run_id).then(h=>{o||i(h)}).catch(h=>{o||d((h==null?void 0:h.message)||"could not load this receipt")}),()=>{o=!0}},[n,t.run_id,s,c]);const a=N[w(s)],j=t.generated_at?new Date(t.generated_at).toLocaleString():"unknown";return e.jsxs("div",{className:`border rounded-card overflow-hidden ${s?a.badge:"border-muted/20"}`,children:[e.jsxs("button",{type:"button",className:"w-full flex items-center gap-3 px-3 py-2.5 text-left",onClick:()=>r(!n),"aria-expanded":n,children:[e.jsx("span",{className:`w-2 h-2 rounded-full flex-shrink-0 ${s?a.dot:"bg-muted/40"}`}),e.jsx("span",{className:"text-sm font-medium flex-1 truncate",children:t.headline?`Recorded: ${t.headline}`:t.run_id}),t.final_verdict&&e.jsxs("span",{className:"text-xs font-mono uppercase tracking-wider flex-shrink-0 text-muted",children:["council ",t.final_verdict]}),e.jsx("span",{className:"text-muted text-xs flex-shrink-0",children:n?"−":"+"})]}),n&&e.jsxs("div",{className:"px-3 pb-3 space-y-3 border-t border-current/10 pt-3",children:[c&&e.jsxs("p",{className:"text-xs text-danger",children:["Could not load this receipt: ",c]}),!c&&!s&&e.jsx("p",{className:"text-xs text-muted",children:"Loading receipt…"}),s&&e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"space-y-1",children:[e.jsx(u,{label:"Run",value:s.run_id}),e.jsx(u,{label:"Generated",value:j}),e.jsx(u,{label:"Loki version",value:s.loki_version||"unknown"}),e.jsx(u,{label:"Cost",value:typeof((m=s.cost)==null?void 0:m.usd)=="number"?`${s.cost.cost_partial===!0?"at least ":""}$${s.cost.usd.toFixed(2)}`:"unknown"}),e.jsx(u,{label:"Files changed",value:typeof((p=s.files_changed)==null?void 0:p.count)=="number"?String(s.files_changed.count):"unknown"}),((g=s.verification)==null?void 0:g.hash)&&e.jsx(u,{label:"Recorded hash",value:s.verification.hash})]}),e.jsxs("div",{className:`rounded-card border px-3 py-2 ${a.badge}`,children:[e.jsx("p",{className:"text-xs font-semibold uppercase tracking-wider mb-1",children:a.label}),e.jsxs("p",{className:"text-xs mb-1",children:[e.jsx("span",{className:"font-semibold",children:"Proven: "}),a.proven]}),e.jsxs("p",{className:"text-xs opacity-80",children:[e.jsx("span",{className:"font-semibold",children:"Not proven: "}),a.notProven]}),(((f=s.integrity_check)==null?void 0:f.reasons)||[]).slice(0,2).map(o=>e.jsx("p",{className:"text-xs opacity-80 mt-1",children:o},o))]}),e.jsxs("p",{className:"text-xs text-muted",children:["Verify it yourself (also re-derives the diff against your repo):"," ",e.jsxs("code",{className:"font-mono",children:["loki proof verify ",s.run_id,(b=s.verification)!=null&&b.attestation?" --jwks <url|file>":""]})]}),t.pr_url&&e.jsx("a",{href:t.pr_url,target:"_blank",rel:"noreferrer noopener",className:"text-xs text-primary underline",children:"View the pull request"})]})]})]})}function P(){const[t,n]=l.useState(null),[r,s]=l.useState(null),[i,c]=l.useState(null);return l.useEffect(()=>{let d=!1;return x.listProofs().then(a=>{d||n(a.proofs||[])}).catch(a=>{d||c((a==null?void 0:a.message)||"could not load receipts")}),x.proofsSummary().then(a=>{d||s(a)}).catch(()=>{}),()=>{d=!0}},[]),e.jsxs("section",{className:"space-y-3",children:[e.jsxs("div",{children:[e.jsx("h2",{className:"text-sm font-semibold",children:"Evidence Receipts"}),e.jsx("p",{className:"text-muted text-xs mt-1",children:"What each build actually checked, recorded so you can verify it yourself."})]}),i&&e.jsxs("p",{className:"text-xs text-danger",children:["Could not load receipts: ",i]}),!i&&t===null&&e.jsx("p",{className:"text-xs text-muted",children:"Loading…"}),r&&e.jsx(y,{summary:r}),t!==null&&t.length===0&&e.jsxs("p",{className:"text-xs text-muted",children:["No receipts yet. One is written at the end of each run (set ",e.jsx("code",{className:"font-mono",children:"LOKI_PROOF=0"})," to opt out)."]}),e.jsx("div",{className:"space-y-2",children:t==null?void 0:t.map(d=>e.jsx(k,{proof:d},d.run_id))})]})}export{P as E};
@@ -1,4 +1,4 @@
1
- import{c as _,r as o,a as k,j as e,m as B,J as v,N as h,a5 as H,M as L,l as q}from"./index-sCyA5kT1.js";import{B as N}from"./Button-BehU-7tC.js";import{C as y}from"./thumbs-up-Bj5vm_ge.js";import{R as S}from"./refresh-cw-D54nCHRH.js";import{C as z}from"./circle-alert-0P4HhIzB.js";import{U as D}from"./user-DuS3X15u.js";import{L as G}from"./loader-circle-ZB7xFqz2.js";import{R as T}from"./rotate-ccw-CFJoICwQ.js";/**
1
+ import{c as _,r as o,a as k,j as e,m as B,J as v,N as h,a5 as H,M as L,l as q}from"./index-D_gr9DjQ.js";import{B as N}from"./Button-BKE7kijL.js";import{C as y}from"./thumbs-up-B2v87stN.js";import{R as S}from"./refresh-cw-D1La5GDI.js";import{C as z}from"./circle-alert-Bk6g0LfO.js";import{U as D}from"./user-DQDpD4Cu.js";import{L as G}from"./loader-circle-D1LGSlkG.js";import{R as T}from"./rotate-ccw-Dlm3A4yL.js";/**
2
2
  * @license lucide-react v0.577.0 - ISC
3
3
  *
4
4
  * This source code is licensed under the ISC license.
@@ -1,4 +1,4 @@
1
- import{c as I,r as c,a as b,j as e,G as me,A as ue,S as Z,k as ee,m as pe,h as he,p as fe,X as te,M as be}from"./index-sCyA5kT1.js";import{B as y}from"./Button-BehU-7tC.js";import{C as ge}from"./chevron-left-BJ2VldHk.js";import{G as P}from"./thumbs-up-Bj5vm_ge.js";import{R as $}from"./refresh-cw-D54nCHRH.js";import{C as Ne}from"./circle-alert-0P4HhIzB.js";import{L as Q,S as je}from"./ProjectPage-TrK9SBhV.js";import{S as se}from"./shield-alert-o3-54oNJ.js";import{F as ye}from"./file-code-corner-BHhWk-yB.js";import{C as V}from"./clock-DxxclmlE.js";import{C as re}from"./check-DNNlHviH.js";import"./ErrorBoundary-P4CkwBs3.js";import"./TerminalOutput-CZcfr298.js";import"./lock-D1UH6v-O.js";import"./package-sDqydd_4.js";import"./file-plus-CXDzSmIV.js";import"./activity-JwnQAJzU.js";import"./scroll-text-D_5t4SMl.js";import"./bot-Dwg7eFlL.js";import"./loader-circle-ZB7xFqz2.js";import"./hammer-CyPoRSsX.js";import"./dollar-sign-qUbMFHU4.js";import"./globe--nSuGAqH.js";import"./trash-2-D4yWbHl5.js";import"./upload-Dem0HZRa.js";import"./plus-ClTe2QX5.js";import"./rotate-ccw-CFJoICwQ.js";import"./code-xml-BJ25iVx3.js";import"./trending-up-CL4ZFYFb.js";import"./layers-D8YjFUHc.js";import"./trending-down-DStUnNZ3.js";import"./server-cUdSs3Jw.js";/**
1
+ import{c as I,r as c,a as b,j as e,G as me,A as ue,S as Z,k as ee,m as pe,h as he,p as fe,X as te,M as be}from"./index-D_gr9DjQ.js";import{B as y}from"./Button-BKE7kijL.js";import{C as ge}from"./chevron-left-MJKoMYpu.js";import{G as P}from"./thumbs-up-B2v87stN.js";import{R as $}from"./refresh-cw-D1La5GDI.js";import{C as Ne}from"./circle-alert-Bk6g0LfO.js";import{L as Q,S as je}from"./ProjectPage-B5cKA8WR.js";import{S as se}from"./shield-alert-DgWjn5rX.js";import{F as ye}from"./file-code-corner-Do_wBHbQ.js";import{C as V}from"./clock-DphpX3yF.js";import{C as re}from"./check-BBDkYdTy.js";import"./ErrorBoundary-oVL59njg.js";import"./TerminalOutput-Cla9B3Ef.js";import"./lock-AWRWK-Aa.js";import"./package-CGXLDs1G.js";import"./file-plus-yM8hYAJy.js";import"./activity-BPaXjKEf.js";import"./scroll-text-BVziFaFm.js";import"./bot-BREtMbFp.js";import"./loader-circle-D1LGSlkG.js";import"./hammer-BgKbsMMT.js";import"./dollar-sign-8w0iMGof.js";import"./globe-CvMW8-NT.js";import"./trash-2-BTk6wQb5.js";import"./upload-iTjkpxy-.js";import"./plus-BXNCq6zK.js";import"./rotate-ccw-Dlm3A4yL.js";import"./code-xml-DPGK4IbU.js";import"./trending-up-B1v8JIc8.js";import"./layers-kRENi5Kn.js";import"./trending-down-YLEdCvay.js";import"./server-C1QO24zQ.js";/**
2
2
  * @license lucide-react v0.577.0 - ISC
3
3
  *
4
4
  * This source code is licensed under the ISC license.