@doist/outline-cli 1.6.0 → 1.7.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/dist/commands/auth.d.ts +12 -0
- package/dist/commands/auth.js +93 -35
- package/dist/commands/auth.js.map +1 -1
- package/dist/lib/auth-constants.d.ts +18 -0
- package/dist/lib/auth-constants.js +25 -0
- package/dist/lib/auth-constants.js.map +1 -0
- package/dist/lib/auth-provider.d.ts +57 -8
- package/dist/lib/auth-provider.js +177 -76
- package/dist/lib/auth-provider.js.map +1 -1
- package/dist/lib/auth.d.ts +24 -11
- package/dist/lib/auth.js +53 -35
- package/dist/lib/auth.js.map +1 -1
- package/dist/lib/config.d.ts +20 -0
- package/dist/lib/config.js.map +1 -1
- package/dist/lib/errors.d.ts +1 -1
- package/dist/lib/errors.js.map +1 -1
- package/dist/lib/migrate-auth.d.ts +11 -0
- package/dist/lib/migrate-auth.js +92 -0
- package/dist/lib/migrate-auth.js.map +1 -0
- package/dist/lib/outline-account.d.ts +26 -0
- package/dist/lib/outline-account.js +12 -0
- package/dist/lib/outline-account.js.map +1 -0
- package/dist/lib/skills/content.d.ts +1 -1
- package/dist/lib/skills/content.js +2 -0
- package/dist/lib/skills/content.js.map +1 -1
- package/dist/lib/user-records.d.ts +14 -0
- package/dist/lib/user-records.js +89 -0
- package/dist/lib/user-records.js.map +1 -0
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
## [1.7.0](https://github.com/Doist/outline-cli/compare/v1.6.0...v1.7.0) (2026-05-17)
|
|
2
|
+
|
|
3
|
+
### Features
|
|
4
|
+
|
|
5
|
+
* **auth:** store API tokens in the OS keyring via @doist/cli-core ([#73](https://github.com/Doist/outline-cli/issues/73)) ([b353aea](https://github.com/Doist/outline-cli/commit/b353aeae3cdcc63308ec8de899f08c1dda23f22c))
|
|
6
|
+
|
|
1
7
|
## [1.6.0](https://github.com/Doist/outline-cli/compare/v1.5.3...v1.6.0) (2026-05-15)
|
|
2
8
|
|
|
3
9
|
### Features
|
package/dist/commands/auth.d.ts
CHANGED
|
@@ -1,2 +1,14 @@
|
|
|
1
|
+
import { type TokenStorageResult } from '@doist/cli-core/auth';
|
|
1
2
|
import type { Command } from 'commander';
|
|
3
|
+
/**
|
|
4
|
+
* Surface a `TokenStorageResult` from a save/clear: the human-readable
|
|
5
|
+
* confirmation goes to stdout, any keyring-fallback warning goes to stderr.
|
|
6
|
+
* Pass `isMachineOutput: true` to suppress the stdout confirmation in
|
|
7
|
+
* `--json` / `--ndjson` mode while still routing the warning to stderr.
|
|
8
|
+
*
|
|
9
|
+
* Exported for direct unit testing — the alternative (driving this via
|
|
10
|
+
* mocked cli-core login/logout hooks) would require stubbing the entire
|
|
11
|
+
* store contract just to assert two console calls.
|
|
12
|
+
*/
|
|
13
|
+
export declare function logTokenStorageResult(result: TokenStorageResult, secureStoreMessage: string, isMachineOutput?: boolean): void;
|
|
2
14
|
export declare function registerAuthCommand(program: Command): void;
|
package/dist/commands/auth.js
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import { attachLoginCommand } from '@doist/cli-core/auth';
|
|
1
|
+
import { attachLoginCommand, attachLogoutCommand, attachStatusCommand, } from '@doist/cli-core/auth';
|
|
2
2
|
import chalk from 'chalk';
|
|
3
3
|
import { apiRequest } from '../lib/api.js';
|
|
4
4
|
import { renderError, renderSuccess } from '../lib/auth-pages.js';
|
|
5
|
-
import { createOutlineAuthProvider, createOutlineTokenStore } from '../lib/auth-provider.js';
|
|
6
|
-
import {
|
|
7
|
-
import { formatError } from '../lib/output.js';
|
|
5
|
+
import { createOutlineAuthProvider, createOutlineTokenStore, getActiveTokenSource, } from '../lib/auth-provider.js';
|
|
6
|
+
import { CliError } from '../lib/errors.js';
|
|
8
7
|
const DEFAULT_OAUTH_CALLBACK_PORT = 54969;
|
|
9
8
|
function resolvePreferredCallbackPort() {
|
|
10
9
|
const raw = process.env.OUTLINE_OAUTH_CALLBACK_PORT?.trim();
|
|
@@ -16,6 +15,24 @@ function resolvePreferredCallbackPort() {
|
|
|
16
15
|
}
|
|
17
16
|
return parsed;
|
|
18
17
|
}
|
|
18
|
+
/**
|
|
19
|
+
* Surface a `TokenStorageResult` from a save/clear: the human-readable
|
|
20
|
+
* confirmation goes to stdout, any keyring-fallback warning goes to stderr.
|
|
21
|
+
* Pass `isMachineOutput: true` to suppress the stdout confirmation in
|
|
22
|
+
* `--json` / `--ndjson` mode while still routing the warning to stderr.
|
|
23
|
+
*
|
|
24
|
+
* Exported for direct unit testing — the alternative (driving this via
|
|
25
|
+
* mocked cli-core login/logout hooks) would require stubbing the entire
|
|
26
|
+
* store contract just to assert two console calls.
|
|
27
|
+
*/
|
|
28
|
+
export function logTokenStorageResult(result, secureStoreMessage, isMachineOutput = false) {
|
|
29
|
+
if (!isMachineOutput && result.storage === 'secure-store') {
|
|
30
|
+
console.log(chalk.dim(secureStoreMessage));
|
|
31
|
+
}
|
|
32
|
+
if (result.warning) {
|
|
33
|
+
console.error(chalk.yellow('Warning:'), result.warning);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
19
36
|
export function registerAuthCommand(program) {
|
|
20
37
|
const auth = program.command('auth').description('Manage authentication');
|
|
21
38
|
const provider = createOutlineAuthProvider();
|
|
@@ -28,43 +45,84 @@ export function registerAuthCommand(program) {
|
|
|
28
45
|
renderSuccess,
|
|
29
46
|
renderError,
|
|
30
47
|
onSuccess({ view, account }) {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
48
|
+
const isMachineOutput = view.json || view.ndjson;
|
|
49
|
+
if (!isMachineOutput) {
|
|
50
|
+
console.log(chalk.green(`Authenticated to ${account.teamName} as ${account.label}`));
|
|
51
|
+
}
|
|
52
|
+
const result = store.getLastStorageResult();
|
|
53
|
+
if (result) {
|
|
54
|
+
logTokenStorageResult(result, 'Token stored securely in the system credential manager', isMachineOutput);
|
|
55
|
+
}
|
|
34
56
|
},
|
|
35
57
|
})
|
|
36
58
|
.description('Authenticate with an Outline instance via OAuth')
|
|
37
59
|
.option('--base-url <url>', 'Outline base URL to use for this login (saved for future logins)')
|
|
38
60
|
.option('--client-id <clientId>', 'OAuth client ID to use for this login (saved for future logins)');
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
61
|
+
// `attachStatusCommand` guarantees `fetchLive` runs before `renderText` /
|
|
62
|
+
// `renderJson` within a single invocation, so the stash is always
|
|
63
|
+
// populated by the time the render hooks read it.
|
|
64
|
+
let statusData = null;
|
|
65
|
+
attachStatusCommand(auth, {
|
|
66
|
+
store,
|
|
67
|
+
description: 'Show current authentication state',
|
|
68
|
+
async fetchLive({ token, account }) {
|
|
69
|
+
try {
|
|
70
|
+
const [{ data: info }, source] = await Promise.all([
|
|
71
|
+
apiRequest('auth.info', {}, { token, baseUrl: account.baseUrl }),
|
|
72
|
+
getActiveTokenSource(),
|
|
73
|
+
]);
|
|
74
|
+
statusData = { email: info.user.email, source };
|
|
75
|
+
return {
|
|
76
|
+
...account,
|
|
77
|
+
id: info.user.id,
|
|
78
|
+
label: info.user.name,
|
|
79
|
+
teamName: info.team.name,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
catch (err) {
|
|
83
|
+
const message = err instanceof Error ? err.message : '';
|
|
84
|
+
if (/\b401\b/.test(message) || /Authentication required/i.test(message)) {
|
|
85
|
+
throw new CliError('NO_TOKEN', 'Not authenticated (token expired or invalid)', [
|
|
86
|
+
'Run `ol auth login` to re-authenticate',
|
|
87
|
+
]);
|
|
88
|
+
}
|
|
89
|
+
throw err;
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
renderText({ account }) {
|
|
93
|
+
if (!statusData)
|
|
94
|
+
throw new Error('status renderText called before fetchLive');
|
|
95
|
+
return [
|
|
96
|
+
`${chalk.green('✓')} Authenticated`,
|
|
97
|
+
` Team: ${chalk.bold(account.teamName ?? '')}`,
|
|
98
|
+
` User: ${account.label} (${statusData.email})`,
|
|
99
|
+
` Base URL: ${account.baseUrl}`,
|
|
100
|
+
` Token source: ${statusData.source}`,
|
|
101
|
+
];
|
|
102
|
+
},
|
|
103
|
+
renderJson({ account }) {
|
|
104
|
+
if (!statusData)
|
|
105
|
+
throw new Error('status renderJson called before fetchLive');
|
|
106
|
+
return {
|
|
107
|
+
id: account.id,
|
|
108
|
+
team: account.teamName,
|
|
109
|
+
baseUrl: account.baseUrl,
|
|
110
|
+
source: statusData.source,
|
|
111
|
+
};
|
|
112
|
+
},
|
|
113
|
+
onNotAuthenticated() {
|
|
114
|
+
throw new CliError('NOT_AUTHENTICATED', 'Not authenticated. Run: ol auth login');
|
|
115
|
+
},
|
|
62
116
|
});
|
|
63
|
-
auth
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
117
|
+
attachLogoutCommand(auth, {
|
|
118
|
+
store,
|
|
119
|
+
description: 'Clear saved authentication',
|
|
120
|
+
onCleared({ view }) {
|
|
121
|
+
const result = store.getLastClearResult();
|
|
122
|
+
if (!result)
|
|
123
|
+
return;
|
|
124
|
+
logTokenStorageResult(result, 'Stored token removed from the system credential manager', view.json || view.ndjson);
|
|
125
|
+
},
|
|
68
126
|
});
|
|
69
127
|
}
|
|
70
128
|
//# sourceMappingURL=auth.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/commands/auth.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/commands/auth.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,kBAAkB,EAClB,mBAAmB,EACnB,mBAAmB,GAEtB,MAAM,sBAAsB,CAAA;AAC7B,OAAO,KAAK,MAAM,OAAO,CAAA;AAEzB,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAA;AAC1C,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAA;AACjE,OAAO,EAEH,yBAAyB,EACzB,uBAAuB,EACvB,oBAAoB,GAGvB,MAAM,yBAAyB,CAAA;AAChC,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAE3C,MAAM,2BAA2B,GAAG,KAAK,CAAA;AAOzC,SAAS,4BAA4B;IACjC,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,2BAA2B,EAAE,IAAI,EAAE,CAAA;IAC3D,IAAI,CAAC,GAAG;QAAE,OAAO,2BAA2B,CAAA;IAC5C,MAAM,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,CAAA;IAC1B,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,MAAM,GAAG,CAAC,IAAI,MAAM,GAAG,MAAM,EAAE,CAAC;QAC7D,OAAO,2BAA2B,CAAA;IACtC,CAAC;IACD,OAAO,MAAM,CAAA;AACjB,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,qBAAqB,CACjC,MAA0B,EAC1B,kBAA0B,EAC1B,eAAe,GAAG,KAAK;IAEvB,IAAI,CAAC,eAAe,IAAI,MAAM,CAAC,OAAO,KAAK,cAAc,EAAE,CAAC;QACxD,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC,CAAA;IAC9C,CAAC;IACD,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;QACjB,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC,OAAO,CAAC,CAAA;IAC3D,CAAC;AACL,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,OAAgB;IAChD,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,WAAW,CAAC,uBAAuB,CAAC,CAAA;IAEzE,MAAM,QAAQ,GAAG,yBAAyB,EAAE,CAAA;IAC5C,MAAM,KAAK,GAAsB,uBAAuB,EAAE,CAAA;IAE1D,kBAAkB,CAAC,IAAI,EAAE;QACrB,QAAQ;QACR,KAAK;QACL,aAAa,EAAE,4BAA4B,EAAE;QAC7C,aAAa,EAAE,GAAG,EAAE,CAAC,EAAE;QACvB,aAAa;QACb,WAAW;QACX,SAAS,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE;YACvB,MAAM,eAAe,GAAG,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,MAAM,CAAA;YAChD,IAAI,CAAC,eAAe,EAAE,CAAC;gBACnB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,oBAAoB,OAAO,CAAC,QAAQ,OAAO,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC,CAAA;YACxF,CAAC;YACD,MAAM,MAAM,GAAG,KAAK,CAAC,oBAAoB,EAAE,CAAA;YAC3C,IAAI,MAAM,EAAE,CAAC;gBACT,qBAAqB,CACjB,MAAM,EACN,wDAAwD,EACxD,eAAe,CAClB,CAAA;YACL,CAAC;QACL,CAAC;KACJ,CAAC;SACG,WAAW,CAAC,iDAAiD,CAAC;SAC9D,MAAM,CACH,kBAAkB,EAClB,kEAAkE,CACrE;SACA,MAAM,CACH,wBAAwB,EACxB,iEAAiE,CACpE,CAAA;IAEL,0EAA0E;IAC1E,kEAAkE;IAClE,kDAAkD;IAClD,IAAI,UAAU,GAAsB,IAAI,CAAA;IAExC,mBAAmB,CAAiB,IAAI,EAAE;QACtC,KAAK;QACL,WAAW,EAAE,mCAAmC;QAChD,KAAK,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE;YAC9B,IAAI,CAAC;gBACD,MAAM,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,MAAM,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;oBAC/C,UAAU,CACN,WAAW,EACX,EAAE,EACF,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,CACtC;oBACD,oBAAoB,EAAE;iBACzB,CAAC,CAAA;gBACF,UAAU,GAAG,EAAE,KAAK,EAAE,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,CAAA;gBAC/C,OAAO;oBACH,GAAG,OAAO;oBACV,EAAE,EAAE,IAAI,CAAC,IAAI,CAAC,EAAE;oBAChB,KAAK,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI;oBACrB,QAAQ,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI;iBAC3B,CAAA;YACL,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACX,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAA;gBACvD,IAAI,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,0BAA0B,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;oBACtE,MAAM,IAAI,QAAQ,CAAC,UAAU,EAAE,8CAA8C,EAAE;wBAC3E,wCAAwC;qBAC3C,CAAC,CAAA;gBACN,CAAC;gBACD,MAAM,GAAG,CAAA;YACb,CAAC;QACL,CAAC;QACD,UAAU,CAAC,EAAE,OAAO,EAAE;YAClB,IAAI,CAAC,UAAU;gBAAE,MAAM,IAAI,KAAK,CAAC,2CAA2C,CAAC,CAAA;YAC7E,OAAO;gBACH,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,gBAAgB;gBACnC,WAAW,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,IAAI,EAAE,CAAC,EAAE;gBAC/C,WAAW,OAAO,CAAC,KAAK,KAAK,UAAU,CAAC,KAAK,GAAG;gBAChD,eAAe,OAAO,CAAC,OAAO,EAAE;gBAChC,mBAAmB,UAAU,CAAC,MAAM,EAAE;aACzC,CAAA;QACL,CAAC;QACD,UAAU,CAAC,EAAE,OAAO,EAAE;YAClB,IAAI,CAAC,UAAU;gBAAE,MAAM,IAAI,KAAK,CAAC,2CAA2C,CAAC,CAAA;YAC7E,OAAO;gBACH,EAAE,EAAE,OAAO,CAAC,EAAE;gBACd,IAAI,EAAE,OAAO,CAAC,QAAQ;gBACtB,OAAO,EAAE,OAAO,CAAC,OAAO;gBACxB,MAAM,EAAE,UAAU,CAAC,MAAM;aAC5B,CAAA;QACL,CAAC;QACD,kBAAkB;YACd,MAAM,IAAI,QAAQ,CAAC,mBAAmB,EAAE,uCAAuC,CAAC,CAAA;QACpF,CAAC;KACJ,CAAC,CAAA;IAEF,mBAAmB,CAAiB,IAAI,EAAE;QACtC,KAAK;QACL,WAAW,EAAE,4BAA4B;QACzC,SAAS,CAAC,EAAE,IAAI,EAAE;YACd,MAAM,MAAM,GAAG,KAAK,CAAC,kBAAkB,EAAE,CAAA;YACzC,IAAI,CAAC,MAAM;gBAAE,OAAM;YACnB,qBAAqB,CACjB,MAAM,EACN,yDAAyD,EACzD,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,MAAM,CAC3B,CAAA;QACL,CAAC;KACJ,CAAC,CAAA;AACN,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Config } from './config.js';
|
|
2
|
+
/** OS keyring `service` identifier for every outline-cli secret. */
|
|
3
|
+
export declare const SECURE_STORE_SERVICE = "outline-cli";
|
|
4
|
+
/** Env var that short-circuits the token store with a manually-supplied token. */
|
|
5
|
+
export declare const TOKEN_ENV_VAR = "OUTLINE_API_TOKEN";
|
|
6
|
+
/**
|
|
7
|
+
* Legacy single-user keyring slot. Outline never wrote a token to the
|
|
8
|
+
* keyring before this migration, so there is no real legacy slot to read —
|
|
9
|
+
* `migrateLegacyAuth` still expects the option, and its best-effort delete
|
|
10
|
+
* after migration is a harmless no-op on an empty entry.
|
|
11
|
+
*/
|
|
12
|
+
export declare const LEGACY_KEYRING_ACCOUNT = "api-token";
|
|
13
|
+
/**
|
|
14
|
+
* `updateConfig` payload that wipes every v1 auth key. Used by both the
|
|
15
|
+
* one-shot migration `cleanupLegacyConfig` and the runtime
|
|
16
|
+
* `dischargeLegacyState` so both paths stay in lockstep.
|
|
17
|
+
*/
|
|
18
|
+
export declare const LEGACY_CLEAR_PAYLOAD: Partial<Config>;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/** OS keyring `service` identifier for every outline-cli secret. */
|
|
2
|
+
export const SECURE_STORE_SERVICE = 'outline-cli';
|
|
3
|
+
/** Env var that short-circuits the token store with a manually-supplied token. */
|
|
4
|
+
export const TOKEN_ENV_VAR = 'OUTLINE_API_TOKEN';
|
|
5
|
+
/**
|
|
6
|
+
* Legacy single-user keyring slot. Outline never wrote a token to the
|
|
7
|
+
* keyring before this migration, so there is no real legacy slot to read —
|
|
8
|
+
* `migrateLegacyAuth` still expects the option, and its best-effort delete
|
|
9
|
+
* after migration is a harmless no-op on an empty entry.
|
|
10
|
+
*/
|
|
11
|
+
export const LEGACY_KEYRING_ACCOUNT = 'api-token';
|
|
12
|
+
/**
|
|
13
|
+
* `updateConfig` payload that wipes every v1 auth key. Used by both the
|
|
14
|
+
* one-shot migration `cleanupLegacyConfig` and the runtime
|
|
15
|
+
* `dischargeLegacyState` so both paths stay in lockstep.
|
|
16
|
+
*/
|
|
17
|
+
export const LEGACY_CLEAR_PAYLOAD = {
|
|
18
|
+
api_token: undefined,
|
|
19
|
+
base_url: undefined,
|
|
20
|
+
oauth_client_id: undefined,
|
|
21
|
+
auth_user_id: undefined,
|
|
22
|
+
auth_user_name: undefined,
|
|
23
|
+
auth_team_name: undefined,
|
|
24
|
+
};
|
|
25
|
+
//# sourceMappingURL=auth-constants.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth-constants.js","sourceRoot":"","sources":["../../src/lib/auth-constants.ts"],"names":[],"mappings":"AAEA,oEAAoE;AACpE,MAAM,CAAC,MAAM,oBAAoB,GAAG,aAAa,CAAA;AAEjD,kFAAkF;AAClF,MAAM,CAAC,MAAM,aAAa,GAAG,mBAAmB,CAAA;AAEhD;;;;;GAKG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG,WAAW,CAAA;AAEjD;;;;GAIG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAoB;IACjD,SAAS,EAAE,SAAS;IACpB,QAAQ,EAAE,SAAS;IACnB,eAAe,EAAE,SAAS;IAC1B,YAAY,EAAE,SAAS;IACvB,cAAc,EAAE,SAAS;IACzB,cAAc,EAAE,SAAS;CAC5B,CAAA"}
|
|
@@ -1,10 +1,59 @@
|
|
|
1
|
-
import { type
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
1
|
+
import { type AccountRef, type AuthProvider, type KeyringTokenStore } from '@doist/cli-core/auth';
|
|
2
|
+
import { type OutlineAccount } from './outline-account.js';
|
|
3
|
+
export type { OutlineAccount } from './outline-account.js';
|
|
4
|
+
export type AuthInfoResponse = {
|
|
5
|
+
user: {
|
|
6
|
+
id: string;
|
|
7
|
+
name: string;
|
|
8
|
+
email: string;
|
|
9
|
+
};
|
|
10
|
+
team: {
|
|
11
|
+
name: string;
|
|
12
|
+
subdomain: string;
|
|
13
|
+
};
|
|
8
14
|
};
|
|
15
|
+
export type OutlineTokenStore = KeyringTokenStore<OutlineAccount>;
|
|
9
16
|
export declare function createOutlineAuthProvider(): AuthProvider<OutlineAccount>;
|
|
10
|
-
|
|
17
|
+
/**
|
|
18
|
+
* Accepts the Outline user UUID or display name. Id matches are
|
|
19
|
+
* case-sensitive (UUIDs are canonical); label matches are
|
|
20
|
+
* case-insensitive so users can pass the name they see in `auth status`.
|
|
21
|
+
*/
|
|
22
|
+
export declare function matchOutlineAccount(account: OutlineAccount, ref: AccountRef): boolean;
|
|
23
|
+
/**
|
|
24
|
+
* True when the v2 store is empty but a legacy v1 token snapshot is still
|
|
25
|
+
* the only thing keeping the CLI authenticated — typically because
|
|
26
|
+
* `migrateLegacyAuth` couldn't reach the Outline API to identify the
|
|
27
|
+
* account (`MigrateSkipReason: 'identify-failed'`) or the OS keyring is
|
|
28
|
+
* unreachable (`'legacy-keyring-unreachable'`, although outline has no
|
|
29
|
+
* v1 keyring slot to read from). Useful for downstream diagnostics.
|
|
30
|
+
*/
|
|
31
|
+
export declare function isLegacyAuthActive(): Promise<boolean>;
|
|
32
|
+
/**
|
|
33
|
+
* `OUTLINE_API_TOKEN` short-circuits `active()` only when no explicit ref
|
|
34
|
+
* is supplied — cli-core's `KeyringTokenStore` doesn't know about the env
|
|
35
|
+
* var, and an explicit ref means the caller targets a specific stored
|
|
36
|
+
* account.
|
|
37
|
+
*
|
|
38
|
+
* `ensureMigrated()` runs **before** every mutating v2 op so the post-op
|
|
39
|
+
* legacy discharge can't race a still-pending migration into re-grabbing
|
|
40
|
+
* the legacy `api_token` we just consumed. `set()` / `clear()` then
|
|
41
|
+
* discharge legacy state **after** the v2 op succeeds. `set()` swallows
|
|
42
|
+
* the cleanup failure (v2 wins in `active()` regardless); `clear()`
|
|
43
|
+
* propagates it so a failed logout fails loudly instead of leaving the
|
|
44
|
+
* user authenticated via the legacy fallback.
|
|
45
|
+
*/
|
|
46
|
+
export declare function createOutlineTokenStore(): OutlineTokenStore;
|
|
47
|
+
/**
|
|
48
|
+
* Where the currently-active token lives. Mirrors `active()`'s resolution
|
|
49
|
+
* order — env → v2 record → legacy plaintext — so the answer can never
|
|
50
|
+
* contradict the token the runtime is actually using.
|
|
51
|
+
*
|
|
52
|
+
* The precedence cascade is intentionally duplicated with `active()`:
|
|
53
|
+
* the only true dedupe options either (a) add an extra config read on
|
|
54
|
+
* every `apiRequest` call (regressing the request hot path) or (b)
|
|
55
|
+
* augment cli-core's `TokenStore` contract. The drift the duplication
|
|
56
|
+
* invites is guarded by the `getActiveTokenSource` regression test that
|
|
57
|
+
* asserts v2-record presence wins over a lingering v1 `api_token`.
|
|
58
|
+
*/
|
|
59
|
+
export declare function getActiveTokenSource(): Promise<'env' | 'secure-store' | 'config-file'>;
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { createInterface } from 'node:readline/promises';
|
|
2
|
-
import { deriveChallenge, generateVerifier, } from '@doist/cli-core/auth';
|
|
2
|
+
import { createKeyringTokenStore, deriveChallenge, generateVerifier, } from '@doist/cli-core/auth';
|
|
3
3
|
import { fetchWithRetry } from '../transport/fetch-with-retry.js';
|
|
4
4
|
import { apiRequest } from './api.js';
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
|
|
5
|
+
import { LEGACY_CLEAR_PAYLOAD, SECURE_STORE_SERVICE, TOKEN_ENV_VAR } from './auth-constants.js';
|
|
6
|
+
import { getBaseUrl, getOAuthClientId } from './auth.js';
|
|
7
|
+
import { getConfig, getConfigPath, updateConfig } from './config.js';
|
|
8
|
+
import { runMigrateLegacyAuth } from './migrate-auth.js';
|
|
9
|
+
import { makeOutlineAccount } from './outline-account.js';
|
|
10
|
+
import { createOutlineUserRecordStore, getDefaultUserRecord } from './user-records.js';
|
|
9
11
|
function asHandshake(value) {
|
|
10
12
|
return value;
|
|
11
13
|
}
|
|
@@ -100,99 +102,198 @@ export function createOutlineAuthProvider() {
|
|
|
100
102
|
token,
|
|
101
103
|
baseUrl: hs.baseUrl,
|
|
102
104
|
});
|
|
103
|
-
return {
|
|
105
|
+
return makeOutlineAccount({
|
|
104
106
|
id: data.user.id,
|
|
105
107
|
label: data.user.name,
|
|
106
108
|
baseUrl: hs.baseUrl,
|
|
107
109
|
oauthClientId: hs.clientId,
|
|
108
110
|
teamName: data.team.name,
|
|
109
|
-
};
|
|
111
|
+
});
|
|
110
112
|
},
|
|
111
113
|
};
|
|
112
114
|
}
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
115
|
+
/**
|
|
116
|
+
* Accepts the Outline user UUID or display name. Id matches are
|
|
117
|
+
* case-sensitive (UUIDs are canonical); label matches are
|
|
118
|
+
* case-insensitive so users can pass the name they see in `auth status`.
|
|
119
|
+
*/
|
|
120
|
+
export function matchOutlineAccount(account, ref) {
|
|
121
|
+
if (account.id === ref)
|
|
122
|
+
return true;
|
|
123
|
+
return account.label.toLowerCase() === ref.toLowerCase();
|
|
124
|
+
}
|
|
125
|
+
/** True when the v2 store is the authoritative source. */
|
|
126
|
+
function migrationIsConclusive(result) {
|
|
127
|
+
return (result.status === 'migrated' ||
|
|
128
|
+
result.status === 'already-migrated' ||
|
|
129
|
+
result.status === 'no-legacy-state');
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Synthesise a snapshot from v1 plaintext state still on disk. Fallback for
|
|
133
|
+
* when migration can't complete (offline `identifyAccount`, WSL
|
|
134
|
+
* `legacy-keyring-unreachable`). Returns `null` when no legacy token is
|
|
135
|
+
* present. The synthesised account uses whatever identity fields the v1
|
|
136
|
+
* config has — for pre-#71 configs the id/label may be empty, but the
|
|
137
|
+
* runtime never renders those (status's `fetchLive` re-derives from the
|
|
138
|
+
* API), so empty placeholders are safe.
|
|
139
|
+
*/
|
|
140
|
+
async function readLegacyTokenSnapshot() {
|
|
141
|
+
const config = await getConfig();
|
|
142
|
+
const token = config.api_token?.trim() || null;
|
|
143
|
+
if (!token)
|
|
144
|
+
return null;
|
|
145
|
+
return {
|
|
146
|
+
token,
|
|
147
|
+
account: makeOutlineAccount({
|
|
148
|
+
id: config.auth_user_id ?? '',
|
|
149
|
+
label: config.auth_user_name ?? '',
|
|
150
|
+
baseUrl: config.base_url,
|
|
151
|
+
oauthClientId: config.oauth_client_id,
|
|
152
|
+
teamName: config.auth_team_name,
|
|
153
|
+
}),
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Discharge v1 plaintext state. Runs **after** a successful v2 write/clear
|
|
158
|
+
* — never before — so a v2-op failure doesn't strand the user with no
|
|
159
|
+
* recoverable credentials. Caller decides whether to swallow or propagate
|
|
160
|
+
* the `updateConfig` failure:
|
|
161
|
+
* - `set()` swallows (v2 record will win in `active()` regardless).
|
|
162
|
+
* - `clear()` propagates (v2 is empty, so a stale legacy token would
|
|
163
|
+
* shadow the logout via the fallback).
|
|
164
|
+
*/
|
|
165
|
+
async function dischargeLegacyState() {
|
|
166
|
+
await updateConfig(LEGACY_CLEAR_PAYLOAD);
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Memoised one-shot migration trigger. Resolves with `null` on rejection so
|
|
170
|
+
* the CLI never fails to start because of a migration error — the legacy
|
|
171
|
+
* snapshot fallback handles that case. Tests reset the memo with
|
|
172
|
+
* `vi.resetModules()` + a dynamic re-import.
|
|
173
|
+
*/
|
|
174
|
+
let migrationPromise;
|
|
175
|
+
function ensureMigrated() {
|
|
176
|
+
if (!migrationPromise) {
|
|
177
|
+
migrationPromise = runMigrateLegacyAuth({ silent: true }).catch(() => null);
|
|
149
178
|
}
|
|
150
|
-
|
|
151
|
-
|
|
179
|
+
return migrationPromise;
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* True when the v2 store is empty but a legacy v1 token snapshot is still
|
|
183
|
+
* the only thing keeping the CLI authenticated — typically because
|
|
184
|
+
* `migrateLegacyAuth` couldn't reach the Outline API to identify the
|
|
185
|
+
* account (`MigrateSkipReason: 'identify-failed'`) or the OS keyring is
|
|
186
|
+
* unreachable (`'legacy-keyring-unreachable'`, although outline has no
|
|
187
|
+
* v1 keyring slot to read from). Useful for downstream diagnostics.
|
|
188
|
+
*/
|
|
189
|
+
export async function isLegacyAuthActive() {
|
|
190
|
+
const result = await ensureMigrated();
|
|
191
|
+
if (result !== null && migrationIsConclusive(result))
|
|
192
|
+
return false;
|
|
193
|
+
const legacy = await readLegacyTokenSnapshot();
|
|
194
|
+
return legacy !== null;
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* `OUTLINE_API_TOKEN` short-circuits `active()` only when no explicit ref
|
|
198
|
+
* is supplied — cli-core's `KeyringTokenStore` doesn't know about the env
|
|
199
|
+
* var, and an explicit ref means the caller targets a specific stored
|
|
200
|
+
* account.
|
|
201
|
+
*
|
|
202
|
+
* `ensureMigrated()` runs **before** every mutating v2 op so the post-op
|
|
203
|
+
* legacy discharge can't race a still-pending migration into re-grabbing
|
|
204
|
+
* the legacy `api_token` we just consumed. `set()` / `clear()` then
|
|
205
|
+
* discharge legacy state **after** the v2 op succeeds. `set()` swallows
|
|
206
|
+
* the cleanup failure (v2 wins in `active()` regardless); `clear()`
|
|
207
|
+
* propagates it so a failed logout fails loudly instead of leaving the
|
|
208
|
+
* user authenticated via the legacy fallback.
|
|
209
|
+
*/
|
|
210
|
+
export function createOutlineTokenStore() {
|
|
211
|
+
const inner = createKeyringTokenStore({
|
|
212
|
+
serviceName: SECURE_STORE_SERVICE,
|
|
213
|
+
userRecords: createOutlineUserRecordStore(),
|
|
214
|
+
recordsLocation: getConfigPath(),
|
|
215
|
+
matchAccount: matchOutlineAccount,
|
|
216
|
+
});
|
|
217
|
+
async function migrationIsInconclusive() {
|
|
218
|
+
const result = await ensureMigrated(); // memoised
|
|
219
|
+
return result === null || !migrationIsConclusive(result);
|
|
152
220
|
}
|
|
153
221
|
return {
|
|
154
222
|
async active(ref) {
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
223
|
+
if (ref === undefined) {
|
|
224
|
+
const envToken = process.env[TOKEN_ENV_VAR]?.trim();
|
|
225
|
+
if (envToken) {
|
|
226
|
+
return {
|
|
227
|
+
token: envToken,
|
|
228
|
+
account: makeOutlineAccount({
|
|
229
|
+
id: '',
|
|
230
|
+
label: '',
|
|
231
|
+
baseUrl: await getBaseUrl(),
|
|
232
|
+
}),
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
await ensureMigrated();
|
|
237
|
+
const fromStore = await inner.active(ref);
|
|
238
|
+
if (fromStore)
|
|
239
|
+
return fromStore;
|
|
240
|
+
const legacy = await readLegacyTokenSnapshot();
|
|
241
|
+
if (legacy && (ref === undefined || matchOutlineAccount(legacy.account, ref))) {
|
|
242
|
+
return legacy;
|
|
243
|
+
}
|
|
244
|
+
return null;
|
|
161
245
|
},
|
|
162
246
|
async set(account, token) {
|
|
163
|
-
await
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
});
|
|
247
|
+
await ensureMigrated();
|
|
248
|
+
await inner.set(account, token);
|
|
249
|
+
if (await migrationIsInconclusive()) {
|
|
250
|
+
// Best-effort: a lingering `api_token` is dormant because
|
|
251
|
+
// `active()` reads v2 first.
|
|
252
|
+
await dischargeLegacyState().catch(() => undefined);
|
|
253
|
+
}
|
|
171
254
|
},
|
|
172
255
|
async clear(ref) {
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
if (ref !== undefined) {
|
|
180
|
-
const snapshot = deriveSnapshot(config);
|
|
181
|
-
if (!snapshot || !matchesRef(snapshot.account, ref))
|
|
182
|
-
throw refMismatch(ref);
|
|
256
|
+
await ensureMigrated();
|
|
257
|
+
await inner.clear(ref);
|
|
258
|
+
if (await migrationIsInconclusive()) {
|
|
259
|
+
// Must succeed: v2 is now empty, so a surviving legacy
|
|
260
|
+
// token would shadow the logout via the fallback.
|
|
261
|
+
await dischargeLegacyState();
|
|
183
262
|
}
|
|
184
|
-
await clearConfig(config);
|
|
185
263
|
},
|
|
186
264
|
async list() {
|
|
187
|
-
|
|
188
|
-
return
|
|
265
|
+
await ensureMigrated();
|
|
266
|
+
return inner.list();
|
|
189
267
|
},
|
|
190
268
|
async setDefault(ref) {
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
throw refMismatch(ref);
|
|
194
|
-
// Single-user store — already the default once `ref` matches.
|
|
269
|
+
await ensureMigrated();
|
|
270
|
+
return inner.setDefault(ref);
|
|
195
271
|
},
|
|
272
|
+
getLastStorageResult: () => inner.getLastStorageResult(),
|
|
273
|
+
getLastClearResult: () => inner.getLastClearResult(),
|
|
196
274
|
};
|
|
197
275
|
}
|
|
276
|
+
/**
|
|
277
|
+
* Where the currently-active token lives. Mirrors `active()`'s resolution
|
|
278
|
+
* order — env → v2 record → legacy plaintext — so the answer can never
|
|
279
|
+
* contradict the token the runtime is actually using.
|
|
280
|
+
*
|
|
281
|
+
* The precedence cascade is intentionally duplicated with `active()`:
|
|
282
|
+
* the only true dedupe options either (a) add an extra config read on
|
|
283
|
+
* every `apiRequest` call (regressing the request hot path) or (b)
|
|
284
|
+
* augment cli-core's `TokenStore` contract. The drift the duplication
|
|
285
|
+
* invites is guarded by the `getActiveTokenSource` regression test that
|
|
286
|
+
* asserts v2-record presence wins over a lingering v1 `api_token`.
|
|
287
|
+
*/
|
|
288
|
+
export async function getActiveTokenSource() {
|
|
289
|
+
if (process.env[TOKEN_ENV_VAR]?.trim())
|
|
290
|
+
return 'env';
|
|
291
|
+
const config = await getConfig();
|
|
292
|
+
const record = getDefaultUserRecord(config);
|
|
293
|
+
if (record)
|
|
294
|
+
return record.fallbackToken ? 'config-file' : 'secure-store';
|
|
295
|
+
if (config.api_token?.trim())
|
|
296
|
+
return 'config-file';
|
|
297
|
+
return 'secure-store';
|
|
298
|
+
}
|
|
198
299
|
//# sourceMappingURL=auth-provider.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth-provider.js","sourceRoot":"","sources":["../../src/lib/auth-provider.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAA;AACxD,OAAO,
|
|
1
|
+
{"version":3,"file":"auth-provider.js","sourceRoot":"","sources":["../../src/lib/auth-provider.ts"],"names":[],"mappings":"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"}
|
package/dist/lib/auth.d.ts
CHANGED
|
@@ -1,15 +1,28 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { SecureStoreUnavailableError } from '@doist/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
|
+
export { SecureStoreUnavailableError, getActiveTokenSource, TOKEN_ENV_VAR };
|
|
6
|
+
export declare class NoTokenError extends CliError {
|
|
7
|
+
constructor();
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Read the active token. Hot path: when `OUTLINE_API_TOKEN` is set we
|
|
11
|
+
* return it directly without consulting the token store, since
|
|
12
|
+
* `apiRequest` already resolves the base URL separately — going through
|
|
13
|
+
* `store.active()` here would trigger a redundant `getBaseUrl()` lookup
|
|
14
|
+
* per request just to synthesise an account we don't need.
|
|
15
|
+
*/
|
|
2
16
|
export declare function getApiToken(): Promise<string>;
|
|
17
|
+
/**
|
|
18
|
+
* Base URL cascade: env var → default user record (v2) → legacy
|
|
19
|
+
* `base_url` config (v1) → built-in default. The record takes priority
|
|
20
|
+
* over the legacy slot so post-migration logins keep defaulting to the
|
|
21
|
+
* same Outline instance.
|
|
22
|
+
*/
|
|
3
23
|
export declare function getBaseUrl(): Promise<string>;
|
|
4
|
-
export declare function getOAuthClientId(): Promise<string | undefined>;
|
|
5
|
-
export declare function getTokenSource(): Promise<'env' | 'config' | null>;
|
|
6
24
|
/**
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* would silently reset the user's update-channel preference too.
|
|
10
|
-
*
|
|
11
|
-
* Pass `existing` to skip the read when the caller has already loaded the
|
|
12
|
-
* config (e.g. ref-validated logout) — keeps that flow at one read + one
|
|
13
|
-
* write instead of two reads.
|
|
25
|
+
* OAuth client id cascade: env var → default user record (v2) → legacy
|
|
26
|
+
* `oauth_client_id` config (v1) → undefined (caller prompts).
|
|
14
27
|
*/
|
|
15
|
-
export declare function
|
|
28
|
+
export declare function getOAuthClientId(): Promise<string | undefined>;
|
package/dist/lib/auth.js
CHANGED
|
@@ -1,56 +1,74 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import { SecureStoreUnavailableError } from '@doist/cli-core/auth';
|
|
2
|
+
import { TOKEN_ENV_VAR } from './auth-constants.js';
|
|
3
|
+
import { createOutlineTokenStore, getActiveTokenSource, } from './auth-provider.js';
|
|
4
|
+
import { getConfig } from './config.js';
|
|
5
|
+
import { CliError } from './errors.js';
|
|
6
|
+
import { DEFAULT_BASE_URL } from './outline-account.js';
|
|
7
|
+
import { getDefaultUserRecord } from './user-records.js';
|
|
8
|
+
export { SecureStoreUnavailableError, getActiveTokenSource, TOKEN_ENV_VAR };
|
|
9
|
+
export class NoTokenError extends CliError {
|
|
10
|
+
constructor() {
|
|
11
|
+
super('NO_TOKEN', `No API token found. Set ${TOKEN_ENV_VAR} env var or run: ol auth login`, [`Set ${TOKEN_ENV_VAR} or run: ol auth login`], 'info');
|
|
12
|
+
this.name = 'NoTokenError';
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Module-level token-store singleton. Built lazily on first call; reused
|
|
17
|
+
* across every `apiRequest` so the request hot path doesn't reconstruct
|
|
18
|
+
* the keyring + user-record adapters per POST.
|
|
19
|
+
*/
|
|
20
|
+
let storeSingleton;
|
|
21
|
+
function tokenStore() {
|
|
22
|
+
if (!storeSingleton)
|
|
23
|
+
storeSingleton = createOutlineTokenStore();
|
|
24
|
+
return storeSingleton;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Read the active token. Hot path: when `OUTLINE_API_TOKEN` is set we
|
|
28
|
+
* return it directly without consulting the token store, since
|
|
29
|
+
* `apiRequest` already resolves the base URL separately — going through
|
|
30
|
+
* `store.active()` here would trigger a redundant `getBaseUrl()` lookup
|
|
31
|
+
* per request just to synthesise an account we don't need.
|
|
32
|
+
*/
|
|
3
33
|
export async function getApiToken() {
|
|
4
|
-
const envToken = process.env
|
|
34
|
+
const envToken = process.env[TOKEN_ENV_VAR]?.trim();
|
|
5
35
|
if (envToken)
|
|
6
36
|
return envToken;
|
|
7
|
-
const
|
|
8
|
-
if (
|
|
9
|
-
|
|
10
|
-
|
|
37
|
+
const snapshot = await tokenStore().active();
|
|
38
|
+
if (!snapshot?.token)
|
|
39
|
+
throw new NoTokenError();
|
|
40
|
+
return snapshot.token;
|
|
11
41
|
}
|
|
42
|
+
/**
|
|
43
|
+
* Base URL cascade: env var → default user record (v2) → legacy
|
|
44
|
+
* `base_url` config (v1) → built-in default. The record takes priority
|
|
45
|
+
* over the legacy slot so post-migration logins keep defaulting to the
|
|
46
|
+
* same Outline instance.
|
|
47
|
+
*/
|
|
12
48
|
export async function getBaseUrl() {
|
|
13
49
|
const envUrl = process.env.OUTLINE_URL;
|
|
14
50
|
if (envUrl)
|
|
15
51
|
return envUrl.replace(/\/$/, '');
|
|
16
52
|
const config = await getConfig();
|
|
53
|
+
const record = getDefaultUserRecord(config);
|
|
54
|
+
if (record?.account.baseUrl)
|
|
55
|
+
return record.account.baseUrl.replace(/\/$/, '');
|
|
17
56
|
if (config.base_url)
|
|
18
57
|
return config.base_url.replace(/\/$/, '');
|
|
19
58
|
return DEFAULT_BASE_URL;
|
|
20
59
|
}
|
|
60
|
+
/**
|
|
61
|
+
* OAuth client id cascade: env var → default user record (v2) → legacy
|
|
62
|
+
* `oauth_client_id` config (v1) → undefined (caller prompts).
|
|
63
|
+
*/
|
|
21
64
|
export async function getOAuthClientId() {
|
|
22
65
|
const envClientId = process.env.OUTLINE_OAUTH_CLIENT_ID;
|
|
23
66
|
if (envClientId)
|
|
24
67
|
return envClientId;
|
|
25
68
|
const config = await getConfig();
|
|
69
|
+
const record = getDefaultUserRecord(config);
|
|
70
|
+
if (record?.account.oauthClientId)
|
|
71
|
+
return record.account.oauthClientId;
|
|
26
72
|
return config.oauth_client_id;
|
|
27
73
|
}
|
|
28
|
-
export async function getTokenSource() {
|
|
29
|
-
if (process.env.OUTLINE_API_TOKEN)
|
|
30
|
-
return 'env';
|
|
31
|
-
const config = await getConfig();
|
|
32
|
-
if (config.api_token)
|
|
33
|
-
return 'config';
|
|
34
|
-
return null;
|
|
35
|
-
}
|
|
36
|
-
/**
|
|
37
|
-
* Clear the auth-related keys without deleting the file. The config is now
|
|
38
|
-
* shared with non-auth settings (notably `update_channel`); a blanket unlink
|
|
39
|
-
* would silently reset the user's update-channel preference too.
|
|
40
|
-
*
|
|
41
|
-
* Pass `existing` to skip the read when the caller has already loaded the
|
|
42
|
-
* config (e.g. ref-validated logout) — keeps that flow at one read + one
|
|
43
|
-
* write instead of two reads.
|
|
44
|
-
*/
|
|
45
|
-
export async function clearConfig(existing) {
|
|
46
|
-
const config = existing ?? (await getConfig());
|
|
47
|
-
const { api_token, base_url, oauth_client_id, auth_user_id, auth_user_name, auth_team_name, ...rest } = config;
|
|
48
|
-
void api_token;
|
|
49
|
-
void base_url;
|
|
50
|
-
void oauth_client_id;
|
|
51
|
-
void auth_user_id;
|
|
52
|
-
void auth_user_name;
|
|
53
|
-
void auth_team_name;
|
|
54
|
-
await setConfig(rest);
|
|
55
|
-
}
|
|
56
74
|
//# sourceMappingURL=auth.js.map
|
package/dist/lib/auth.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/lib/auth.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/lib/auth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,2BAA2B,EAAE,MAAM,sBAAsB,CAAA;AAClE,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AACnD,OAAO,EACH,uBAAuB,EACvB,oBAAoB,GAEvB,MAAM,oBAAoB,CAAA;AAC3B,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AACvC,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAA;AACtC,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAA;AACvD,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAA;AAExD,OAAO,EAAE,2BAA2B,EAAE,oBAAoB,EAAE,aAAa,EAAE,CAAA;AAE3E,MAAM,OAAO,YAAa,SAAQ,QAAQ;IACtC;QACI,KAAK,CACD,UAAU,EACV,2BAA2B,aAAa,gCAAgC,EACxE,CAAC,OAAO,aAAa,wBAAwB,CAAC,EAC9C,MAAM,CACT,CAAA;QACD,IAAI,CAAC,IAAI,GAAG,cAAc,CAAA;IAC9B,CAAC;CACJ;AAED;;;;GAIG;AACH,IAAI,cAA6C,CAAA;AACjD,SAAS,UAAU;IACf,IAAI,CAAC,cAAc;QAAE,cAAc,GAAG,uBAAuB,EAAE,CAAA;IAC/D,OAAO,cAAc,CAAA;AACzB,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW;IAC7B,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,EAAE,IAAI,EAAE,CAAA;IACnD,IAAI,QAAQ;QAAE,OAAO,QAAQ,CAAA;IAC7B,MAAM,QAAQ,GAAG,MAAM,UAAU,EAAE,CAAC,MAAM,EAAE,CAAA;IAC5C,IAAI,CAAC,QAAQ,EAAE,KAAK;QAAE,MAAM,IAAI,YAAY,EAAE,CAAA;IAC9C,OAAO,QAAQ,CAAC,KAAK,CAAA;AACzB,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU;IAC5B,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,WAAW,CAAA;IACtC,IAAI,MAAM;QAAE,OAAO,MAAM,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAA;IAE5C,MAAM,MAAM,GAAG,MAAM,SAAS,EAAE,CAAA;IAChC,MAAM,MAAM,GAAG,oBAAoB,CAAC,MAAM,CAAC,CAAA;IAC3C,IAAI,MAAM,EAAE,OAAO,CAAC,OAAO;QAAE,OAAO,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAA;IAC7E,IAAI,MAAM,CAAC,QAAQ;QAAE,OAAO,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAA;IAC9D,OAAO,gBAAgB,CAAA;AAC3B,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB;IAClC,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,CAAC,uBAAuB,CAAA;IACvD,IAAI,WAAW;QAAE,OAAO,WAAW,CAAA;IAEnC,MAAM,MAAM,GAAG,MAAM,SAAS,EAAE,CAAA;IAChC,MAAM,MAAM,GAAG,oBAAoB,CAAC,MAAM,CAAC,CAAA;IAC3C,IAAI,MAAM,EAAE,OAAO,CAAC,aAAa;QAAE,OAAO,MAAM,CAAC,OAAO,CAAC,aAAa,CAAA;IACtE,OAAO,MAAM,CAAC,eAAe,CAAA;AACjC,CAAC"}
|
package/dist/lib/config.d.ts
CHANGED
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
import { type CoreConfig } from '@doist/cli-core';
|
|
2
|
+
/**
|
|
3
|
+
* One row of the `users[]` array. `id` is the Outline user UUID. `token` is
|
|
4
|
+
* a plaintext fallback persisted only when the OS keyring is unavailable at
|
|
5
|
+
* write time (WSL, headless Linux, missing native binary).
|
|
6
|
+
*/
|
|
7
|
+
export type StoredUser = {
|
|
8
|
+
id: string;
|
|
9
|
+
name: string;
|
|
10
|
+
base_url?: string;
|
|
11
|
+
oauth_client_id?: string;
|
|
12
|
+
team_name?: string;
|
|
13
|
+
token?: string;
|
|
14
|
+
};
|
|
2
15
|
export type Config = CoreConfig & {
|
|
16
|
+
config_version?: number;
|
|
17
|
+
users?: StoredUser[];
|
|
18
|
+
default_user_id?: string;
|
|
19
|
+
/**
|
|
20
|
+
* Legacy v1 single-user fields. Read by the migration helper; removed
|
|
21
|
+
* from disk by `cleanupLegacyConfig` after the v2 record write succeeds.
|
|
22
|
+
*/
|
|
3
23
|
api_token?: string;
|
|
4
24
|
base_url?: string;
|
|
5
25
|
oauth_client_id?: string;
|
package/dist/lib/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/lib/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAEH,aAAa,IAAI,iBAAiB,EAClC,UAAU,IAAI,cAAc,EAC5B,YAAY,IAAI,gBAAgB,EAChC,WAAW,IAAI,eAAe,GACjC,MAAM,iBAAiB,CAAA;AAExB,MAAM,QAAQ,GAAG,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/lib/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAEH,aAAa,IAAI,iBAAiB,EAClC,UAAU,IAAI,cAAc,EAC5B,YAAY,IAAI,gBAAgB,EAChC,WAAW,IAAI,eAAe,GACjC,MAAM,iBAAiB,CAAA;AAExB,MAAM,QAAQ,GAAG,aAAa,CAAA;AAiC9B;;;;GAIG;AACH,MAAM,UAAU,aAAa;IACzB,OAAO,iBAAiB,CAAC,QAAQ,CAAC,CAAA;AACtC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,SAAS;IAC3B,OAAO,cAAc,CAAS,aAAa,EAAE,CAAC,CAAA;AAClD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,MAAc;IAC1C,OAAO,eAAe,CAAC,aAAa,EAAE,EAAE,MAAM,CAAC,CAAA;AACnD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,OAAwB;IACvD,OAAO,gBAAgB,CAAS,aAAa,EAAE,EAAE,OAAO,CAAC,CAAA;AAC7D,CAAC"}
|
package/dist/lib/errors.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { CliError as CoreCliError, type CliErrorCode, type ErrorType } from '@doist/cli-core';
|
|
2
2
|
export { CoreCliError as BaseCliError };
|
|
3
3
|
export type { ErrorType } from '@doist/cli-core';
|
|
4
|
-
export type ErrorCode = 'ACCOUNT_NOT_FOUND' | 'AUTH_VERIFICATION_FAILED' | 'CONFIRMATION_REQUIRED' | 'CONFLICTING_OPTIONS' | 'INVALID_PARENT' | 'MISSING_OPTION' | 'OAUTH_CALLBACK_PORT_INVALID' | 'OAUTH_CALLBACK_SERVER_FAILED' | 'OAUTH_CLIENT_ID_REQUIRED' | 'OAUTH_LOGIN_FAILED' | 'UNKNOWN_AGENT';
|
|
4
|
+
export type ErrorCode = 'ACCOUNT_NOT_FOUND' | 'AUTH_VERIFICATION_FAILED' | 'CONFIRMATION_REQUIRED' | 'CONFLICTING_OPTIONS' | 'INVALID_PARENT' | 'MISSING_OPTION' | 'NO_TOKEN' | 'OAUTH_CALLBACK_PORT_INVALID' | 'OAUTH_CALLBACK_SERVER_FAILED' | 'OAUTH_CLIENT_ID_REQUIRED' | 'OAUTH_LOGIN_FAILED' | 'UNKNOWN_AGENT';
|
|
5
5
|
export declare class CliError extends CoreCliError<ErrorCode> {
|
|
6
6
|
constructor(code: ErrorCode | CliErrorCode, message: string, hints?: string[], type?: ErrorType);
|
|
7
7
|
}
|
package/dist/lib/errors.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/lib/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,IAAI,YAAY,EAAqC,MAAM,iBAAiB,CAAA;AAE7F,OAAO,EAAE,YAAY,IAAI,YAAY,EAAE,CAAA;
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/lib/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,IAAI,YAAY,EAAqC,MAAM,iBAAiB,CAAA;AAE7F,OAAO,EAAE,YAAY,IAAI,YAAY,EAAE,CAAA;AAiBvC,MAAM,OAAO,QAAS,SAAQ,YAAuB;IACjD,YACI,IAA8B,EAC9B,OAAe,EACf,KAAgB,EAChB,OAAkB,OAAO;QAEzB,KAAK,CAAC,IAAI,EAAE,OAAO,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAA;IACzC,CAAC;CACJ;AAED,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type MigrateAuthResult } from '@doist/cli-core/auth';
|
|
2
|
+
import { type OutlineAccount } from './outline-account.js';
|
|
3
|
+
/**
|
|
4
|
+
* One-time migration of v1 plaintext auth state into the v2 `users[]`
|
|
5
|
+
* shape. Called lazily by `createOutlineTokenStore` on first store access.
|
|
6
|
+
* Idempotent via the `config_version` marker — once set to `>= 2`, this
|
|
7
|
+
* helper short-circuits with `already-migrated`.
|
|
8
|
+
*/
|
|
9
|
+
export declare function runMigrateLegacyAuth(options?: {
|
|
10
|
+
silent: boolean;
|
|
11
|
+
}): Promise<MigrateAuthResult<OutlineAccount>>;
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { migrateLegacyAuth } from '@doist/cli-core/auth';
|
|
2
|
+
import { fetchWithRetry } from '../transport/fetch-with-retry.js';
|
|
3
|
+
import { LEGACY_CLEAR_PAYLOAD, LEGACY_KEYRING_ACCOUNT, SECURE_STORE_SERVICE, } from './auth-constants.js';
|
|
4
|
+
import { getConfig, updateConfig } from './config.js';
|
|
5
|
+
import { DEFAULT_BASE_URL, makeOutlineAccount } from './outline-account.js';
|
|
6
|
+
import { createOutlineUserRecordStore } from './user-records.js';
|
|
7
|
+
/**
|
|
8
|
+
* Pinned to this migration's target schema. Decoupled from any future
|
|
9
|
+
* `config_version` bump so this helper doesn't re-run for users already
|
|
10
|
+
* on v2 or beyond.
|
|
11
|
+
*/
|
|
12
|
+
const V2_SCHEMA_VERSION = 2;
|
|
13
|
+
/**
|
|
14
|
+
* Cap migration's `auth.info` lookup so a stalled connection can't hang
|
|
15
|
+
* the CLI indefinitely on first command. cli-core treats the resulting
|
|
16
|
+
* timeout as `identify-failed`, falling back to the legacy snapshot.
|
|
17
|
+
*/
|
|
18
|
+
const IDENTIFY_TIMEOUT_MS = 10_000;
|
|
19
|
+
/**
|
|
20
|
+
* Direct `auth.info` call used only by the migration path. We bypass
|
|
21
|
+
* `apiRequest` (and its spinner + `getApiToken`/`getBaseUrl` cascade) so
|
|
22
|
+
* this module stays out of the runtime auth/token-store import graph and
|
|
23
|
+
* so the silent migration doesn't render a spinner during postinstall-style
|
|
24
|
+
* invocations.
|
|
25
|
+
*/
|
|
26
|
+
async function identifyOutlineAccount(token) {
|
|
27
|
+
const config = await getConfig();
|
|
28
|
+
const baseUrl = (config.base_url ?? DEFAULT_BASE_URL).replace(/\/$/, '');
|
|
29
|
+
const res = await fetchWithRetry({
|
|
30
|
+
url: `${baseUrl}/api/auth.info`,
|
|
31
|
+
options: {
|
|
32
|
+
method: 'POST',
|
|
33
|
+
headers: {
|
|
34
|
+
'Content-Type': 'application/json',
|
|
35
|
+
Authorization: `Bearer ${token}`,
|
|
36
|
+
},
|
|
37
|
+
body: JSON.stringify({}),
|
|
38
|
+
timeout: IDENTIFY_TIMEOUT_MS,
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
if (!res.ok) {
|
|
42
|
+
throw new Error(`auth.info failed: ${res.status} ${res.statusText}`);
|
|
43
|
+
}
|
|
44
|
+
const json = await res.json();
|
|
45
|
+
return makeOutlineAccount({
|
|
46
|
+
id: json.data.user.id,
|
|
47
|
+
label: json.data.user.name,
|
|
48
|
+
baseUrl,
|
|
49
|
+
oauthClientId: config.oauth_client_id ?? '',
|
|
50
|
+
teamName: json.data.team.name,
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* One-time migration of v1 plaintext auth state into the v2 `users[]`
|
|
55
|
+
* shape. Called lazily by `createOutlineTokenStore` on first store access.
|
|
56
|
+
* Idempotent via the `config_version` marker — once set to `>= 2`, this
|
|
57
|
+
* helper short-circuits with `already-migrated`.
|
|
58
|
+
*/
|
|
59
|
+
export async function runMigrateLegacyAuth(options = { silent: true }) {
|
|
60
|
+
return migrateLegacyAuth({
|
|
61
|
+
serviceName: SECURE_STORE_SERVICE,
|
|
62
|
+
legacyAccount: LEGACY_KEYRING_ACCOUNT,
|
|
63
|
+
userRecords: createOutlineUserRecordStore(),
|
|
64
|
+
hasMigrated: async () => {
|
|
65
|
+
const config = await getConfig();
|
|
66
|
+
// Two signals of "already on v2": the explicit marker, or
|
|
67
|
+
// the structural presence of `users[]` (a fresh install that
|
|
68
|
+
// logged in directly via cli-core writes records without ever
|
|
69
|
+
// setting `config_version`, so the marker alone would
|
|
70
|
+
// miscount that case as "needs migration").
|
|
71
|
+
if ((config.config_version ?? 0) >= V2_SCHEMA_VERSION)
|
|
72
|
+
return true;
|
|
73
|
+
if ((config.users ?? []).length > 0)
|
|
74
|
+
return true;
|
|
75
|
+
return false;
|
|
76
|
+
},
|
|
77
|
+
markMigrated: async () => {
|
|
78
|
+
await updateConfig({ config_version: V2_SCHEMA_VERSION });
|
|
79
|
+
},
|
|
80
|
+
loadLegacyPlaintextToken: async () => {
|
|
81
|
+
const config = await getConfig();
|
|
82
|
+
return config.api_token?.trim() || null;
|
|
83
|
+
},
|
|
84
|
+
identifyAccount: identifyOutlineAccount,
|
|
85
|
+
cleanupLegacyConfig: async () => {
|
|
86
|
+
await updateConfig(LEGACY_CLEAR_PAYLOAD);
|
|
87
|
+
},
|
|
88
|
+
silent: options.silent,
|
|
89
|
+
logPrefix: 'outline-cli',
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=migrate-auth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migrate-auth.js","sourceRoot":"","sources":["../../src/lib/migrate-auth.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { AuthAccount } from '@doist/cli-core/auth';
|
|
2
|
+
/**
|
|
3
|
+
* Narrow account shape persisted by the keyring-backed token store.
|
|
4
|
+
*
|
|
5
|
+
* `id` is the Outline user UUID; `label` is the user's display name.
|
|
6
|
+
* `baseUrl` and `oauthClientId` ride along on the account because Outline
|
|
7
|
+
* is self-hostable — every persisted token is tied to the instance it was
|
|
8
|
+
* issued for, not to a global default. `teamName` is rendered by
|
|
9
|
+
* `ol auth status`; it's the only field we cache for display.
|
|
10
|
+
*/
|
|
11
|
+
export type OutlineAccount = AuthAccount & {
|
|
12
|
+
id: string;
|
|
13
|
+
label: string;
|
|
14
|
+
baseUrl: string;
|
|
15
|
+
oauthClientId: string;
|
|
16
|
+
teamName?: string;
|
|
17
|
+
};
|
|
18
|
+
export declare const DEFAULT_BASE_URL = "https://app.getoutline.com";
|
|
19
|
+
/** Canonical `OutlineAccount` factory. Applies the `baseUrl` default. */
|
|
20
|
+
export declare function makeOutlineAccount(input: {
|
|
21
|
+
id: string;
|
|
22
|
+
label: string;
|
|
23
|
+
baseUrl?: string;
|
|
24
|
+
oauthClientId?: string;
|
|
25
|
+
teamName?: string;
|
|
26
|
+
}): OutlineAccount;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export const DEFAULT_BASE_URL = 'https://app.getoutline.com';
|
|
2
|
+
/** Canonical `OutlineAccount` factory. Applies the `baseUrl` default. */
|
|
3
|
+
export function makeOutlineAccount(input) {
|
|
4
|
+
return {
|
|
5
|
+
id: input.id,
|
|
6
|
+
label: input.label,
|
|
7
|
+
baseUrl: input.baseUrl ?? DEFAULT_BASE_URL,
|
|
8
|
+
oauthClientId: input.oauthClientId ?? '',
|
|
9
|
+
teamName: input.teamName,
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=outline-account.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"outline-account.js","sourceRoot":"","sources":["../../src/lib/outline-account.ts"],"names":[],"mappings":"AAmBA,MAAM,CAAC,MAAM,gBAAgB,GAAG,4BAA4B,CAAA;AAE5D,yEAAyE;AACzE,MAAM,UAAU,kBAAkB,CAAC,KAMlC;IACG,OAAO;QACH,EAAE,EAAE,KAAK,CAAC,EAAE;QACZ,KAAK,EAAE,KAAK,CAAC,KAAK;QAClB,OAAO,EAAE,KAAK,CAAC,OAAO,IAAI,gBAAgB;QAC1C,aAAa,EAAE,KAAK,CAAC,aAAa,IAAI,EAAE;QACxC,QAAQ,EAAE,KAAK,CAAC,QAAQ;KAC3B,CAAA;AACL,CAAC"}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
export declare const SKILL_NAME = "outline-cli";
|
|
2
2
|
export declare const SKILL_DESCRIPTION = "Search and manage Outline wiki documents and collections via the ol CLI";
|
|
3
|
-
export declare const SKILL_CONTENT = "# Outline CLI (ol)\n\nUse this skill when the user wants to interact with their Outline wiki/knowledge base.\n\n## Quick Reference\n\n- `ol search \"query\"` - Search documents\n- `ol doc list` - List documents\n- `ol doc get <id>` - Read a document\n- `ol doc open <id>` - Open document in browser\n- `ol doc create --title \"Title\" --collection <id>` - Create document\n- `ol col list` - List collections\n\n## Output Formats\n\nAll list commands support:\n- `--json` - JSON output (essential fields)\n- `--ndjson` - Newline-delimited JSON (streaming)\n- `--full` - Include all fields in JSON\n\n## Document References\n\nDocuments can be referenced by:\n- URL ID (the slug suffix after the last hyphen)\n- Full Outline URL (auto-extracted)\n- Document ID\n\n## Commands\n\n### Search\n```bash\nol search \"query\"\nol search \"query\" --limit 10\nol search \"query\" --collection <id>\nol search \"query\" --status published\nol search \"query\" --json\n```\n\n### Documents\n```bash\nol doc list --collection <id> --limit 25\nol doc list --sort title --direction ASC\nol doc get <id> # Rendered markdown\nol doc get <id> --raw # Raw markdown\nol doc get <id> --json\nol doc open <id> # Open in browser\nol doc create --title \"Title\" --collection <id> --text \"# Content\"\nol doc create --title \"Title\" --parent <ref> --text \"# Content\" # Nest under parent (collection inferred)\nol doc create --title \"Title\" --collection <id> --file ./doc.md\nol doc update <id> --title \"New Title\"\nol doc update <id> --file ./updated.md\nol doc move <id> --collection <target-id> # Move to collection root\nol doc move <id> --parent <ref> # Nest under parent (collection inferred)\nol doc archive <id>\nol doc unarchive <id>\nol doc delete <id> --confirm\n```\n\n### Collections\n```bash\nol col list\nol col get <id>\nol col create --name \"Name\" --description \"Desc\" --color \"#hex\"\nol col create --name \"Private\" --private\nol col update <id> --name \"New Name\"\nol col delete <id> --confirm\n```\n\n### Authentication\n```bash\nol auth login # OAuth login (opens browser); prompts for base URL + client ID if needed\nol auth login --base-url <url> # Specify Outline base URL for this login (saved for future use)\nol auth login --client-id <id> # Specify OAuth client ID for this login (saved for future use)\nol auth login --callback-port <port> # Override local OAuth callback port\nol auth login --read-only # Request read-only scopes (where supported by the Outline instance)\nol auth login --json | --ndjson # Machine-readable success envelope\nol auth status # Show current auth state\nol auth logout # Clear saved credentials\n```\n\n### Update & Changelog\n```bash\nol update # Update CLI to latest version\nol update --check # Check for updates without installing, show channel\nol update --check --json # Check for updates as a JSON envelope\nol update --check --ndjson # Check for updates as NDJSON output\nol update --channel # Show current update channel\nol update switch --stable # Switch to stable release channel\nol update switch --pre-release # Switch to pre-release (next) channel\nol changelog # Show recent changelog entries\nol changelog -n 3 # Show last 3 versions\n```\n\n## Examples\n\n### Find and read a document\n```bash\nol search \"onboarding\" --json | jq '.[0].document.urlId'\nol doc get <urlId>\n```\n\n### Create a document from a file\n```bash\nol doc create --title \"Meeting Notes\" --collection <id> --file ./notes.md --publish\n```\n\n### List all collections and their documents\n```bash\nol col list --json\nol doc list --collection <id> --sort title --direction ASC\n```\n\n### Bulk export with ndjson\n```bash\nol doc list --ndjson --full | jq -r '.title'\n```\n";
|
|
3
|
+
export declare const SKILL_CONTENT = "# Outline CLI (ol)\n\nUse this skill when the user wants to interact with their Outline wiki/knowledge base.\n\n## Quick Reference\n\n- `ol search \"query\"` - Search documents\n- `ol doc list` - List documents\n- `ol doc get <id>` - Read a document\n- `ol doc open <id>` - Open document in browser\n- `ol doc create --title \"Title\" --collection <id>` - Create document\n- `ol col list` - List collections\n\n## Output Formats\n\nAll list commands support:\n- `--json` - JSON output (essential fields)\n- `--ndjson` - Newline-delimited JSON (streaming)\n- `--full` - Include all fields in JSON\n\n## Document References\n\nDocuments can be referenced by:\n- URL ID (the slug suffix after the last hyphen)\n- Full Outline URL (auto-extracted)\n- Document ID\n\n## Commands\n\n### Search\n```bash\nol search \"query\"\nol search \"query\" --limit 10\nol search \"query\" --collection <id>\nol search \"query\" --status published\nol search \"query\" --json\n```\n\n### Documents\n```bash\nol doc list --collection <id> --limit 25\nol doc list --sort title --direction ASC\nol doc get <id> # Rendered markdown\nol doc get <id> --raw # Raw markdown\nol doc get <id> --json\nol doc open <id> # Open in browser\nol doc create --title \"Title\" --collection <id> --text \"# Content\"\nol doc create --title \"Title\" --parent <ref> --text \"# Content\" # Nest under parent (collection inferred)\nol doc create --title \"Title\" --collection <id> --file ./doc.md\nol doc update <id> --title \"New Title\"\nol doc update <id> --file ./updated.md\nol doc move <id> --collection <target-id> # Move to collection root\nol doc move <id> --parent <ref> # Nest under parent (collection inferred)\nol doc archive <id>\nol doc unarchive <id>\nol doc delete <id> --confirm\n```\n\n### Collections\n```bash\nol col list\nol col get <id>\nol col create --name \"Name\" --description \"Desc\" --color \"#hex\"\nol col create --name \"Private\" --private\nol col update <id> --name \"New Name\"\nol col delete <id> --confirm\n```\n\n### Authentication\n```bash\nol auth login # OAuth login (opens browser); prompts for base URL + client ID if needed\nol auth login --base-url <url> # Specify Outline base URL for this login (saved for future use)\nol auth login --client-id <id> # Specify OAuth client ID for this login (saved for future use)\nol auth login --callback-port <port> # Override local OAuth callback port\nol auth login --read-only # Request read-only scopes (where supported by the Outline instance)\nol auth login --json | --ndjson # Machine-readable success envelope\nol auth status # Show current auth state\nol auth status --json | --ndjson # Machine-readable status envelope ({id, team, baseUrl, source})\nol auth logout # Clear saved credentials\nol auth logout --json | --ndjson # Machine-readable logout envelope ({ok: true}; --ndjson is silent)\n```\n\n### Update & Changelog\n```bash\nol update # Update CLI to latest version\nol update --check # Check for updates without installing, show channel\nol update --check --json # Check for updates as a JSON envelope\nol update --check --ndjson # Check for updates as NDJSON output\nol update --channel # Show current update channel\nol update switch --stable # Switch to stable release channel\nol update switch --pre-release # Switch to pre-release (next) channel\nol changelog # Show recent changelog entries\nol changelog -n 3 # Show last 3 versions\n```\n\n## Examples\n\n### Find and read a document\n```bash\nol search \"onboarding\" --json | jq '.[0].document.urlId'\nol doc get <urlId>\n```\n\n### Create a document from a file\n```bash\nol doc create --title \"Meeting Notes\" --collection <id> --file ./notes.md --publish\n```\n\n### List all collections and their documents\n```bash\nol col list --json\nol doc list --collection <id> --sort title --direction ASC\n```\n\n### Bulk export with ndjson\n```bash\nol doc list --ndjson --full | jq -r '.title'\n```\n";
|
|
@@ -77,7 +77,9 @@ ol auth login --callback-port <port> # Override local OAuth callback port
|
|
|
77
77
|
ol auth login --read-only # Request read-only scopes (where supported by the Outline instance)
|
|
78
78
|
ol auth login --json | --ndjson # Machine-readable success envelope
|
|
79
79
|
ol auth status # Show current auth state
|
|
80
|
+
ol auth status --json | --ndjson # Machine-readable status envelope ({id, team, baseUrl, source})
|
|
80
81
|
ol auth logout # Clear saved credentials
|
|
82
|
+
ol auth logout --json | --ndjson # Machine-readable logout envelope ({ok: true}; --ndjson is silent)
|
|
81
83
|
\`\`\`
|
|
82
84
|
|
|
83
85
|
### Update & Changelog
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"content.js","sourceRoot":"","sources":["../../../src/lib/skills/content.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,UAAU,GAAG,aAAa,CAAA;AACvC,MAAM,CAAC,MAAM,iBAAiB,GAC1B,yEAAyE,CAAA;AAE7E,MAAM,CAAC,MAAM,aAAa,GAAG
|
|
1
|
+
{"version":3,"file":"content.js","sourceRoot":"","sources":["../../../src/lib/skills/content.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,UAAU,GAAG,aAAa,CAAA;AACvC,MAAM,CAAC,MAAM,iBAAiB,GAC1B,yEAAyE,CAAA;AAE7E,MAAM,CAAC,MAAM,aAAa,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAsH5B,CAAA"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { UserRecord, UserRecordStore } from '@doist/cli-core/auth';
|
|
2
|
+
import { type Config } from './config.js';
|
|
3
|
+
import { type OutlineAccount } from './outline-account.js';
|
|
4
|
+
/**
|
|
5
|
+
* `UserRecordStore` adapter over `config.users[]`. `StoredUser.token` is
|
|
6
|
+
* surfaced as `fallbackToken` — cli-core's name for the plaintext copy
|
|
7
|
+
* persisted only when the keyring is unreachable.
|
|
8
|
+
*/
|
|
9
|
+
export declare function createOutlineUserRecordStore(): UserRecordStore<OutlineAccount>;
|
|
10
|
+
/**
|
|
11
|
+
* Resolve the default-or-first `UserRecord` from an already-loaded config.
|
|
12
|
+
* Returns `null` when no users are stored.
|
|
13
|
+
*/
|
|
14
|
+
export declare function getDefaultUserRecord(config: Partial<Config>): UserRecord<OutlineAccount> | null;
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { getConfig, updateConfig } from './config.js';
|
|
2
|
+
import { makeOutlineAccount } from './outline-account.js';
|
|
3
|
+
/**
|
|
4
|
+
* `UserRecordStore` adapter over `config.users[]`. `StoredUser.token` is
|
|
5
|
+
* surfaced as `fallbackToken` — cli-core's name for the plaintext copy
|
|
6
|
+
* persisted only when the keyring is unreachable.
|
|
7
|
+
*/
|
|
8
|
+
export function createOutlineUserRecordStore() {
|
|
9
|
+
return {
|
|
10
|
+
async list() {
|
|
11
|
+
const config = await getConfig();
|
|
12
|
+
return (config.users ?? []).map(toRecord);
|
|
13
|
+
},
|
|
14
|
+
async upsert(record) {
|
|
15
|
+
const config = await getConfig();
|
|
16
|
+
const existing = config.users ?? [];
|
|
17
|
+
const next = fromRecord(record);
|
|
18
|
+
const index = existing.findIndex((u) => u.id === record.account.id);
|
|
19
|
+
const users = index >= 0
|
|
20
|
+
? [...existing.slice(0, index), next, ...existing.slice(index + 1)]
|
|
21
|
+
: [...existing, next];
|
|
22
|
+
await updateConfig({ users });
|
|
23
|
+
},
|
|
24
|
+
async remove(id) {
|
|
25
|
+
const config = await getConfig();
|
|
26
|
+
const existing = config.users ?? [];
|
|
27
|
+
const index = existing.findIndex((u) => u.id === id);
|
|
28
|
+
if (index < 0)
|
|
29
|
+
return;
|
|
30
|
+
const users = [...existing.slice(0, index), ...existing.slice(index + 1)];
|
|
31
|
+
const updates = { users };
|
|
32
|
+
if (config.default_user_id === id)
|
|
33
|
+
updates.default_user_id = undefined;
|
|
34
|
+
await updateConfig(updates);
|
|
35
|
+
},
|
|
36
|
+
async getDefaultId() {
|
|
37
|
+
const config = await getConfig();
|
|
38
|
+
return config.default_user_id ?? null;
|
|
39
|
+
},
|
|
40
|
+
async setDefaultId(id) {
|
|
41
|
+
await updateConfig({ default_user_id: id ?? undefined });
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Resolve the default-or-first `UserRecord` from an already-loaded config.
|
|
47
|
+
* Returns `null` when no users are stored.
|
|
48
|
+
*/
|
|
49
|
+
export function getDefaultUserRecord(config) {
|
|
50
|
+
const users = config.users ?? [];
|
|
51
|
+
if (users.length === 0)
|
|
52
|
+
return null;
|
|
53
|
+
const defaultId = config.default_user_id;
|
|
54
|
+
const user = (defaultId && users.find((u) => u.id === defaultId)) || users[0];
|
|
55
|
+
return toRecord(user);
|
|
56
|
+
}
|
|
57
|
+
function toRecord(user) {
|
|
58
|
+
const account = makeOutlineAccount({
|
|
59
|
+
id: user.id,
|
|
60
|
+
label: user.name,
|
|
61
|
+
baseUrl: user.base_url,
|
|
62
|
+
oauthClientId: user.oauth_client_id,
|
|
63
|
+
teamName: user.team_name,
|
|
64
|
+
});
|
|
65
|
+
const trimmed = user.token?.trim();
|
|
66
|
+
const record = { account };
|
|
67
|
+
if (trimmed)
|
|
68
|
+
record.fallbackToken = trimmed;
|
|
69
|
+
return record;
|
|
70
|
+
}
|
|
71
|
+
function fromRecord(record) {
|
|
72
|
+
// Replace, don't merge: an absent `fallbackToken` strips the plaintext
|
|
73
|
+
// slot so it can't shadow a fresh keyring-backed write. cli-core contract.
|
|
74
|
+
const trimmed = record.fallbackToken?.trim();
|
|
75
|
+
const next = {
|
|
76
|
+
id: record.account.id,
|
|
77
|
+
name: record.account.label,
|
|
78
|
+
};
|
|
79
|
+
if (record.account.baseUrl)
|
|
80
|
+
next.base_url = record.account.baseUrl;
|
|
81
|
+
if (record.account.oauthClientId)
|
|
82
|
+
next.oauth_client_id = record.account.oauthClientId;
|
|
83
|
+
if (record.account.teamName)
|
|
84
|
+
next.team_name = record.account.teamName;
|
|
85
|
+
if (trimmed && trimmed.length > 0)
|
|
86
|
+
next.token = trimmed;
|
|
87
|
+
return next;
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=user-records.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"user-records.js","sourceRoot":"","sources":["../../src/lib/user-records.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@doist/outline-cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.7.0",
|
|
4
4
|
"description": "CLI for the Outline wiki/knowledge base API",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
"node": ">=20.18.1"
|
|
49
49
|
},
|
|
50
50
|
"dependencies": {
|
|
51
|
-
"@doist/cli-core": "0.
|
|
51
|
+
"@doist/cli-core": "0.16.1",
|
|
52
52
|
"chalk": "5.6.2",
|
|
53
53
|
"commander": "14.0.2",
|
|
54
54
|
"marked": "18.0.3",
|