Skip to content

Publish Aether Agent to PyPI as the aether-agent launcher - #129

Merged
AetherAI3 merged 1 commit into
mainfrom
feat/pypi-launcher
Sep 3, 2026
Merged

AetherAI3 merged 1 commit into
mainfrom
feat/pypi-launcher

Conversation

@AetherAI3

Copy link
Copy Markdown
Owner

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.

pipx install aether-agent
aether-agent auth login
aether-agent code --test-cmd "npm test" "fix the failing test"

A launcher, not a second agent

The tests hold it to that:

  • Forwards everything. Every argument outside the self namespace goes 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|doctor|path|uninstall belong to the launcher, so a launcher command can never shadow an agent command.
  • One known version. The version of this package is the version of aether-agents it installs. packages/sync-version.mjs copies package.json's version into the pyproject and the module, --check fails CI on drift, and the publish workflow proves the release tag agrees before building.
  • No admin rights, no lifecycle scripts. Installation goes into a private prefix under the user's own data directory with --ignore-scripts. An aether already on PATH wins, and nothing is installed behind the user's back.
  • No injectable install argument. 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.

Publishing

publish-pypi.yml mirrors release.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_dispatch defaults to a dry run so the path can be rehearsed.

Before the first real publish, the pypi-production environment needs its Trusted Publisher registered on pypi.org (owner AetherAI3, repo aether-agent, workflow publish-pypi.yml, environment pypi-production).

CI

New pypi-launcher job: unit tests (29), strict mypy, the version-sync check, and a wheel install smoke test, on Python 3.10 — the floor requires-python declares, so the published wheel is proved on the oldest interpreter it claims.

npm run verify:production and the README truth suite both pass unchanged; the packed npm tarball is untouched (673 files) because packages/ is outside the files allowlist.

🤖 Generated with Claude Code

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>
@AetherAI3
AetherAI3 merged commit b1972ac into main Sep 3, 2026
7 checks passed
@AetherAI3
AetherAI3 deleted the feat/pypi-launcher branch September 3, 2026 16:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant