loki-mode 10.7.1 → 10.9.1
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/README.md +2 -2
- package/SKILL.md +2 -2
- package/VERSION +1 -1
- package/autonomy/loki +20 -32
- package/autonomy/run.sh +9 -5
- package/autonomy/tui.sh +5 -1
- package/dashboard/__init__.py +1 -1
- package/dashboard/api_phases.py +1 -1
- package/dashboard/api_start.py +0 -1
- package/dashboard/server.py +28 -146
- package/docs/COCKPIT-SPEC.md +3 -1
- package/docs/DASHBOARD-9.12-EVIDENCE.md +1 -1
- package/docs/DASHBOARD-ARCHITECTURE.md +13 -11
- package/docs/DASHBOARD_V2_CHECKLIST.md +2 -0
- package/docs/INSTALLATION.md +12 -11
- package/docs/MERGE-DEDUP-MAP.md +1 -1
- package/docs/MERGE-ROUTE-MAP.md +1 -1
- package/docs/MERGE3-PLAN.md +2 -2
- package/docs/R3-COST-OBSERVABILITY-DESIGN.md +2 -2
- package/docs/R4-TRUST-TRAJECTORY-DESIGN.md +1 -1
- package/docs/R5-AUTO-WIKI-DESIGN.md +1 -1
- package/docs/R6-ROLLBACK-CHECKPOINT-PLAN.md +1 -1
- package/docs/RARV-C-100X-PLAN.md +3 -3
- package/docs/SONNET5-DEFAULT-PLAN.md +2 -2
- package/docs/SYNERGY-TASKS.md +1 -1
- package/docs/TOOL-INTEGRATION.md +1 -1
- package/docs/TOP-100-BACKLOG.md +1 -1
- package/docs/alternative-installations.md +1 -1
- package/docs/architecture/ADR-001-runtime-migration.md +1 -1
- package/docs/architecture/DASHBOARD_V2_ARCHITECTURE.md +2 -0
- package/docs/audit-logging.md +2 -0
- package/docs/authentication.md +2 -0
- package/docs/authorization.md +2 -0
- package/docs/certification/02-enterprise-features/lab.md +2 -0
- package/docs/certification/02-enterprise-features/lesson.md +2 -0
- package/docs/certification/04-production-deployment/lab.md +2 -0
- package/docs/certification/04-production-deployment/lesson.md +2 -0
- package/docs/control-plane-migration.md +97 -0
- package/docs/dashboard-guide.md +6 -7
- package/docs/dev/release-checklist.md +9 -11
- package/docs/enterprise/integration-cookbook.md +2 -0
- package/docs/enterprise/migration.md +1 -1
- package/docs/enterprise/performance.md +3 -1
- package/docs/enterprise/sdk-guide.md +2 -0
- package/docs/enterprise/security.md +2 -0
- package/docs/plans/FORGE-AUTONOMOUS-QUEUE.md +2 -2
- package/docs/research-2026-07/OBSERVABILITY-TOOL-SPEC.md +1 -1
- package/docs/retrospectives/v7.5.15-honesty-audit.md +2 -2
- package/docs/test-scenarios/enterprise-scenarios.md +2 -2
- package/docs/v10/ARCHITECT-CUTS.md +9 -9
- package/docs/v10/BACKLOG.md +7 -7
- package/docs/v10/BOARD.md +25 -25
- package/docs/v10/CONTROL-PLANE.md +4 -4
- package/docs/v10/CP-ENTERPRISE-UI.md +270 -9
- package/docs/v10/D51-PHASE-B.md +1 -1
- package/docs/v10/DECISIONS.md +3 -3
- package/docs/v10/DEPS.md +10 -10
- package/docs/v10/FAILURE-CLASSES.md +66 -1
- package/docs/v10/LEGACY-REMOVAL.md +2 -2
- package/docs/v10/METRICS.md +6 -0
- package/docs/v10/SWARM.md +2 -2
- package/loki-ts/data/model-pricing.json +6 -6
- package/loki-ts/dist/cockpit.js +11 -11
- package/loki-ts/dist/loki.js +819 -809
- package/loki-ts/package.json +1 -1
- package/magic/core/design_tokens.py +1 -2
- package/magic/core/generator.py +2 -2
- package/magic/tokens/README.md +1 -3
- package/mcp/__init__.py +1 -1
- package/package.json +3 -8
- package/packages/control-plane/dist/server.js +17954 -5696
- package/packages/control-plane/drizzle/0004_cpe03_local_repos_actions.sql +17 -0
- package/packages/control-plane/drizzle/meta/0004_snapshot.json +542 -0
- package/packages/control-plane/drizzle/meta/_journal.json +9 -2
- package/packages/control-plane/package.json +4 -1
- package/packages/control-plane/src/db/schema.ts +19 -0
- package/packages/control-plane/src/server/app.ts +11 -22
- package/packages/control-plane/src/server/audit.ts +15 -0
- package/packages/control-plane/src/server/auth.ts +7 -1
- package/packages/control-plane/src/server/legacy/routes-audit.ts +50 -0
- package/packages/control-plane/src/server/legacy/routes-control.ts +26 -0
- package/packages/control-plane/src/server/legacy/routes-memory.ts +30 -0
- package/packages/control-plane/src/server/legacy/routes-metrics.ts +18 -0
- package/packages/control-plane/src/server/legacy/routes.ts +237 -0
- package/packages/control-plane/src/server/legacy/shim.ts +201 -0
- package/packages/control-plane/src/server/repos.ts +27 -0
- package/packages/control-plane/src/server/routes/artifacts.ts +104 -0
- package/packages/control-plane/src/server/routes/audit.ts +17 -0
- package/packages/control-plane/src/server/routes/checkpoints.ts +171 -0
- package/packages/control-plane/src/server/routes/config.ts +319 -0
- package/packages/control-plane/src/server/routes/control.ts +75 -0
- package/packages/control-plane/src/server/routes/cost.ts +70 -0
- package/packages/control-plane/src/server/routes/cost_ledger.ts +210 -0
- package/packages/control-plane/src/server/routes/fleet.ts +101 -0
- package/packages/control-plane/src/server/routes/index.ts +71 -0
- package/packages/control-plane/src/server/routes/integrations.ts +123 -0
- package/packages/control-plane/src/server/routes/memory.ts +219 -0
- package/packages/control-plane/src/server/routes/merge_risk.ts +209 -0
- package/packages/control-plane/src/server/routes/metrics.ts +93 -0
- package/packages/control-plane/src/server/routes/notify.ts +48 -0
- package/packages/control-plane/src/server/routes/providers.ts +111 -0
- package/packages/control-plane/src/server/routes/session_control.ts +155 -0
- package/packages/control-plane/src/server/routes/start.ts +39 -0
- package/packages/control-plane/src/server/routes/stats.ts +76 -0
- package/packages/control-plane/src/server/routes/stream.ts +85 -0
- package/packages/control-plane/src/server/routes/verify.ts +66 -0
- package/packages/control-plane/src/server/spawn.ts +79 -0
- package/packages/control-plane/src/shipper/discover.ts +19 -1
- package/packages/control-plane/ui/dist/assets/index-BQbFv65M.css +1 -0
- package/packages/control-plane/ui/dist/assets/index-Dbhu244h.js +155 -0
- package/packages/control-plane/ui/dist/index.html +2 -2
- package/plugins/loki-mode/.claude-plugin/plugin.json +1 -1
- package/references/magic-modules-patterns.md +2 -2
- package/skills/factory-operations.md +3 -3
- package/skills/magic-modules.md +1 -1
- package/skills/release-cadence.md +1 -1
- package/tools/audit-docs.py +1 -1
- package/tools/index-codebase.py +1 -1
- package/web-app/dist/assets/{AdminPage-BJSU5hY1.js → AdminPage-DqMnfZrH.js} +1 -1
- package/web-app/dist/assets/{Avatar-CSOSpw9c.js → Avatar-BEof29du.js} +1 -1
- package/web-app/dist/assets/{Badge-COr7vj0D.js → Badge-WD53OL5m.js} +1 -1
- package/web-app/dist/assets/{Button-DGoWFERp.js → Button-Rzk5flhU.js} +1 -1
- package/web-app/dist/assets/{CockpitPage-CBpWkNXY.js → CockpitPage-Bqz3PCMh.js} +1 -1
- package/web-app/dist/assets/{ComparePage-BwT8vqrL.js → ComparePage-Byo2NzAZ.js} +1 -1
- package/web-app/dist/assets/{ErrorBoundary-D7SicDDA.js → ErrorBoundary-BuE0kH3W.js} +1 -1
- package/web-app/dist/assets/{EvidenceReceiptPanel-bx-5liG2.js → EvidenceReceiptPanel-CKFHOPBx.js} +1 -1
- package/web-app/dist/assets/{GitHubIssuesPanel-BAeeWLqY.js → GitHubIssuesPanel-Df_kTMqI.js} +1 -1
- package/web-app/dist/assets/{GitHubPRsPanel-CssD8lGI.js → GitHubPRsPanel-CouvcKrZ.js} +1 -1
- package/web-app/dist/assets/{HomePage-SL5PT3m5.js → HomePage-dEpLzj0x.js} +2 -2
- package/web-app/dist/assets/{LoginPage-C8hFOZOM.js → LoginPage-6DVjUND5.js} +1 -1
- package/web-app/dist/assets/{MagicPage-xHbHkrPf.js → MagicPage-BDQ9MOfY.js} +1 -1
- package/web-app/dist/assets/{MetricsPage-B_2CKFcC.js → MetricsPage-CXiJ04G_.js} +1 -1
- package/web-app/dist/assets/{NotFoundPage-CXwXMq35.js → NotFoundPage-BaXhZvyj.js} +1 -1
- package/web-app/dist/assets/{ProjectPage-AaisMQI-.js → ProjectPage-BzE4V7Ti.js} +3 -3
- package/web-app/dist/assets/{ProjectsPage-Cej5ResX.js → ProjectsPage-ipf6TLMX.js} +1 -1
- package/web-app/dist/assets/{SettingsPage-DKv4mn9H.js → SettingsPage-BDxkUKN6.js} +1 -1
- package/web-app/dist/assets/{ShowcasePage-Dkiyzne4.js → ShowcasePage-YQXD72SM.js} +1 -1
- package/web-app/dist/assets/{SystemSettingsPage-DaBLQ5dU.js → SystemSettingsPage-Dou7zHS0.js} +1 -1
- package/web-app/dist/assets/{TeamsPage-B2yUr31k.js → TeamsPage-CQarfcYr.js} +1 -1
- package/web-app/dist/assets/{TemplatesPage-CfYiANhU.js → TemplatesPage-CPi1R8Xj.js} +1 -1
- package/web-app/dist/assets/{TerminalOutput-CMXoexLM.js → TerminalOutput-WiGhWdYD.js} +1 -1
- package/web-app/dist/assets/{activity-B0dzoZtY.js → activity-DOa7ejQA.js} +1 -1
- package/web-app/dist/assets/{bell-DFOTaSoo.js → bell-CX-VAlYN.js} +1 -1
- package/web-app/dist/assets/{bot-Dz-WSQyP.js → bot-EDXaZmHc.js} +1 -1
- package/web-app/dist/assets/{check-L1QIRueJ.js → check-CnBdKk3L.js} +1 -1
- package/web-app/dist/assets/{chevron-left-1TrnIEZS.js → chevron-left-BJ_ydRQW.js} +1 -1
- package/web-app/dist/assets/{circle-alert-8Bf6xEDx.js → circle-alert-tz8ufhc_.js} +1 -1
- package/web-app/dist/assets/{clock-DB8NE6P5.js → clock-DFBZPSsH.js} +1 -1
- package/web-app/dist/assets/{cloud-DUSL3wZS.js → cloud-uy3p_5Q7.js} +1 -1
- package/web-app/dist/assets/{code-xml-DbjryWNl.js → code-xml-3CbYD9YL.js} +1 -1
- package/web-app/dist/assets/{database-DrezrMSs.js → database-YiNBXc9c.js} +1 -1
- package/web-app/dist/assets/{dollar-sign-CGbaLMzR.js → dollar-sign-D0wBa4Cv.js} +1 -1
- package/web-app/dist/assets/{file-code-corner-Dfypa-r4.js → file-code-corner-DDdZVZ5v.js} +1 -1
- package/web-app/dist/assets/{file-plus-CzV5L2Gd.js → file-plus-D09LTBqt.js} +1 -1
- package/web-app/dist/assets/{globe-BoYjTwPQ.js → globe-BirTPwff.js} +1 -1
- package/web-app/dist/assets/{hammer-WO-HuiyJ.js → hammer-DaE5qgnd.js} +1 -1
- package/web-app/dist/assets/{index-CjIOUjnd.js → index-D8t7KnQf.js} +3 -3
- package/web-app/dist/assets/{layers-Bn6jtKNk.js → layers-DVVrckRY.js} +1 -1
- package/web-app/dist/assets/{loader-circle-gERWRnmW.js → loader-circle-2-nMGmcb.js} +1 -1
- package/web-app/dist/assets/{lock-BIiMLJq8.js → lock-D0Xj43pC.js} +1 -1
- package/web-app/dist/assets/{package-dLvDSVys.js → package-BB7S3egm.js} +1 -1
- package/web-app/dist/assets/{plus-BHM3ZLhZ.js → plus-BLhoMrn3.js} +1 -1
- package/web-app/dist/assets/{refresh-cw-BAp_alv6.js → refresh-cw-DMc2uPLw.js} +1 -1
- package/web-app/dist/assets/{rotate-ccw-CBhwq_MG.js → rotate-ccw-DM3IP9x7.js} +1 -1
- package/web-app/dist/assets/{scroll-text-DoS-6dfU.js → scroll-text-lH2uW7rB.js} +1 -1
- package/web-app/dist/assets/{server-5JgdDWQO.js → server-BkerZcdR.js} +1 -1
- package/web-app/dist/assets/{shield-alert-DjmlLT34.js → shield-alert-B8ohcUfu.js} +1 -1
- package/web-app/dist/assets/{thumbs-up-BtiEXl3p.js → thumbs-up-CTIxLzPD.js} +1 -1
- package/web-app/dist/assets/{trash-2-ClsWU_Vg.js → trash-2-C3zIpsH5.js} +1 -1
- package/web-app/dist/assets/{trending-down-DPS4M7_X.js → trending-down-BTT7wV4Y.js} +1 -1
- package/web-app/dist/assets/{trending-up-D6OJ38-V.js → trending-up-BzHdCDvF.js} +1 -1
- package/web-app/dist/assets/{upload-DOCzLUpF.js → upload-HxR34-A8.js} +1 -1
- package/web-app/dist/assets/{usePolling-CljQTWj_.js → usePolling-DDj6Fupy.js} +1 -1
- package/web-app/dist/assets/{user-C0G82Weu.js → user-qH5y9yNy.js} +1 -1
- package/web-app/dist/index.html +1 -1
- package/dashboard/static/assets/mermaid.min.js +0 -2030
- package/dashboard/static/cost.html +0 -344
- package/dashboard/static/favicon.svg +0 -5
- package/dashboard/static/index.html +0 -15577
- package/dashboard/static/proofs.html +0 -194
- package/dashboard/static/start.html +0 -131
- package/dashboard/static/trust.html +0 -325
- package/packages/control-plane/src/server/actions.ts +0 -60
- package/packages/control-plane/ui/dist/assets/index-DHDI_dHM.css +0 -1
- package/packages/control-plane/ui/dist/assets/index-DaQr0MPS.js +0 -129
package/README.md
CHANGED
|
@@ -310,7 +310,7 @@ The URL comes from `LOKI_CONTROL_URL`, else `http://127.0.0.1:${LOKI_CONTROL_POR
|
|
|
310
310
|
|
|
311
311
|
Answering a BLOCKED run: when a run stops on a spec conflict, its run page shows the question and an answer box. Submitting posts to `POST /v1/runs/<source>/<run>/answer` (JSON `{"answer": "..."}`, up to 4000 characters, loopback only) and writes `~/.loki/control/answers/<source>/<run>.answer.txt` (override the directory with `LOKI_CONTROL_ANSWER_DIR`). The page prints the resume command, `loki answer <run>`, which starts a fresh run carrying the task, the question and that file's text (or pass `--text "..."` yourself; with no run id it picks the newest BLOCKED run). A run that is not BLOCKED exits 2.
|
|
312
312
|
|
|
313
|
-
|
|
313
|
+
The Control Plane is the only UI. `loki dashboard`, `loki dashboard start` and `loki dashboard open` open it (starting `loki control serve` if none is running). The classic dashboard UI and its port 57374 are removed in 10.8; see [docs/control-plane-migration.md](docs/control-plane-migration.md) for the route mapping and what is not yet available.
|
|
314
314
|
|
|
315
315
|
Container, Helm and ECS deployment: [docs/control-plane-container.md](docs/control-plane-container.md).
|
|
316
316
|
|
|
@@ -588,7 +588,7 @@ Legacy engine, being removed (these run on the previous engine; `loki --help` st
|
|
|
588
588
|
| `loki stop`, `pause`, `resume` | Control a legacy run |
|
|
589
589
|
| `loki steer "<note>"` | Nudge a legacy run (needs `LOKI_PROMPT_INJECTION=1`) |
|
|
590
590
|
| `loki why`, `loki next` | Explain or continue a legacy run |
|
|
591
|
-
| `loki dashboard` |
|
|
591
|
+
| `loki dashboard` | Open the Control Plane UI (see [docs/control-plane-migration.md](docs/control-plane-migration.md)) |
|
|
592
592
|
| `loki review`, `loki test`, `loki analyze`, `loki memory`, `loki failover`, `loki enterprise`, `loki import`, `loki ci` | Review, test generation, codebase analysis, memory, failover, enterprise, issue import, CI gate |
|
|
593
593
|
| `loki modernize heal <path>` | Legacy system healing |
|
|
594
594
|
|
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 v10.
|
|
6
|
+
# Loki Mode v10.9.1
|
|
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 legacy engine, which still ships and is being removed (`loki start owner/repo#N` still routes to it; prefer `loki owner/repo#N`).
|
|
9
9
|
|
|
@@ -473,4 +473,4 @@ See `CHANGELOG.md` entries [7.5.7], [7.5.8], [7.5.13] for the per-fix list and r
|
|
|
473
473
|
|
|
474
474
|
---
|
|
475
475
|
|
|
476
|
-
**v10.
|
|
476
|
+
**v10.9.1 | [Autonomi](https://www.autonomi.dev/) flagship product | core skill**
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
10.
|
|
1
|
+
10.9.1
|
package/autonomy/loki
CHANGED
|
@@ -1981,6 +1981,9 @@ _loki_control_ui() {
|
|
|
1981
1981
|
echo "loki: the Control Plane needs bun (https://bun.sh); using the classic dashboard." >&2
|
|
1982
1982
|
return 1
|
|
1983
1983
|
fi
|
|
1984
|
+
# FC-07: LOKI_CONTROL=0 is the test off switch (supervisor, run-all-tests, local-ci);
|
|
1985
|
+
# it must never start a detached `control serve` that writes ~/.loki/control.
|
|
1986
|
+
[ "${LOKI_CONTROL:-}" = "0" ] && return 1
|
|
1984
1987
|
local cli="$SKILL_DIR/loki-ts/dist/loki.js" cdir="${HOME}/.loki/control" spid
|
|
1985
1988
|
[ -f "$cli" ] || cli="$SKILL_DIR/loki-ts/src/cli.ts"
|
|
1986
1989
|
mkdir -p "$cdir" && chmod 700 "$cdir" 2>/dev/null || true
|
|
@@ -2029,12 +2032,11 @@ cmd_ui() {
|
|
|
2029
2032
|
LOKI_DASHBOARD_CORS="http://localhost:$port,http://127.0.0.1:$port" \
|
|
2030
2033
|
cmd_dashboard_start --host 127.0.0.1 --port "$port" >&2 || return 1
|
|
2031
2034
|
fi
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
fi
|
|
2035
|
+
# The classic server is API-only; the UI is the Control Plane.
|
|
2036
|
+
local url="http://127.0.0.1:${port}"
|
|
2037
|
+
echo "loki: the classic dashboard at $url serves the API only (the browser UI moved to the Control Plane)." >&2
|
|
2038
|
+
echo "loki: start the Control Plane UI with: loki ui (needs bun)" >&2
|
|
2039
|
+
echo "$url"
|
|
2038
2040
|
}
|
|
2039
2041
|
|
|
2040
2042
|
# Tight newcomer landing for the bare `loki` invocation (no args).
|
|
@@ -3912,18 +3914,9 @@ cmd_start() {
|
|
|
3912
3914
|
# + cockpit, honoring the card's "open dashboard" promise. The
|
|
3913
3915
|
# cockpit hint is printed by _loki_start_handoff for the Both path.
|
|
3914
3916
|
args+=("--bg")
|
|
3915
|
-
#
|
|
3916
|
-
#
|
|
3917
|
-
|
|
3918
|
-
# stay unredirected: loki_open_url checks the TTY.
|
|
3919
|
-
local _dfile="${PWD}/.loki/dashboard/url"
|
|
3920
|
-
rm -f "$_dfile" 2>/dev/null
|
|
3921
|
-
(
|
|
3922
|
-
for _ in $(seq 1 120); do
|
|
3923
|
-
if [ -s "$_dfile" ]; then loki_open_url "$(head -1 "$_dfile")" 2>/dev/null; exit 0; fi
|
|
3924
|
-
sleep 0.5
|
|
3925
|
-
done
|
|
3926
|
-
) &
|
|
3917
|
+
# The browser UI is the Control Plane; open it (guarded by
|
|
3918
|
+
# loki_open_url: no-op headless and in tests).
|
|
3919
|
+
( LOKI_UI_NO_CLASSIC=1 cmd_ui 2>/dev/null || true ) &
|
|
3927
3920
|
;;
|
|
3928
3921
|
launch-bg-watch)
|
|
3929
3922
|
# Watch (cockpit only) detaches without opening the browser.
|
|
@@ -7558,7 +7551,7 @@ cmd_dashboard_help() {
|
|
|
7558
7551
|
echo " loki dashboard start --tls-cert cert.pem --tls-key key.pem"
|
|
7559
7552
|
echo " loki dashboard status # Check if running"
|
|
7560
7553
|
echo " loki dashboard url --format json # Get URL as JSON"
|
|
7561
|
-
echo " loki dashboard open #
|
|
7554
|
+
echo " loki dashboard open # Print the Control Plane hint (no browser)"
|
|
7562
7555
|
echo " loki dashboard stop # Stop server"
|
|
7563
7556
|
}
|
|
7564
7557
|
|
|
@@ -7786,7 +7779,7 @@ cmd_dashboard_start() {
|
|
|
7786
7779
|
# a second port.
|
|
7787
7780
|
echo -e "${DIM}Want to submit a spec in the browser? Open the 'Lab' tab in this dashboard.${NC}"
|
|
7788
7781
|
echo ""
|
|
7789
|
-
echo -e "
|
|
7782
|
+
echo -e "Control Plane UI: ${CYAN}loki ui${NC}"
|
|
7790
7783
|
echo -e "Check status: ${CYAN}loki dashboard status${NC}"
|
|
7791
7784
|
echo -e "Stop server: ${CYAN}loki dashboard stop${NC}"
|
|
7792
7785
|
else
|
|
@@ -7904,7 +7897,7 @@ cmd_dashboard_status() {
|
|
|
7904
7897
|
fi
|
|
7905
7898
|
|
|
7906
7899
|
echo ""
|
|
7907
|
-
echo -e "
|
|
7900
|
+
echo -e "Control Plane UI: ${CYAN}loki ui${NC}"
|
|
7908
7901
|
echo -e "Stop server: ${CYAN}loki dashboard stop${NC}"
|
|
7909
7902
|
else
|
|
7910
7903
|
echo -e "${YELLOW}Status: Not running (stale PID file)${NC}"
|
|
@@ -8037,14 +8030,10 @@ cmd_dashboard_open() {
|
|
|
8037
8030
|
|
|
8038
8031
|
local url="${scheme}://${host}:${port}"
|
|
8039
8032
|
|
|
8040
|
-
|
|
8041
|
-
|
|
8042
|
-
|
|
8043
|
-
|
|
8044
|
-
echo ""
|
|
8045
|
-
echo "Browser not opened (headless, test, or no opener)."
|
|
8046
|
-
echo "Please open in browser: $url"
|
|
8047
|
-
fi
|
|
8033
|
+
# The legacy browser UI was removed; this server is API-only. Never open a
|
|
8034
|
+
# browser here: point at the Control Plane instead.
|
|
8035
|
+
echo -e "${YELLOW}The dashboard server at $url serves the API only; the browser UI is now the Control Plane.${NC}"
|
|
8036
|
+
echo -e "Open the Control Plane with: ${CYAN}loki ui${NC}"
|
|
8048
8037
|
}
|
|
8049
8038
|
|
|
8050
8039
|
# Welcome opener (the "magic opener"). Shows a branded welcome page in the
|
|
@@ -18398,8 +18387,7 @@ print(json.dumps({'id': manifest.id, 'dir': str(pipeline.migration_dir)}))
|
|
|
18398
18387
|
|
|
18399
18388
|
sleep 2
|
|
18400
18389
|
if kill -0 "$migrate_dashboard_pid" 2>/dev/null; then
|
|
18401
|
-
echo -e " Dashboard:
|
|
18402
|
-
loki_open_url "${url_scheme}://127.0.0.1:${dashboard_port}/" 2>/dev/null || true
|
|
18390
|
+
echo -e " Dashboard API: ${CYAN}${url_scheme}://127.0.0.1:${dashboard_port}/${NC} (UI: loki ui)"
|
|
18403
18391
|
echo ""
|
|
18404
18392
|
else
|
|
18405
18393
|
echo -e " ${YELLOW}Dashboard failed to start (check logs)${NC}"
|
|
@@ -21331,7 +21319,7 @@ tokens_per_tier = {
|
|
|
21331
21319
|
pricing = {
|
|
21332
21320
|
'Fable': {'input': 10.00, 'output': 50.00},
|
|
21333
21321
|
'Opus': {'input': 5.00, 'output': 25.00},
|
|
21334
|
-
'Sonnet': {'input':
|
|
21322
|
+
'Sonnet': {'input': 2.00, 'output': 10.00},
|
|
21335
21323
|
'Haiku': {'input': 1.00, 'output': 5.00},
|
|
21336
21324
|
}
|
|
21337
21325
|
|
package/autonomy/run.sh
CHANGED
|
@@ -7581,7 +7581,7 @@ _write_pricing_json() {
|
|
|
7581
7581
|
"fable": {"input": 10.00, "output": 50.00, "label": "Fable 5 (top, 2x Opus)", "provider": "claude"},
|
|
7582
7582
|
"claude-fable-5": {"input": 10.00, "output": 50.00, "label": "Fable 5 (top, 2x Opus)", "provider": "claude"},
|
|
7583
7583
|
"opus": {"input": 5.00, "output": 25.00, "label": "Opus (latest)", "provider": "claude"},
|
|
7584
|
-
"sonnet": {"input":
|
|
7584
|
+
"sonnet": {"input": 2.00, "output": 10.00, "label": "Sonnet (latest)", "provider": "claude"},
|
|
7585
7585
|
"haiku": {"input": 1.00, "output": 5.00, "label": "Haiku (latest)", "provider": "claude"},
|
|
7586
7586
|
"gpt-5.3-codex": {"input": 1.75, "output": 14.00, "label": "GPT-5.3 Codex", "provider": "codex"}
|
|
7587
7587
|
}
|
|
@@ -20250,7 +20250,7 @@ pricing = {
|
|
|
20250
20250
|
'fable': {'input': 10.00, 'output': 50.00},
|
|
20251
20251
|
'claude-fable-5': {'input': 10.00, 'output': 50.00},
|
|
20252
20252
|
'opus': {'input': 5.00, 'output': 25.00},
|
|
20253
|
-
'sonnet': {'input':
|
|
20253
|
+
'sonnet': {'input': 2.00, 'output': 10.00},
|
|
20254
20254
|
'haiku': {'input': 1.00, 'output': 5.00},
|
|
20255
20255
|
'gpt-5.3-codex': {'input': 1.75, 'output': 14.00},
|
|
20256
20256
|
'gpt-5.6-sol': {'input': 2.50, 'output': 20.00},
|
|
@@ -24828,10 +24828,14 @@ except Exception:
|
|
|
24828
24828
|
if [[ "${ENABLE_DASHBOARD:-true}" == "true" ]] && command -v curl &>/dev/null; then
|
|
24829
24829
|
local project_cwd
|
|
24830
24830
|
project_cwd="$(pwd)"
|
|
24831
|
-
|
|
24831
|
+
# CPE-24: the Control Plane answers 501 (no focus API). Best-effort; say so
|
|
24832
|
+
# honestly in debug output rather than treating the miss as an error.
|
|
24833
|
+
_focus_code="$(curl -s -o /dev/null -w '%{http_code}' -X POST "http://127.0.0.1:${DASHBOARD_PORT}/api/focus" \
|
|
24832
24834
|
-H "Content-Type: application/json" \
|
|
24833
|
-
-d "{\"project_dir\": \"${project_cwd}\"}"
|
|
24834
|
-
|
|
24835
|
+
-d "{\"project_dir\": \"${project_cwd}\"}" 2>/dev/null || true)"
|
|
24836
|
+
case "$_focus_code" in
|
|
24837
|
+
501|410) type log_debug >/dev/null 2>&1 && log_debug "project focus: not available on the Control Plane (HTTP ${_focus_code})" ;;
|
|
24838
|
+
esac
|
|
24835
24839
|
fi
|
|
24836
24840
|
|
|
24837
24841
|
# Initialize Cross-Provider Failover (v6.19.0)
|
package/autonomy/tui.sh
CHANGED
|
@@ -24,7 +24,11 @@ TUI_HAS_COLOR=false
|
|
|
24
24
|
TUI_COLS=80
|
|
25
25
|
if [ -t 1 ]; then
|
|
26
26
|
TUI_HAS_COLOR=true
|
|
27
|
-
|
|
27
|
+
# FC-10: tput can print 0 or nothing on a pty with no size; fall back to COLUMNS, then 80
|
|
28
|
+
TUI_COLS=$(tput cols 2>/dev/null || true)
|
|
29
|
+
case "$TUI_COLS" in '' | *[!0-9]*) TUI_COLS="${COLUMNS:-80}" ;; esac
|
|
30
|
+
case "$TUI_COLS" in '' | *[!0-9]*) TUI_COLS=80 ;; esac
|
|
31
|
+
[ "$TUI_COLS" -ge 40 ] || TUI_COLS=80
|
|
28
32
|
fi
|
|
29
33
|
|
|
30
34
|
# Unicode box drawing (degrade to ASCII if terminal doesn't support)
|
package/dashboard/__init__.py
CHANGED
package/dashboard/api_phases.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""Read-only phase history for a run, derived from real `phase_change` events.
|
|
2
2
|
|
|
3
|
-
WHY THIS EXISTS. dashboard
|
|
3
|
+
WHY THIS EXISTS. The legacy dashboard session timeline (removed in CPE-24) SYNTHESIZED
|
|
4
4
|
its timeline. It took a scalar status (uptime, current phase, iteration count),
|
|
5
5
|
rotated a fixed list `['planning','building','testing','reviewing']`, and gave
|
|
6
6
|
each invented segment a randomized duration:
|
package/dashboard/api_start.py
CHANGED
|
@@ -37,7 +37,6 @@ from . import auth
|
|
|
37
37
|
logger = logging.getLogger(__name__)
|
|
38
38
|
router = APIRouter(tags=["start"])
|
|
39
39
|
|
|
40
|
-
START_HTML = os.path.join(os.path.dirname(os.path.abspath(__file__)), "static", "start.html")
|
|
41
40
|
_LOKI_BIN = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "bin", "loki")
|
|
42
41
|
_REPO_RE = re.compile(r"^[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+$")
|
|
43
42
|
_TOKEN_RE = re.compile(r"^[A-Za-z0-9_\-]{10,255}$")
|
package/dashboard/server.py
CHANGED
|
@@ -1149,14 +1149,8 @@ app.add_middleware(
|
|
|
1149
1149
|
allow_headers=["Content-Type", "Authorization", "X-Requested-With"],
|
|
1150
1150
|
)
|
|
1151
1151
|
|
|
1152
|
-
# RESPONSE COMPRESSION.
|
|
1153
|
-
#
|
|
1154
|
-
# an 81% reduction. Until now only CORS and the collab WS auth middleware were
|
|
1155
|
-
# registered, so every dashboard load shipped the full 780KB.
|
|
1156
|
-
#
|
|
1157
|
-
# That single fact is the most plausible cause of "the dashboard feels slow":
|
|
1158
|
-
# it is not a rendering problem, it is 630KB of avoidable transfer on first
|
|
1159
|
-
# paint, and it costs one middleware to fix.
|
|
1152
|
+
# RESPONSE COMPRESSION. Large JSON API payloads compress well, and it costs one
|
|
1153
|
+
# middleware. (The legacy single-file UI bundle that motivated it was removed.)
|
|
1160
1154
|
#
|
|
1161
1155
|
# minimum_size=1024 leaves small JSON responses uncompressed, where the CPU
|
|
1162
1156
|
# round-trip outweighs the saving. GZipMiddleware is stdlib-backed and does
|
|
@@ -1360,16 +1354,12 @@ else:
|
|
|
1360
1354
|
app.include_router(api_operator_router)
|
|
1361
1355
|
|
|
1362
1356
|
# D51 Phase A: first-run onboarding + backlog API and its /start page.
|
|
1363
|
-
from .api_start import router as api_start_router
|
|
1357
|
+
from .api_start import router as api_start_router
|
|
1364
1358
|
app.include_router(api_start_router)
|
|
1365
1359
|
from .api_runs_v1 import router as api_runs_v1_router
|
|
1366
1360
|
app.include_router(api_runs_v1_router)
|
|
1367
1361
|
|
|
1368
1362
|
|
|
1369
|
-
@app.get("/start", include_in_schema=False, dependencies=[Depends(auth.require_scope("read"))])
|
|
1370
|
-
async def serve_start_page():
|
|
1371
|
-
return FileResponse(_START_HTML, media_type="text/html")
|
|
1372
|
-
|
|
1373
1363
|
|
|
1374
1364
|
# Phase Merge-4: Mount Purple Lab FastAPI app under /lab/ so it appears as a
|
|
1375
1365
|
# sidebar entry in Dashboard. Same `app` is also wrapped by `standalone_app`
|
|
@@ -7752,7 +7742,7 @@ _DEFAULT_PRICING = {
|
|
|
7752
7742
|
"fable": {"input": 10.00, "output": 50.00},
|
|
7753
7743
|
"claude-fable-5": {"input": 10.00, "output": 50.00},
|
|
7754
7744
|
"opus": {"input": 5.00, "output": 25.00},
|
|
7755
|
-
"sonnet": {"input":
|
|
7745
|
+
"sonnet": {"input": 2.00, "output": 10.00},
|
|
7756
7746
|
"haiku": {"input": 1.00, "output": 5.00},
|
|
7757
7747
|
# OpenAI Codex
|
|
7758
7748
|
"gpt-5.3-codex": {"input": 1.50, "output": 12.00},
|
|
@@ -8587,18 +8577,16 @@ _PROVIDER_LABELS = {
|
|
|
8587
8577
|
}
|
|
8588
8578
|
|
|
8589
8579
|
# Display-only pricing notes, keyed by model. These annotate the pricing table in
|
|
8590
|
-
# the UI WITHOUT changing any computed cost number. The Sonnet 5
|
|
8591
|
-
#
|
|
8592
|
-
#
|
|
8593
|
-
# direction (a bill can only come in lower than quoted, never higher). This note
|
|
8594
|
-
# tells the user why the quote is conservative during the intro window.
|
|
8580
|
+
# the UI WITHOUT changing any computed cost number. The Sonnet 5 intro price
|
|
8581
|
+
# ($2/$10 per MTok) became the standard price, so the 2026-09-01 increase to
|
|
8582
|
+
# $3/$15 will not occur (platform.claude.com pricing page, read 2026-10-03).
|
|
8595
8583
|
_UNVERIFIED_RATE_NOTE = (
|
|
8596
8584
|
"Unverified placeholder rate, scaled from gpt-5.3; not from OpenAI's pricing page."
|
|
8597
8585
|
)
|
|
8598
8586
|
_MODEL_PRICING_NOTES = {
|
|
8599
8587
|
"sonnet": (
|
|
8600
|
-
"
|
|
8601
|
-
"
|
|
8588
|
+
"Standard pricing: $2 / $10 per MTok. "
|
|
8589
|
+
"The intro price became the standard price; no increase to $3 / $15."
|
|
8602
8590
|
),
|
|
8603
8591
|
# See the UNVERIFIED RATES comment on the pricing table: shown, not hidden.
|
|
8604
8592
|
"gpt-5.6-sol": _UNVERIFIED_RATE_NOTE,
|
|
@@ -11581,49 +11569,11 @@ def optimize_prompts(sessions: int = 10, dry_run: bool = True):
|
|
|
11581
11569
|
|
|
11582
11570
|
from fastapi.responses import FileResponse, HTMLResponse
|
|
11583
11571
|
|
|
11584
|
-
# Find static files in multiple possible locations
|
|
11585
11572
|
DASHBOARD_DIR = os.path.dirname(os.path.abspath(__file__))
|
|
11586
11573
|
PROJECT_ROOT = os.path.dirname(DASHBOARD_DIR)
|
|
11587
11574
|
|
|
11588
|
-
|
|
11589
|
-
|
|
11590
|
-
def _static_candidates(skill_dir=None, home=None):
|
|
11591
|
-
"""Static dirs in preference order: package, LOKI_SKILL_DIR, ~/.claude skill, dev build."""
|
|
11592
|
-
skill_dir = os.environ.get("LOKI_SKILL_DIR", "") if skill_dir is None else skill_dir
|
|
11593
|
-
home_skill = os.path.join(home or os.path.expanduser("~"), ".claude", "skills", "loki-mode")
|
|
11594
|
-
cands = [os.path.join(DASHBOARD_DIR, "static")]
|
|
11595
|
-
if skill_dir:
|
|
11596
|
-
cands.append(os.path.join(skill_dir, "dashboard", "static"))
|
|
11597
|
-
cands.append(os.path.join(home_skill, "dashboard", "static"))
|
|
11598
|
-
cands.append(os.path.join(PROJECT_ROOT, "dashboard-ui", "dist"))
|
|
11599
|
-
if skill_dir:
|
|
11600
|
-
cands.append(os.path.join(skill_dir, "dashboard-ui", "dist"))
|
|
11601
|
-
cands.append(os.path.join(home_skill, "dashboard-ui", "dist"))
|
|
11602
|
-
return cands
|
|
11603
|
-
|
|
11604
|
-
|
|
11605
|
-
def _pick_static_dir(candidates):
|
|
11606
|
-
"""First candidate that CONTAINS index.html; an empty or partial dir must not win."""
|
|
11607
|
-
for loc in candidates:
|
|
11608
|
-
if os.path.isfile(os.path.join(loc, "index.html")):
|
|
11609
|
-
return loc
|
|
11610
|
-
return None
|
|
11611
|
-
|
|
11612
|
-
|
|
11613
|
-
STATIC_LOCATIONS = _static_candidates()
|
|
11614
|
-
STATIC_DIR = _pick_static_dir(STATIC_LOCATIONS)
|
|
11615
|
-
if STATIC_DIR:
|
|
11616
|
-
logger.info("Dashboard frontend served from: %s", STATIC_DIR)
|
|
11617
|
-
else:
|
|
11618
|
-
logger.warning("Dashboard frontend NOT found (looked in %s); serving built-in fallback page", STATIC_LOCATIONS)
|
|
11619
|
-
|
|
11620
|
-
if STATIC_DIR:
|
|
11621
|
-
from fastapi.staticfiles import StaticFiles
|
|
11622
|
-
|
|
11623
|
-
# Check if assets directory exists (built frontend)
|
|
11624
|
-
ASSETS_DIR = os.path.join(STATIC_DIR, "assets")
|
|
11625
|
-
if os.path.isdir(ASSETS_DIR):
|
|
11626
|
-
app.mount("/assets", StaticFiles(directory=ASSETS_DIR), name="assets")
|
|
11575
|
+
DASHBOARD_DIR = os.path.dirname(os.path.abspath(__file__))
|
|
11576
|
+
PROJECT_ROOT = os.path.dirname(DASHBOARD_DIR)
|
|
11627
11577
|
|
|
11628
11578
|
# ---------------------------------------------------------------------------
|
|
11629
11579
|
# Activity Logger & Session Diff
|
|
@@ -11684,75 +11634,29 @@ async def log_activity(entry: dict):
|
|
|
11684
11634
|
return result
|
|
11685
11635
|
|
|
11686
11636
|
|
|
11687
|
-
#
|
|
11688
|
-
|
|
11689
|
-
async def serve_favicon():
|
|
11690
|
-
"""Serve the dashboard favicon."""
|
|
11691
|
-
if STATIC_DIR:
|
|
11692
|
-
favicon_path = os.path.join(STATIC_DIR, "favicon.svg")
|
|
11693
|
-
if os.path.isfile(favicon_path):
|
|
11694
|
-
return FileResponse(favicon_path, media_type="image/svg+xml")
|
|
11695
|
-
return Response(status_code=404)
|
|
11696
|
-
|
|
11697
|
-
|
|
11698
|
-
# Serve the self-contained cost + observability panel (R3). Zero-build
|
|
11699
|
-
# standalone page that fetches /api/cost/timeline. Mirrors the proofs.html
|
|
11700
|
-
# pattern: works without the SPA build.
|
|
11701
|
-
@app.get("/cost", include_in_schema=False, dependencies=[Depends(auth.require_scope("read"))])
|
|
11702
|
-
async def serve_cost_panel():
|
|
11703
|
-
"""Serve the standalone cost + observability HTML panel."""
|
|
11704
|
-
if STATIC_DIR:
|
|
11705
|
-
cost_path = os.path.join(STATIC_DIR, "cost.html")
|
|
11706
|
-
if os.path.isfile(cost_path):
|
|
11707
|
-
return FileResponse(cost_path, media_type="text/html")
|
|
11708
|
-
return Response(status_code=404)
|
|
11709
|
-
|
|
11710
|
-
|
|
11711
|
-
# R4: standalone trust-trajectory page that fetches /api/trust/trajectory.
|
|
11712
|
-
# Mirrors the cost.html / /cost pattern: works without the SPA build.
|
|
11713
|
-
@app.get("/trust", include_in_schema=False, dependencies=[Depends(auth.require_scope("read"))])
|
|
11714
|
-
async def serve_trust_panel():
|
|
11715
|
-
"""Serve the standalone trust-trajectory HTML panel."""
|
|
11716
|
-
if STATIC_DIR:
|
|
11717
|
-
trust_path = os.path.join(STATIC_DIR, "trust.html")
|
|
11718
|
-
if os.path.isfile(trust_path):
|
|
11719
|
-
return FileResponse(trust_path, media_type="text/html")
|
|
11720
|
-
return Response(status_code=404)
|
|
11721
|
-
|
|
11722
|
-
|
|
11723
|
-
# Serve index.html or standalone HTML for root
|
|
11637
|
+
# The legacy dashboard UI was removed (CPE-24). The Control Plane serves the UI;
|
|
11638
|
+
# this server keeps only the /api/* routes that have no Control Plane home yet.
|
|
11724
11639
|
@app.get("/", include_in_schema=False, dependencies=[Depends(auth.require_scope("read"))])
|
|
11725
11640
|
async def serve_index():
|
|
11726
|
-
"""
|
|
11727
|
-
|
|
11728
|
-
index_candidates = []
|
|
11729
|
-
if STATIC_DIR:
|
|
11730
|
-
index_candidates.append(os.path.join(STATIC_DIR, "index.html"))
|
|
11731
|
-
index_candidates.append(os.path.join(STATIC_DIR, "loki-dashboard-standalone.html"))
|
|
11641
|
+
"""Tell a browser where the UI moved; never serve developer JSON to it."""
|
|
11642
|
+
return HTMLResponse(content=_moved_page(), status_code=410)
|
|
11732
11643
|
|
|
11733
|
-
# Also check dashboard-ui directly for standalone
|
|
11734
|
-
standalone_path = os.path.join(PROJECT_ROOT, "dashboard-ui", "dist", "loki-dashboard-standalone.html")
|
|
11735
|
-
if standalone_path not in index_candidates:
|
|
11736
|
-
index_candidates.append(standalone_path)
|
|
11737
11644
|
|
|
11738
|
-
|
|
11739
|
-
if os.path.isfile(index_path):
|
|
11740
|
-
return FileResponse(index_path, media_type="text/html")
|
|
11741
|
-
|
|
11742
|
-
# No frontend anywhere: a browser must never see developer JSON.
|
|
11743
|
-
return HTMLResponse(content=_fallback_page(), status_code=503)
|
|
11744
|
-
|
|
11745
|
-
|
|
11746
|
-
def _fallback_page() -> str:
|
|
11645
|
+
def _moved_page() -> str:
|
|
11747
11646
|
import html as _html
|
|
11647
|
+
cp = os.environ.get("LOKI_CONTROL_PLANE_URL", "").strip()
|
|
11648
|
+
where = (
|
|
11649
|
+
f'<p>Control Plane: <a href="{_html.escape(cp, quote=True)}">{_html.escape(cp)}</a></p>'
|
|
11650
|
+
if cp.startswith(("http://", "https://"))
|
|
11651
|
+
else "<p>Run <code>loki ui</code> to open the Control Plane.</p>"
|
|
11652
|
+
)
|
|
11748
11653
|
return (
|
|
11749
11654
|
'<!doctype html><html lang="en"><head><meta charset="utf-8">'
|
|
11750
|
-
"<title>Loki Mode
|
|
11655
|
+
"<title>Loki Mode API</title></head>"
|
|
11751
11656
|
'<body style="font-family:system-ui,sans-serif;max-width:40rem;margin:4rem auto;padding:0 1rem">'
|
|
11752
|
-
"<h1>
|
|
11753
|
-
f"<p>
|
|
11754
|
-
"
|
|
11755
|
-
"<p>Reinstall to fix it:</p><pre>npm install -g loki-mode@latest</pre>"
|
|
11657
|
+
"<h1>The dashboard UI moved to the Control Plane</h1>"
|
|
11658
|
+
f"<p>This server (version {_html.escape(str(_version))}) now serves the /api routes only.</p>"
|
|
11659
|
+
f"{where}"
|
|
11756
11660
|
'<p><a href="/docs">API docs</a> | <a href="/health">Health</a></p>'
|
|
11757
11661
|
"</body></html>"
|
|
11758
11662
|
)
|
|
@@ -13101,40 +13005,18 @@ async def post_wiki_ask(req: WikiAskRequest):
|
|
|
13101
13005
|
|
|
13102
13006
|
|
|
13103
13007
|
# ---------------------------------------------------------------------------
|
|
13104
|
-
#
|
|
13105
|
-
# or static asset mounts. This lets the dashboard UI handle client-side routing.
|
|
13008
|
+
# Catch-all: unknown paths are real 404s (the SPA was removed, CPE-24).
|
|
13106
13009
|
# Must be registered LAST so it never shadows an API endpoint.
|
|
13107
13010
|
# ---------------------------------------------------------------------------
|
|
13108
13011
|
@app.get("/{full_path:path}", include_in_schema=False, dependencies=[Depends(auth.require_scope("read"))])
|
|
13109
13012
|
async def serve_spa_catchall(full_path: str):
|
|
13110
|
-
"""
|
|
13111
|
-
|
|
13112
|
-
v7.6.1 B-10 fix: requests under /api/, /lab/api/, or /ws/ that fall through
|
|
13113
|
-
here are missing routes, not SPA navigation. Returning index.html (text/html)
|
|
13114
|
-
silently masks 404s and breaks JSON clients (the dashboard UI's loki-memory-browser
|
|
13115
|
-
pinged /api/learning/metrics expecting JSON and got an HTML SPA on prior
|
|
13116
|
-
failures). Return a JSON 404 instead so clients fail loud.
|
|
13117
|
-
"""
|
|
13118
|
-
# API paths that fell through are real 404s, not SPA routes.
|
|
13013
|
+
"""Return a JSON 404 for API-like paths so JSON clients fail loud, else a bare 404."""
|
|
13119
13014
|
api_like = full_path.startswith("api/") or full_path.startswith("lab/api/") or full_path.startswith("ws/")
|
|
13120
13015
|
if api_like:
|
|
13121
13016
|
return JSONResponse(
|
|
13122
13017
|
status_code=404,
|
|
13123
13018
|
content={"error": "Not Found", "path": f"/{full_path}"},
|
|
13124
13019
|
)
|
|
13125
|
-
if STATIC_DIR:
|
|
13126
|
-
static_root = os.path.realpath(STATIC_DIR)
|
|
13127
|
-
# Try to serve the exact file first (e.g. /vite.svg, /manifest.json)
|
|
13128
|
-
# Use realpath to prevent path traversal attacks
|
|
13129
|
-
candidate = os.path.realpath(os.path.join(STATIC_DIR, full_path))
|
|
13130
|
-
if not candidate.startswith(static_root + os.sep) and candidate != static_root:
|
|
13131
|
-
return Response(status_code=404)
|
|
13132
|
-
if os.path.isfile(candidate):
|
|
13133
|
-
return FileResponse(candidate)
|
|
13134
|
-
# Fall back to index.html for client-side routes
|
|
13135
|
-
index_path = os.path.join(STATIC_DIR, "index.html")
|
|
13136
|
-
if os.path.isfile(index_path):
|
|
13137
|
-
return FileResponse(index_path, media_type="text/html")
|
|
13138
13020
|
return Response(status_code=404)
|
|
13139
13021
|
|
|
13140
13022
|
|
package/docs/COCKPIT-SPEC.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# Loki Cockpit (rec #6) - build spec
|
|
2
2
|
|
|
3
|
+
> **Historical:** this describes the classic dashboard, removed in Loki Mode 10.8. The Control Plane is the only UI; see [control-plane-migration.md](control-plane-migration.md).
|
|
4
|
+
|
|
3
5
|
Status: IN BUILD (v7.126.0 target). Design approved via mockup
|
|
4
6
|
(scratchpad/loki-cockpit.html). Grounded in real engine state + Autonomi identity.
|
|
5
7
|
|
|
@@ -59,7 +61,7 @@ No chafa / no headless Chrome hard dependency. Pipeline:
|
|
|
59
61
|
- S3 (render pipeline): SVG-from-state builder + SVG->PNG + terminal-image
|
|
60
62
|
encoder + capability detection + fallback. Tests (encoder byte-shape, detection).
|
|
61
63
|
- S4 (dashboard identity redesign): apply logo/purple/fonts/multi-repo switcher to
|
|
62
|
-
|
|
64
|
+
legacy-ui; rebuild dist. Lightweight gate.
|
|
63
65
|
- S5 (docs + wiki + release): help text, README, wiki, CHANGELOG, 14 version files.
|
|
64
66
|
|
|
65
67
|
## Constraints
|
|
@@ -28,7 +28,7 @@ carries a `reason`, an unmeasured number reads `None` rather than `0`, and
|
|
|
28
28
|
| `source` | 10 of 43 | Most rows cannot be audited back to the file they came from |
|
|
29
29
|
|
|
30
30
|
```bash
|
|
31
|
-
cd
|
|
31
|
+
cd legacy-ui
|
|
32
32
|
grep -l "freshness_s" components/*.js core/*.js | wc -l # 0
|
|
33
33
|
grep -l "measured" components/*.js core/*.js | wc -l # 0
|
|
34
34
|
```
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# Dashboard Architecture: Real-Data, Near-Realtime
|
|
2
2
|
|
|
3
|
+
> **Historical:** this describes the classic dashboard, removed in Loki Mode 10.8. The Control Plane is the only UI; see [control-plane-migration.md](control-plane-migration.md).
|
|
4
|
+
|
|
3
5
|
Design only. No implementation in this document.
|
|
4
6
|
|
|
5
7
|
Status: proposal. Measured against the worktree at
|
|
@@ -41,7 +43,7 @@ brief's number and my measurement disagree, both are shown.
|
|
|
41
43
|
|
|
42
44
|
One correction against myself: my first pass reported `0` aria attributes in the
|
|
43
45
|
built artifact and I nearly wrote that up as a shipped accessibility regression.
|
|
44
|
-
It was a measurement error. `
|
|
46
|
+
It was a measurement error. `legacy-ui-static/index.html` has lines long enough
|
|
45
47
|
(577 chars plus minified runs) that `grep` treated the file as binary and
|
|
46
48
|
suppressed output. With `grep -a` the brief's numbers are confirmed. This is the
|
|
47
49
|
`grep-absence false-green` failure mode: an empty result was an absent
|
|
@@ -178,7 +180,7 @@ machine-checkable instead of a naming habit.
|
|
|
178
180
|
`/api/health/processes`.
|
|
179
181
|
|
|
180
182
|
Built artifact realtime constructs: 9
|
|
181
|
-
(`grep -oaE "new WebSocket|EventSource|setInterval"
|
|
183
|
+
(`grep -oaE "new WebSocket|EventSource|setInterval" legacy-ui-static/index.html | wc -l`).
|
|
182
184
|
The brief's "34 realtime constructs" is not reproducible by any pattern I tried;
|
|
183
185
|
I mark that figure **not independently verified** rather than repeat it.
|
|
184
186
|
|
|
@@ -234,8 +236,8 @@ The bundle is 779,725 bytes raw, 150,341 gzipped
|
|
|
234
236
|
gzip stays; nothing in this design touches it.
|
|
235
237
|
|
|
236
238
|
**Progressive disclosure here cannot mean code-splitting.**
|
|
237
|
-
`
|
|
238
|
-
file with zero runtime dependencies, written to both `
|
|
239
|
+
`legacy-ui/scripts/build-standalone.js` produces a single self-contained
|
|
240
|
+
file with zero runtime dependencies, written to both `legacy-ui-static/` and
|
|
239
241
|
`dist/`. That property is why the dashboard works offline and inside Docker
|
|
240
242
|
without a CDN. Splitting the bundle would trade a real capability for a
|
|
241
243
|
transfer-size win that gzip has already mostly taken (5.2x). So disclosure is
|
|
@@ -265,12 +267,12 @@ Measured on the shipped artifact with `grep -a`:
|
|
|
265
267
|
|
|
266
268
|
| Property | Count | Command |
|
|
267
269
|
|---|---|---|
|
|
268
|
-
| Custom elements registered | 39 | `grep -oa "customElements.define"
|
|
270
|
+
| Custom elements registered | 39 | `grep -oa "customElements.define" legacy-ui-static/index.html \| wc -l` |
|
|
269
271
|
| `aria-*` attributes | 100 | `grep -oa "aria-[a-z]*=" ... \| wc -l` |
|
|
270
272
|
| `aria-` occurrences | 113 | `grep -oa "aria-" ... \| wc -l` |
|
|
271
273
|
| `role="` | 38 | `grep -oa 'role="' ... \| wc -l` |
|
|
272
274
|
| `@media` breakpoints | 13 | `grep -oa "@media" ... \| wc -l` |
|
|
273
|
-
| Component source files | 43 | `ls
|
|
275
|
+
| Component source files | 43 | `ls legacy-ui/components \| grep -v vendor \| wc -l` |
|
|
274
276
|
| Registrations in source | 44 | `grep -roha "customElements.define" components core \| wc -l` |
|
|
275
277
|
|
|
276
278
|
The brief's 113 aria and 41 role are consistent with mine within pattern choice
|
|
@@ -278,7 +280,7 @@ The brief's 113 aria and 41 role are consistent with mine within pattern choice
|
|
|
278
280
|
figure is 43 files carrying 44 registrations, not 46.
|
|
279
281
|
|
|
280
282
|
**One gap worth noting:** source registers 44 custom elements, the shipped
|
|
281
|
-
artifact registers 39. Five components exist in `
|
|
283
|
+
artifact registers 39. Five components exist in `legacy-ui/` and do not reach
|
|
282
284
|
the browser. This is out of scope for this document and I did not chase it, but
|
|
283
285
|
it is recorded here because it means the source tree is not a reliable proxy for
|
|
284
286
|
what ships. All acceptance criteria in section 5 therefore measure the **built
|
|
@@ -369,10 +371,10 @@ Every row is measurable by a command. No aspirational entries.
|
|
|
369
371
|
|---|---|---|---|
|
|
370
372
|
| A1 | No route regression | `grep -cE '^@app\.(get\|post\|put\|delete\|patch\|websocket)' dashboard/server.py` | >= 165 |
|
|
371
373
|
| A2 | api_v2 routes intact | `grep -cE '@router\.(get\|post\|put\|delete)' dashboard/api_v2.py` | >= 24 |
|
|
372
|
-
| A3 | Accessibility not regressed | `grep -oa "aria-"
|
|
373
|
-
| A4 | Roles not regressed | `grep -oa 'role="'
|
|
374
|
-
| A5 | Custom elements not regressed | `grep -oa "customElements.define"
|
|
375
|
-
| A6 | Breakpoints not regressed | `grep -oa "@media"
|
|
374
|
+
| A3 | Accessibility not regressed | `grep -oa "aria-" legacy-ui-static/index.html \| wc -l` | >= 113 |
|
|
375
|
+
| A4 | Roles not regressed | `grep -oa 'role="' legacy-ui-static/index.html \| wc -l` | >= 38 |
|
|
376
|
+
| A5 | Custom elements not regressed | `grep -oa "customElements.define" legacy-ui-static/index.html \| wc -l` | >= 39 |
|
|
377
|
+
| A6 | Breakpoints not regressed | `grep -oa "@media" legacy-ui-static/index.html \| wc -l` | >= 13 |
|
|
376
378
|
| A7 | gzip still active | `grep -c "GZipMiddleware" dashboard/server.py` | >= 1 |
|
|
377
379
|
| A8 | Zero runtime dependencies | Built artifact contains no external script/CDN src | 0 external hosts |
|
|
378
380
|
| A9 | Runs store is written by the engine | Drive the lifecycle writer directly (source `run.sh`, call the phase-boundary function), then `GET /api/v2/runs`. No provider call, no spend | Returns >= 1 record with a real run id |
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# Loki Mode Dashboard V2 - Implementation Checklist
|
|
2
2
|
|
|
3
|
+
> **Historical:** this describes the classic dashboard, removed in Loki Mode 10.8. The Control Plane is the only UI; see [control-plane-migration.md](control-plane-migration.md).
|
|
4
|
+
|
|
3
5
|
## Overview
|
|
4
6
|
Enterprise-grade Kanban dashboard with FastAPI backend, cross-project support, and CLI integration.
|
|
5
7
|
|