Repository navigation
feat: aeon init + credential manifest with drift test - #1158
Merged
Merged
Conversation
…ift test Each adapter's rh-meta block now lists its credentials (secret, kind, auth_mode, label, prefix, get_url, login_cmd, aeon_cmd, cred_paths, expires/refresh, aux_secrets), most preferred first in exactly the order scripts/resolve-harness.sh picks them, plus default_model. The old auth summary is derived from that list by the generator, so the two cannot disagree. The generator also writes harness-adapter/gateways.json from a gw-meta block in adapters/claude.sh: every llm-gateway.sh provider in the default cascade order with secrets, prefixes, base URL and transport. Drift fixed on the way: - grok no longer claims an OpenRouter fallback it does not have - pi lists ANTHROPIC_OAUTH_TOKEN, which resolve-harness.sh already honours - cli.install/min_version match the real pins (kimi/vibe had no install command; fx said 0.0.5 vs v0.0.12; cursor/hermes said latest) - cursor with no CURSOR_API_KEY is labelled AUTH_MODE=none instead of openrouter (label only; install still fails closed) - hivemindos added to the dashboard gateway registry scripts/tests/test_credential_manifest.sh (wired into ci-tests) holds the mirror to resolve-harness.sh (parsed and exercised), harness-auth.ts, the aeon.yml env blocks, install pins, llm-gateway.sh and gateway-registry.ts. ci-harnesses-json now diffs gateways.json too.
./aeon init takes a clone of aeonfun/aeon to a running instance and is safe to re-run (each step checks first, then prints what it fixed): 1. gh installed, signed in, token has repo + workflow (gh auth login --web -s workflow / gh auth refresh) 2. instance repo created from the template, not a fork (forks start with Actions off); this folder is switched to it with aeonfun/aeon kept as upstream, refusing when that could lose work; --dir clones elsewhere. Never adopts aeonfun/aeon, aaronjmars/aeon or any name that redirects. 3. gh repo set-default to the instance 4. Actions enabled and allowed to open PRs; the default token permission and an existing allowed_actions choice are left as they are; workflows GitHub left off (disabled_fork / disabled_inactivity) are re-enabled 5. GH_GLOBAL from the gh token, only when it has repo + workflow 6. model: harness menu and credential list from harnesses.json, reusing the aeon auth flows (login capture, setup-token, pasted key, gateways) 7. Telegram: bot token, then a /start nonce deep link found by polling getUpdates without an offset; 409 or timeout falls back to manual paste 8. summary checklist, then optionally the dashboard Also: - captureGithubToken (dashboard route + aeon auth --github) refuses a token without repo + workflow, or a fine-grained one it cannot inspect - aeon auth --harness grok (X login capture or xAI key) - aeon auth refuses to write secrets while gh points at the template - bin/onboard: instance + gh default checks, model check from the manifest (gateway-only setups pass), one secret list per run, --remote dispatches heartbeat (the onboard skill never existed) - codex manifest note: the capture eventually expires
- README quick start: clone + ./aeon init; the manual path stays as a fallback and now says forks start with Actions disabled and why gh repo set-default is mandatory - GH_GLOBAL = one classic PAT (repo + workflow) or the gh login token via ./aeon init / aeon auth --github; GH_SECRETS_PAT = optional, tried first by the rotating-login refresh paths, falls back to GH_GLOBAL. Same wording in CONFIGURATION.md, mcp-oauth.md, harnesses.md and bin/onboard - docs/harnesses.md: credential precedence table generated from the manifest; fx/cursor rows now show their aeon auth --key path - aeon setup skill (both copies, drift unchanged): Start mode runs ./aeon init - CONTRIBUTING: new gateways also go in the gw-meta block - apps/cli README: init + grok auth
…ompts Review fixes for aeon init: - Switch-over fetches and checks out the instance under a temporary remote first and renames remotes only after that worked, so an interruption can never leave main tracking the template; a re-run inside an instance repairs a branch that still tracks something other than origin. - commitAndPush pushes to origin by name (HEAD:<branch>, pull --rebase from origin) and refuses when origin is the template; the edit stays local. - --harness on an already-connected harness reports it and only switches aeon.yml; no login is re-run (grok rotates its capture on login). - Without a terminal every confirm is "no" unless --yes, and no browser login or key prompt is started. - --dir waits for the template copy to have aeon.yml, verifies the clone, removes only what it created, refuses a non-empty folder, and reports a failed hand-over. - Dirty / local-commit checks run before the repo is created and count commits on every branch. - Secret checks query the instance with -R, so a dry run from a template clone never reads aeonfun/aeon. - Telegram: still no offset; a backlog of 100+ updates goes straight to the manual chat id. - GH_GLOBAL scope refusals are a typed error, 400 from the route, with neutral wording; docs and init note that gh login tokens can be revoked and recommend a dedicated classic PAT for long-lived instances. - bin/onboard: --remote help matches what heartbeat does; an unknown harness warns and checks claude, as resolve-harness.sh runs it. - test_credential_manifest: key-order independent registry parse, guarded lookups, oauth capture parity check, clearer CLAUDE_AUTH_SECRETS message. Tests: apps/cli/test/init-sandbox.sh (fake gh + bare repos, 34 checks, in ci-apps) and lib/github-push.test.ts (commitAndPush against local repos).
- A template copy starts a fresh history, so when the folder's branch
shares no commits with origin, init now checks out origin/<branch>
(only with a clean tree and no unpushed commits) instead of only
re-pointing @{u}; otherwise it stops with a hint.
- --dir is checked before the repo is created: a file or a non-empty,
non-Aeon folder is refused (no ENOTDIR crash). A failed clone empties a
folder that already existed instead of removing it.
- Clearer hints when the aeon-instance remote cannot be added and when the
old origin cannot be moved (existing origin-previous).
- fake-gh `repo create` now makes a fresh root commit like GitHub; sandbox
adds the fresh-history repair (and its dirty-folder refusal) and the
--dir-on-a-file case.
- First run prints one line, "Installing dashboard dependencies (first run only)...", and runs `npm ci` (or `npm install` with no lockfile) with --no-audit --no-fund --loglevel=error. Output goes to a temp log that is shown (last 30 lines + path) only when the install fails, instead of pages of eslint peer warnings and a dev-only audit summary. - The banner drops the two-column emoji and pads every row to the same width for any port length (checked with 4, 5 and 7 digit ports).
A live connect-check on a GitHub-hosted runner passed with a CLAUDE_CODE_OAUTH_TOKEN from claude setup-token, so "frequently rejected" overstated it. The note now says it can be rejected in some cases, to confirm with Test connection, and to switch to an API key or a gateway key (OpenRouter etc.) only if it shows zero usage.
aaronjmars
added a commit
that referenced
this pull request
Oct 3, 2026
main tree equals the #1158 head, which this branch already contains.
5 tasks done
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.
What this does
Setting up Aeon today takes about 11 manual steps, and two of them fail silently (a fork has Actions off, and without
gh repo set-defaultsecrets land on aeonfun/aeon). This PR makes it one command and gives every surface one list of "which secret runs which harness".1. Credential manifest (
harness-adapter/harnesses.json+ newgateways.json)rh-metablock now lists its credentials, most preferred first, in exactly the orderscripts/resolve-harness.shpicks them:secret,kind(oauth_capture / oauth_token / api_key / oidc),auth_mode,label,prefix,get_url,login_cmd,aeon_cmd,cred_paths,expires/refresh,aux_secrets. Plusdefault_model.authsummary is now derived from that list by the generator, so the two cannot disagree.gateways.json(from agw-metablock inadapters/claude.sh) lists everyllm-gateway.shprovider in cascade order: secrets, prefixes, base URL, transport, where to get a key.ANTHROPIC_OAUTH_TOKEN; install commands andmin_versionmatch the real pins (kimi, vibe, fx, cursor, hermes were wrong); cursor with no key is labelledAUTH_MODE=noneinstead ofopenrouter(label only); hivemindos added togateway-registry.ts.2. Drift test (ci-tests)
scripts/tests/test_credential_manifest.sh(248 checks) holds the manifest to:resolve-harness.sh: precedence parsed from the source and exercised (each credential alone gives its auth_mode; a more preferred one wins over a less preferred one); default_model.harness-auth.ts: authSecrets order, OAuth secret, credPaths (read-only parse).aeon.yml: every credential is bound in Resolve harness / Install harness CLI / Run.llm-gateway.sh(order, secrets, route arm, base URL host, sidecar or not) andgateway-registry.ts(slugs, labels, secrets, prefixes) +CLAUDE_AUTH_SECRETS.ci-harnesses-jsonnow diffsgateways.jsontoo.3.
./aeon initSafe to re-run; every step checks first and prints what it fixed:
repo+workflow.upstream. The instance is fetched and checked out under a temporary remote first, and the remotes are renamed only after that worked, so an interrupted run can be re-run safely. Inside an existing instance, init repairs a branch that still tracks something other thanorigin. If that branch is still on the template history (template copies start with a fresh history), init checks out the instance branch instead, but only when that cannot lose work. It refuses uncommitted changes or unpushed commits on any branch, and checks this before it creates anything.--dirwaits for the template copy to finish, clones it somewhere else and hands over to that copy's./aeon init. It never adopts aeonfun/aeon, aaronjmars/aeon or any name that redirects.gh repo set-defaultto the instance.allowed_actionschoice are left as they are. Workflows GitHub left off (disabled_fork,disabled_inactivity) are turned back on.GH_GLOBALfrom the gh token, only if it has repo + workflow.aeon authflows (login capture,claude setup-token, pasted key, gateway keys) and points out the one-key OpenRouter option. If the chosen--harnessis already connected, init only switchesaeon.ymlto it and never re-runs a login.t.me/<bot>?start=<nonce>link. Init pollsgetUpdateswithout an offset, so no updates are consumed. On a 409, a backlog of 100 or more updates, or a timeout, it falls back to asking you to paste the chat id.Flags:
--name,--private,--dir,--harness,--yes,--no-telegram,--no-dashboard,--dry-run. Without a terminal, every confirmation counts as "no" unless--yesis set, and init never starts a browser login or asks for a key.commitAndPush(used by init and the dashboard to saveaeon.yml) now pushes tooriginby name and refuses whenoriginis the template; the edit stays on disk.4. Smaller fixes
aeon auth --githuband the dashboard GitHub connect now refuse a token without repo + workflow, or a fine-grained token whose scopes cannot be read. The route returns a 400 that says to set GH_GLOBAL to a classic PAT with repo + workflow. The docs and init note that GitHub revokes gh login tokens (after a year unused, or once more than 10 exist), so a long-lived instance should use a dedicated classic PAT.aeon auth --harness grokworks from the CLI.aeon authrefuses to write secrets while gh points at the template.bin/onboard(still read-only): checks the instance and gh default repo. The model check now uses the manifest, so gateway-only setups pass. An unknown harness is a warning and is checked as claude, which is whatresolve-harness.shruns. It reads the secret list once per run.--remotenow dispatchesheartbeat(theonboardskill it used to dispatch never existed). The help text says what that costs, and that heartbeat commitsdocs/status.mdand stays quiet unless it finds something.Root
./aeonlauncher: on the first run it prints one line and runs a quietnpm ci, and shows the npm log only if the install fails. The banner box lines up for any port length (no emoji).5. Docs
./aeon init; the manual path stays as a fallback and says forks start with Actions off../aeon init. The known drift between the two copies is unchanged.How it was verified
bash apps/cli/test/init-sandbox.sh(new, runs in the ci-apps cli job): 43 checks against a fakeghbacked by local bare repos, with no network. Its template copies get a fresh root commit, like GitHub. Covers the switch-over, an idempotent re-run with zero settings changes, repairing a branch left tracking the template, moving a folder that is still on the template history onto the instance (and refusing when that folder has local edits), resuming an interrupted switch-over,--harnesson an already-connected harness (pushesharness: codexto the instance and leaves the template untouched), no--yeswithout a terminal, a dirty folder refused before the repo is created, and--dirhand-over plus refusing a non-empty folder or a file before anything is created. Turning off the repair step and the pre-create check made 4 checks fail.lib/github-push.test.ts(new):commitAndPushpushes to origin while the branch tracks the template, and refuses when origin is the template.bash scripts/tests/test_credential_manifest.sh: pass (266 checks). The registry parse no longer depends on key order (checked with a reordered, multi-line entry). Mutation check: 9 deliberate drifts (swapped precedence in resolve-harness.sh and in harness-auth.ts, default model, registry label, a gateway dropped from the registry, gateway order, fx pin, unbound workflow secret, a removed OpenRouter path). Every one failed the test.test_generate_harnesses_json,test_resolve_harness,test_install_harness,test_dashboard_model_choices,test_workflow_harness_choices, the adapter tests (grok, codex, cursor, hermes, pi),test_llm_gateway,test_all_secrets_allowlist,test_validate_readme_catalog,check-aeon-skill-sync.npm run typecheck,npm run lint(0 errors; the 14 warnings were there before),npm test(234/234, including the new scope-check and push tests).npm run typecheckandnpm run lint, both clean.aeon initend to end: run against a clone of the template, with aghshim that lets reads through and blocks every GitHub write. It found an existing instance, switched the folder over (origin is the instance, upstream is aeonfun/aeon, main tracks origin), set the default repo, and detected the existing GH_GLOBAL, model and Telegram. A second run reported every step as already done. On a healthy live instance it makes zero PUTs.--dry-run --yesfrom a fresh template clone shows the redirect guard (aaronjmars/aeonis refused) and the create path. Secrets are read only from the target repo (-R <target>), never from aeonfun/aeon.bin/onboardchecked on a live instance (14/14 pass, read-only), on a template clone (flags the instance), and with a gateway-only secret set (passes via the Bankr gateway).Not verified live: creating a brand-new repo from the template, and the interactive key, Telegram and login prompts. These need a real account write or a TTY, and no throwaway repo was created.
Known gap, kept visible
XAI_API_KEYis not bound in aeon.yml's Run step env (it was dropped for per-skill least privilege). So thegrokgateway only resolves for skills that list XAI_API_KEY inrequires:. The grok harness is unaffected. The test holds this as a named known gap and fails once the binding comes back.