@doist/cli-core 0.18.0 → 0.20.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.
Files changed (46) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +63 -20
  3. package/dist/auth/errors.d.ts +1 -1
  4. package/dist/auth/errors.d.ts.map +1 -1
  5. package/dist/auth/flow.d.ts.map +1 -1
  6. package/dist/auth/flow.js +7 -8
  7. package/dist/auth/flow.js.map +1 -1
  8. package/dist/auth/index.d.ts +7 -3
  9. package/dist/auth/index.d.ts.map +1 -1
  10. package/dist/auth/index.js +3 -1
  11. package/dist/auth/index.js.map +1 -1
  12. package/dist/auth/keyring/internal.d.ts +25 -0
  13. package/dist/auth/keyring/internal.d.ts.map +1 -1
  14. package/dist/auth/keyring/internal.js +24 -7
  15. package/dist/auth/keyring/internal.js.map +1 -1
  16. package/dist/auth/keyring/token-store.d.ts +7 -1
  17. package/dist/auth/keyring/token-store.d.ts.map +1 -1
  18. package/dist/auth/keyring/token-store.js +59 -20
  19. package/dist/auth/keyring/token-store.js.map +1 -1
  20. package/dist/auth/persist.d.ts +9 -1
  21. package/dist/auth/persist.d.ts.map +1 -1
  22. package/dist/auth/persist.js +20 -0
  23. package/dist/auth/persist.js.map +1 -1
  24. package/dist/auth/providers/dcr.d.ts +71 -0
  25. package/dist/auth/providers/dcr.d.ts.map +1 -0
  26. package/dist/auth/providers/dcr.js +187 -0
  27. package/dist/auth/providers/dcr.js.map +1 -0
  28. package/dist/auth/providers/oauth.d.ts +105 -0
  29. package/dist/auth/providers/oauth.d.ts.map +1 -0
  30. package/dist/auth/providers/oauth.js +145 -0
  31. package/dist/auth/providers/oauth.js.map +1 -0
  32. package/dist/auth/providers/pkce.d.ts +16 -5
  33. package/dist/auth/providers/pkce.d.ts.map +1 -1
  34. package/dist/auth/providers/pkce.js +92 -63
  35. package/dist/auth/providers/pkce.js.map +1 -1
  36. package/dist/auth/refresh.d.ts +49 -0
  37. package/dist/auth/refresh.d.ts.map +1 -0
  38. package/dist/auth/refresh.js +184 -0
  39. package/dist/auth/refresh.js.map +1 -0
  40. package/dist/auth/status.d.ts +12 -4
  41. package/dist/auth/status.d.ts.map +1 -1
  42. package/dist/auth/status.js +45 -5
  43. package/dist/auth/status.js.map +1 -1
  44. package/dist/auth/types.d.ts +17 -0
  45. package/dist/auth/types.d.ts.map +1 -1
  46. package/package.json +9 -4
