mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-09-10 10:12:20 +02:00
refactor(docs): separate Pages site source (#6176)
* refactor(docs): separate Pages site source * fix(docs): preserve published guide pages * fix(ci): run asset ownership tests for site changes * fix(docs): track future website media * fix(ci): let Pages deployments finish * build(deps): update Pages checkout action * fix(ci): follow moved setup guide * fix(docs): repair published setup guide * fix(docs): retarget preview encoder
This commit is contained in:
@@ -30,6 +30,8 @@ secrets.env~
|
||||
.idea/
|
||||
dev-docs/
|
||||
docs/
|
||||
website/
|
||||
assets/branding/
|
||||
*.md
|
||||
*.db
|
||||
*.sqlite
|
||||
|
||||
+1
-1
@@ -6,4 +6,4 @@
|
||||
# A per-area ownership map (security/auth, CI, frontend, agent internals, with
|
||||
# multiple named owners per line) is being worked out in issue #593; once
|
||||
# agreed it replaces this file. Until then, required reviews and the security
|
||||
# CI gate (docs/security-ci.md) remain in force via branch protection.
|
||||
# CI gate (website/security-ci.md) remain in force via branch protection.
|
||||
|
||||
@@ -110,7 +110,7 @@ jobs:
|
||||
fetch-depth: 0
|
||||
persist-credentials: false
|
||||
|
||||
# Detect whether this PR only touches documentation files.
|
||||
# Detect whether this PR only touches repository prose outside the Pages site.
|
||||
# If so, skip the expensive pytest run while still reporting a passing check.
|
||||
- name: Check for docs-only changes
|
||||
id: docs-check
|
||||
@@ -122,9 +122,10 @@ jobs:
|
||||
BASE="${{ github.event.before }}"
|
||||
HEAD="${{ github.sha }}"
|
||||
fi
|
||||
# List all changed files; if every file matches docs/markdown patterns, skip pytest.
|
||||
# Keep website/ and assets/branding/ out of this bypass: pytest owns
|
||||
# regression guards for their published-file and orphan-asset contracts.
|
||||
changed=$(git diff --name-only "$BASE" "$HEAD" 2>/dev/null || git diff --name-only HEAD~1 HEAD)
|
||||
non_docs=$(echo "$changed" | grep -Ev '^(docs/|.*\.md$|\.github/[^/]+\.md$)' || true)
|
||||
non_docs=$(echo "$changed" | grep -Ev '^(docs/|[^/]+\.md$|\.github/[^/]+\.md$)' || true)
|
||||
if [ -z "$non_docs" ]; then
|
||||
echo "docs_only=true" >> "$GITHUB_OUTPUT"
|
||||
echo "Docs-only change detected — skipping pytest."
|
||||
|
||||
@@ -23,12 +23,16 @@ on:
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
- 'docs/**'
|
||||
- 'website/**'
|
||||
- 'assets/branding/**'
|
||||
- '.github/ISSUE_TEMPLATE/**'
|
||||
push:
|
||||
branches: [main]
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
- 'docs/**'
|
||||
- 'website/**'
|
||||
- 'assets/branding/**'
|
||||
- '.github/ISSUE_TEMPLATE/**'
|
||||
workflow_dispatch:
|
||||
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
name: Deploy GitHub Pages
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
paths:
|
||||
- 'website/**'
|
||||
- '.github/workflows/deploy-pages.yml'
|
||||
workflow_dispatch:
|
||||
|
||||
permissions: {}
|
||||
|
||||
concurrency:
|
||||
group: pages
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Package static site
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
pages: read
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: actions/configure-pages@45bfe0192ca1faeb007ade9deae92b16b8254a0d # v6.0.0
|
||||
- uses: actions/jekyll-build-pages@44a6e6beabd48582f863aeeb6cb2151cc1716697 # v1.0.13
|
||||
with:
|
||||
source: website
|
||||
destination: _site
|
||||
- uses: actions/upload-pages-artifact@fc324d3547104276b827a68afc52ff2a11cc49c9 # v5.0.0
|
||||
with:
|
||||
path: _site
|
||||
|
||||
deploy:
|
||||
name: Deploy static site
|
||||
needs: build
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
pages: write
|
||||
id-token: write
|
||||
environment:
|
||||
name: github-pages
|
||||
url: ${{ steps.deployment.outputs.page_url }}
|
||||
steps:
|
||||
- name: Deploy to GitHub Pages
|
||||
id: deployment
|
||||
uses: actions/deploy-pages@cd2ce8fcbc39b97be8ca5fce6e763baed58fa128 # v5.0.0
|
||||
@@ -14,6 +14,8 @@ on:
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
- 'docs/**'
|
||||
- 'website/**'
|
||||
- 'assets/branding/**'
|
||||
- '.github/ISSUE_TEMPLATE/**'
|
||||
|
||||
concurrency:
|
||||
|
||||
+18
@@ -85,6 +85,24 @@ output.txt.txt
|
||||
!docs/**/*.gif
|
||||
!docs/**/*.webp
|
||||
|
||||
# …except shipped website and branding media.
|
||||
!website/**/*.jpg
|
||||
!website/**/*.jpeg
|
||||
!website/**/*.png
|
||||
!website/**/*.gif
|
||||
!website/**/*.bmp
|
||||
!website/**/*.webp
|
||||
!website/**/*.tiff
|
||||
!website/**/*.pdf
|
||||
!assets/branding/**/*.jpg
|
||||
!assets/branding/**/*.jpeg
|
||||
!assets/branding/**/*.png
|
||||
!assets/branding/**/*.gif
|
||||
!assets/branding/**/*.bmp
|
||||
!assets/branding/**/*.webp
|
||||
!assets/branding/**/*.tiff
|
||||
!assets/branding/**/*.pdf
|
||||
|
||||
# Reports and temp files
|
||||
reports/
|
||||
tasks/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<p align="center">
|
||||
<img src="docs/odysseus-wordmark.png" alt="Odysseus" width="238">
|
||||
<img src="assets/branding/odysseus-wordmark.png" alt="Odysseus" width="238">
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
<p align="center">
|
||||
<a href="#quick-start">Quick Start</a> ·
|
||||
<a href="docs/setup.md">Setup Guide</a> ·
|
||||
<a href="website/setup.md">Setup Guide</a> ·
|
||||
<a href="CONTRIBUTING.md">Contributing</a> ·
|
||||
<a href="ROADMAP.md">Roadmap</a>
|
||||
</p>
|
||||
@@ -18,7 +18,7 @@
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<img src="docs/odysseus-browser.jpg" alt="Odysseus interface">
|
||||
<img src="assets/branding/odysseus-browser.jpg" alt="Odysseus interface">
|
||||
</p>
|
||||
|
||||
---
|
||||
@@ -36,7 +36,7 @@ docker compose up -d --build
|
||||
|
||||
Open `http://localhost:7000` when the containers are healthy. The first admin password is printed in `docker compose logs odysseus`.
|
||||
|
||||
Native installs, GPU notes, Windows/macOS instructions, HTTPS, and configuration live in the [setup guide](docs/setup.md).
|
||||
Native installs, GPU notes, Windows/macOS instructions, HTTPS, and configuration live in the [setup guide](website/setup.md).
|
||||
|
||||
## Features
|
||||
|
||||
@@ -51,7 +51,7 @@ Native installs, GPU notes, Windows/macOS instructions, HTTPS, and configuration
|
||||
|
||||
## Demo
|
||||
|
||||
A full hover-to-play tour lives on the landing page: [`docs/index.html`](docs/index.html).
|
||||
A full hover-to-play tour lives on the [Odysseus landing page](https://odysseus-dev.github.io/odysseus/). Its source lives under [`website/`](website/).
|
||||
|
||||
## Contributing
|
||||
|
||||
@@ -64,7 +64,7 @@ Odysseus is a self-hosted workspace with powerful local tools. Keep auth enabled
|
||||
- Keep `AUTH_ENABLED=true` for any network-accessible deployment.
|
||||
- Keep `LOCALHOST_BYPASS=false` outside local development.
|
||||
|
||||
Deployment details are in the [setup guide](docs/setup.md#security-notes).
|
||||
Deployment details are in the [setup guide](website/setup.md#security-notes).
|
||||
|
||||
## Star History
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 185 KiB After Width: | Height: | Size: 185 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 79 KiB After Width: | Height: | Size: 79 KiB |
+4
-4
@@ -27,13 +27,13 @@ echo " port: $PORT"
|
||||
rm -rf "$APP"
|
||||
mkdir -p "$APP/Contents/MacOS" "$APP/Contents/Resources"
|
||||
|
||||
# ── Icon (best effort) — center-crop docs/odysseus.jpg to a square .icns ──
|
||||
if [ -f "$REPO_DIR/docs/odysseus.jpg" ] && command -v sips >/dev/null 2>&1; then
|
||||
# ── Icon (best effort) — center-crop the branding image to a square .icns ──
|
||||
if [ -f "$REPO_DIR/assets/branding/odysseus.jpg" ] && command -v sips >/dev/null 2>&1; then
|
||||
TMPIMG="$(mktemp -d)"
|
||||
# Center-crop to a square, scale to 512 (sips' icns encoder caps at 512), and
|
||||
# let sips emit the .icns directly — more robust across macOS versions than
|
||||
# building an .iconset by hand.
|
||||
sips -c 720 720 "$REPO_DIR/docs/odysseus.jpg" --out "$TMPIMG/sq.png" >/dev/null 2>&1 || cp "$REPO_DIR/docs/odysseus.jpg" "$TMPIMG/sq.png"
|
||||
sips -c 720 720 "$REPO_DIR/assets/branding/odysseus.jpg" --out "$TMPIMG/sq.png" >/dev/null 2>&1 || cp "$REPO_DIR/assets/branding/odysseus.jpg" "$TMPIMG/sq.png"
|
||||
sips -z 512 512 "$TMPIMG/sq.png" --out "$TMPIMG/icon.png" >/dev/null 2>&1
|
||||
if sips -s format icns "$TMPIMG/icon.png" --out "$APP/Contents/Resources/odysseus.icns" >/dev/null 2>&1; then
|
||||
echo " icon: odysseus.icns"
|
||||
@@ -42,7 +42,7 @@ if [ -f "$REPO_DIR/docs/odysseus.jpg" ] && command -v sips >/dev/null 2>&1; then
|
||||
fi
|
||||
rm -rf "$TMPIMG"
|
||||
else
|
||||
echo " icon: (skipped — no docs/odysseus.jpg)"
|
||||
echo " icon: (skipped — no assets/branding/odysseus.jpg)"
|
||||
fi
|
||||
|
||||
# ── Info.plist ──
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
# Encode a source screen-recording (.mkv) into web-optimized preview clips for
|
||||
# the landing page: docs/<name>.webm (VP9) + docs/<name>.mp4 (H.264).
|
||||
# the landing page: website/<name>.webm (VP9) + website/<name>.mp4 (H.264).
|
||||
#
|
||||
# ./encode_previews.sh <input> <name> [max_secs]
|
||||
#
|
||||
@@ -13,7 +13,7 @@ set -euo pipefail
|
||||
IN="${1:?input file}"
|
||||
NAME="${2:?output basename}"
|
||||
MAX="${3:-30}"
|
||||
OUT_DIR="$(cd "$(dirname "$0")/../docs" && pwd)"
|
||||
OUT_DIR="$(cd "$(dirname "$0")/../website" && pwd)"
|
||||
|
||||
dur=$(ffprobe -v error -show_entries format=duration -of csv=p=0 "$IN" | cut -d. -f1)
|
||||
dur=${dur:-0}
|
||||
|
||||
@@ -1,30 +1,39 @@
|
||||
"""Regression guard for issue #1335 — PR review screenshots were committed into
|
||||
docs/ (docs/a11y/*.png from #738, docs/gallery-314-*.png from #644) where they
|
||||
served no purpose: nothing in the repo referenced them, so they just showed up
|
||||
as "random images" in the doc folder.
|
||||
"""Repository asset ownership guards for issues #1335 and #6175.
|
||||
|
||||
This test fails if any image under docs/ is orphaned — present in the tree but
|
||||
referenced by no tracked text file. The intended doc assets (the README hero
|
||||
image and the feature preview clips) are referenced, so they pass; a stray
|
||||
screenshot dropped in by a future PR would not.
|
||||
Public Markdown and landing-page media belong in website/, while shared
|
||||
README/packaging imagery belongs in assets/branding/. Images in either managed
|
||||
root must be referenced by tracked text, and every tracked website video must
|
||||
be referenced by the site's entry point.
|
||||
"""
|
||||
import re
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
from urllib.parse import urlsplit
|
||||
|
||||
import pytest
|
||||
|
||||
REPO = Path(__file__).resolve().parent.parent
|
||||
IMAGE_EXTS = {".png", ".jpg", ".jpeg", ".gif", ".webp", ".bmp"}
|
||||
VIDEO_EXTS = {".webm", ".mp4", ".mov", ".m4v"}
|
||||
PUBLIC_GUIDES = {
|
||||
"agent-migration.md",
|
||||
"attachments.md",
|
||||
"backup-restore.md",
|
||||
"email-outlook.md",
|
||||
"pr-blocker-audit.md",
|
||||
"security-ci.md",
|
||||
"setup.md",
|
||||
}
|
||||
# Files a referenced image name could legitimately appear in.
|
||||
TEXT_EXTS = {".md", ".html", ".htm", ".js", ".ts", ".css", ".py", ".sh",
|
||||
".json", ".yml", ".yaml", ".txt"}
|
||||
|
||||
|
||||
def _tracked(paths_under):
|
||||
"""Git-tracked files under a path, or None if git isn't available."""
|
||||
def _tracked(*paths_under):
|
||||
"""Git-tracked files under paths, or None if git isn't available."""
|
||||
try:
|
||||
out = subprocess.run(
|
||||
["git", "ls-files", paths_under],
|
||||
["git", "ls-files", "--", *paths_under],
|
||||
cwd=REPO, capture_output=True, text=True, timeout=30,
|
||||
)
|
||||
except (OSError, subprocess.SubprocessError):
|
||||
@@ -34,12 +43,14 @@ def _tracked(paths_under):
|
||||
return [REPO / line for line in out.stdout.splitlines() if line.strip()]
|
||||
|
||||
|
||||
def test_no_orphan_images_in_docs():
|
||||
docs_images = _tracked("docs")
|
||||
if docs_images is None:
|
||||
def test_no_orphan_documentation_or_branding_images():
|
||||
managed_files = _tracked("website", "assets/branding")
|
||||
if managed_files is None:
|
||||
pytest.skip("not a git checkout")
|
||||
docs_images = [p for p in docs_images if p.suffix.lower() in IMAGE_EXTS]
|
||||
assert docs_images, "expected docs/ to still contain referenced doc assets"
|
||||
managed_images = [p for p in managed_files if p.suffix.lower() in IMAGE_EXTS]
|
||||
assert any("assets/branding" in p.as_posix() for p in managed_images), (
|
||||
"expected assets/branding/ to contain the shared project imagery"
|
||||
)
|
||||
|
||||
# All tracked text we might reference an image from.
|
||||
all_tracked = _tracked(".") or []
|
||||
@@ -55,10 +66,117 @@ def test_no_orphan_images_in_docs():
|
||||
|
||||
orphans = [
|
||||
str(img.relative_to(REPO))
|
||||
for img in docs_images
|
||||
for img in managed_images
|
||||
if img.name not in blob
|
||||
]
|
||||
assert not orphans, (
|
||||
"unreferenced image(s) committed under docs/ — likely PR screenshots "
|
||||
f"added by accident (see #1335): {orphans}"
|
||||
"unreferenced image(s) committed under website/ or assets/branding/ "
|
||||
f"(see #1335 and #6175): {orphans}"
|
||||
)
|
||||
|
||||
|
||||
def test_pages_site_owns_its_entrypoint_and_media():
|
||||
docs_files = _tracked("docs")
|
||||
website_files = _tracked("website")
|
||||
if docs_files is None or website_files is None:
|
||||
pytest.skip("not a git checkout")
|
||||
|
||||
assert REPO / "website/index.html" in website_files
|
||||
assert REPO / "docs/index.html" not in docs_files
|
||||
assert not [p for p in docs_files if p.suffix.lower() in VIDEO_EXTS | {".md"}]
|
||||
|
||||
website_paths = {p.relative_to(REPO / "website").as_posix() for p in website_files}
|
||||
assert PUBLIC_GUIDES <= website_paths
|
||||
for guide in PUBLIC_GUIDES:
|
||||
text = (REPO / "website" / guide).read_text(encoding="utf-8")
|
||||
assert text.startswith("---\nlayout: default\n---\n"), guide
|
||||
|
||||
website_videos = [p for p in website_files if p.suffix.lower() in VIDEO_EXTS]
|
||||
assert website_videos, "expected website/ to contain the landing-page videos"
|
||||
|
||||
entrypoint = (REPO / "website/index.html").read_text(encoding="utf-8")
|
||||
unreferenced = [
|
||||
str(video.relative_to(REPO))
|
||||
for video in website_videos
|
||||
if video.name not in entrypoint
|
||||
]
|
||||
assert not unreferenced, f"unreferenced website video(s): {unreferenced}"
|
||||
|
||||
workflow = (REPO / ".github/workflows/deploy-pages.yml").read_text(encoding="utf-8")
|
||||
assert "actions/jekyll-build-pages@" in workflow
|
||||
assert "source: website" in workflow
|
||||
assert "destination: _site" in workflow
|
||||
assert "path: _site" in workflow
|
||||
assert "cancel-in-progress: false" in workflow
|
||||
|
||||
|
||||
def test_pages_guides_keep_relative_links_inside_site():
|
||||
site_root = (REPO / "website").resolve()
|
||||
|
||||
for guide in sorted(PUBLIC_GUIDES):
|
||||
guide_path = REPO / "website" / guide
|
||||
text = guide_path.read_text(encoding="utf-8")
|
||||
for target in re.findall(r"\]\(([^)]+)\)", text):
|
||||
parsed = urlsplit(target)
|
||||
if parsed.scheme or parsed.netloc or not parsed.path:
|
||||
continue
|
||||
|
||||
resolved = (guide_path.parent / parsed.path).resolve()
|
||||
assert resolved.is_relative_to(site_root), (
|
||||
f"{guide} links outside the Pages source: {target}"
|
||||
)
|
||||
assert resolved.exists(), f"{guide} has a missing local link: {target}"
|
||||
|
||||
|
||||
def test_setup_preserves_docker_go_template_literal():
|
||||
setup = (REPO / "website/setup.md").read_text(encoding="utf-8")
|
||||
guarded_command = """<!-- {% raw %} -->
|
||||
```bash
|
||||
docker info --format '{{.DockerRootDir}}'
|
||||
```
|
||||
<!-- {% endraw %} -->"""
|
||||
|
||||
assert guarded_command in setup
|
||||
|
||||
|
||||
def test_preview_encoder_targets_pages_source():
|
||||
encoder = (REPO / "scripts/encode_previews.sh").read_text(encoding="utf-8")
|
||||
|
||||
assert "landing page: website/<name>.webm" in encoder
|
||||
assert 'OUT_DIR="$(cd "$(dirname "$0")/../website" && pwd)"' in encoder
|
||||
|
||||
|
||||
def test_ci_runs_asset_ownership_guards_for_managed_roots():
|
||||
workflow = (REPO / ".github/workflows/ci.yml").read_text(encoding="utf-8")
|
||||
match = re.search(r"grep -Ev '([^']+)'", workflow)
|
||||
assert match, "expected the docs-only path classifier in CI"
|
||||
docs_only = re.compile(match.group(1))
|
||||
|
||||
assert docs_only.match("README.md")
|
||||
assert docs_only.match("docs/example.md")
|
||||
assert not docs_only.match("website/setup.md")
|
||||
assert not docs_only.match("website/new-preview.webm")
|
||||
assert not docs_only.match("assets/branding/new-logo.png")
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
"website/favicon.png",
|
||||
"website/media/social-card.jpg",
|
||||
"website/guides/reference.pdf",
|
||||
"assets/branding/new-logo.gif",
|
||||
"assets/branding/print/logo.tiff",
|
||||
],
|
||||
)
|
||||
def test_managed_site_media_is_not_ignored(path):
|
||||
result = subprocess.run(
|
||||
["git", "check-ignore", "--no-index", "--quiet", path],
|
||||
cwd=REPO,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=30,
|
||||
)
|
||||
if result.returncode == 128:
|
||||
pytest.skip("not a git checkout")
|
||||
assert result.returncode == 1, f"{path} is unexpectedly ignored"
|
||||
|
||||
@@ -30,7 +30,7 @@ def test_kokoro_feature_markers_match_supported_python_range(python_version, sel
|
||||
|
||||
|
||||
def test_setup_documents_container_constraint_and_install_command():
|
||||
setup = (ROOT / "docs" / "setup.md").read_text(encoding="utf-8")
|
||||
setup = (ROOT / "website" / "setup.md").read_text(encoding="utf-8")
|
||||
|
||||
assert "pip install -r requirements-optional.txt" in setup
|
||||
assert "default Docker image currently uses Python 3.14" in setup
|
||||
|
||||
@@ -123,10 +123,10 @@ def test_docker_compose_binds_web_ui_to_loopback_by_default():
|
||||
|
||||
|
||||
def test_readme_native_quickstart_uses_loopback():
|
||||
# The README refresh (#4306) moved the native quickstart into docs/setup.md,
|
||||
# The Pages source split (#6175) moved the native quickstart into website/setup.md,
|
||||
# so accept the loopback guidance from either the README or the setup guide.
|
||||
docs = Path("README.md").read_text(encoding="utf-8")
|
||||
docs += "\n" + Path("docs/setup.md").read_text(encoding="utf-8")
|
||||
docs += "\n" + Path("website/setup.md").read_text(encoding="utf-8")
|
||||
assert "python -m uvicorn app:app --host 127.0.0.1 --port 7000" in docs
|
||||
assert "0.0.0.0` only when you intentionally want" in docs
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ def test_create_default_admin_normalizes_env_username(tmp_path, monkeypatch):
|
||||
def test_main_loads_admin_password_from_env_file(tmp_path, monkeypatch):
|
||||
"""Regression: setup.py must honor an admin password pre-seeded in .env on
|
||||
native installs, even when the var is not exported into the shell
|
||||
(docs/setup.md documents this). Previously setup.py never called
|
||||
(website/setup.md documents this). Previously setup.py never called
|
||||
load_dotenv(), so os.getenv() saw nothing and a random password was
|
||||
generated instead."""
|
||||
import bcrypt
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
title: Odysseus
|
||||
description: A self-hosted AI workspace for chat, agents, tools, and local models.
|
||||
theme: jekyll-theme-primer
|
||||
|
||||
plugins:
|
||||
- jekyll-relative-links
|
||||
|
||||
relative_links:
|
||||
enabled: true
|
||||
collections: true
|
||||
@@ -1,3 +1,7 @@
|
||||
---
|
||||
layout: default
|
||||
---
|
||||
|
||||
# Agent migration manifests
|
||||
|
||||
Odysseus should be able to learn from another agent without blindly trusting
|
||||
@@ -1,3 +1,7 @@
|
||||
---
|
||||
layout: default
|
||||
---
|
||||
|
||||
# Attachment References and Upload Storage
|
||||
|
||||
Odysseus stores uploaded bytes once under the configured upload directory and
|
||||
@@ -1,3 +1,7 @@
|
||||
---
|
||||
layout: default
|
||||
---
|
||||
|
||||
# Backup & Restore
|
||||
|
||||
Odysseus keeps all of your state in the `data/` directory — the SQLite database
|
||||
@@ -1,3 +1,7 @@
|
||||
---
|
||||
layout: default
|
||||
---
|
||||
|
||||
# Outlook / Office 365 email accounts
|
||||
|
||||
Odysseus email accounts currently use IMAP and SMTP with username/password
|
||||
@@ -1,3 +1,7 @@
|
||||
---
|
||||
layout: default
|
||||
---
|
||||
|
||||
# PR Blocker Audit
|
||||
|
||||
`scripts/pr_blocker_audit.py` is a small, read-only triage helper for maintainers who need to inspect open pull request overlap before reviewing or starting related work.
|
||||
@@ -1,3 +1,7 @@
|
||||
---
|
||||
layout: default
|
||||
---
|
||||
|
||||
# Security CI guide
|
||||
|
||||
This project runs a set of automated security checks on pull requests and
|
||||
@@ -1,3 +1,7 @@
|
||||
---
|
||||
layout: default
|
||||
---
|
||||
|
||||
# Odysseus Setup Guide
|
||||
|
||||
This page keeps the detailed install, deployment, troubleshooting, and configuration notes out of the front README.
|
||||
@@ -15,8 +19,7 @@ On first setup, Odysseus creates an admin account (`admin` unless
|
||||
For Docker installs, the same line is in `docker compose logs odysseus`.
|
||||
Use that for the first login, then change it in **Settings**.
|
||||
|
||||
Contributing? See [CONTRIBUTING.md](../CONTRIBUTING.md) for setup, testing, and
|
||||
pull request guidelines.
|
||||
Contributing? See [CONTRIBUTING.md](https://github.com/odysseus-dev/odysseus/blob/dev/CONTRIBUTING.md) for setup, testing, and pull request guidelines.
|
||||
|
||||
### Docker (recommended)
|
||||
```bash
|
||||
@@ -205,9 +208,11 @@ failed to fulfil mount request: open /usr/lib/wsl/lib/libdxcore.so: no such file
|
||||
|
||||
Check with `snap list docker` or:
|
||||
|
||||
<!-- {% raw %} -->
|
||||
```bash
|
||||
docker info --format '{{.DockerRootDir}}'
|
||||
```
|
||||
<!-- {% endraw %} -->
|
||||
|
||||
A Docker root under `/var/snap/docker/` means snap confinement can prevent
|
||||
Docker from seeing WSL2's `/usr/lib/wsl/lib` GPU libraries even when the files
|
||||
@@ -476,7 +481,7 @@ uv pip sync requirements.lock # reproduce it exactly la
|
||||
### Outlook / Office 365 email
|
||||
Odysseus email accounts currently use IMAP/SMTP username-password auth. Outlook
|
||||
and Microsoft 365 generally require OAuth instead, so normal Microsoft mailbox
|
||||
passwords will fail. See [docs/email-outlook.md](docs/email-outlook.md) for the
|
||||
passwords will fail. See [the Outlook email guide](email-outlook.md) for the
|
||||
current limitation and the planned integration direction.
|
||||
|
||||
## Security Notes
|
||||
@@ -733,7 +738,7 @@ src/ llm_core, agent_loop, agent_tools, chat_processor, search/
|
||||
routes/ chat, session, document, memory, model … endpoints
|
||||
services/ docs, memory, search, hwfit (Cookbook) …
|
||||
static/ index.html + app.js + style.css + js/ (modular front-end)
|
||||
docs/ landing page (index.html) + preview clips
|
||||
website/ landing page (index.html) + preview clips
|
||||
```
|
||||
|
||||
## Data
|
||||
Reference in New Issue
Block a user