`hanzo` depends on `hanzo-cli`, and BOTH declared `[project.scripts] hanzo`.
Entry-point resolution is install-order dependent, so which CLI a user got was
a coin flip — and hanzo-cli kept winning. The result:
$ hanzo auth login
Error: No such command 'auth'.
So the "auth login is broken by Cloudflare 1010" story was real but unreachable:
users never got as far as the HTTP call, because the binary they had did not
have an `auth` command at all. Publishing that fix would have changed nothing.
One command, one owner:
- hanzo-cli drops its console script. `hanzo` is the ONE command.
- `paas` and `bot` are mounted into it — the only two groups with no equivalent.
`kms` is NOT mounted: `hanzo secrets` is a strict superset (audit/grant/
revoke/rollback/rotate/versions on top of the same get/list/set/delete), and
two commands for one concern is what we are removing.
- hanzo_cli grows a __main__ so it stays invocable as `python -m hanzo_cli`,
and its e2e suite targets that instead of whatever `hanzo` resolves to.
Endpoints, one definition:
- models.IAMConfig (the class the package EXPORTS) gains the HIP-0111 endpoint
properties. config.IAMConfig had them, but nothing imports that one — a
second same-named class nobody uses is how the legacy path survived.
- config.py + fastapi.py stop hand-assembling paths and read the constants.
- password_login posted to `/oauth/token`, which is not a 404: IAM serves a
200 text/html SPA catch-all for unregistered paths, so it received a login
PAGE and json() blew up on HTML. Verified: legacy 200 text/html vs canonical
401 application/json.
PKCE, because this is a public client:
- browser_login ships no client_secret, so the authorization code was the only
secret in the flow and it arrives over a plaintext loopback redirect. The SDK
already accepted code_challenge/code_verifier; the CLI simply never passed
them. RFC 8252 §8.1 / RFC 7636 S256.
Version, one source:
- pyproject said 0.4.4, __init__ said 0.3.47, cli.py said 0.3.48, and the
installed binary reported 0.1.0 — four answers, none right. Now resolved
from the installed distribution.
Verified on a clean venv: entry-point providers 2 -> 1, commands 10 -> 58,
`hanzo --version` 0.1.0 -> 0.4.4, all four endpoints canonical. hanzo-iam
19/19 pass; hanzo-cli's 20 failures are unchanged before and after and are all
live-service auth (no stored token in this environment).
Co-authored-by: Hanzo Dev <[email protected]>
Follow-up to 9c377be (hanzo-iam 1.30.0). Consumer packages now read only
the canonical IAM_ prefix — no upstream-brand alias chain, matching the
new IAMConfig.ENV_PREFIX contract enforced by test_env.py.
Touched modules:
- hanzo-cli: hanzo_cli/auth.py, hanzo_cli/bot/commands.py
- hanzo-tools-auth: hanzo_tools/auth/session.py
- hanzo-tools-iam: hanzo_tools/iam/iam_tool.py
- hanzo: src/hanzo/commands/{auth,iam}.py
- hanzoai: auth.py, llm_client.py
- tests: test_auth_pkce.py (import IAM_CLIENT_ID)
Also adds the generated pkg/hanzo-iam/uv.lock — matches the convention
of the other 17 axis pkgs that track their lockfiles.
Add install, login, logout, run, stop subcommands to `hanzo bot` for
managing local bot agent nodes that connect to gw.hanzo.bot. Browser
OAuth flow authenticates against hanzobot-client-id with dedicated token
storage at ~/.hanzo/bot/token.json. Password grant fallback via
--no-browser flag.
Normalize env var lookup: IAM_ canonical prefix with HANZO_IAM_ accepted
as backwards-compatible fallback via _env() helper in both auth.py and
bot/commands.py.