@@ -0,0 +1,184 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { open, readFile, stat, unlink } from 'node:fs/promises';
3
+ import { setTimeout as sleep } from 'node:timers/promises';
4
+ import { CliError, getErrorMessage } from '../errors.js';
5
+ import { bundleFromExchange, persistBundle } from './persist.js';
6
+ const DEFAULT_SKEW_MS = 60_000;
7
+ const LOCK_WAIT_TIMEOUT_MS = 2_000;
8
+ const LOCK_POLL_INTERVAL_MS = 50;
9
+ // A lock older than this was almost certainly left by a crashed holder — the
10
+ // refresh POST is bounded by a provider-side timeout well under this — so it's
11
+ // safe to steal rather than block every future refresh forever.
12
+ const LOCK_STALE_MS = 15_000;
13
+ /**
14
+ * Rotate the access token using the stored refresh token. Proactive when
15
+ * `accessTokenExpiresAt` is within `skewMs` of now; reactive when `force:
16
+ * true`. Uses an `O_EXCL` file lock at `lockPath` so concurrent CLI
17
+ * invocations don't issue parallel refresh-token grants — one POSTs, the
18
+ * others re-read the rotated bundle from the store.
19
+ *
20
+ * Throws `AUTH_REFRESH_UNAVAILABLE` when refresh isn't possible in the
21
+ * current setup: store doesn't implement `activeBundle` + `setBundle`,
22
+ * provider doesn't implement `refreshToken`, no credential, or no refresh
23
+ * token. Server-side rejections surface as `AUTH_REFRESH_EXPIRED` (re-login
24
+ * required) or `AUTH_REFRESH_TRANSIENT` (retryable).
25
+ */
26
+ export async function refreshAccessToken(options) {
27
+ const { store, provider, ref, force, lockPath } = options;
28
+ const skewMs = options.skewMs ?? DEFAULT_SKEW_MS;
29
+ const handshake = options.handshake ?? {};
30
+ // Refresh must both read the full bundle and persist the rotated one. A
31
+ // store missing either capability can't participate — fail loudly rather
32
+ // than silently dropping the rotated refresh token via a `set()` fallback.
33
+ if (!store.activeBundle || !store.setBundle) {
34
+ throw new CliError('AUTH_REFRESH_UNAVAILABLE', 'TokenStore must implement activeBundle + setBundle for refresh.', { hints: ['Re-run the login command to reauthorize.'] });
35
+ }
36
+ if (!provider.refreshToken) {
37
+ throw new CliError('AUTH_REFRESH_UNAVAILABLE', 'Auth provider does not implement refreshToken.');
38
+ }
39
+ const activeBundle = store.activeBundle.bind(store);
40
+ const refreshGrant = provider.refreshToken.bind(provider);
41
+ const snapshot = await activeBundle(ref);
42
+ if (!snapshot) {
43
+ throw new CliError('AUTH_REFRESH_UNAVAILABLE', 'No stored credential to refresh.', {
44
+ hints: ['Re-run the login command to reauthorize.'],
45
+ });
46
+ }
47
+ if (!force && !needsRefresh(snapshot.bundle, skewMs)) {
48
+ return { rotated: false, bundle: snapshot.bundle, account: snapshot.account };
49
+ }
50
+ if (!snapshot.bundle.refreshToken) {
51
+ throw new CliError('AUTH_REFRESH_UNAVAILABLE', 'Stored credential has no refresh token.', {
52
+ hints: ['Re-run the login command to reauthorize.'],
53
+ });
54
+ }
55
+ const lockToken = await acquireLock(lockPath);
56
+ if (!lockToken) {
57
+ // Holder didn't release in time. It may have rotated then crashed
58
+ // before unlinking — re-read once and adopt the rotated bundle if so.
59
+ const fresh = await activeBundle(ref);
60
+ if (fresh && hasRotated(snapshot.bundle, fresh.bundle)) {
61
+ return { rotated: true, bundle: fresh.bundle, account: fresh.account };
62
+ }
63
+ throw new CliError('AUTH_REFRESH_TRANSIENT', 'Timed out waiting for a concurrent refresh to complete.', { hints: ['Try again.'] });
64
+ }
65
+ try {
66
+ // Re-read under the lock (covers a clean acquire too): a concurrent
67
+ // holder may have rotated between our snapshot read and acquiring the
68
+ // lock, so adopt their bundle rather than POST a now-stale refresh
69
+ // token. A throw here releases the lock via `finally`.
70
+ const current = await activeBundle(ref);
71
+ if (!current) {
72
+ // A concurrent `logout`/`clear` removed the credential after our
73
+ // first read — do not POST + persist it back into existence.
74
+ throw new CliError('AUTH_REFRESH_UNAVAILABLE', 'Credential was removed during refresh.', {
75
+ hints: ['Re-run the login command to reauthorize.'],
76
+ });
77
+ }
78
+ if (hasRotated(snapshot.bundle, current.bundle)) {
79
+ return { rotated: true, bundle: current.bundle, account: current.account };
80
+ }
81
+ const refreshToken = current.bundle.refreshToken;
82
+ if (!refreshToken) {
83
+ throw new CliError('AUTH_REFRESH_UNAVAILABLE', 'Stored credential has no refresh token.', { hints: ['Re-run the login command to reauthorize.'] });
84
+ }
85
+ const exchange = await refreshGrant({ refreshToken, handshake });
86
+ const account = exchange.account ?? current.account;
87
+ const bundle = bundleFromExchange(exchange, current.bundle);
88
+ await persistBundle({ store, account, bundle });
89
+ return { rotated: true, bundle, account };
90
+ }
91
+ finally {
92
+ await releaseLock(lockPath, lockToken);
93
+ }
94
+ }
95
+ function needsRefresh(bundle, skewMs) {
96
+ // No expiry tracked → can't proactively refresh; defer to reactive 401.
97
+ if (bundle.accessTokenExpiresAt === undefined)
98
+ return false;
99
+ return bundle.accessTokenExpiresAt - Date.now() < skewMs;
100
+ }
101
+ function hasRotated(before, after) {
102
+ return (after.accessToken !== before.accessToken ||
103
+ after.accessTokenExpiresAt !== before.accessTokenExpiresAt ||
104
+ after.refreshToken !== before.refreshToken ||
105
+ after.refreshTokenExpiresAt !== before.refreshTokenExpiresAt);
106
+ }
107
+ /**
108
+ * Acquire the lock, returning a unique ownership token (or `null` on
109
+ * contention timeout). The token is written into the lock file so
110
+ * `releaseLock` only unlinks a lock it still owns — a holder whose stale
111
+ * lock was stolen mid-flight won't delete the new holder's lock.
112
+ */
113
+ async function acquireLock(lockPath) {
114
+ const token = randomUUID();
115
+ if (await tryAcquire(lockPath, token))
116
+ return token;
117
+ const deadline = Date.now() + LOCK_WAIT_TIMEOUT_MS;
118
+ while (Date.now() < deadline) {
119
+ await sleep(LOCK_POLL_INTERVAL_MS);
120
+ if (await tryAcquire(lockPath, token))
121
+ return token;
122
+ }
123
+ return null;
124
+ }
125
+ // Write the lock with O_EXCL. On EEXIST, steal it if it's stale (older than
126
+ // LOCK_STALE_MS — assumes the provider bounds its HTTP, which the built-in
127
+ // PKCE provider does); otherwise report contention.
128
+ async function tryAcquire(lockPath, token) {
129
+ if (await tryWriteLock(lockPath, token))
130
+ return true;
131
+ if (await lockIsStale(lockPath)) {
132
+ await forceUnlink(lockPath);
133
+ return tryWriteLock(lockPath, token);
134
+ }
135
+ return false;
136
+ }
137
+ async function lockIsStale(lockPath) {
138
+ try {
139
+ const { mtimeMs } = await stat(lockPath);
140
+ return Date.now() - mtimeMs > LOCK_STALE_MS;
141
+ }
142
+ catch {
143
+ return false;
144
+ }
145
+ }
146
+ async function tryWriteLock(lockPath, token) {
147
+ let handle;
148
+ try {
149
+ handle = await open(lockPath, 'wx');
150
+ }
151
+ catch (error) {
152
+ const code = error.code;
153
+ if (code === 'EEXIST')
154
+ return false;
155
+ throw new CliError('AUTH_REFRESH_TRANSIENT', `Failed to acquire refresh lock: ${getErrorMessage(error)}`, { hints: ['Try again.'] });
156
+ }
157
+ try {
158
+ await handle.writeFile(token);
159
+ }
160
+ finally {
161
+ await handle.close();
162
+ }
163
+ return true;
164
+ }
165
+ async function releaseLock(lockPath, token) {
166
+ try {
167
+ const owner = (await readFile(lockPath, 'utf8')).trim();
168
+ if (owner === token)
169
+ await unlink(lockPath);
170
+ }
171
+ catch {
172
+ // best-effort: a missing/unreadable lock (manual cleanup, crash, a
173
+ // steal by another holder) must not surface as a refresh failure.
174
+ }
175
+ }
176
+ async function forceUnlink(lockPath) {
177
+ try {
178
+ await unlink(lockPath);
179
+ }
180
+ catch {
181
+ // already gone
182
+ }
183
+ }
184
+ //# sourceMappingURL=refresh.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"refresh.js","sourceRoot":"","sources":["../../src/auth/refresh.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  import type { Command } from 'commander';
2
2
  import type { ViewOptions } from '../options.js';
