Compare commits

...
Sign in to create a new pull request.

7 commits
dev ... main

Author SHA1 Message Date
1a7d67df7a Add anti-wipe guard for hfToken in cookbook state sync
Some checks failed
CI / Focused test guidance (report-only) (push) Has been cancelled
CI / Python syntax (compileall) (push) Has been cancelled
CI / JS syntax (node --check) (push) Has been cancelled
CI / Python tests (pytest) (push) Has been cancelled
Container scan / hadolint (Dockerfile lint) (push) Has been cancelled
Container scan (Trivy) / Trivy (image scan, advisory) (push) Has been cancelled
Container scan (Trivy) / Trivy (image scan + SARIF upload) (push) Has been cancelled
Dependency review / dependency-review (PR gate) (push) Has been cancelled
Dependency review / pip-audit (advisory) (push) Has been cancelled
ci / docker publish / build (amd64) (push) Has been cancelled
ci / docker publish / build (arm64) (push) Has been cancelled
Secret scan / gitleaks (push) Has been cancelled
Workflow security / actionlint (push) Has been cancelled
Workflow security / zizmor (Actions SAST) (push) Has been cancelled
ci / docker publish / merge manifest + tag (push) Has been cancelled
The frontend strips hfToken from state responses (GET replaces it with masked
version). When it syncs back via POST, incoming state has no hfToken. The
servers anti-wipe guard existed but hfToken had no equivalent protection,
so the token could be wiped by a sync race. Added hfToken preservation
matching the servers guard pattern.
2026-07-07 00:39:50 -04:00
80f38c9443 Detect llama.cpp server readiness for auto-endpoint registration
Some checks are pending
CI / Focused test guidance (report-only) (push) Waiting to run
CI / Python syntax (compileall) (push) Waiting to run
CI / JS syntax (node --check) (push) Waiting to run
CI / Python tests (pytest) (push) Waiting to run
Container scan / hadolint (Dockerfile lint) (push) Waiting to run
Container scan (Trivy) / Trivy (image scan, advisory) (push) Waiting to run
Container scan (Trivy) / Trivy (image scan + SARIF upload) (push) Waiting to run
Dependency review / dependency-review (PR gate) (push) Waiting to run
Dependency review / pip-audit (advisory) (push) Waiting to run
ci / docker publish / build (amd64) (push) Waiting to run
ci / docker publish / build (arm64) (push) Waiting to run
ci / docker publish / merge manifest + tag (push) Blocked by required conditions
Secret scan / gitleaks (push) Waiting to run
Workflow security / actionlint (push) Waiting to run
Workflow security / zizmor (Actions SAST) (push) Waiting to run
_parseServePhase only recognized vLLM, FastAPI, and Ollama ready states.
llama-server's output ('server is listening on http://...') didn't match
any pattern, so _serveReady was never set and auto-registration never fired.
Added regex matching llamacpp-style listening messages.
2026-07-07 00:26:47 -04:00
6918d9417f Fix ReferenceError: streamingTTS is not defined in chat error handler
Some checks are pending
CI / Focused test guidance (report-only) (push) Waiting to run
CI / Python syntax (compileall) (push) Waiting to run
CI / JS syntax (node --check) (push) Waiting to run
CI / Python tests (pytest) (push) Waiting to run
Container scan / hadolint (Dockerfile lint) (push) Waiting to run
Container scan (Trivy) / Trivy (image scan, advisory) (push) Waiting to run
Container scan (Trivy) / Trivy (image scan + SARIF upload) (push) Waiting to run
Dependency review / dependency-review (PR gate) (push) Waiting to run
Dependency review / pip-audit (advisory) (push) Waiting to run
ci / docker publish / build (amd64) (push) Waiting to run
ci / docker publish / build (arm64) (push) Waiting to run
ci / docker publish / merge manifest + tag (push) Blocked by required conditions
Secret scan / gitleaks (push) Waiting to run
Workflow security / actionlint (push) Waiting to run
Workflow security / zizmor (Actions SAST) (push) Waiting to run
streamingTTS const is out of scope in the catch block at handleChatSubmit.
Replaced with direct window.aiTTSManager check using optional chaining.
2026-07-07 00:23:10 -04:00
9cbe6a165b Connect Ulysses to searxng network for search engine access
Some checks are pending
CI / Focused test guidance (report-only) (push) Waiting to run
CI / Python syntax (compileall) (push) Waiting to run
CI / JS syntax (node --check) (push) Waiting to run
CI / Python tests (pytest) (push) Waiting to run
Container scan / hadolint (Dockerfile lint) (push) Waiting to run
Container scan (Trivy) / Trivy (image scan, advisory) (push) Waiting to run
Container scan (Trivy) / Trivy (image scan + SARIF upload) (push) Waiting to run
Dependency review / dependency-review (PR gate) (push) Waiting to run
Dependency review / pip-audit (advisory) (push) Waiting to run
ci / docker publish / build (amd64) (push) Waiting to run
ci / docker publish / build (arm64) (push) Waiting to run
ci / docker publish / merge manifest + tag (push) Blocked by required conditions
Secret scan / gitleaks (push) Waiting to run
Workflow security / actionlint (push) Waiting to run
Workflow security / zizmor (Actions SAST) (push) Waiting to run
searxng runs on a separate Docker network (searxng_default) and the
container couldn't reach host-port 8888 due to Docker bridge isolation.
Added searxng_default as external network and attached odysseus service
to it, allowing direct container-to-container communication via
http://searxng:8080.
2026-07-06 23:24:00 -04:00
886d941a65 Point SEARXNG_INSTANCE to standalone searxng at host.docker.internal:8888
Some checks are pending
CI / Focused test guidance (report-only) (push) Waiting to run
CI / Python syntax (compileall) (push) Waiting to run
CI / JS syntax (node --check) (push) Waiting to run
CI / Python tests (pytest) (push) Waiting to run
Container scan / hadolint (Dockerfile lint) (push) Waiting to run
Container scan (Trivy) / Trivy (image scan, advisory) (push) Waiting to run
Container scan (Trivy) / Trivy (image scan + SARIF upload) (push) Waiting to run
Dependency review / dependency-review (PR gate) (push) Waiting to run
Dependency review / pip-audit (advisory) (push) Waiting to run
ci / docker publish / build (amd64) (push) Waiting to run
ci / docker publish / build (arm64) (push) Waiting to run
ci / docker publish / merge manifest + tag (push) Blocked by required conditions
Secret scan / gitleaks (push) Waiting to run
Workflow security / actionlint (push) Waiting to run
Workflow security / zizmor (Actions SAST) (push) Waiting to run
Bundled searxng was removed — the old http://searxng:8080 DNS name
resolves to nothing. Standalone searxng runs on Helm at port 8888,
reachable from container via host.docker.internal.
2026-07-06 22:29:11 -04:00
e7b5274af6 Add LD_LIBRARY_PATH=/app/.local/bin to container env for native llama.cpp binary
Some checks are pending
CI / Focused test guidance (report-only) (push) Waiting to run
CI / Python syntax (compileall) (push) Waiting to run
CI / JS syntax (node --check) (push) Waiting to run
CI / Python tests (pytest) (push) Waiting to run
Container scan / hadolint (Dockerfile lint) (push) Waiting to run
Container scan (Trivy) / Trivy (image scan, advisory) (push) Waiting to run
Container scan (Trivy) / Trivy (image scan + SARIF upload) (push) Waiting to run
Dependency review / dependency-review (PR gate) (push) Waiting to run
Dependency review / pip-audit (advisory) (push) Waiting to run
ci / docker publish / build (amd64) (push) Waiting to run
ci / docker publish / build (arm64) (push) Waiting to run
ci / docker publish / merge manifest + tag (push) Blocked by required conditions
Secret scan / gitleaks (push) Waiting to run
Workflow security / actionlint (push) Waiting to run
Workflow security / zizmor (Actions SAST) (push) Waiting to run
The manual native CUDA build's rpath points to /tmp/llama.cpp/build/bin/
(host build directory) instead of $ORIGIN. Setting LD_LIBRARY_PATH in the
Docker environment ensures the shared libraries are found inside the container
and survives container restarts (unlike the ephemeral ~/.config env file).
2026-07-06 21:56:56 -04:00
4b98744495 ulysses: remove bundled searxng — conflicts with standalone instance at :8888
Some checks are pending
CI / Focused test guidance (report-only) (push) Waiting to run
CI / Python syntax (compileall) (push) Waiting to run
CI / JS syntax (node --check) (push) Waiting to run
CI / Python tests (pytest) (push) Waiting to run
Container scan / hadolint (Dockerfile lint) (push) Waiting to run
Container scan (Trivy) / Trivy (image scan, advisory) (push) Waiting to run
Container scan (Trivy) / Trivy (image scan + SARIF upload) (push) Waiting to run
Dependency review / dependency-review (PR gate) (push) Waiting to run
Dependency review / pip-audit (advisory) (push) Waiting to run
ci / docker publish / build (amd64) (push) Waiting to run
ci / docker publish / build (arm64) (push) Waiting to run
ci / docker publish / merge manifest + tag (push) Blocked by required conditions
Secret scan / gitleaks (push) Waiting to run
Workflow security / actionlint (push) Waiting to run
Workflow security / zizmor (Actions SAST) (push) Waiting to run
2026-07-06 21:26:20 -04:00
4 changed files with 64 additions and 48 deletions

