@todoist/outline-cli 0.0.1 → 3.0.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 (108) hide show
  1. package/CHANGELOG.md +359 -0
  2. package/README.md +145 -29
  3. package/dist/commands/account.d.ts +2 -0
  4. package/dist/commands/account.js +107 -0
  5. package/dist/commands/account.js.map +1 -0
  6. package/dist/commands/auth.d.ts +2 -0
  7. package/dist/commands/auth.js +169 -0
  8. package/dist/commands/auth.js.map +1 -0
  9. package/dist/commands/changelog.d.ts +2 -0
  10. package/dist/commands/changelog.js +15 -0
  11. package/dist/commands/changelog.js.map +1 -0
  12. package/dist/commands/collection.d.ts +2 -0
  13. package/dist/commands/collection.js +106 -0
  14. package/dist/commands/collection.js.map +1 -0
  15. package/dist/commands/document.d.ts +2 -0
  16. package/dist/commands/document.js +291 -0
  17. package/dist/commands/document.js.map +1 -0
  18. package/dist/commands/search.d.ts +2 -0
  19. package/dist/commands/search.js +49 -0
  20. package/dist/commands/search.js.map +1 -0
  21. package/dist/commands/skill.d.ts +2 -0
  22. package/dist/commands/skill.js +80 -0
  23. package/dist/commands/skill.js.map +1 -0
  24. package/dist/commands/update/index.d.ts +2 -0
  25. package/dist/commands/update/index.js +14 -0
  26. package/dist/commands/update/index.js.map +1 -0
  27. package/dist/index.d.ts +2 -0
  28. package/dist/index.js +56 -0
  29. package/dist/index.js.map +1 -0
  30. package/dist/lib/api.d.ts +18 -0
  31. package/dist/lib/api.js +107 -0
  32. package/dist/lib/api.js.map +1 -0
  33. package/dist/lib/auth-constants.d.ts +18 -0
  34. package/dist/lib/auth-constants.js +25 -0
  35. package/dist/lib/auth-constants.js.map +1 -0
  36. package/dist/lib/auth-output.d.ts +21 -0
  37. package/dist/lib/auth-output.js +38 -0
  38. package/dist/lib/auth-output.js.map +1 -0
  39. package/dist/lib/auth-pages.d.ts +2 -0
  40. package/dist/lib/auth-pages.js +101 -0
  41. package/dist/lib/auth-pages.js.map +1 -0
  42. package/dist/lib/auth-provider.d.ts +85 -0
  43. package/dist/lib/auth-provider.js +363 -0
  44. package/dist/lib/auth-provider.js.map +1 -0
  45. package/dist/lib/auth.d.ts +73 -0
  46. package/dist/lib/auth.js +188 -0
  47. package/dist/lib/auth.js.map +1 -0
  48. package/dist/lib/config.d.ts +45 -0
  49. package/dist/lib/config.js +20 -0
  50. package/dist/lib/config.js.map +1 -0
  51. package/dist/lib/errors.d.ts +8 -0
  52. package/dist/lib/errors.js +9 -0
  53. package/dist/lib/errors.js.map +1 -0
  54. package/dist/lib/global-args.d.ts +30 -0
  55. package/dist/lib/global-args.js +66 -0
  56. package/dist/lib/global-args.js.map +1 -0
  57. package/dist/lib/markdown.d.ts +2 -0
  58. package/dist/lib/markdown.js +12 -0
  59. package/dist/lib/markdown.js.map +1 -0
  60. package/dist/lib/migrate-auth.d.ts +11 -0
  61. package/dist/lib/migrate-auth.js +92 -0
  62. package/dist/lib/migrate-auth.js.map +1 -0
  63. package/dist/lib/outline-account.d.ts +32 -0
  64. package/dist/lib/outline-account.js +22 -0
  65. package/dist/lib/outline-account.js.map +1 -0
  66. package/dist/lib/output.d.ts +15 -0
  67. package/dist/lib/output.js +85 -0
  68. package/dist/lib/output.js.map +1 -0
  69. package/dist/lib/refs.d.ts +33 -0
  70. package/dist/lib/refs.js +182 -0
  71. package/dist/lib/refs.js.map +1 -0
  72. package/dist/lib/skills/content.d.ts +3 -0
  73. package/dist/lib/skills/content.js +160 -0
  74. package/dist/lib/skills/content.js.map +1 -0
  75. package/dist/lib/skills/create-installer.d.ts +9 -0
  76. package/dist/lib/skills/create-installer.js +77 -0
  77. package/dist/lib/skills/create-installer.js.map +1 -0
  78. package/dist/lib/skills/index.d.ts +5 -0
  79. package/dist/lib/skills/index.js +40 -0
  80. package/dist/lib/skills/index.js.map +1 -0
  81. package/dist/lib/skills/types.d.ts +10 -0
  82. package/dist/lib/skills/types.js +2 -0
  83. package/dist/lib/skills/types.js.map +1 -0
  84. package/dist/lib/skills/update-installed.d.ts +6 -0
  85. package/dist/lib/skills/update-installed.js +23 -0
  86. package/dist/lib/skills/update-installed.js.map +1 -0
  87. package/dist/lib/spinner.d.ts +2 -0
  88. package/dist/lib/spinner.js +5 -0
  89. package/dist/lib/spinner.js.map +1 -0
  90. package/dist/lib/user-records.d.ts +21 -0
  91. package/dist/lib/user-records.js +126 -0
  92. package/dist/lib/user-records.js.map +1 -0
  93. package/dist/lib/user-ref-store.d.ts +16 -0
  94. package/dist/lib/user-ref-store.js +37 -0
  95. package/dist/lib/user-ref-store.js.map +1 -0
  96. package/dist/postinstall.d.ts +1 -0
  97. package/dist/postinstall.js +16 -0
  98. package/dist/postinstall.js.map +1 -0
  99. package/dist/transport/fetch-with-retry.d.ts +15 -0
  100. package/dist/transport/fetch-with-retry.js +144 -0
  101. package/dist/transport/fetch-with-retry.js.map +1 -0
  102. package/dist/transport/http-dispatcher.d.ts +37 -0
  103. package/dist/transport/http-dispatcher.js +95 -0
  104. package/dist/transport/http-dispatcher.js.map +1 -0
  105. package/package.json +71 -8
  106. package/scripts/check-skill-sync.js +40 -0
  107. package/scripts/postinstall.js +12 -0
  108. package/scripts/sync-skill.js +30 -0
