@doist/cli-core 0.8.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,9 @@
1
+ ## [0.9.0](https://github.com/Doist/cli-core/compare/v0.8.0...v0.9.0) (2026-05-09)
2
+
3
+ ### Features
4
+
5
+ * **auth:** add attachLoginCommand Commander helper ([#13](https://github.com/Doist/cli-core/issues/13)) ([bb921b8](https://github.com/Doist/cli-core/commit/bb921b83cee128c627bf7c318a016ef4a3b85582))
6
+
1
7
  ## [0.8.0](https://github.com/Doist/cli-core/compare/v0.7.1...v0.8.0) (2026-05-09)
2
8
 
3
9
  ### Features
package/README.md CHANGED
@@ -12,20 +12,20 @@ npm install @doist/cli-core
12
12
 
13
13
  ## What's in it
14
14
 
15
- | Module | Key exports | Purpose |
16
- | -------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
17
- | `auth` (subpath) | `registerAuthCommand`, `runOAuthFlow`, `startCallbackServer`, `createPkceProvider`, `createConfigTokenStore`, PKCE helpers, `AuthProvider` / `TokenStore` types | OAuth runtime plus the `registerAuthCommand` Commander registrar for `<cli> [auth] login`. Ships the standard public-client PKCE flow + single-user config-backed token store; the `AuthProvider` and `TokenStore` interfaces are the escape hatches for DCR, OS-keychain, multi-account, etc. `commander` (when using the registrar) and `open` (browser launch) are optional peer-deps. |
18
- | `commands` (subpath) | `registerChangelogCommand`, `registerUpdateCommand` (+ semver helpers) | Commander wiring for cli-core's standard commands (e.g. `<cli> changelog`, `<cli> update`, `<cli> update switch`). **Requires** `commander` as an optional peer-dep. |
19
- | `config` | `getConfigPath`, `readConfig`, `readConfigStrict`, `writeConfig`, `updateConfig`, `CoreConfig`, `UpdateChannel` | Read / write a per-CLI JSON config file with typed error codes; `CoreConfig` is the shape of fields cli-core itself owns (extend it for per-CLI fields). |
20
- | `empty` | `printEmpty` | Print an empty-state message gated on `--json` / `--ndjson` so machine consumers never see human strings on stdout. |
21
- | `errors` | `CliError` | Typed CLI error class with `code` and exit-code mapping. |
22
- | `global-args` | `parseGlobalArgs`, `createGlobalArgsStore`, `createAccessibleGate`, `createSpinnerGate`, `getProgressJsonlPath`, `isProgressJsonlEnabled` | Parse well-known global flags (`--json`, `--ndjson`, `--quiet`, `--verbose`, `--accessible`, `--no-spinner`, `--progress-jsonl`) and derive predicates from them. |
23
- | `json` | `formatJson`, `formatNdjson` | Stable JSON / newline-delimited JSON formatting for stdout. |
24
- | `markdown` (subpath) | `preloadMarkdown`, `renderMarkdown`, `TerminalRendererOptions` | Lazy-init terminal markdown renderer. **Requires** `marked` and `marked-terminal-renderer` as peer-deps — install only if your CLI uses this subpath. |
25
- | `options` | `ViewOptions` | Type contract for `{ json?, ndjson? }` per-command options that machine-output gates derive from. |
26
- | `spinner` | `createSpinner` | Loading spinner factory wrapping `yocto-spinner` with disable gates. |
27
- | `terminal` | `isCI`, `isStderrTTY`, `isStdinTTY`, `isStdoutTTY` | TTY / CI detection helpers. |
28
- | `testing` (subpath) | `describeEmptyMachineOutput` | Vitest helpers reusable by consuming CLIs (e.g. parametrised empty-state suite covering `--json` / `--ndjson` / human modes). |
15
+ | Module | Key exports | Purpose |
16
+ | -------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
17
+ | `auth` (subpath) | `attachLoginCommand`, `runOAuthFlow`, `createPkceProvider`, PKCE helpers, `AuthProvider` / `TokenStore` types | OAuth runtime plus the `attachLoginCommand` Commander helper for `<cli> [auth] login`. Ships the standard public-client PKCE flow (`createPkceProvider`); `AuthProvider` and `TokenStore` are the escape hatches for DCR, OS-keychain, multi-account, etc. — consumers implement `TokenStore` directly (a single-user config-file version is ~30 LoC). `commander` (when using `attachLoginCommand`) and `open` (browser launch) are optional peer-deps. |
18
+ | `commands` (subpath) | `registerChangelogCommand`, `registerUpdateCommand` (+ semver helpers) | Commander wiring for cli-core's standard commands (e.g. `<cli> changelog`, `<cli> update`, `<cli> update switch`). **Requires** `commander` as an optional peer-dep. |
19
+ | `config` | `getConfigPath`, `readConfig`, `readConfigStrict`, `writeConfig`, `updateConfig`, `CoreConfig`, `UpdateChannel` | Read / write a per-CLI JSON config file with typed error codes; `CoreConfig` is the shape of fields cli-core itself owns (extend it for per-CLI fields). |
20
+ | `empty` | `printEmpty` | Print an empty-state message gated on `--json` / `--ndjson` so machine consumers never see human strings on stdout. |
21
+ | `errors` | `CliError` | Typed CLI error class with `code` and exit-code mapping. |
22
+ | `global-args` | `parseGlobalArgs`, `createGlobalArgsStore`, `createAccessibleGate`, `createSpinnerGate`, `getProgressJsonlPath`, `isProgressJsonlEnabled` | Parse well-known global flags (`--json`, `--ndjson`, `--quiet`, `--verbose`, `--accessible`, `--no-spinner`, `--progress-jsonl`) and derive predicates from them. |
23
+ | `json` | `formatJson`, `formatNdjson` | Stable JSON / newline-delimited JSON formatting for stdout. |
24
+ | `markdown` (subpath) | `preloadMarkdown`, `renderMarkdown`, `TerminalRendererOptions` | Lazy-init terminal markdown renderer. **Requires** `marked` and `marked-terminal-renderer` as peer-deps — install only if your CLI uses this subpath. |
25
+ | `options` | `ViewOptions` | Type contract for `{ json?, ndjson? }` per-command options that machine-output gates derive from. |
26
+ | `spinner` | `createSpinner` | Loading spinner factory wrapping `yocto-spinner` with disable gates. |
27
+ | `terminal` | `isCI`, `isStderrTTY`, `isStdinTTY`, `isStdoutTTY` | TTY / CI detection helpers. |
28
+ | `testing` (subpath) | `describeEmptyMachineOutput` | Vitest helpers reusable by consuming CLIs (e.g. parametrised empty-state suite covering `--json` / `--ndjson` / human modes). |
29
29
 
30
30
  ## Usage
31
31
 
@@ -124,9 +124,9 @@ The semver helpers (`parseVersion`, `compareVersions`, `isNewer`, `getInstallTag
124
124
 
125
125
  ### Auth (optional subpath)
126
126
 
127
- Wire `<cli> [auth] login` and the supporting OAuth runtime in one call. cli-core ships the standard public-client PKCE flow (`createPkceProvider`) and a single-user config-backed `TokenStore` (`createConfigTokenStore`). Bespoke flows (Dynamic Client Registration, device code, magic link, username/password) implement the `AuthProvider` interface directly — no cli-core release needed.
127
+ Wire `<cli> [auth] login` and the supporting OAuth runtime. cli-core ships the standard public-client PKCE flow (`createPkceProvider`) and the `attachLoginCommand` Commander helper that drives `runOAuthFlow` end-to-end. Bespoke flows (Dynamic Client Registration, device code, magic link, username/password) implement the `AuthProvider` interface directly — no cli-core release needed.
128
128
 
129
- `logout`, `status`, and `token` are intentionally **not** in this surface yet. They're short and currently CLI-specific in shape; each CLI keeps its own implementations until a concrete migration proves them worth sharing. OS-keychain-backed storage and multi-account stores are likewise out of scope for this first release — implement the `TokenStore` interface directly until the shared shape stabilises.
129
+ Token storage is a `TokenStore` the consumer provides — cli-core does not ship a default. The interface is small enough that a single-user config-file version is ~30 lines inline (use `getConfigPath` + `readConfig` / `writeConfig`). OS-keychain-backed storage, multi-account stores, and the sibling Commander helpers (`logout` / `status` / `token`) are all deferred until concrete consumer migrations prove the shared shape.
130
130
 
131
131
  Install peer-deps in the consuming CLI:
132
132
 
@@ -137,17 +137,12 @@ npm install commander open # `open` is optional
137
137
  Then:
138
138
 
139
139
  ```ts
140
- import { createSpinner, getConfigPath } from '@doist/cli-core'
141
- import {
142
- createConfigTokenStore,
143
- createPkceProvider,
144
- registerAuthCommand,
145
- } from '@doist/cli-core/auth'
140
+ import { createPkceProvider, attachLoginCommand } from '@doist/cli-core/auth'
141
+ import type { TokenStore } from '@doist/cli-core/auth'
146
142
 
147
143
  type Account = { id: string; label?: string; email: string }
148
144
 
149
- const configPath = getConfigPath('outline-cli')
150
- const store = createConfigTokenStore<Account>({ configPath })
145
+ const store: TokenStore<Account> = createMyTokenStore() // consumer-supplied
151
146
 
152
147
  const provider = createPkceProvider<Account>({
153
148
  authorizeUrl: ({ handshake }) => `${handshake.baseUrl as string}/oauth/authorize`,
@@ -157,22 +152,26 @@ const provider = createPkceProvider<Account>({
157
152
  validate: async ({ token, handshake }) => probeUser(token, handshake.baseUrl as string),
158
153
  })
159
154
 
160
- const { withSpinner } = createSpinner()
161
- registerAuthCommand<Account>(program, {
162
- displayName: 'Outline',
155
+ const auth = program.command('auth')
156
+ const login = attachLoginCommand<Account>(auth, {
163
157
  provider,
164
158
  store,
159
+ preferredPort: 54969,
160
+ portFallbackCount: 5,
165
161
  resolveScopes: ({ readOnly }) => (readOnly ? ['read'] : ['read', 'write']),
166
- callbackPort: { preferred: 54969, fallbackCount: 5 },
167
- renderSuccess: (ctx) => `<html>...</html>`,
168
- renderError: (ctx) => `<html>...</html>`,
169
- withSpinner,
162
+ renderSuccess: () => `<html>...</html>`,
163
+ renderError: (message) => `<html>${message}</html>`,
164
+ onSuccess: ({ account, view, flags }) => {
165
+ if (view.json) console.log(JSON.stringify({ account, flags }))
166
+ else console.log(`Signed in as ${account.label ?? account.id}`)
167
+ },
170
168
  })
169
+ login.description('Authenticate via OAuth')
171
170
  ```
172
171
 
173
- The success / error HTML is a render hook — every CLI brings its own template (no shared layout enforced). Errors are `CliError` (`AUTH_OAUTH_FAILED`, `AUTH_STATE_MISMATCH`, `AUTH_CALLBACK_TIMEOUT`, `AUTH_PORT_BIND_FAILED`, `AUTH_TOKEN_EXCHANGE_FAILED`, `AUTH_STORE_WRITE_FAILED`); the consumer's top-level handler formats and exits.
172
+ `attachLoginCommand` wires `--read-only`, `--callback-port`, `--json`, `--ndjson` and returns the new `Command` so the consumer can chain `.description(...)` / `.option(...)` / `.addHelpText(...)`. Consumer-attached options land in the `flags` object passed to `resolveScopes` and (post-flow) to `onSuccess`. Under `--json` / `--ndjson` the authorize-URL fallback (printed when `open` is missing or fails to launch) is routed to stderr so the JSON envelope on stdout stays clean; pass `onAuthorizeUrl` to override. The success / error HTML is a render hook — every CLI brings its own template (no shared layout enforced). Errors are `CliError` (`AUTH_OAUTH_FAILED`, `AUTH_CALLBACK_TIMEOUT`, `AUTH_PORT_BIND_FAILED`, `AUTH_TOKEN_EXCHANGE_FAILED`, `AUTH_STORE_WRITE_FAILED`); the consumer's top-level handler formats and exits.
174
173
 
175
- For a lower-level integration that doesn't want the registrar, `runOAuthFlow` and `startCallbackServer` are exposed directly.
174
+ For a lower-level integration that doesn't want the Commander helper, `runOAuthFlow` is exposed directly with the same option set.
176
175
 
177
176
  ## Development
178
177
 
@@ -1,6 +1,8 @@
1
1
  export type { AuthErrorCode } from './errors.js';
2
2
  export { runOAuthFlow } from './flow.js';
3
3
  export type { RunOAuthFlowOptions, RunOAuthFlowResult } from './flow.js';
4
+ export { attachLoginCommand } from './login.js';
5
+ export type { AttachLoginCommandOptions, AttachLoginContext } from './login.js';
4
6
  export { DEFAULT_VERIFIER_ALPHABET, deriveChallenge, generateState, generateVerifier, } from './pkce.js';
5
7
  export type { GenerateVerifierOptions } from './pkce.js';
6
8
  export { createPkceProvider } from './providers/pkce.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/auth/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAChD,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAA;AACxC,YAAY,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAA;AACxE,OAAO,EACH,yBAAyB,EACzB,eAAe,EACf,aAAa,EACb,gBAAgB,GACnB,MAAM,WAAW,CAAA;AAClB,YAAY,EAAE,uBAAuB,EAAE,MAAM,WAAW,CAAA;AACxD,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAA;AACxD,YAAY,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAA;AAC9E,YAAY,EACR,WAAW,EACX,cAAc,EACd,eAAe,EACf,YAAY,EACZ,aAAa,EACb,cAAc,EACd,YAAY,EACZ,aAAa,EACb,UAAU,EACV,aAAa,GAChB,MAAM,YAAY,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/auth/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAChD,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAA;AACxC,YAAY,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAA;AACxE,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAA;AAC/C,YAAY,EAAE,yBAAyB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAA;AAC/E,OAAO,EACH,yBAAyB,EACzB,eAAe,EACf,aAAa,EACb,gBAAgB,GACnB,MAAM,WAAW,CAAA;AAClB,YAAY,EAAE,uBAAuB,EAAE,MAAM,WAAW,CAAA;AACxD,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAA;AACxD,YAAY,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAA;AAC9E,YAAY,EACR,WAAW,EACX,cAAc,EACd,eAAe,EACf,YAAY,EACZ,aAAa,EACb,cAAc,EACd,YAAY,EACZ,aAAa,EACb,UAAU,EACV,aAAa,GAChB,MAAM,YAAY,CAAA"}
@@ -1,4 +1,5 @@
1
1
  export { runOAuthFlow } from './flow.js';
2
+ export { attachLoginCommand } from './login.js';
2
3
  export { DEFAULT_VERIFIER_ALPHABET, deriveChallenge, generateState, generateVerifier, } from './pkce.js';
3
4
  export { createPkceProvider } from './providers/pkce.js';
4
5
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/auth/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAA;AAExC,OAAO,EACH,yBAAyB,EACzB,eAAe,EACf,aAAa,EACb,gBAAgB,GACnB,MAAM,WAAW,CAAA;AAElB,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/auth/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAA;AAExC,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAA;AAE/C,OAAO,EACH,yBAAyB,EACzB,eAAe,EACf,aAAa,EACb,gBAAgB,GACnB,MAAM,WAAW,CAAA;AAElB,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAA"}
@@ -0,0 +1,54 @@
1
+ import type { Command } from 'commander';
2
+ import type { ViewOptions } from '../options.js';
3
+ import type { AuthAccount, AuthProvider, TokenStore } from './types.js';
4
+ export type AttachLoginContext<TAccount extends AuthAccount> = {
5
+ account: TAccount;
6
+ /** `--json` / `--ndjson` flag values, both present (defaulted to `false`). */
7
+ view: Required<ViewOptions>;
8
+ /**
9
+ * Stripped per-CLI flags — the parsed options object with the standard
10
+ * registrar flags (`--read-only`, `--callback-port`, `--json`, `--ndjson`)
11
+ * removed. Same view `resolveScopes` saw at flow start.
12
+ */
13
+ flags: Record<string, unknown>;
14
+ };
15
+ export type AttachLoginCommandOptions<TAccount extends AuthAccount = AuthAccount> = {
16
+ provider: AuthProvider<TAccount>;
17
+ store: TokenStore<TAccount>;
18
+ /** Default local OAuth callback port. Overridable per-invocation via `--callback-port`. */
19
+ preferredPort: number;
20
+ /** Walk up this many sequential ports if `preferredPort` is busy. Default: 5. */
21
+ portFallbackCount?: number;
22
+ /** Resolve the scope list to request from the runtime flags + read-only state. */
23
+ resolveScopes(ctx: {
24
+ readOnly: boolean;
25
+ flags: Record<string, unknown>;
26
+ }): string[];
27
+ renderSuccess(): string;
28
+ renderError(message: string): string;
29
+ /** Override the browser opener (tests). When omitted, `runOAuthFlow` imports `open`. */
30
+ openBrowser?(url: string): Promise<void>;
31
+ /**
32
+ * Override the authorize-URL fallback callback that fires when the browser
33
+ * can't be opened (no `open` peer / opener throws). When omitted, the URL
34
+ * is written to stderr in machine-output mode (so the JSON / NDJSON
35
+ * envelope on stdout stays clean) and to stdout via `runOAuthFlow`'s
36
+ * default in human mode.
37
+ */
38
+ onAuthorizeUrl?(url: string): void;
39
+ /** Called after the token is persisted. */
40
+ onSuccess(ctx: AttachLoginContext<TAccount>): void | Promise<void>;
41
+ };
42
+ /**
43
+ * Attach `login` as a subcommand of `parent`. Wires the standard flag set
44
+ * (`--read-only`, `--callback-port`, `--json`, `--ndjson`) and drives
45
+ * `runOAuthFlow`. Returns the new `Command` so the consumer can chain
46
+ * `.description(...)` / `.option(...)` / `.addHelpText(...)` for additional
47
+ * flags or help text.
48
+ *
49
+ * Additional Commander options the consumer attaches to the returned command
50
+ * land on the same parsed options object Commander hands to the action, so
51
+ * `resolveScopes` and `onSuccess` see them via `flags`.
52
+ */
53
+ export declare function attachLoginCommand<TAccount extends AuthAccount>(parent: Command, options: AttachLoginCommandOptions<TAccount>): Command;
54
+ //# sourceMappingURL=login.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"login.d.ts","sourceRoot":"","sources":["../../src/auth/login.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAExC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAEhD,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAEvE,MAAM,MAAM,kBAAkB,CAAC,QAAQ,SAAS,WAAW,IAAI;IAC3D,OAAO,EAAE,QAAQ,CAAA;IACjB,8EAA8E;IAC9E,IAAI,EAAE,QAAQ,CAAC,WAAW,CAAC,CAAA;IAC3B;;;;OAIG;IACH,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CACjC,CAAA;AAED,MAAM,MAAM,yBAAyB,CAAC,QAAQ,SAAS,WAAW,GAAG,WAAW,IAAI;IAChF,QAAQ,EAAE,YAAY,CAAC,QAAQ,CAAC,CAAA;IAChC,KAAK,EAAE,UAAU,CAAC,QAAQ,CAAC,CAAA;IAC3B,2FAA2F;IAC3F,aAAa,EAAE,MAAM,CAAA;IACrB,iFAAiF;IACjF,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B,kFAAkF;IAClF,aAAa,CAAC,GAAG,EAAE;QAAE,QAAQ,EAAE,OAAO,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,GAAG,MAAM,EAAE,CAAA;IACnF,aAAa,IAAI,MAAM,CAAA;IACvB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAAA;IACpC,wFAAwF;IACxF,WAAW,CAAC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACxC;;;;;;OAMG;IACH,cAAc,CAAC,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;IAClC,2CAA2C;IAC3C,SAAS,CAAC,GAAG,EAAE,kBAAkB,CAAC,QAAQ,CAAC,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CACrE,CAAA;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,SAAS,WAAW,EAC3D,MAAM,EAAE,OAAO,EACf,OAAO,EAAE,yBAAyB,CAAC,QAAQ,CAAC,GAC7C,OAAO,CAwCT"}
@@ -0,0 +1,61 @@
1
+ import { CliError } from '../errors.js';
2
+ import { runOAuthFlow } from './flow.js';
3
+ /**
4
+ * Attach `login` as a subcommand of `parent`. Wires the standard flag set
5
+ * (`--read-only`, `--callback-port`, `--json`, `--ndjson`) and drives
6
+ * `runOAuthFlow`. Returns the new `Command` so the consumer can chain
7
+ * `.description(...)` / `.option(...)` / `.addHelpText(...)` for additional
8
+ * flags or help text.
9
+ *
10
+ * Additional Commander options the consumer attaches to the returned command
11
+ * land on the same parsed options object Commander hands to the action, so
12
+ * `resolveScopes` and `onSuccess` see them via `flags`.
13
+ */
14
+ export function attachLoginCommand(parent, options) {
15
+ return parent
16
+ .command('login')
17
+ .option('--read-only', 'Request read-only scopes')
18
+ .option('--callback-port <port>', 'Override the local OAuth callback port', parsePort)
19
+ .option('--json', 'Emit machine-readable JSON output')
20
+ .option('--ndjson', 'Emit machine-readable NDJSON output')
21
+ .action(async (cmd) => {
22
+ const { readOnly, callbackPort, json, ndjson, ...flags } = cmd;
23
+ const view = {
24
+ json: Boolean(json),
25
+ ndjson: Boolean(ndjson),
26
+ };
27
+ const machineOutput = view.json || view.ndjson;
28
+ const result = await runOAuthFlow({
29
+ provider: options.provider,
30
+ store: options.store,
31
+ scopes: options.resolveScopes({ readOnly: Boolean(readOnly), flags }),
32
+ readOnly: Boolean(readOnly),
33
+ flags,
34
+ preferredPort: callbackPort ?? options.preferredPort,
35
+ portFallbackCount: options.portFallbackCount,
36
+ renderSuccess: options.renderSuccess,
37
+ renderError: options.renderError,
38
+ openBrowser: options.openBrowser,
39
+ // In machine-output mode, route the fallback URL to stderr so
40
+ // the JSON / NDJSON envelope on stdout stays clean — the user
41
+ // can still see the URL if `open` is missing or throws. In
42
+ // human mode, leave it undefined so `runOAuthFlow`'s TTY
43
+ // default (stdout) fires. Consumer override wins either way.
44
+ onAuthorizeUrl: options.onAuthorizeUrl ??
45
+ (machineOutput
46
+ ? (url) => {
47
+ process.stderr.write(`Open this URL in your browser:\n ${url}\n`);
48
+ }
49
+ : undefined),
50
+ });
51
+ await options.onSuccess({ account: result.account, view, flags });
52
+ });
53
+ }
54
+ function parsePort(raw) {
55
+ const port = /^\d+$/.test(raw) ? Number(raw) : Number.NaN;
56
+ if (!Number.isFinite(port) || port > 65535) {
57
+ throw new CliError('AUTH_PORT_BIND_FAILED', `Invalid --callback-port '${raw}': expected an integer in [0..65535].`);
58
+ }
59
+ return port;
60
+ }
61
+ //# sourceMappingURL=login.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"login.js","sourceRoot":"","sources":["../../src/auth/login.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AAEvC,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAA;AAwCxC;;;;;;;;;;GAUG;AACH,MAAM,UAAU,kBAAkB,CAC9B,MAAe,EACf,OAA4C;IAE5C,OAAO,MAAM;SACR,OAAO,CAAC,OAAO,CAAC;SAChB,MAAM,CAAC,aAAa,EAAE,0BAA0B,CAAC;SACjD,MAAM,CAAC,wBAAwB,EAAE,wCAAwC,EAAE,SAAS,CAAC;SACrF,MAAM,CAAC,QAAQ,EAAE,mCAAmC,CAAC;SACrD,MAAM,CAAC,UAAU,EAAE,qCAAqC,CAAC;SACzD,MAAM,CAAC,KAAK,EAAE,GAA4B,EAAE,EAAE;QAC3C,MAAM,EAAE,QAAQ,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,KAAK,EAAE,GAAG,GAAG,CAAA;QAC9D,MAAM,IAAI,GAA0B;YAChC,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC;YACnB,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC;SAC1B,CAAA;QACD,MAAM,aAAa,GAAG,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,MAAM,CAAA;QAC9C,MAAM,MAAM,GAAG,MAAM,YAAY,CAAW;YACxC,QAAQ,EAAE,OAAO,CAAC,QAAQ;YAC1B,KAAK,EAAE,OAAO,CAAC,KAAK;YACpB,MAAM,EAAE,OAAO,CAAC,aAAa,CAAC,EAAE,QAAQ,EAAE,OAAO,CAAC,QAAQ,CAAC,EAAE,KAAK,EAAE,CAAC;YACrE,QAAQ,EAAE,OAAO,CAAC,QAAQ,CAAC;YAC3B,KAAK;YACL,aAAa,EAAG,YAAmC,IAAI,OAAO,CAAC,aAAa;YAC5E,iBAAiB,EAAE,OAAO,CAAC,iBAAiB;YAC5C,aAAa,EAAE,OAAO,CAAC,aAAa;YACpC,WAAW,EAAE,OAAO,CAAC,WAAW;YAChC,WAAW,EAAE,OAAO,CAAC,WAAW;YAChC,8DAA8D;YAC9D,8DAA8D;YAC9D,2DAA2D;YAC3D,yDAAyD;YACzD,6DAA6D;YAC7D,cAAc,EACV,OAAO,CAAC,cAAc;gBACtB,CAAC,aAAa;oBACV,CAAC,CAAC,CAAC,GAAW,EAAE,EAAE;wBACZ,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,qCAAqC,GAAG,IAAI,CAAC,CAAA;oBACtE,CAAC;oBACH,CAAC,CAAC,SAAS,CAAC;SACvB,CAAC,CAAA;QACF,MAAM,OAAO,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAA;IACrE,CAAC,CAAC,CAAA;AACV,CAAC;AAED,SAAS,SAAS,CAAC,GAAW;IAC1B,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAA;IACzD,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,IAAI,GAAG,KAAK,EAAE,CAAC;QACzC,MAAM,IAAI,QAAQ,CACd,uBAAuB,EACvB,4BAA4B,GAAG,uCAAuC,CACzE,CAAA;IACL,CAAC;IACD,OAAO,IAAI,CAAA;AACf,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@doist/cli-core",
3
- "version": "0.8.0",
3
+ "version": "0.9.0",
4
4
  "description": "Shared core utilities for Doist CLI projects",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",