View file

@ -21,6 +21,7 @@ services:
# Ollama at http://host.docker.internal:11434. # Ollama at http://host.docker.internal:11434.
- "host.docker.internal:host-gateway" - "host.docker.internal:host-gateway"
environment: environment:
- LD_LIBRARY_PATH=/app/.local/bin
- LLM_HOST=${LLM_HOST:-localhost} - LLM_HOST=${LLM_HOST:-localhost}
- LLM_HOSTS=${LLM_HOSTS:-} - LLM_HOSTS=${LLM_HOSTS:-}
- OPENAI_API_KEY=${OPENAI_API_KEY:-} - OPENAI_API_KEY=${OPENAI_API_KEY:-}
@ -71,11 +72,14 @@ services:
- PUID=${PUID:-1000} - PUID=${PUID:-1000}
- PGID=${PGID:-1000} - PGID=${PGID:-1000}
depends_on: depends_on:
searxng: # searxng:
condition: service_healthy # condition: service_healthy
chromadb: chromadb:
condition: service_started condition: service_started
restart: unless-stopped restart: unless-stopped
networks:
- default
- searxng-net
chromadb: chromadb:
image: docker.io/chromadb/chroma:latest image: docker.io/chromadb/chroma:latest
@ -87,55 +91,55 @@ services:
- ANONYMIZED_TELEMETRY=FALSE - ANONYMIZED_TELEMETRY=FALSE
restart: unless-stopped restart: unless-stopped
searxng: # searxng:
# Pinned, not :latest — odysseus waits on searxng's healthcheck # Pinned, not :latest — odysseus waits on searxng's healthcheck
# (depends_on: condition: service_healthy), so a broken upstream `latest` # (depends_on: condition: service_healthy), so a broken upstream `latest`
# tag blocks the whole app from starting. 2026.6.2 crashes on boot with # tag blocks the whole app from starting. 2026.6.2 crashes on boot with
# `KeyError: 'default_doi_resolver'`, failing the healthcheck (issue #1414). # `KeyError: 'default_doi_resolver'`, failing the healthcheck (issue #1414).
# Bump this deliberately after verifying a newer tag boots clean. # Bump this deliberately after verifying a newer tag boots clean.
image: docker.io/searxng/searxng:2026.5.31-7159b8aed # image: docker.io/searxng/searxng:2026.5.31-7159b8aed
entrypoint: # entrypoint:
- /bin/sh # - /bin/sh
- -c # - -c
- | # - |
set -eu # set -eu
if [ ! -s /etc/searxng/settings.yml ] || grep -q 'odysseus-local-searxng-json-2026-05-30\|__SEARXNG_SECRET__' /etc/searxng/settings.yml; then # if [ ! -s /etc/searxng/settings.yml ] || grep -q 'odysseus-local-searxng-json-2026-05-30\|__SEARXNG_SECRET__' /etc/searxng/settings.yml; then
secret="$${SEARXNG_SECRET:-}" # secret="$${SEARXNG_SECRET:-}"
if [ -z "$$secret" ]; then # if [ -z "$$secret" ]; then
secret="$$(python -c 'import secrets; print(secrets.token_urlsafe(48))')" # secret="$$(python -c 'import secrets; print(secrets.token_urlsafe(48))')"
fi # fi
sed "s|__SEARXNG_SECRET__|$$secret|g" /tmp/searxng-settings.yml.template > /etc/searxng/settings.yml # sed "s|__SEARXNG_SECRET__|$$secret|g" /tmp/searxng-settings.yml.template > /etc/searxng/settings.yml
fi # fi
exec /usr/local/searxng/entrypoint.sh # exec /usr/local/searxng/entrypoint.sh
ports: # ports:
- "127.0.0.1:8080:8080" # - "127.0.0.1:8080:8080"
volumes: # volumes:
- searxng-data:/etc/searxng # - searxng-data:/etc/searxng
- ./config/searxng/settings.yml:/tmp/searxng-settings.yml.template:ro,z # - ./config/searxng/settings.yml:/tmp/searxng-settings.yml.template:ro,z
environment: # environment:
- SEARXNG_BASE_URL=http://localhost:8080/ # - SEARXNG_BASE_URL=http://localhost:8080/
- SEARXNG_SECRET=${SEARXNG_SECRET:-} # restart: unless-stopped
# The official searxng image runs as the non-root `searxng` user, but its # # The official searxng image runs as the non-root `searxng` user, but its
# entrypoint still needs to chown /etc/searxng on first boot, drop privs via # # entrypoint still needs to chown /etc/searxng on first boot, drop privs via
# su-exec, and (with our wrapper above) write settings.yml into the named # # su-exec, and (with our wrapper above) write settings.yml into the named
# volume. Without these capabilities the wrapper aborts at the redirection # # volume. Without these capabilities the wrapper aborts at the redirection
# with EACCES and the container fails its healthcheck with permission # # with EACCES and the container fails its healthcheck with permission
# errors during setup. Mirrors the cap set recommended by the upstream # # errors during setup. Mirrors the cap set recommended by the upstream
# searxng-docker compose file. See issue #721. # # searxng-docker compose file. See issue #721.
cap_drop: # cap_drop:
- ALL # - ALL
cap_add: # cap_add:
- CHOWN # - CHOWN
- SETGID # - SETGID
- SETUID # - SETUID
- DAC_OVERRIDE # - DAC_OVERRIDE
healthcheck: # healthcheck:
test: ["CMD-SHELL", "python -c \"import urllib.request; urllib.request.urlopen('http://localhost:8080/', timeout=5).read(1)\""] # test: ["CMD-SHELL", "python -c \"import urllib.request; urllib.request.urlopen('http://localhost:8080/', timeout=5).read(1)\""]
interval: 5s # interval: 5s
timeout: 6s # timeout: 6s
retries: 20 # retries: 20
start_period: 10s # start_period: 10s
restart: unless-stopped # restart: unless-stopped
ntfy: ntfy:
image: docker.io/binwiederhier/ntfy image: docker.io/binwiederhier/ntfy
@ -149,6 +153,11 @@ services:
restart: unless-stopped restart: unless-stopped
volumes: volumes:
searxng-data: # searxng-data:
chromadb-data: chromadb-data:
ntfy-cache: ntfy-cache:
networks:
searxng-net:
external: true
name: searxng_default