3
- import type { AuthAccount, TokenStore } from './types.js';
3
+ import type { AuthAccount, TokenBundle, TokenStore } from './types.js';
4
4
  export type AttachStatusContext<TAccount extends AuthAccount> = {
5
5
  account: TAccount;
6
6
  /** `--json` / `--ndjson` flag values, both present (defaulted to `false`). */
@@ -20,6 +20,12 @@ export type AttachStatusCommandOptions<TAccount extends AuthAccount = AuthAccoun
20
20
  fetchLive?(ctx: {
21
21
  account: TAccount;
22
22
  token: string;
23
+ /**
24
+ * Full bundle when the store implements `activeBundle` — lets a
25
+ * consumer render expiry without a second read. Absent when the
26
+ * store only exposes `active()` (no refresh-side metadata available).
27
+ */
28
+ bundle?: TokenBundle;
23
29
  /** `--json` / `--ndjson` flag values, both present (defaulted to `false`). */
24
30
  view: Required<ViewOptions>;
25
31
  flags: Record<string, unknown>;
@@ -49,9 +55,11 @@ export type AttachStatusCommandOptions<TAccount extends AuthAccount = AuthAccoun
49
55
  }): void | Promise<void>;
50
56
  };
51
57
  /**
52
- * Attach `status` as a subcommand of `parent`. Reads `store.active()`, optionally
53
- * confirms via `fetchLive`, then dispatches to `renderText` (human) or
54
- * `renderJson` (machine). Returns the new `Command` so the consumer can chain.
58
+ * Attach `status` as a subcommand of `parent`. Reads the active credential
59
+ * (preferring `store.activeBundle` when `fetchLive` is set, so the token and
60
+ * bundle come from one read), optionally confirms via `fetchLive`, then
61
+ * dispatches to `renderText` (human) or `renderJson` (machine). Returns the
62
+ * new `Command` so the consumer can chain.
55
63
  */
