Skip to main content

Apply and manage upstream PR patches on a self-hosted Odysseus install

Project description

odysseus-patches

PyPI Python License: AGPL-3.0 CI

Apply open upstream Odysseus PRs to your own install as tracked, SHA-pinned patches — re-applied across updates while the PR is open, retired automatically once it merges, flagged when it conflicts. CLI + a web panel inside Odysseus's own UI.

Community project — not affiliated with or endorsed by the Odysseus maintainers. If they ever want this in core, the migration offer stands: AGPL-3.0 license and upstream CLI conventions are deliberate.

Quick start

pipx install odysseus-patches                    # install (or: brew install botinate/tap/odysseus-patches)
cd /path/to/your/odysseus
odysseus-patches add 3681                         # apply an open upstream PR, pinned to a SHA you review
odysseus-patches install-ui                       # (optional) add a Patches panel to Odysseus's sidebar

Then restart Odysseus. Full install options and every command are below.

Why

The Odysseus community ships fixes faster than upstream merges them. When the bug you're hitting has an open PR, you shouldn't have to choose between waiting weeks and hand-maintaining a fork.

Install

pipx install odysseus-patches            # PyPI (recommended)
pipx install 'odysseus-patches[mcp]'     # + MCP status server for the agent

brew install botinate/tap/odysseus-patches   # Homebrew (macOS/Linux)

No clone needed. If a release hasn't landed on PyPI/Homebrew yet, install straight from git (works today, still no clone):

pipx install git+https://github.com/botinate/odysseus-patches

It's a single self-contained tool with no runtime dependencies, so any of these put one odysseus-patches command on your PATH (the web panel finds it automatically). Requirements: a git-checkout install of Odysseus (any platform — the Docker flow builds from the working tree, so patches reach containers on rebuild). Zip-download installs cannot be patched.

Use

cd /path/to/odysseus
odysseus-patches add 3681        # review diffstat, confirm, apply pinned
odysseus-patches add 3681 --review  # ...or have your Odysseus AI security-review the diff first
odysseus-patches list
odysseus-patches update          # instead of `git pull --ff-only`
odysseus-patches upgrade 3681    # PR got new commits: review + re-pin
odysseus-patches remove 3681
odysseus-patches propose 3681    # stage only; approve/reject later
odysseus-patches approve 3681    # apply a staged proposal
odysseus-patches config set api_token <odysseus-api-token>   # one-time, enables AI review
odysseus-patches install-hook update_windows.bat   # wire into your updater

update exit codes: 0 nothing changed · 10 updated, rebuild/restart Odysseus · 20 a patch needs attention · 1 error.

How it works

Your tracked branch (dev) never carries local commits, so upstream's git pull --ff-only always works. Patches live as squashed commits on a generated patched branch, rebuilt from data/patches/manifest.json on every update. PR content is fetched only from the upstream repo's own refs/pull/N/head namespace and pinned to the commit SHA you reviewed — a force-pushed PR can never silently change what your install runs.

Agent visibility (optional)

install-ui auto-registers a read-only MCP server in Odysseus (a row in its mcp_servers table, with the absolute interpreter path so there's no PATH guesswork) — restart Odysseus and the agent gets the tools, no manual integration setup. uninstall-ui removes it.

The MCP server needs the mcp package in the Python that runs it, so register from an environment that has it: run install-ui with Odysseus's own venv (/path/to/odysseus/venv/bin/odysseus-patches -C /path/to/odysseus install-ui, its venv already has mcp), or pipx install 'odysseus-patches[mcp]'. To run the server by hand: odysseus-patches -C /path/to/odysseus mcp.

Tools: list_patches, patch_status, propose_patch. The agent can report patch state and propose patches (optionally pre-reviewed by AI) — but applying always requires a human approve. The agent cannot apply, upgrade, or remove anything by design.

Web UI panel (optional)

Manage patches inside Odysseus's own interface instead of the terminal:

odysseus-patches install-ui      # injects the panel into your Odysseus install
# ...restart Odysseus, then open Tools -> Patches (admin only)
odysseus-patches uninstall-ui    # remove it

It is not a separate server and not an upstream PR: install-ui drops two files into your Odysseus install and adds one line to its app.py, all owned by this extension and reapplied automatically when you run odysseus-patches update. The panel reuses Odysseus's own admin login and themes (no new auth — it's admin-only, behind your existing login), and uses Odysseus's own notifications.

