aether-context ships to two registries from one commit: the Python package to
PyPI, and an npm launcher that installs that exact
PyPI release. Both publishes are manual workflow dispatches β tagging and publishing are
separate decisions, so cutting a tag never pretends to ship a package.
.github/workflows/publish.ymlβ PyPI, via OIDC Trusted Publishing. No API token is stored in the repo or in GitHub secrets..github/workflows/publish-npm.ymlβ npm, via anNPM_TOKENautomation token in thenpm-productionenvironment, published with--provenance.
Source of truth for the repo: https://github.com/AetherAI3/Unlimited-Context-LLM
Order matters: PyPI first, npm second. The npm launcher pins
aether-context==<its own version>, so publishing it before that version exists on PyPI ships a launcher that fails on every first run. The npm workflow has a preflight that refuses to publish in that state.
The publish workflow authenticates to PyPI via OIDC. PyPI rejects the upload unless a matching Trusted Publisher exists.
-
Sign in at https://pypi.org.
-
The project does not exist yet, so add a pending publisher under Your account β Publishing β Add a pending publisher. (For an existing project it is Manage β Publishing β Add a new publisher.)
-
Enter exactly:
Field Value PyPI project name aether-contextOwner AetherAI3Repository name Unlimited-Context-LLMWorkflow name publish.ymlEnvironment name pypi-productionThe project-name field is not the repo name. It must equal the distribution name in
pyproject.toml(aether-context). Getting this wrong does not fail the OIDC exchange β authentication succeeds and the upload is then rejected with400 Non-user identities cannot create new projects, which reads like a permissions problem but is a name mismatch. (Hyphen vs underscore is fine; PyPI normalizes those.)Owner must be the repo's owner at publish time. OIDC matches it literally and does not follow GitHub's transfer redirect β this repo moved from
DBarr3toAetherAI3, so a publisher namingDBarr3will never match. -
(GitHub) Confirm a repo Environment named
pypi-productionexists (Settings β Environments). The workflow'senvironment: pypi-productionreferences it; attach required reviewers there if you want a manual approval gate before each publish.
-
Create the
npm-productionenvironment (Settings β Environments). -
Generate an Automation token at npmjs.com β Access Tokens (automation tokens bypass 2FA, which CI cannot answer), and add it to that environment as the secret
NPM_TOKEN:gh secret set NPM_TOKEN --env npm-production --repo AetherAI3/Unlimited-Context-LLM
-
Bump the version in
aether_context/__init__.py:__version__ = "X.Y.Z"
That is the single source of truth.
pyproject.tomldeclaresdynamic = ["version"]and reads the attribute, so there is no second literal to keep in sync. -
Bump the npm launcher in
packages/npm-cli/package.jsonto the sameX.Y.Z. The launcher installs the PyPI release matching its own version, andtests/test_release_parity.pyfails the build if the two drift. -
Update
CHANGELOG.md: move items out of## [Unreleased]into a new## [X.Y.Z] β YYYY-MM-DDsection. The format follows Keep a Changelog and SemVer. -
Commit and merge to
mainvia PR, and let CI go green. -
Tag the merge commit and push:
git tag -a vX.Y.Z -m "aether-context vX.Y.Z" git push origin vX.Y.ZThe tag does not trigger anything β it is the auditable ref you publish from.
-
Publish to PyPI, passing the tag as the
refinput:gh workflow run publish.yml -f ref=vX.Y.Z
The job builds an sdist and wheel, asserts the wheel contains no
aether_agentfiles, runstwine check, then uploads via OIDC. -
Publish to npm, once PyPI shows the new version:
gh workflow run publish-npm.yml -f ref=vX.Y.Z
Add
-f dry_run=truefirst to see the packed tarball without publishing. -
Verify both:
pip install --upgrade "aether-context==X.Y.Z" && aether-context --version npx --yes aether-context@X.Y.Z --version
The wheel ships aether_context only, and declares one console script, aether-context.
aether_agent/ also lives in this repo and is exercised by the test suite, but it is not
packaged. PyPI's separate aether-agent
distribution already owns that import path and the aether command, and pip does not detect
file conflicts across distributions β shipping a second copy would silently overwrite the
other package's files on any machine that installed both. publish.yml re-checks the built
wheel for aether_agent files and fails the release if any appear.
400 Non-user identities cannot create new projects. The pending publisher's project name does not match the distribution name. Fix that field toaether-context; nothing is uploaded on a failed attempt, so the version is still free.invalid-publisherat the OIDC step. An owner / repository / workflow / environment mismatch. Re-check all four against the table above β the environment ispypi-production, notpypi.- Re-publishing. PyPI files are immutable: a version cannot be re-uploaded, even after it is deleted. If a release is broken, bump to a new patch version.
- Version mismatch. The published version comes from
aether_context.__version__, not the tag string. Keep them in lockstep (tagvX.Y.Zβ__version__ = "X.Y.Z"). - npm 2FA. A publish token fails in CI when 2FA is enforced; use an automation token.
- Local dev hygiene. Install the pre-commit hook so commits stay ruff-clean:
pip install pre-commit && pre-commit install(config:.pre-commit-config.yaml).