56
64
  export declare function attachStatusCommand<TAccount extends AuthAccount = AuthAccount>(parent: Command, options: AttachStatusCommandOptions<TAccount>): Command;
57
65
  //# sourceMappingURL=status.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../src/auth/status.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAGxC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAChD,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAGzD,MAAM,MAAM,mBAAmB,CAAC,QAAQ,SAAS,WAAW,IAAI;IAC5D,OAAO,EAAE,QAAQ,CAAA;IACjB,8EAA8E;IAC9E,IAAI,EAAE,QAAQ,CAAC,WAAW,CAAC,CAAA;IAC3B,oHAAoH;IACpH,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CACjC,CAAA;AAED,MAAM,MAAM,0BAA0B,CAAC,QAAQ,SAAS,WAAW,GAAG,WAAW,IAAI;IACjF,KAAK,EAAE,UAAU,CAAC,QAAQ,CAAC,CAAA;IAC3B,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB;;;;;OAKG;IACH,SAAS,CAAC,CAAC,GAAG,EAAE;QACZ,OAAO,EAAE,QAAQ,CAAA;QACjB,KAAK,EAAE,MAAM,CAAA;QACb,8EAA8E;QAC9E,IAAI,EAAE,QAAQ,CAAC,WAAW,CAAC,CAAA;QAC3B,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KACjC,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAA;IACrB;;;OAGG;IACH,UAAU,CAAC,GAAG,EAAE,mBAAmB,CAAC,QAAQ,CAAC,GAAG,MAAM,GAAG,SAAS,MAAM,EAAE,CAAA;IAC1E;;;;OAIG;IACH,UAAU,CAAC,CAAC,GAAG,EAAE;QAAE,OAAO,EAAE,QAAQ,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,GAAG,OAAO,CAAA;IAChF;;;OAGG;IACH,kBAAkB,CAAC,CAAC,GAAG,EAAE;QACrB,8EAA8E;QAC9E,IAAI,EAAE,QAAQ,CAAC,WAAW,CAAC,CAAA;QAC3B,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KACjC,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CAC3B,CAAA;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,SAAS,WAAW,GAAG,WAAW,EAC1E,MAAM,EAAE,OAAO,EACf,OAAO,EAAE,0BAA0B,CAAC,QAAQ,CAAC,GAC9C,OAAO,CA2CT"}
