@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 +6 -0
- package/README.md +32 -33
- package/dist/auth/index.d.ts +2 -0
- package/dist/auth/index.d.ts.map +1 -1
- package/dist/auth/index.js +1 -0
- package/dist/auth/index.js.map +1 -1
- package/dist/auth/login.d.ts +54 -0
- package/dist/auth/login.d.ts.map +1 -0
- package/dist/auth/login.js +61 -0
- package/dist/auth/login.js.map +1 -0
- package/package.json +1 -1
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
|
|
16
|
-
| -------------------- |
|
|
17
|
-
| `auth` (subpath) | `
|
|
18
|
-
| `commands` (subpath) | `registerChangelogCommand`, `registerUpdateCommand` (+ semver helpers)
|
|
19
|
-
| `config` | `getConfigPath`, `readConfig`, `readConfigStrict`, `writeConfig`, `updateConfig`, `CoreConfig`, `UpdateChannel`
|
|
20
|
-
| `empty` | `printEmpty`
|
|
21
|
-
| `errors` | `CliError`
|
|
22
|
-
| `global-args` | `parseGlobalArgs`, `createGlobalArgsStore`, `createAccessibleGate`, `createSpinnerGate`, `getProgressJsonlPath`, `isProgressJsonlEnabled`
|
|
23
|
-
| `json` | `formatJson`, `formatNdjson`
|
|
24
|
-
| `markdown` (subpath) | `preloadMarkdown`, `renderMarkdown`, `TerminalRendererOptions`
|
|
25
|
-
| `options` | `ViewOptions`
|
|
26
|
-
| `spinner` | `createSpinner`
|
|
27
|
-
| `terminal` | `isCI`, `isStderrTTY`, `isStdinTTY`, `isStdoutTTY`
|
|
28
|
-
| `testing` (subpath) | `describeEmptyMachineOutput`
|
|
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
|
|
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
|
-
|
|
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 {
|
|
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
|
|
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
|
|
161
|
-
|
|
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
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
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`, `
|
|
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
|
|
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
|
|
package/dist/auth/index.d.ts
CHANGED
|
@@ -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';
|
package/dist/auth/index.d.ts.map
CHANGED
|
@@ -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"}
|
package/dist/auth/index.js
CHANGED
|
@@ -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
|
package/dist/auth/index.js.map
CHANGED
|
@@ -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"}
|