Publish Aether Agent to PyPI as the aether-agent launcher - #129
Merged
Merged
Conversation
Aether Agent has shipped on npm only, so a machine that manages its tools with pip or pipx had to reach for a global npm install to get it. This adds packages/pypi-cli, published to PyPI as `aether-agent`: the same agent, the same commands, installed the way that machine already installs things. It is a launcher, not a second agent, and the tests hold it to that: - Every argument outside the `self` namespace is forwarded to the real `aether` CLI unchanged, and its exit code becomes the process's exit code. `doctor`, `auth`, `sessions`, `config` and the rest reach the agent; only `self install`, `self doctor`, `self path`, and `self uninstall` belong to the launcher, so a launcher command can never shadow an agent command. - The version of this package is the version of `aether-agents` it installs. packages/sync-version.mjs copies package.json's version into both the pyproject and the module, `--check` fails CI when they drift, and the publish workflow proves the release tag agrees before building. - Installation goes into a private prefix under the user's own data directory with --ignore-scripts, so it needs no administrator rights and runs no package lifecycle scripts. An `aether` already on PATH wins, and nothing is installed behind the user's back. - AETHER_AGENT_NPM_VERSION is validated before it can reach an npm argument list, so an override cannot smuggle a flag or shell syntax into the install command. - No runtime dependencies: it shells out to node and npm, which the agent requires anyway. The Node floor is asserted against package.json's engines field rather than hardcoded twice. publish-pypi.yml mirrors release.yml: same immutable release tag, same ancestor-of-main check, same evidence upload, and PyPI Trusted Publishing (OIDC) rather than a stored token. workflow_dispatch defaults to a dry run so the path can be rehearsed before the first real publish. The pypi-production environment must have its Trusted Publisher registered on pypi.org first. The pypi-launcher CI job runs the unit tests, strict mypy, the version-sync check, and a wheel install smoke test on Python 3.10 -- the floor requires-python declares. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Aether Agent has shipped on npm only, so a machine that manages its tools with pip or pipx had to reach for a global npm install to get it. This adds
packages/pypi-cli, published to PyPI asaether-agent: the same agent, the same commands, installed the way that machine already installs things.A launcher, not a second agent
The tests hold it to that:
selfnamespace goes to the realaetherCLI unchanged, and its exit code becomes the process's exit code.doctor,auth,sessions,configand the rest reach the agent; onlyself install|doctor|path|uninstallbelong to the launcher, so a launcher command can never shadow an agent command.aether-agentsit installs.packages/sync-version.mjscopiespackage.json's version into the pyproject and the module,--checkfails CI on drift, and the publish workflow proves the release tag agrees before building.--ignore-scripts. Anaetheralready on PATH wins, and nothing is installed behind the user's back.AETHER_AGENT_NPM_VERSIONis validated before it can reach an npm argument list, so an override cannot smuggle a flag or shell syntax into the install command.nodeandnpm, which the agent requires anyway. The Node floor is asserted againstpackage.json'senginesfield rather than hardcoded twice.Publishing
publish-pypi.ymlmirrorsrelease.yml: the same immutable release tag, the same ancestor-of-main check, the same evidence upload, and PyPI Trusted Publishing (OIDC) instead of a stored token.workflow_dispatchdefaults to a dry run so the path can be rehearsed.Before the first real publish, the
pypi-productionenvironment needs its Trusted Publisher registered on pypi.org (ownerAetherAI3, repoaether-agent, workflowpublish-pypi.yml, environmentpypi-production).CI
New
pypi-launcherjob: unit tests (29), strictmypy, the version-sync check, and a wheel install smoke test, on Python 3.10 — the floorrequires-pythondeclares, so the published wheel is proved on the oldest interpreter it claims.npm run verify:productionand the README truth suite both pass unchanged; the packed npm tarball is untouched (673 files) becausepackages/is outside thefilesallowlist.🤖 Generated with Claude Code