View file

@ -2696,6 +2696,9 @@ def setup_cookbook_routes() -> APIRouter:
elif disk_env.get("servers") and not inc_env.get("servers"): elif disk_env.get("servers") and not inc_env.get("servers"):
inc_env["servers"] = disk_env["servers"] inc_env["servers"] = disk_env["servers"]
logger.warning("cookbook state POST: incoming env.servers empty; preserved on-disk servers (anti-wipe guard)") logger.warning("cookbook state POST: incoming env.servers empty; preserved on-disk servers (anti-wipe guard)")
if disk_env.get("hfToken") and not inc_env.get("hfToken"):
inc_env["hfToken"] = disk_env["hfToken"]
logger.debug("cookbook state POST: incoming env missing hfToken; preserved on-disk token (anti-wipe guard)")
disk_tasks = on_disk.get("tasks") or [] if isinstance(on_disk, dict) else [] disk_tasks = on_disk.get("tasks") or [] if isinstance(on_disk, dict) else []
incoming_tasks = data.get("tasks") if isinstance(data.get("tasks"), list) else [] incoming_tasks = data.get("tasks") if isinstance(data.get("tasks"), list) else []

View file

@ -3036,7 +3036,7 @@ import { wireArrowUpRecall, getLastUserMessageFromChatHistory } from './composer
} }
} else { } else {
// Stop streaming TTS on any error/abort // Stop streaming TTS on any error/abort
if (streamingTTS && window.aiTTSManager) window.aiTTSManager.stop(); if (window.aiTTSManager?.autoPlay && window.aiTTSManager?.available) window.aiTTSManager.stop();
if (currentAbort && currentAbort.signal.aborted) { if (currentAbort && currentAbort.signal.aborted) {
const abortReason = currentAbort._reason || ''; const abortReason = currentAbort._reason || '';

View file

@ -408,6 +408,10 @@ export function _parseServePhase(snapshot) {
if (/Ollama API ready on port\s+\d+/i.test(flat)) { if (/Ollama API ready on port\s+\d+/i.test(flat)) {
return { phase: 'ready', status: 'ready' }; return { phase: 'ready', status: 'ready' };
} }
// llama.cpp server (llama-server / llama-cli) — "server is listening" variants
if (/(?:server is listening|HTTP server listening|listening on (?:http|port))/i.test(flat)) {
return { phase: 'ready', status: 'ready' };
}
const llamaBuildMatches = [...flat.matchAll(/\[\s*(\d{1,3})%\]\s*(?:Building|Linking)/gi)]; const llamaBuildMatches = [...flat.matchAll(/\[\s*(\d{1,3})%\]\s*(?:Building|Linking)/gi)];
if (llamaBuildMatches.length) { if (llamaBuildMatches.length) {
const pct = Math.min(100, parseInt(llamaBuildMatches[llamaBuildMatches.length - 1][1], 10)); const pct = Math.min(100, parseInt(llamaBuildMatches[llamaBuildMatches.length - 1][1], 10));