1
+ {"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../src/auth/status.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAGxC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAChD,OAAO,KAAK,EAAc,WAAW,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAqDlF,MAAM,MAAM,mBAAmB,CAAC,QAAQ,SAAS,WAAW,IAAI;IAC5D,OAAO,EAAE,QAAQ,CAAA;IACjB,8EAA8E;IAC9E,IAAI,EAAE,QAAQ,CAAC,WAAW,CAAC,CAAA;IAC3B,oHAAoH;IACpH,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CACjC,CAAA;AAED,MAAM,MAAM,0BAA0B,CAAC,QAAQ,SAAS,WAAW,GAAG,WAAW,IAAI;IACjF,KAAK,EAAE,UAAU,CAAC,QAAQ,CAAC,CAAA;IAC3B,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB;;;;;OAKG;IACH,SAAS,CAAC,CAAC,GAAG,EAAE;QACZ,OAAO,EAAE,QAAQ,CAAA;QACjB,KAAK,EAAE,MAAM,CAAA;QACb;;;;WAIG;QACH,MAAM,CAAC,EAAE,WAAW,CAAA;QACpB,8EAA8E;QAC9E,IAAI,EAAE,QAAQ,CAAC,WAAW,CAAC,CAAA;QAC3B,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KACjC,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAA;IACrB;;;OAGG;IACH,UAAU,CAAC,GAAG,EAAE,mBAAmB,CAAC,QAAQ,CAAC,GAAG,MAAM,GAAG,SAAS,MAAM,EAAE,CAAA;IAC1E;;;;OAIG;IACH,UAAU,CAAC,CAAC,GAAG,EAAE;QAAE,OAAO,EAAE,QAAQ,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,GAAG,OAAO,CAAA;IAChF;;;OAGG;IACH,kBAAkB,CAAC,CAAC,GAAG,EAAE;QACrB,8EAA8E;QAC9E,IAAI,EAAE,QAAQ,CAAC,WAAW,CAAC,CAAA;QAC3B,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KACjC,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CAC3B,CAAA;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,SAAS,WAAW,GAAG,WAAW,EAC1E,MAAM,EAAE,OAAO,EACf,OAAO,EAAE,0BAA0B,CAAC,QAAQ,CAAC,GAC9C,OAAO,CA4CT"}
@@ -1,10 +1,49 @@
1
1
  import { CliError } from '../errors.js';
2
2
  import { formatJson, formatNdjson } from '../json.js';
3
- import { attachUserFlag, extractUserRef, requireSnapshotForRef } from './user-flag.js';
3
+ import { accountNotFoundError, attachUserFlag, extractUserRef, requireSnapshotForRef, } from './user-flag.js';
4
4
  /**
5
- * Attach `status` as a subcommand of `parent`. Reads `store.active()`, optionally
6
- * confirms via `fetchLive`, then dispatches to `renderText` (human) or
7
- * `renderJson` (machine). Returns the new `Command` so the consumer can chain.
5
+ * Resolve the auth snapshot for `status`. When the consumer supplies
6
+ * `fetchLive` (and the store implements `activeBundle`), read the bundle once
7
+ * and derive both the access token and the bundle from it — avoiding a second
8
+ * keyring round-trip on the hot path. Otherwise fall back to the narrow
9
+ * `active()` read. A bundle-read fault (e.g. a refresh-slot error) also falls
10
+ * back to `active()` so it can't break status, which is the user's first port
11
+ * of call when something is wrong. An explicit-ref miss throws
12
+ * `ACCOUNT_NOT_FOUND`, matching `requireSnapshotForRef`.
13
+ */
14
+ async function resolveStatusSnapshot(store, ref, wantsBundle) {
15
+ if (wantsBundle && store.activeBundle) {
16
+ try {
17
+ const snap = await store.activeBundle(ref);
18
+ if (snap) {
19
+ return {
20
+ token: snap.bundle.accessToken,
21
+ account: snap.account,
22
+ bundle: snap.bundle,
23
+ };
24
+ }
25
+ if (ref !== undefined)
26
+ throw accountNotFoundError(ref);
27
+ return null;
28
+ }
29
+ catch (error) {
30
+ // Only a typed read failure falls through to the access-only
31
+ // `active()` read (e.g. a refresh-slot fault that `active()`
32
+ // doesn't hit). Anything else — `ACCOUNT_NOT_FOUND`, or an
33
+ // unexpected store bug — propagates rather than being masked.
34
+ if (!(error instanceof CliError) || error.code !== 'AUTH_STORE_READ_FAILED')
35
+ throw error;
36
+ }
37
+ }
38
+ const snapshot = await requireSnapshotForRef(store, ref);
39
+ return snapshot ? { token: snapshot.token, account: snapshot.account } : null;
40
+ }
41
+ /**
42
+ * Attach `status` as a subcommand of `parent`. Reads the active credential
43
+ * (preferring `store.activeBundle` when `fetchLive` is set, so the token and
44
+ * bundle come from one read), optionally confirms via `fetchLive`, then
45
+ * dispatches to `renderText` (human) or `renderJson` (machine). Returns the
46
+ * new `Command` so the consumer can chain.
8
47
  */