@@ -0,0 +1,85 @@
1
+ import { type AccountRef, type AuthProvider, type KeyringTokenStore } from '@todoist/cli-core/auth';
2
+ import { type OutlineAccount } from './outline-account.js';
3
+ export type { OutlineAccount } from './outline-account.js';
4
+ export { matchOutlineAccount } from './outline-account.js';
5
+ export type AuthInfoResponse = {
6
+ user: {
7
+ id: string;
8
+ name: string;
9
+ email: string;
10
+ };
11
+ team: {
12
+ name: string;
13
+ subdomain: string;
14
+ };
15
+ };
16
+ export type OutlineTokenStore = KeyringTokenStore<OutlineAccount>;
17
+ /**
18
+ * Read a line from stdin, prompting on stderr so `--json` / `--ndjson`
19
+ * envelopes on stdout stay clean. With `hidden: true` the typed characters are
20
+ * masked (for secrets): the prompt label is shown, then readline's echo is
21
+ * routed through a muted `Writable` so keystrokes don't leak to the terminal.
22
+ */
23
+ export declare function prompt(question: string, options?: {
24
+ hidden?: boolean;
25
+ }): Promise<string>;
26
+ export declare function resolveBaseUrl(options?: {
27
+ baseUrl?: unknown;
28
+ }): Promise<string>;
29
+ /**
30
+ * Verify a token by probing `auth.info` and map the response to the persisted
31
+ * account shape. Shared by the OAuth `validate` hook and `auth token` so the
32
+ * identity-resolution rules live in one place.
33
+ */
34
+ export declare function identifyAccount(token: string, baseUrl: string, oauthClientId?: string): Promise<OutlineAccount>;
35
+ export declare function createOutlineAuthProvider(): AuthProvider<OutlineAccount>;
36
+ /**
37
+ * True when the v2 store is empty but a legacy v1 token snapshot is still
38
+ * the only thing keeping the CLI authenticated — typically because
39
+ * `migrateLegacyAuth` couldn't reach the Outline API to identify the
40
+ * account (`MigrateSkipReason: 'identify-failed'`) or the OS keyring is
41
+ * unreachable (`'legacy-keyring-unreachable'`, although outline has no
42
+ * v1 keyring slot to read from). Useful for downstream diagnostics.
43
+ */
44
+ export declare function isLegacyAuthActive(): Promise<boolean>;
45
+ /** Where `ol account current` resolves the active credential from. */
46
+ export type ActiveAccountSource = {
47
+ source: 'stored';
48
+ account: OutlineAccount;
49
+ isDefault: boolean;
50
+ } | {
51
+ source: 'env';
52
+ } | {
53
+ source: 'legacy';
54
+ } | null;
55
+ /**
56
+ * Authoritatively classify the active credential without re-deriving the
57
+ * cascade from membership heuristics. Mirrors `active()`'s precedence — env
58
+ * (only when no explicit ref) → stored v2 → legacy snapshot — but reports
59
+ * *which* branch matched, so `ol account current` can render the right shape.
60
+ *
61
+ * The v2 lookup uses the bare keyring store (no legacy fallback) and matches by
62
+ * `ref`, so a renamed legacy snapshot sharing a UUID with a v2 record can't be
63
+ * misreported as `stored`: a `--user <old-name>` that the v2 record no longer
64
+ * matches falls through to the legacy branch.
65
+ */
66
+ export declare function resolveActiveAccountSource(ref?: AccountRef): Promise<ActiveAccountSource>;
67
+ export declare function createOutlineTokenStore(): OutlineTokenStore;
68
+ /**
69
+ * Where the currently-active token lives. Mirrors `active()`'s resolution
70
+ * order — env → v2 record → legacy plaintext — so the answer can never
71
+ * contradict the token the runtime is actually using.
72
+ *
73
+ * Pass `ref` to report the source of a specific `--user` account (used by
74
+ * `auth status --user <ref>`): a ref selects the matching record and skips the
75
+ * env short-circuit, so the source reflects the account being shown rather
76
+ * than an ambient env token. Without a ref the default/env cascade applies.
77
+ *
78
+ * The precedence cascade is intentionally duplicated with `active()`:
79
+ * the only true dedupe options either (a) add an extra config read on
80
+ * every `apiRequest` call (regressing the request hot path) or (b)
81
+ * augment cli-core's `TokenStore` contract. The drift the duplication
82
+ * invites is guarded by the `getActiveTokenSource` regression test that
83
+ * asserts v2-record presence wins over a lingering v1 `api_token`.
84
+ */
85
+ export declare function getActiveTokenSource(ref?: AccountRef): Promise<'env' | 'secure-store' | 'config-file'>;
@@ -0,0 +1,363 @@
1
+ import { createInterface } from 'node:readline/promises';
2
+ import { Writable } from 'node:stream';
3
+ import { createKeyringTokenStore, createPkceProvider, } from '@todoist/cli-core/auth';
4
+ import { fetchWithRetry } from '../transport/fetch-with-retry.js';
5
+ import { apiRequest } from './api.js';
6
+ import { LEGACY_CLEAR_PAYLOAD, SECURE_STORE_SERVICE, TOKEN_ENV_VAR } from './auth-constants.js';
7
+ import { getBaseUrl, getOAuthClientId } from './auth.js';
8
+ import { getConfig, getConfigPath, updateConfig } from './config.js';
9
+ import { runMigrateLegacyAuth } from './migrate-auth.js';
10
+ import { makeOutlineAccount, matchOutlineAccount } from './outline-account.js';
11
+ import { createOutlineUserRecordStore, getDefaultUserRecord, recordForRef } from './user-records.js';
12
+ export { matchOutlineAccount } from './outline-account.js';
13
+ function stringFlag(value) {
14
+ return typeof value === 'string' && value.trim() ? value.trim() : undefined;
15
+ }
16
+ /**
17
+ * Read a line from stdin, prompting on stderr so `--json` / `--ndjson`
18
+ * envelopes on stdout stay clean. With `hidden: true` the typed characters are
19
+ * masked (for secrets): the prompt label is shown, then readline's echo is
20
+ * routed through a muted `Writable` so keystrokes don't leak to the terminal.
21
+ */
22
+ export async function prompt(question, options = {}) {
23
+ if (!options.hidden) {
24
+ const rl = createInterface({ input: process.stdin, output: process.stderr });
25
+ try {
26
+ return (await rl.question(question)).trim();
27
+ }
28
+ finally {
29
+ rl.close();
30
+ }
31
+ }
32
+ // Echo the prompt label, then mute so typed characters aren't shown. A muted
33
+ // `Writable` (public API) avoids poking readline's private `_writeToOutput`.
34
+ let muted = false;
35
+ const output = new Writable({
36
+ write(chunk, _encoding, callback) {
37
+ if (!muted)
38
+ process.stderr.write(chunk);
39
+ callback();
40
+ },
41
+ });
42
+ const rl = createInterface({ input: process.stdin, output, terminal: true });
43
+ try {
44
+ const pending = rl.question(question);
45
+ muted = true;
46
+ const answer = (await pending).trim();
47
+ process.stderr.write('\n');
48
+ return answer;
49
+ }
50
+ finally {
51
+ rl.close();
52
+ }
53
+ }
54
+ export async function resolveBaseUrl(options = {}) {
55
+ const fromFlag = stringFlag(options.baseUrl);
56
+ if (fromFlag)
57
+ return fromFlag.replace(/\/$/, '');
58
+ const fromEnv = process.env.OUTLINE_URL?.trim();
59
+ if (fromEnv)
60
+ return fromEnv.replace(/\/$/, '');
61
+ const configured = await getBaseUrl();
62
+ // Never block a non-interactive shell (CI, piped input) on a prompt —
63
+ // `auth token` is meant to be scriptable, so fall back to the default.
64
+ if (!process.stdin.isTTY)
65
+ return configured.replace(/\/$/, '');
66
+ const answered = await prompt(`Base URL (default: ${configured}): `);
67
+ return (answered || configured).replace(/\/$/, '');
68
+ }
69
+ /**
70
+ * Verify a token by probing `auth.info` and map the response to the persisted
71
+ * account shape. Shared by the OAuth `validate` hook and `auth token` so the
72
+ * identity-resolution rules live in one place.
73
+ */
74
+ export async function identifyAccount(token, baseUrl, oauthClientId) {
75
+ const { data } = await apiRequest('auth.info', {}, { token, baseUrl });
76
+ return makeOutlineAccount({
77
+ id: data.user.id,
78
+ label: data.user.name,
79
+ baseUrl,
80
+ oauthClientId,
81
+ teamName: data.team.name,
82
+ });
83
+ }
84
+ async function resolveClientId(flags) {
85
+ const fromFlag = stringFlag(flags.clientId);
86
+ if (fromFlag)
87
+ return fromFlag;
88
+ const existing = await getOAuthClientId();
89
+ if (existing)
90
+ return existing;
91
+ const answered = await prompt('OAuth Client ID: ');
92
+ if (!answered) {
93
+ throw new Error('OAuth client ID is required. Create a public OAuth app in Outline settings, then pass --client-id <id> or set OUTLINE_OAUTH_CLIENT_ID.');
94
+ }
95
+ return answered;
96
+ }
97
+ /**
98
+ * Routes cli-core's OAuth HTTP through outline's transport so the proxy /
99
+ * decompression dispatcher applies — to the token exchange AND the
100
+ * oauth4webapi refresh grant (cli-core threads this into oauth4webapi's
101
+ * `customFetch`), which would otherwise capture the bare global `fetch`.
102
+ */
103
+ const outlineFetch = (input, init) => fetchWithRetry({ url: input, options: init ?? {} });
104
+ export function createOutlineAuthProvider() {
105
+ // Captured at `authorize` (which may prompt for it) and reused by
106
+ // `exchangeCode` / `validate` so a single login can't double-prompt. A
107
+ // provider built only for silent refresh never runs `authorize`, so this
108
+ // stays undefined there and `tokenUrl` falls back to stored config — the
109
+ // refresh path must never prompt.
110
+ let baseUrl;
111
+ return createPkceProvider({
112
+ authorizeUrl: async ({ flags }) => {
113
+ baseUrl = await resolveBaseUrl(flags);
114
+ return `${baseUrl}/oauth/authorize`;
115
+ },
116
+ tokenUrl: async ({ handshake }) => {
117
+ // `handshake.baseUrl` lets a caller scope refresh to a specific
118
+ // account's instance (status --user); otherwise the authorize-time
119
+ // value, then the default config.
120
+ const base = baseUrl ?? handshake.baseUrl ?? (await getBaseUrl());
121
+ return `${base}/oauth/token`;
122
+ },
123
+ // Prefer a handshake-scoped client id (account-aware refresh); else
124
+ // resolve from flag/config (only prompts when neither is set, so it's
125
+ // safe on the refresh path — the logged-in record carries one).
126
+ clientId: ({ handshake, flags }) => {
127
+ const fromHandshake = handshake.clientId;
128
+ return typeof fromHandshake === 'string' && fromHandshake
129
+ ? fromHandshake
130
+ : resolveClientId(flags);
131
+ },
132
+ validate: async ({ token, handshake }) => {
133
+ const base = baseUrl ?? (await getBaseUrl());
134
+ return identifyAccount(token, base, handshake.clientId);
135
+ },
136
+ fetchImpl: outlineFetch,
137
+ });
138
+ }
139
+ /** True when the v2 store is the authoritative source. */
140
+ function migrationIsConclusive(result) {
141
+ return (result.status === 'migrated' ||
142
+ result.status === 'already-migrated' ||
143
+ result.status === 'no-legacy-state');
144
+ }
145
+ /**
146
+ * Synthesise a snapshot from v1 plaintext state still on disk. Fallback for
147
+ * when migration can't complete (offline `identifyAccount`, WSL
148
+ * `legacy-keyring-unreachable`). Returns `null` when no legacy token is
149
+ * present. The synthesised account uses whatever identity fields the v1
150
+ * config has — for pre-#71 configs the id/label may be empty, but the
151
+ * runtime never renders those (status's `fetchLive` re-derives from the
152
+ * API), so empty placeholders are safe.
153
+ */
154
+ async function readLegacyTokenSnapshot() {
155
+ const config = await getConfig();
156
+ const token = config.api_token?.trim() || null;
157
+ if (!token)
158
+ return null;
159
+ return {
160
+ token,
161
+ account: makeOutlineAccount({
162
+ id: config.auth_user_id ?? '',
163
+ label: config.auth_user_name ?? '',
164
+ baseUrl: config.base_url,
165
+ oauthClientId: config.oauth_client_id,
166
+ teamName: config.auth_team_name,
167
+ }),
168
+ };
169
+ }
170
+ /**
171
+ * Discharge v1 plaintext state. Runs **after** a successful v2 write/clear
172
+ * — never before — so a v2-op failure doesn't strand the user with no
173
+ * recoverable credentials. Caller decides whether to swallow or propagate
174
+ * the `updateConfig` failure:
175
+ * - `set()` swallows (v2 record will win in `active()` regardless).
176
+ * - `clear()` propagates (v2 is empty, so a stale legacy token would
177
+ * shadow the logout via the fallback).
178
+ */
179
+ async function dischargeLegacyState() {
180
+ await updateConfig(LEGACY_CLEAR_PAYLOAD);
181
+ }
182
+ /**
183
+ * Memoised one-shot migration trigger. Resolves with `null` on rejection so
184
+ * the CLI never fails to start because of a migration error — the legacy
185
+ * snapshot fallback handles that case. Tests reset the memo with
186
+ * `vi.resetModules()` + a dynamic re-import.
187
+ */
188
+ let migrationPromise;
189
+ function ensureMigrated() {
190
+ if (!migrationPromise) {
191
+ migrationPromise = runMigrateLegacyAuth({ silent: true }).catch(() => null);
192
+ }
193
+ return migrationPromise;
194
+ }
195
+ /**
196
+ * True when the v2 store is empty but a legacy v1 token snapshot is still
197
+ * the only thing keeping the CLI authenticated — typically because
198
+ * `migrateLegacyAuth` couldn't reach the Outline API to identify the
199
+ * account (`MigrateSkipReason: 'identify-failed'`) or the OS keyring is
200
+ * unreachable (`'legacy-keyring-unreachable'`, although outline has no
201
+ * v1 keyring slot to read from). Useful for downstream diagnostics.
202
+ */
203
+ export async function isLegacyAuthActive() {
204
+ const result = await ensureMigrated();
205
+ if (result !== null && migrationIsConclusive(result))
206
+ return false;
207
+ const legacy = await readLegacyTokenSnapshot();
208
+ return legacy !== null;
209
+ }
210
+ /**
211
+ * `OUTLINE_API_TOKEN` short-circuits `active()` only when no explicit ref
212
+ * is supplied — cli-core's `KeyringTokenStore` doesn't know about the env
213
+ * var, and an explicit ref means the caller targets a specific stored
214
+ * account.
215
+ *
216
+ * `ensureMigrated()` runs **before** every mutating v2 op so the post-op
217
+ * legacy discharge can't race a still-pending migration into re-grabbing
218
+ * the legacy `api_token` we just consumed. `set()` / `clear()` then
219
+ * discharge legacy state **after** the v2 op succeeds. `set()` swallows
220
+ * the cleanup failure (v2 wins in `active()` regardless); `clear()`
221
+ * propagates it so a failed logout fails loudly instead of leaving the
222
+ * user authenticated via the legacy fallback.
223
+ */
224
+ /** The bare cli-core keyring store (v2 records only — no env/legacy fallback). */
225
+ function createInnerStore() {
226
+ return createKeyringTokenStore({
227
+ serviceName: SECURE_STORE_SERVICE,
228
+ userRecords: createOutlineUserRecordStore(),
229
+ recordsLocation: getConfigPath(),
230
+ matchAccount: matchOutlineAccount,
231
+ });
232
+ }
233
+ /**
234
+ * Authoritatively classify the active credential without re-deriving the
235
+ * cascade from membership heuristics. Mirrors `active()`'s precedence — env
236
+ * (only when no explicit ref) → stored v2 → legacy snapshot — but reports
237
+ * *which* branch matched, so `ol account current` can render the right shape.
238
+ *
239
+ * The v2 lookup uses the bare keyring store (no legacy fallback) and matches by
240
+ * `ref`, so a renamed legacy snapshot sharing a UUID with a v2 record can't be
241
+ * misreported as `stored`: a `--user <old-name>` that the v2 record no longer
242
+ * matches falls through to the legacy branch.
243
+ */
244
+ export async function resolveActiveAccountSource(ref) {
245
+ if (ref === undefined && process.env[TOKEN_ENV_VAR]?.trim())
246
+ return { source: 'env' };
247
+ await ensureMigrated();
248
+ const stored = await createInnerStore().activeAccount(ref);
249
+ if (stored)
250
+ return { source: 'stored', account: stored.account, isDefault: stored.isDefault };
251
+ const legacy = await readLegacyTokenSnapshot();
252
+ if (legacy && (ref === undefined || matchOutlineAccount(legacy.account, ref))) {
253
+ return { source: 'legacy' };
254
+ }
255
+ return null;
256
+ }
257
+ export function createOutlineTokenStore() {
258
+ const inner = createInnerStore();
259
+ async function migrationIsInconclusive() {
260
+ const result = await ensureMigrated(); // memoised
261
+ return result === null || !migrationIsConclusive(result);
262
+ }
263
+ /**
264
+ * Shared env → v2 → legacy resolution for `active` / `activeBundle`.
265
+ * `fromV2` reads the matching cli-core method; `fromEnvOrLegacy` maps an
266
+ * env/legacy token into the caller's shape (env + legacy carry no refresh
267
+ * material, so the bundle form surfaces as access-only).
268
+ */
269
+ async function resolveAuth(ref, fromV2, fromEnvOrLegacy) {
270
+ if (ref === undefined) {
271
+ const envToken = process.env[TOKEN_ENV_VAR]?.trim();
272
+ if (envToken) {
273
+ return fromEnvOrLegacy(envToken, makeOutlineAccount({ id: '', label: '', baseUrl: await getBaseUrl() }));
274
+ }
275
+ }
276
+ await ensureMigrated();
277
+ const fromStore = await fromV2();
278
+ if (fromStore)
279
+ return fromStore;
280
+ const legacy = await readLegacyTokenSnapshot();
281
+ if (legacy && (ref === undefined || matchOutlineAccount(legacy.account, ref))) {
282
+ return fromEnvOrLegacy(legacy.token, legacy.account);
283
+ }
284
+ return null;
285
+ }
286
+ // `ensureMigrated` runs before the v2 write so the post-write discharge
287
+ // can't race a pending migration into re-grabbing the legacy token. The
288
+ // discharge is best-effort: a lingering `api_token` is dormant because
289
+ // reads hit v2 first. (`clear` needs the stricter propagating variant.)
290
+ async function writeThenDischargeLegacy(write) {
291
+ await ensureMigrated();
292
+ await write();
293
+ if (await migrationIsInconclusive()) {
294
+ await dischargeLegacyState().catch(() => undefined);
295
+ }
296
+ }
297
+ return {
298
+ active(ref) {
299
+ return resolveAuth(ref, () => inner.active(ref), (token, account) => ({ token, account }));
300
+ },
301
+ activeAccount(ref) {
302
+ return resolveAuth(ref, () => inner.activeAccount(ref), (_token, account) => ({ account, isDefault: true }));
303
+ },
304
+ activeBundle(ref) {
305
+ return resolveAuth(ref, () => inner.activeBundle(ref), (token, account) => ({ account, bundle: { accessToken: token } }));
306
+ },
307
+ set(account, token) {
308
+ return writeThenDischargeLegacy(() => inner.set(account, token));
309
+ },
310
+ setBundle(account, bundle, options) {
311
+ return writeThenDischargeLegacy(() => inner.setBundle(account, bundle, options));
312
+ },
313
+ async clear(ref) {
314
+ await ensureMigrated();
315
+ const cleared = await inner.clear(ref);
316
+ if (await migrationIsInconclusive()) {
317
+ // Must succeed: v2 is now empty, so a surviving legacy
318
+ // token would shadow the logout via the fallback.
319
+ await dischargeLegacyState();
320
+ }
321
+ return cleared;
322
+ },
323
+ async list() {
324
+ await ensureMigrated();
325
+ return inner.list();
326
+ },
327
+ async setDefault(ref) {
328
+ await ensureMigrated();
329
+ return inner.setDefault(ref);
330
+ },
331
+ getLastStorageResult: () => inner.getLastStorageResult(),
332
+ getLastClearResult: () => inner.getLastClearResult(),
333
+ };
334
+ }
335
+ /**
336
+ * Where the currently-active token lives. Mirrors `active()`'s resolution
337
+ * order — env → v2 record → legacy plaintext — so the answer can never
338
+ * contradict the token the runtime is actually using.
339
+ *
340
+ * Pass `ref` to report the source of a specific `--user` account (used by
341
+ * `auth status --user <ref>`): a ref selects the matching record and skips the
342
+ * env short-circuit, so the source reflects the account being shown rather
343
+ * than an ambient env token. Without a ref the default/env cascade applies.
344
+ *
345
+ * The precedence cascade is intentionally duplicated with `active()`:
346
+ * the only true dedupe options either (a) add an extra config read on
347
+ * every `apiRequest` call (regressing the request hot path) or (b)
348
+ * augment cli-core's `TokenStore` contract. The drift the duplication
349
+ * invites is guarded by the `getActiveTokenSource` regression test that
350
+ * asserts v2-record presence wins over a lingering v1 `api_token`.
351
+ */
352
+ export async function getActiveTokenSource(ref) {
353
+ if (ref === undefined && process.env[TOKEN_ENV_VAR]?.trim())
354
+ return 'env';
355
+ const config = await getConfig();
356
+ const record = ref !== undefined ? recordForRef(config, ref) : getDefaultUserRecord(config);
357
+ if (record)
358
+ return record.fallbackToken ? 'config-file' : 'secure-store';
359
+ if (ref === undefined && config.api_token?.trim())
360
+ return 'config-file';
361
+ return 'secure-store';
362
+ }
363
+ //# sourceMappingURL=auth-provider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth-provider.js","sourceRoot":"","sources":["../../src/lib/auth-provider.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,73 @@
1
+ import { SecureStoreUnavailableError, type TokenRefreshOptions } from '@todoist/cli-core/auth';
2
+ import { TOKEN_ENV_VAR } from './auth-constants.js';
3
+ import { getActiveTokenSource } from './auth-provider.js';
4
+ import { CliError } from './errors.js';
5
+ import { type OutlineAccount } from './outline-account.js';
6
+ /** Base URL + optional refresh handshake for a single request. */
7
+ type RequestContext = {
8
+ baseUrl: string;
9
+ handshake?: {
10
+ baseUrl: string;
11
+ clientId: string;
12
+ };
13
+ };
14
+ export { SecureStoreUnavailableError, getActiveTokenSource, TOKEN_ENV_VAR };
15
+ export declare class NoTokenError extends CliError {
16
+ constructor();
17
+ }
18
+ /**
19
+ * Best-effort proactive rotation before a request. Returns the token to use
20
+ * (the rotated one, or the current one if no rotation was needed) so the
21
+ * caller doesn't re-read the store; `undefined` when the account isn't
22
+ * refreshable (no refresh token / env / failure) — the caller then falls back
23
+ * to `getApiToken()` and the 401 path stays authoritative. The wrapped store
24
+ * scopes the bundle to any global `--user`; `handshake` pins the token
25
+ * endpoint to that account's instance + client id.
26
+ */
27
+ export declare function proactiveRefresh(handshake?: RequestContext['handshake']): Promise<string | undefined>;
28
+ /**
29
+ * Refresh wiring for cli-core's `auth status` / `auth token view` attachers,
30
+ * so both hand back a token that is usable right now rather than whatever
31
+ * was last stored. The attachers pass their own (`--user`-aware) store, so
32
+ * the handshake is resolved from the account cli-core actually selected —
33
+ * pinning the refresh to that account's instance and client id. Env, legacy
34
+ * and record-less accounts carry no refresh token and are served as-is.
35
+ */
36
+ export declare function getTokenRefreshOptions(): TokenRefreshOptions<OutlineAccount>;
37
+ /**
38
+ * Reactive rotation after a 401. Returns `true` when the token rotated (the
39
+ * caller retries once). A rejected/absent refresh token surfaces as
40
+ * `NoTokenError` (re-login); a transient failure propagates unchanged.
41
+ */
42
+ export declare function reactiveRefresh(handshake?: RequestContext['handshake']): Promise<boolean>;
43
+ /**
44
+ * Read the active token. Hot path: when `OUTLINE_API_TOKEN` is set we
45
+ * return it directly without consulting the token store, since
46
+ * `apiRequest` already resolves the base URL separately — going through
47
+ * `store.active()` here would trigger a redundant `getBaseUrl()` lookup
48
+ * per request just to synthesise an account we don't need.
49
+ */
50
+ export declare function getApiToken(): Promise<string>;
51
+ /**
52
+ * Account-agnostic base URL cascade: env var → default user record (v2) →
53
+ * legacy `base_url` config (v1) → built-in default. Deliberately ignores the
54
+ * global `--user` selector so the login flow (which resolves its default
55
+ * prompt through here) isn't skewed by account selection; the request path
56
+ * applies `--user` separately via {@link getRequestContext}.
57
+ */
58
+ export declare function getBaseUrl(): Promise<string>;
59
+ /**
60
+ * Account-agnostic OAuth client id cascade: env var → default user record
61
+ * (v2) → legacy `oauth_client_id` config (v1) → undefined (caller prompts).
62
+ * Like {@link getBaseUrl}, ignores `--user` so login stays default-driven.
63
+ */
64
+ export declare function getOAuthClientId(): Promise<string | undefined>;
65
+ /**
66
+ * Resolve the base URL — and, when a `--user` account is selected, the refresh
67
+ * handshake (its instance + client id) — for a single managed request. Falls
68
+ * back to the account-agnostic {@link getBaseUrl} cascade without `--user`.
69
+ * An unknown ref resolves no handshake and the default base URL; the wrapped
70
+ * token store then raises `ACCOUNT_NOT_FOUND` when the token is read, so the
71
+ * request never fires.
72
+ */
73
+ export declare function getRequestContext(): Promise<RequestContext>;