From the panel (Tools → Patches, admin only) you can do everything the CLI does short of git surgery: add a PR (with an optional AI review), upgrade, approve/reject agent proposals, remove, review, view diffs, check for updates, and set your API token under Settings. Applied changes take effect on the next Odysseus restart.

AI security review (optional)

add/upgrade/approve can ask your own Odysseus instance to review the diff for vulnerabilities and sketchy code before anything is applied (uses your default model — one-time setup: an Odysseus API token with chat scope, odysseus-patches config set api_token <token>). Findings urge you to report the PR and require an explicit "install anyway". A clean review is evidence, not proof — review sensitive diffs yourself.

Branch model & safety

Patches live on a generated patched branch (= dev + one squashed [patch] PR#N commit per patch). Your checkout sits on patched when patches are active and on dev when none are. patched is a build artifact — every add/approve/upgrade/remove/update rebuilds it from scratch (git checkout -B patched dev).

Because of that, don't develop on the patched branch — any commit you make there that isn't a managed [patch] commit gets discarded on the next rebuild. Branch your own work off dev instead. The tool guards both footguns and refuses (rather than silently switching or discarding) when:

  • you run a patch command while the checkout is on one of your own branches (not dev/patched), or
  • patched carries commits that aren't managed patches.

Pass --force to override a guard if you really mean it. The web panel's status and the MCP patch_status tool surface the same warnings.

Security model

Applying a patch is running someone else's code. Mitigations: you review the diff(stat) at add/upgrade time; the SHA you reviewed is what keeps being applied; updates never adopt new PR content without an explicit upgrade.

The agent can propose, never apply. Over MCP this is structural (a proposal is never built into the patched branch). The web panel's state-changing routes add an independent guard that refuses Odysseus's internal agent-loopback token and requires a per-request header, so the agent can't reach them through the generic API bridge even if Odysseus's blocklist changes — applying always needs a human in the UI/CLI. Read-only status/diff stay available to the agent.

data/patches/manifest.json and config.json are trust anchors. The manifest decides which commit each patch is pinned to (what code gets applied on the next rebuild) and the config holds your API token (written 0600, atomically, with no world-readable window). Both are validated strictly on load (types, hex SHAs, owner/repo and branch names that can't start with - or contain .., http(s) token endpoint) — but validation only blocks malformed input. Anything that can write these files with well-formed values can still change what runs, so protect them like source code.

Deployment boundary. Two things the tool can't enforce for you:

  • Don't give the agent filesystem write access to data/patches/. The agent's propose-only path is enforced in code, but an agent that can edit the manifest directly sidesteps it. Keep that directory off the agent's tool reach.
  • Keep the token endpoint loopback or HTTPS. odysseus_url defaults to 127.0.0.1; if you point it at a remote host over plain http, the API token rides the network in cleartext. Use https:// for non-loopback hosts.

License

AGPL-3.0 — same as upstream Odysseus, so this code can migrate into core without relicensing.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

odysseus_patches-0.3.0.tar.gz (74.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

odysseus_patches-0.3.0-py3-none-any.whl (57.9 kB view details)

Uploaded Python 3

File details

Details for the file odysseus_patches-0.3.0.tar.gz.

File metadata

  • Download URL: odysseus_patches-0.3.0.tar.gz
  • Upload date:
  • Size: 74.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for odysseus_patches-0.3.0.tar.gz
Algorithm Hash digest
SHA256 f75603a3a41e29252ce263398d07fa349d5766728883438dfa79d3d9ad1bd9d4
MD5 8ed2acc1bfcaba7b63cf04fb36270a20
BLAKE2b-256 6afb39a73b5db58d5a2191bbebe52b2670965975e3e6feb5b88f2b9a57264a2f

See more details on using hashes here.

Provenance

The following attestation bundles were made for odysseus_patches-0.3.0.tar.gz:

Publisher: publish.yml on botinate/odysseus-patches

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file odysseus_patches-0.3.0-py3-none-any.whl.

File metadata

File hashes

Hashes for odysseus_patches-0.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 68992bc604be35200f4c44af5f3f9b387864013f709e30ce93bd8ecc2645bd85
MD5 d0c831df93b03245ecd20760fbebdbdb
BLAKE2b-256 4d8cd95139b3859e14e4658564a2c9d860454f6f81e10a0a33254e9a80620e10

See more details on using hashes here.

Provenance

The following attestation bundles were made for odysseus_patches-0.3.0-py3-none-any.whl:

Publisher: publish.yml on botinate/odysseus-patches

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page