chore(auth): global --user account selector (cli-core 0.23)#77
Conversation
Bump @doist/cli-core to 0.23.0 and add the plumbing for a root `--user <ref>` account selector: parse + validate it (rejecting a value-less flag or a forgotten value that's actually a command), warm the global-args cache, then strip it before Commander parses. The resolved ref is exposed via getRequestedUserRef() for the account-aware store wiring still to come; selection is not yet wired into request token/base-URL resolution. Also extend the Outline token store with activeAccount() and have clear() return the cleared account, matching the cli-core 0.23 TokenStore shape. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Thread the global `ol --user <ref>` selector into account resolution so every command can act as a specific stored account, matched by Outline user UUID or display name. - withUserRefAware wraps the token store to substitute the global ref for no-ref reads (active/activeBundle/activeAccount/clear), used by both the request path and cli-core's auth status/logout attachers. An explicit ref wins; an unknown ref fails fast with ACCOUNT_NOT_FOUND. - getBaseUrl/getOAuthClientId resolve the selected account's instance via recordForRef, so token, base URL, and client id all track the same account (each account can live on a different Outline instance). - An OUTLINE_API_TOKEN wins outright: the request path skips ref resolution so token/base URL/client id stay pinned to the default source together. matchOutlineAccount moves to outline-account.ts (re-exported from auth-provider) to keep the wrap free of a lib->commands import. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
doistbot
left a comment
There was a problem hiding this comment.
This PR introduces a global --user selector to enable multi-account support across commands and bumps @doist/cli-core to version 0.23.0. The implementation effectively wires the user-facing selection through the existing storage layer, significantly enhancing the flexibility of the CLI. A few areas need refinement, including adjusting the argument parser to correctly ignore late flags, ensuring accurate legacy account resolution and status reporting, keeping configuration resolvers account-agnostic, updating the skill documentation to match the new option, and applying some minor testing and type-safety improvements.
- Validate `--user` only in root position; a flag after the command falls through to Commander as an unknown option instead of a false "requires a value" error. Move the validate→warm→strip flow into `applyUserSelector` so the order is exercised by tests rather than living untested in index.ts. - Check account existence in the ref-aware wrap via `activeAccount` (token-free and legacy-aware) instead of `list()`, so `--user <legacy-name>` resolves a pending v1→v2 account instead of throwing ACCOUNT_NOT_FOUND. - Keep `getBaseUrl`/`getOAuthClientId` account-agnostic so `auth login` isn't skewed by `--user`; resolve the selected account's instance + client id only on the request path via `getRequestContext`, passed as the refresh handshake. - Make `getActiveTokenSource(ref?)` account-aware so `auth status --user <ref>` reports the selected account's token source, not the default/env one. - Build the wrap without a type assertion; drop the duplicate `toRecord` allocation in `recordForRef`. - Document the global `--user` option in SKILL_CONTENT + root help, and regenerate SKILL.md. - Tests: static imports + a real `applyUserSelector` entrypoint test; `.rejects` matchers; and an `auth status --user` integration test that proves the wired store routes to the selected account. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Extract the two-account config into a shared TWO_USER_CONFIG fixture (was hand-rolled in three test files), collapse the ACCOUNT_NOT_FOUND case to a single assertion, and drop two getRequestedUserRef cases already covered by the applyUserSelector entrypoint test and cli-core's own parser. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
## Summary Surfaces the multi-account storage (already shipped in #77 — `users[]` config, keyring, `--user` selector, `withUserRefAware` store-wrap) to users via a new `ol account` command group, wiring cli-core's four generic account attachers. - `ol account list` (and bare `ol account`, via default subcommand) — lists stored accounts with an accessibility-aware default marker; `--json` emits a `{ accounts, default }` envelope, `--ndjson` streams one per line. - `ol account use <id|name>` — sets the default account used when `--user` is omitted. - `ol account current` — shows the active account, honours `ol --user <ref>`, and reports the `OUTLINE_API_TOKEN` env source and legacy single-user sessions via `onNotAuthenticated` (rather than rendering them as a blank stored account). - `ol account remove <id|name>` — clears the keyring + config entry and surfaces any keyring-fallback warning. - Machine output drops the OAuth client id (`{ id, label, teamName, baseUrl, isDefault }`). Reuses existing plumbing: `createOutlineTokenStore`, `withUserRefAware`, `matchOutlineAccount`, `logTokenStorageResult`. No changes to the env/legacy resolution logic beyond a read-only wrapper for `current`. ## Test plan - [x] `npm run type-check` - [x] `npm run test` — 200/200 pass, incl. new `src/commands/account.test.ts` (list/use/current/remove, env-token, legacy, empty, not-authenticated) - [x] `npm run format:check` clean - [x] `npm run check:skill-sync` in sync - [x] `node dist/index.js account --help` renders all four subcommands + examples - [ ] Manual smoke against a real multi-account config: `ol account`, `ol account use`, `ol --user <ref> account current`, `OUTLINE_API_TOKEN=… ol account current` 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
🎉 This PR is included in version 1.9.0 🎉 The release is available on: Your semantic-release bot 📦🚀 |
Summary
Adds a global
ol --user <ref>selector so every command can act as a specific stored account (matched by Outline user UUID or display name), and bumps@doist/cli-coreto 0.23.0 to support it.Part of the cli-core migration — this is the multi-account selection layer. The storage was already multi-account; this wires the user-facing selection through.
Commit 1 —
--userplumbing + cli-core 0.23@doist/cli-core0.19 → 0.23.0.--user, thenstripUserFlagit before Commander parses (Commander has no global option). Validation rejects a value-less flag or a forgotten value that's actually a command.getRequestedUserRef(); extend the Outline token store withactiveAccount()and haveclear()return the cleared account (0.23 shape).Commit 2 — honor
--useracross all commandswithUserRefAwarewraps the token store to substitute the global ref for no-ref reads (active/activeBundle/activeAccount/clear), used by both the request path and cli-core'sauth status/auth logoutattachers. Explicit ref wins; unknown ref fails fast withACCOUNT_NOT_FOUND.getBaseUrl/getOAuthClientIdresolve the selected account viarecordForRef, so token + base URL + client id all track the same account (each account can live on a different Outline instance).OUTLINE_API_TOKENwins outright: the request path skips ref resolution so all three stay pinned to the default source together.Behavior notes
--usermust precede the subcommand (ol --user X document list); after the subcommand it reaches Commander. Matches todoist/twist.--helptext for--userandSKILL_CONTENTnot updated yet — deferred until theaccount list/use/current/removecommands land so the multi-account story documents as a coherent whole.Test plan
npm run type-check,npm run format,npm test(186 passing, +remaining)--user→ACCOUNT_NOT_FOUNDenvelope;--user <name>routes the request to that account's instance + token; bare/--user <command>validation errors; env token overrides--user.🤖 Generated with Claude Code