9
48
  export function attachStatusCommand(parent, options) {
10
49
  const command = parent
@@ -19,7 +58,7 @@ export function attachStatusCommand(parent, options) {
19
58
  ndjson: Boolean(ndjson),
20
59
  };
21
60
  const ref = extractUserRef(cmd);
22
- const snapshot = await requireSnapshotForRef(options.store, ref);
61
+ const snapshot = await resolveStatusSnapshot(options.store, ref, Boolean(options.fetchLive));
23
62
  if (!snapshot) {
24
63
  if (options.onNotAuthenticated) {
25
64
  await options.onNotAuthenticated({ view, flags });
@@ -31,6 +70,7 @@ export function attachStatusCommand(parent, options) {
31
70
  ? await options.fetchLive({
32
71
  account: snapshot.account,
33
72
  token: snapshot.token,
73
+ ...(snapshot.bundle ? { bundle: snapshot.bundle } : {}),
34
74
  view,
35
75
  flags,
36
76
  })
@@ -1 +1 @@
1
- {"version":3,"file":"status.js","sourceRoot":"","sources":["../../src/auth/status.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AACvC,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAGrD,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAA;AAgDtF;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CAC/B,MAAe,EACf,OAA6C;IAE7C,MAAM,OAAO,GAAG,MAAM;SACjB,OAAO,CAAC,QAAQ,CAAC;SACjB,WAAW,CAAC,OAAO,CAAC,WAAW,IAAI,wCAAwC,CAAC;SAC5E,MAAM,CAAC,QAAQ,EAAE,mCAAmC,CAAC;SACrD,MAAM,CAAC,UAAU,EAAE,qCAAqC,CAAC,CAAA;IAC9D,OAAO,cAAc,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,KAAK,EAAE,GAA4B,EAAE,EAAE;QACzE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,KAAK,EAAE,GAAG,GAAG,CAAA;QACnD,MAAM,IAAI,GAA0B;YAChC,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC;YACnB,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC;SAC1B,CAAA;QACD,MAAM,GAAG,GAAG,cAAc,CAAC,GAAG,CAAC,CAAA;QAC/B,MAAM,QAAQ,GAAG,MAAM,qBAAqB,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAA;QAChE,IAAI,CAAC,QAAQ,EAAE,CAAC;YACZ,IAAI,OAAO,CAAC,kBAAkB,EAAE,CAAC;gBAC7B,MAAM,OAAO,CAAC,kBAAkB,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAA;gBACjD,OAAM;YACV,CAAC;YACD,MAAM,IAAI,QAAQ,CAAC,mBAAmB,EAAE,gBAAgB,CAAC,CAAA;QAC7D,CAAC;QACD,MAAM,OAAO,GAAG,OAAO,CAAC,SAAS;YAC7B,CAAC,CAAC,MAAM,OAAO,CAAC,SAAS,CAAC;gBACpB,OAAO,EAAE,QAAQ,CAAC,OAAO;gBACzB,KAAK,EAAE,QAAQ,CAAC,KAAK;gBACrB,IAAI;gBACJ,KAAK;aACR,CAAC;YACJ,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAA;QACtB,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;YACZ,MAAM,OAAO,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,CAAA;YACrF,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAA;YAChC,OAAM;QACV,CAAC;QACD,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YACd,MAAM,OAAO,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,CAAA;YACrF,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAA;YACpC,OAAM;QACV,CAAC;QACD,MAAM,IAAI,GAAG,OAAO,CAAC,UAAU,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAA;QACzD,MAAM,KAAK,GAAG,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;QACtD,KAAK,MAAM,IAAI,IAAI,KAAK;YAAE,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;IAC/C,CAAC,CAAC,CAAA;AACN,CAAC"}
1
+ {"version":3,"file":"status.js","sourceRoot":"","sources":["../../src/auth/status.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AACvC,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAGrD,OAAO,EACH,oBAAoB,EACpB,cAAc,EACd,cAAc,EACd,qBAAqB,GACxB,MAAM,gBAAgB,CAAA;AAQvB;;;;;;;;;GASG;AACH,KAAK,UAAU,qBAAqB,CAChC,KAA2B,EAC3B,GAA2B,EAC3B,WAAoB;IAEpB,IAAI,WAAW,IAAI,KAAK,CAAC,YAAY,EAAE,CAAC;QACpC,IAAI,CAAC;YACD,MAAM,IAAI,GAAG,MAAM,KAAK,CAAC,YAAY,CAAC,GAAG,CAAC,CAAA;YAC1C,IAAI,IAAI,EAAE,CAAC;gBACP,OAAO;oBACH,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,WAAW;oBAC9B,OAAO,EAAE,IAAI,CAAC,OAAO;oBACrB,MAAM,EAAE,IAAI,CAAC,MAAM;iBACtB,CAAA;YACL,CAAC;YACD,IAAI,GAAG,KAAK,SAAS;gBAAE,MAAM,oBAAoB,CAAC,GAAG,CAAC,CAAA;YACtD,OAAO,IAAI,CAAA;QACf,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACb,6DAA6D;YAC7D,6DAA6D;YAC7D,2DAA2D;YAC3D,8DAA8D;YAC9D,IAAI,CAAC,CAAC,KAAK,YAAY,QAAQ,CAAC,IAAI,KAAK,CAAC,IAAI,KAAK,wBAAwB;gBAAE,MAAM,KAAK,CAAA;QAC5F,CAAC;IACL,CAAC;IACD,MAAM,QAAQ,GAAG,MAAM,qBAAqB,CAAC,KAAK,EAAE,GAAG,CAAC,CAAA;IACxD,OAAO,QAAQ,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,QAAQ,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,IAAI,CAAA;AACjF,CAAC;AAsDD;;;;;;GAMG;AACH,MAAM,UAAU,mBAAmB,CAC/B,MAAe,EACf,OAA6C;IAE7C,MAAM,OAAO,GAAG,MAAM;SACjB,OAAO,CAAC,QAAQ,CAAC;SACjB,WAAW,CAAC,OAAO,CAAC,WAAW,IAAI,wCAAwC,CAAC;SAC5E,MAAM,CAAC,QAAQ,EAAE,mCAAmC,CAAC;SACrD,MAAM,CAAC,UAAU,EAAE,qCAAqC,CAAC,CAAA;IAC9D,OAAO,cAAc,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,KAAK,EAAE,GAA4B,EAAE,EAAE;QACzE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,KAAK,EAAE,GAAG,GAAG,CAAA;QACnD,MAAM,IAAI,GAA0B;YAChC,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC;YACnB,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC;SAC1B,CAAA;QACD,MAAM,GAAG,GAAG,cAAc,CAAC,GAAG,CAAC,CAAA;QAC/B,MAAM,QAAQ,GAAG,MAAM,qBAAqB,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,EAAE,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAA;QAC5F,IAAI,CAAC,QAAQ,EAAE,CAAC;YACZ,IAAI,OAAO,CAAC,kBAAkB,EAAE,CAAC;gBAC7B,MAAM,OAAO,CAAC,kBAAkB,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAA;gBACjD,OAAM;YACV,CAAC;YACD,MAAM,IAAI,QAAQ,CAAC,mBAAmB,EAAE,gBAAgB,CAAC,CAAA;QAC7D,CAAC;QACD,MAAM,OAAO,GAAG,OAAO,CAAC,SAAS;YAC7B,CAAC,CAAC,MAAM,OAAO,CAAC,SAAS,CAAC;gBACpB,OAAO,EAAE,QAAQ,CAAC,OAAO;gBACzB,KAAK,EAAE,QAAQ,CAAC,KAAK;gBACrB,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBACvD,IAAI;gBACJ,KAAK;aACR,CAAC;YACJ,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAA;QACtB,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;YACZ,MAAM,OAAO,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,CAAA;YACrF,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAA;YAChC,OAAM;QACV,CAAC;QACD,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YACd,MAAM,OAAO,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,CAAA;YACrF,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAA;YACpC,OAAM;QACV,CAAC;QACD,MAAM,IAAI,GAAG,OAAO,CAAC,UAAU,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAA;QACzD,MAAM,KAAK,GAAG,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;QACtD,KAAK,MAAM,IAAI,IAAI,KAAK;YAAE,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;IAC/C,CAAC,CAAC,CAAA;AACN,CAAC"}
@@ -79,6 +79,11 @@ export type TokenBundle = {
79
79
  accessTokenExpiresAt?: number;
80
80
  refreshTokenExpiresAt?: number;
81
81
  };
82
+ /** Read-side snapshot returned by `activeBundle`. Mirrors `setBundle`'s write side. */
83
+ export type ActiveBundleSnapshot<TAccount extends AuthAccount = AuthAccount> = {
84
+ account: TAccount;
85
+ bundle: TokenBundle;
86
+ };
82
87
  /** Opaque account selector. Stores own the matching rule (id, email, label, …). */
83
88
  export type AccountRef = string;
84
89
  /**
@@ -112,6 +117,18 @@ export type TokenStore<TAccount extends AuthAccount = AuthAccount> = {
112
117
  setBundle?(account: TAccount, bundle: TokenBundle, options?: {
113
118
  promoteDefault?: boolean;
114
119
  }): Promise<void>;
120
+ /**
121
+ * Full-bundle read for refresh-capable consumers. Returns the matching
122
+ * account + bundle, or `null` on miss. Optional on the contract — the
123
+ * silent-refresh helper throws `AUTH_REFRESH_UNAVAILABLE` when a custom
124
+ * store doesn't implement it. `KeyringTokenStore` overrides this as
125
+ * required so cli-core helpers can call it without a non-null assertion.
126
+ *
127
+ * Stores MAY throw `CliError('AUTH_STORE_READ_FAILED', …)` on the same
128
+ * conditions as `active()` (e.g. keyring offline while a matching
129
+ * record exists).
130
+ */
131
+ activeBundle?(ref?: AccountRef): Promise<ActiveBundleSnapshot<TAccount> | null>;
115
132
  /** Remove a stored credential. No-op when `ref` doesn't match. */
116
133
  clear(ref?: AccountRef): Promise<void>;
117
134
  /** Every stored account with a default marker. */
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/auth/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/auth/types.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@doist/cli-core",
3
- "version": "0.18.0",
3
+ "version": "0.20.0",
4
4
  "description": "Shared core utilities for Doist CLI projects",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -66,15 +66,16 @@
66
66
  "@semantic-release/changelog": "6.0.3",
67
67
  "@semantic-release/exec": "7.1.0",
68
68
  "@semantic-release/git": "10.0.1",
69
- "@types/node": "25.7.0",
69
+ "@types/node": "25.8.0",
70
70
  "commander": "14.0.3",
71
71
  "conventional-changelog-conventionalcommits": "9.3.1",
72
72
  "lefthook": "2.1.6",
73
73
  "marked": "18.0.3",
74
74
  "marked-terminal-renderer": "2.2.0",
75
+ "oauth4webapi": "3.8.6",
75
76
  "open": "11.0.0",
76
- "oxfmt": "0.49.0",
77
- "oxlint": "1.64.0",
77
+ "oxfmt": "0.50.0",
78
+ "oxlint": "1.65.0",
78
79
  "semantic-release": "25.0.3",
79
80
  "typescript": "6.0.3",
80
81
  "vitest": "4.1.6"
@@ -90,6 +91,7 @@
90
91
  "commander": ">=14",
91
92
  "marked": ">=18",
92
93
  "marked-terminal-renderer": ">=2",
94
+ "oauth4webapi": ">=3",
93
95
  "open": ">=10",
94
96
  "vitest": ">=4.1"
95
97
  },
@@ -103,6 +105,9 @@
103
105
  "marked-terminal-renderer": {
104
106
  "optional": true
105
107
  },
108
+ "oauth4webapi": {
109
+ "optional": true
110
+ },
106
111
  "open": {
107
112
  "optional": true
108
113
  },