feat: add mnfst management CLI with browser OAuth login - #2673
Draft
guillaumegay13 wants to merge 59 commits into
Draft
feat: add mnfst management CLI with browser OAuth login#2673guillaumegay13 wants to merge 59 commits into
guillaumegay13 wants to merge 59 commits into
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #2673 +/- ##
==========================================
+ Coverage 96.44% 96.46% +0.01%
==========================================
Files 599 605 +6
Lines 35231 35372 +141
Branches 9815 9841 +26
==========================================
+ Hits 33979 34122 +143
+ Misses 691 689 -2
Partials 561 561
Flags with carried forward coverage won't be shown. Click here to find out more. 🚀 New features to boost your workflow:
|
…ertions The expired-code test wrote SQL now() (the DB container's UTC) into cli_auth_codes.expires_at, whose invariant is the Node process's local wall clock. West of UTC the cutoff landed hours in the future and the test expected 400 but got 200. Compute the cutoff with toLocalSqlTimestamp() and scope the UPDATE to the row under test. Also: @IsString() + readable messages on the DTOs, count cli keys on the happy path so a double-mint fails, pin expiresAt as ISO-8601 UTC at the configured TTL, and retitle the happy-path test so it does not claim to prove the PAT authenticates.
… failed authorize
…r token before validation
Contributor
Database schema changesMigrations in this PR:
Summary
Schema diagramShowing only changed tables. 🟢 added, 🟡 modified, 🔴 removed. Changed columns are annotated inline. erDiagram
api_keys["🟡 api_keys (modified)"] {
varchar id PK
varchar key
varchar created_by_user_id
varchar name
timestamp_without_time_zone created_at
timestamp_without_time_zone last_used_at
varchar_128_ key_hash
varchar_12_ key_prefix
varchar tenant_id
timestamp_without_time_zone expires_at "🟢 added"
}
cli_auth_codes["🟢 cli_auth_codes (added)"] {
varchar id PK
varchar_64_ code_hash
varchar_128_ state
varchar tenant_id
varchar user_id
timestamp_without_time_zone created_at
timestamp_without_time_zone expires_at
}
Detailed column-level changes🟢
|
Bundle ReportChanges will increase total bundle size by 2.8kB (0.18%) ⬆️. This is within the configured threshold ✅ Detailed changes
Affected Assets, Files, and Routes:view changes for bundle: manifest-frontend-esmAssets Changed:
|
…nt platforms command
…ubscription handling
… and custom header tiers, drop deprecated complexity tiers
…tion revokes instantly
…otate --key-file; sim-driven polish
…yle impersonation)
…atform catalog generated from it
…by agent create and agent setup
…T_TELEMETRY_DISABLED
The quota counter buckets a request into
GREATEST(date_trunc('month', timestamp), PLAN_REQUEST_QUOTA_RESET_AT).
The fixture asserted against the shipped reset default (2026-07-09), which
only equals that window while the clock sits inside July 2026, while the
rows it inserts as "live" are stamped from clock_timestamp() at migration
time. From 2026-08-01 the trigger bucketed live rows into the calendar-month
window and the assertions kept reading the July row, so three tests saw only
the historical baseline (3 -> 2, 3 -> 2, 4 -> 2). Upstream main reproduces
this identically today; no production code is involved.
Override PLAN_REQUEST_QUOTA_RESET_AT for the suite with
max(now - 2h, start of current UTC month) and derive the asserted window and
the historical seed timestamps from it, so every fixture row shares one
window regardless of the month the suite runs in.
…, oauth cadence Twelve localized findings from the final review of the mnfst CLI. Routing: - routing test defaulted /v1/messages to a hardcoded claude-sonnet-5 on the false premise that "auto" is not a route override there; every public surface routes "auto" and reads a concrete model as an explicit override, so both surfaces now default to auto (--model stays the override). - --as is validated against PLATFORM_CATALOG before any network call; an unknown value used to silently test the wrong surface. Catalog drift: - CI now runs git diff --exit-code on provider-catalog.gen.ts after the cli build: the build regenerates the file, so the drift specs can never catch a stale commit. The generator formats its output with the repo prettier config so the committed file and a fresh gen are byte-identical. - The spec drift-compares SETUP_TEMPLATES too, and the generator emits CATEGORY_CATALOG from manifest-shared AGENT_CATEGORIES so --category is validated client-side on agent create/update. Keys and setup: - The masked "$(mnfst agent key show <slug>)" placeholder never expanded: key show printed JSON, and the placeholder sits in JSON/single-quoted contexts. Added `agent key show --raw` (bare key, still never in argv) and replaced the placeholder with a literal ref naming that command. - agent create --if-absent now also returns setup, rendered from the stored platform, so the idempotent path is not poorer than the create path. OAuth device flow: - The computed poll interval was clamped to 2s, violating Kiro's 5s cadence, and per-poll pollIntervalMs was ignored. Sleep the server-suggested value (1s floor, no cap) and honor a mid-flow slow_down; tests drive it through OAUTH_POLL.deviceIntervalOverrideMs. Hygiene: - Telemetry drains the response body so an unread socket cannot delay exit, and both USAGE and the CLI README now disclose the persistent anonymous install id, the exact payload fields, the opt-out, and the endpoint override. - USAGE matches the real flag sets (no --yes on provider enable, --key-label on agent configure, --agent on provider custom, --enabled only on set). - AgentKeyAuthGuard clears its static caches in onModuleDestroy. - Corrected the custom-provider probe hint's anthropic models path.
CodeQL flags /^-+|-+$/ as js/polynomial-redos (high). The trim is now a linear index scan with identical output.
Three empty-context sims (CrewAI crew, day-zero install, OpenClaw onboarding) exposed lines that misled: hollow connections (cached_model_count: 0) read as usable, wrong-host 401s read as auth failures, agent-env collisions in multi-agent .envs, and the single-provider constraint on configure fallback chains.
…dation parity Four gaps the CLI left to guesswork: - `mnfst doctor` runs config → host → auth → providers → agents in dependency order and prints one JSON verdict. Ordering is the point: a wrong MANIFEST_URL and a wrong key produce identical errors from any single command, so a live host that rejects the credential is reported as a wrong-install/wrong-key problem instead of "run mnfst login" — which fixes nothing when the credential came from the environment. A check whose prerequisite failed is marked skipped, not failed. - `mnfst provider refresh [<provider>]` re-runs model discovery tenant-wide, then reads the connections back so the caller sees the new per-connection model counts. It is the fix for a connection that is active with zero cached models, which routing cannot use. - `agent configure` and `routing custom create` now share one discovered-model gate (model-check.ts), so the two ways of writing a route cannot disagree. Custom-tier creation validates before the tier is created, so a typo leaves nothing behind. `--force` skips the check on both — the backend still routes uncatalogued models through provider-qualified passthrough, and the CLI must not be what makes a brand-new model unusable. The error names both remedies. - `mnfst model prices [--provider <p>]` reports install-wide pricing with no agent required, opening a `model *` namespace for tenant-wide model readouts (`models <agent>` keeps the per-agent question).
… and model prices The wrong-host 401 row told the reader to curl /api/v1/health by hand; `mnfst doctor` answers the same question and separates a dead host from a key that is wrong for this host. The two hollow-connection rows now name `mnfst provider refresh` as the fix instead of "reconnect and hope", and the model-validation row covers `routing custom create` and `--force` as well. Pricing before any agent exists no longer needs a probe agent — `mnfst model prices` is install-wide.
…ning it The operating guide for this CLI lived only in the repo, so the agents that most need it — the ones driving `mnfst` from a coding session — never saw it. - A gen step embeds .claude/skills/mnfst-cli/SKILL.md into src/skill-content.gen.ts (committed, prettier-stable, same contract as provider-catalog.gen.ts). CI's drift gate now diffs both gen files after rebuilding; a drift spec fails locally before that. - `mnfst skill show` prints the markdown raw — the second command that is not JSON, after `agent env`, because the output is meant to be read or piped, not parsed. `mnfst skill install` writes <dir>/mnfst-cli/SKILL.md and is idempotent (updated:false on identical content), with --agents-dir and --project for the other two conventions. - Runtime detection (agent-runtime.ts) reads an explicit marker table — CLAUDECODE, CURSOR_TRACE_ID, CODEX_SANDBOX/CODEX_HOME. No process sniffing and no heuristics: the only thing detection drives is an unsolicited stderr line, so a wrong guess is worse than no guess. Adding a runtime is one line. It feeds three things: `skill install` defaults to the detected runtime's own skills directory (installing where the running agent does not look would be theatre), telemetry carries a coarse `agent_runtime` id only when detected (key omitted otherwise, so schema_version stays 1), and a once-per-runtime stderr hint fires after a successful command when the guide is missing. The hint is state-tracked in ~/.config/manifest/skill-nudge.json, never runs on failure or on the skill commands themselves, and swallows every error — a nudge that throws would turn a working command into a broken one. - doctor gains an informational `skill` check that reports where the guide was found, or not_found; it never fails the run.
This was referenced Aug 2, 2026
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 changed
packages/cli:mnfst, an agent-first management CLI for Manifest (zero runtime deps, JSON stdout, 237 tests, 100% line coverage).mnfst loginopens the dashboard, one click on the new/cli/authpage mints a 30-day sliding-expiry token via a one-time code exchange (cli_auth_codestable, single-use, 5 min TTL). Headless keeps--token-stdin/--token-env;logoutrevokes server-side.api_keys.expires_at, slid forward on use byApiKeyGuard(CLI_TOKEN_TTL_DAYS, default 30). Existing keys stay non-expiring.agent create --if-absent(returns per-platformsetupinstructions),configure(default route + fallbacks via--models a,b,c, custom header tiers via--tier),setup [--reveal],env,key path|show [--raw], managed keystore,rotate-key(revocation now instant),provider enable|disable,run(env-injected child processes).connectwith per-platform auth (api_key, browser OAuth for xai/openai/gemini, paste-code for Anthropic, device flow for Kiro/MiniMax),catalog,custom add|list|remove(probe-first), per-agent enable/disable.status(composite readback),test(one real request through the agent platform's actual API surface — messages vs chat completions — with fake-200 Manifest errors unmasked),customtier lifecycle. The deprecated complexity tiers are absent from the CLI by design.requests getwith API-faithful cursor pagination, decision-relevant row trim +--full;models <agent> [--cost] [--capabilities]mirroring/v1/models.PLATFORM_API_SURFACES, new inmanifest-shared), setup snippets (moved tomanifest-shared, dashboard re-exports), and categories are all codegen'd from shared at build with drift specs plus a CIgit diff --exit-codegate.MANIFEST_TELEMETRY_DISABLED=1opt-out, disclosed in--helpand README. Ingest lands via mnfst/peacock#222..claude/skills/mnfst-cli/— operating guide tested against cold-start agents.AgentKeyAuthGuardcaches made class-static (Nest instantiates class-referenced@UseGuardsper host module, so rotation cleared a different instance than the proxy authenticated with — revoked keys survived up to 5 min); e2etenant-request-usage-migrationdate-bomb fix (fixtures were pinned to July 2026 and fail everywhere — including upstream main — since Aug 1; the quota window is now derived from the clock).💭 Why
Coding agents and orchestrators should operate Manifest without the dashboard: provision an agent per automation, route it, verify it, wire it, observe it — one credential, no key ever crossing a transcript. Humans get one browser click; agents get env auth and JSON everywhere.
👤 For users
mnfst login→agent create --name bot --platform openai-sdk --if-absent→agent configure bot --models cheap,fallback --provider xai→routing test bot→agent env bot >> .env. Display names work everywhere (CLI slugifies like the backend). Destructive ops require--yes; multi-auth providers require explicit--auth-type.🔧 For operators
Three additive migrations (
api_keys.expires_at,cli_auth_codes, plus the guard change is code-only). New optional envs:CLI_TOKEN_TTL_DAYS,MANIFEST_TELEMETRY_DISABLED,MANIFEST_CLI_TELEMETRY_ENDPOINT. CI: cli job builds shared first and gates on generated-catalog freshness. The e2e date-bomb fix also applies to main (its CI will fail on the next run without it — consider cherry-picking).📝 Notes
--key-file, never stdout.API_KEYtenant binding +setup admin, limits CRUD, tab completion, npm publishing.