@doist/twist-cli 2.36.3 → 2.36.4
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/helpers.d.ts +8 -1
- package/dist/commands/auth/helpers.d.ts.map +1 -1
- package/dist/commands/auth/helpers.js +12 -7
- package/dist/commands/auth/helpers.js.map +1 -1
- package/dist/commands/auth/index.d.ts.map +1 -1
- package/dist/commands/auth/index.js +12 -12
- package/dist/commands/auth/index.js.map +1 -1
- package/dist/commands/auth/login.d.ts +3 -3
- package/dist/commands/auth/login.d.ts.map +1 -1
- package/dist/commands/auth/login.js +24 -62
- package/dist/commands/auth/login.js.map +1 -1
- package/dist/commands/auth/logout.d.ts +10 -1
- package/dist/commands/auth/logout.d.ts.map +1 -1
- package/dist/commands/auth/logout.js +18 -6
- package/dist/commands/auth/logout.js.map +1 -1
- package/dist/commands/auth/status.d.ts +15 -3
- package/dist/commands/auth/status.d.ts.map +1 -1
- package/dist/commands/auth/status.js +87 -22
- package/dist/commands/auth/status.js.map +1 -1
- package/dist/commands/changelog.d.ts +0 -5
- package/dist/commands/changelog.d.ts.map +1 -1
- package/dist/commands/changelog.js +9 -103
- package/dist/commands/changelog.js.map +1 -1
- package/dist/commands/channel/list.js +5 -5
- package/dist/commands/channel/list.js.map +1 -1
- package/dist/commands/config/view.d.ts.map +1 -1
- package/dist/commands/config/view.js +3 -3
- package/dist/commands/config/view.js.map +1 -1
- package/dist/commands/conversation/helpers.d.ts.map +1 -1
- package/dist/commands/conversation/helpers.js +4 -3
- package/dist/commands/conversation/helpers.js.map +1 -1
- package/dist/commands/conversation/unread.d.ts.map +1 -1
- package/dist/commands/conversation/unread.js +5 -4
- package/dist/commands/conversation/unread.js.map +1 -1
- package/dist/commands/conversation/view.d.ts.map +1 -1
- package/dist/commands/conversation/view.js +8 -6
- package/dist/commands/conversation/view.js.map +1 -1
- package/dist/commands/inbox.d.ts.map +1 -1
- package/dist/commands/inbox.js +9 -7
- package/dist/commands/inbox.js.map +1 -1
- package/dist/commands/thread/view.d.ts.map +1 -1
- package/dist/commands/thread/view.js +14 -12
- package/dist/commands/thread/view.js.map +1 -1
- package/dist/commands/update/index.d.ts.map +1 -1
- package/dist/commands/update/index.js +43 -14
- package/dist/commands/update/index.js.map +1 -1
- package/dist/index.js +27 -4
- package/dist/index.js.map +1 -1
- package/dist/lib/api.d.ts +16 -5
- package/dist/lib/api.d.ts.map +1 -1
- package/dist/lib/api.js +49 -3
- package/dist/lib/api.js.map +1 -1
- package/dist/lib/auth-pages.d.ts +3 -0
- package/dist/lib/auth-pages.d.ts.map +1 -0
- package/dist/lib/{oauth-server.js → auth-pages.js} +3 -120
- package/dist/lib/auth-pages.js.map +1 -0
- package/dist/lib/auth-provider.d.ts +28 -0
- package/dist/lib/auth-provider.d.ts.map +1 -0
- package/dist/lib/auth-provider.js +241 -0
- package/dist/lib/auth-provider.js.map +1 -0
- package/dist/lib/auth.d.ts +16 -10
- package/dist/lib/auth.d.ts.map +1 -1
- package/dist/lib/auth.js +18 -0
- package/dist/lib/auth.js.map +1 -1
- package/dist/lib/config.d.ts +23 -2
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +95 -47
- package/dist/lib/config.js.map +1 -1
- package/dist/lib/errors.d.ts +15 -6
- package/dist/lib/errors.d.ts.map +1 -1
- package/dist/lib/errors.js +14 -9
- package/dist/lib/errors.js.map +1 -1
- package/dist/lib/global-args.d.ts +35 -21
- package/dist/lib/global-args.d.ts.map +1 -1
- package/dist/lib/global-args.js +84 -64
- package/dist/lib/global-args.js.map +1 -1
- package/dist/lib/markdown.d.ts +1 -0
- package/dist/lib/markdown.d.ts.map +1 -1
- package/dist/lib/markdown.js +9 -14
- package/dist/lib/markdown.js.map +1 -1
- package/dist/lib/options.d.ts +2 -3
- package/dist/lib/options.d.ts.map +1 -1
- package/dist/lib/output.d.ts +5 -3
- package/dist/lib/output.d.ts.map +1 -1
- package/dist/lib/output.js +15 -17
- package/dist/lib/output.js.map +1 -1
- package/dist/lib/skills/content.d.ts +1 -1
- package/dist/lib/skills/content.d.ts.map +1 -1
- package/dist/lib/skills/content.js +16 -6
- package/dist/lib/skills/content.js.map +1 -1
- package/dist/lib/spinner.d.ts +2 -23
- package/dist/lib/spinner.d.ts.map +1 -1
- package/dist/lib/spinner.js +6 -107
- package/dist/lib/spinner.js.map +1 -1
- package/dist/lib/update.d.ts +9 -13
- package/dist/lib/update.d.ts.map +1 -1
- package/dist/lib/update.js +17 -43
- package/dist/lib/update.js.map +1 -1
- package/package.json +6 -6
- package/dist/commands/update/action.d.ts +0 -12
- package/dist/commands/update/action.d.ts.map +0 -1
- package/dist/commands/update/action.js +0 -104
- package/dist/commands/update/action.js.map +0 -1
- package/dist/commands/update/switch.d.ts +0 -5
- package/dist/commands/update/switch.d.ts.map +0 -1
- package/dist/commands/update/switch.js +0 -28
- package/dist/commands/update/switch.js.map +0 -1
- package/dist/lib/oauth-server.d.ts +0 -13
- package/dist/lib/oauth-server.d.ts.map +0 -1
- package/dist/lib/oauth-server.js.map +0 -1
- package/dist/lib/oauth.d.ts +0 -31
- package/dist/lib/oauth.d.ts.map +0 -1
- package/dist/lib/oauth.js +0 -150
- package/dist/lib/oauth.js.map +0 -1
- package/dist/lib/pkce.d.ts +0 -16
- package/dist/lib/pkce.d.ts.map +0 -1
- package/dist/lib/pkce.js +0 -35
- package/dist/lib/pkce.js.map +0 -1
- package/dist/test-helpers/empty-output.d.ts +0 -18
- package/dist/test-helpers/empty-output.d.ts.map +0 -1
- package/dist/test-helpers/empty-output.js +0 -43
- package/dist/test-helpers/empty-output.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
## [2.36.4](https://github.com/Doist/twist-cli/compare/v2.36.3...v2.36.4) (2026-05-13)
|
|
2
|
+
|
|
3
|
+
### Bug Fixes
|
|
4
|
+
|
|
5
|
+
- surface batch API errors in commands ([#159](https://github.com/Doist/twist-cli/issues/159)) ([3b30c3c](https://github.com/Doist/twist-cli/commit/3b30c3ccc17138b257e850562b4e80e500c68d34))
|
|
6
|
+
|
|
1
7
|
## [2.36.3](https://github.com/Doist/twist-cli/compare/v2.36.2...v2.36.3) (2026-05-09)
|
|
2
8
|
|
|
3
9
|
### Bug Fixes
|
|
@@ -1,3 +1,10 @@
|
|
|
1
1
|
import type { TokenStorageResult } from '../../lib/auth.js';
|
|
2
|
-
|
|
2
|
+
/**
|
|
3
|
+
* Surface a `TokenStorageResult` from a save/clear operation: the
|
|
4
|
+
* human-readable confirmation goes to stdout, any keyring-fallback warning
|
|
5
|
+
* goes to stderr. Pass `isMachineOutput: true` when the command is in
|
|
6
|
+
* `--json` / `--ndjson` mode so the stdout confirmation is suppressed and
|
|
7
|
+
* the warning still reaches the operator on stderr.
|
|
8
|
+
*/
|
|
9
|
+
export declare function logTokenStorageResult(result: TokenStorageResult, secureStoreMessage: string, isMachineOutput?: boolean): void;
|
|
3
10
|
//# sourceMappingURL=helpers.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../../src/commands/auth/helpers.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAA;AAE3D,wBAAgB,qBAAqB,CACjC,MAAM,EAAE,kBAAkB,EAC1B,kBAAkB,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../../src/commands/auth/helpers.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAA;AAE3D;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CACjC,MAAM,EAAE,kBAAkB,EAC1B,kBAAkB,EAAE,MAAM,EAC1B,eAAe,UAAQ,GACxB,IAAI,CAON"}
|
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
/**
|
|
3
|
+
* Surface a `TokenStorageResult` from a save/clear operation: the
|
|
4
|
+
* human-readable confirmation goes to stdout, any keyring-fallback warning
|
|
5
|
+
* goes to stderr. Pass `isMachineOutput: true` when the command is in
|
|
6
|
+
* `--json` / `--ndjson` mode so the stdout confirmation is suppressed and
|
|
7
|
+
* the warning still reaches the operator on stderr.
|
|
8
|
+
*/
|
|
9
|
+
export function logTokenStorageResult(result, secureStoreMessage, isMachineOutput = false) {
|
|
10
|
+
if (!isMachineOutput && result.storage === 'secure-store') {
|
|
4
11
|
console.log(chalk.dim(secureStoreMessage));
|
|
5
|
-
if (result.warning) {
|
|
6
|
-
console.error(chalk.yellow('Warning:'), result.warning);
|
|
7
|
-
}
|
|
8
|
-
return;
|
|
9
12
|
}
|
|
10
|
-
|
|
13
|
+
if (result.warning) {
|
|
14
|
+
console.error(chalk.yellow('Warning:'), result.warning);
|
|
15
|
+
}
|
|
11
16
|
}
|
|
12
17
|
//# sourceMappingURL=helpers.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"helpers.js","sourceRoot":"","sources":["../../../src/commands/auth/helpers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAGzB,MAAM,UAAU,qBAAqB,CACjC,MAA0B,EAC1B,kBAA0B;
|
|
1
|
+
{"version":3,"file":"helpers.js","sourceRoot":"","sources":["../../../src/commands/auth/helpers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAGzB;;;;;;GAMG;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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/auth/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/auth/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAOnC,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAgB1D"}
|
|
@@ -1,20 +1,20 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import { createTwistTokenStore } from '../../lib/auth-provider.js';
|
|
2
|
+
import { attachTwistLoginCommand } from './login.js';
|
|
3
|
+
import { attachTwistLogoutCommand } from './logout.js';
|
|
4
|
+
import { attachTwistStatusCommand } from './status.js';
|
|
4
5
|
import { loginWithToken } from './token.js';
|
|
5
6
|
export function registerAuthCommand(program) {
|
|
6
7
|
const auth = program.command('auth').description('Manage authentication');
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
// Shared store instance: login stashes the post-`set` storage result for
|
|
9
|
+
// its success handler, logout reads the post-`clear` result for the same
|
|
10
|
+
// keyring-fallback warning surface. Status uses `active()` as the
|
|
11
|
+
// authenticated-snapshot gate.
|
|
12
|
+
const store = createTwistTokenStore();
|
|
13
|
+
attachTwistLoginCommand(auth, store);
|
|
14
|
+
attachTwistLogoutCommand(auth, store);
|
|
15
|
+
attachTwistStatusCommand(auth, store);
|
|
11
16
|
auth.command('token [token]')
|
|
12
17
|
.description('Save API token for CLI authentication')
|
|
13
18
|
.action(loginWithToken);
|
|
14
|
-
auth.command('status')
|
|
15
|
-
.description('Show current authentication status')
|
|
16
|
-
.option('--json', 'Output as JSON')
|
|
17
|
-
.action(showStatus);
|
|
18
|
-
auth.command('logout').description('Remove saved authentication token').action(logout);
|
|
19
19
|
}
|
|
20
20
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/commands/auth/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/commands/auth/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAA;AAClE,OAAO,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAA;AACpD,OAAO,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAA;AACtD,OAAO,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAA;AACtD,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAE3C,MAAM,UAAU,mBAAmB,CAAC,OAAgB;IAChD,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,WAAW,CAAC,uBAAuB,CAAC,CAAA;IAEzE,yEAAyE;IACzE,yEAAyE;IACzE,kEAAkE;IAClE,+BAA+B;IAC/B,MAAM,KAAK,GAAG,qBAAqB,EAAE,CAAA;IAErC,uBAAuB,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;IACpC,wBAAwB,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;IACrC,wBAAwB,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;IAErC,IAAI,CAAC,OAAO,CAAC,eAAe,CAAC;SACxB,WAAW,CAAC,uCAAuC,CAAC;SACpD,MAAM,CAAC,cAAc,CAAC,CAAA;AAC/B,CAAC"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import type { Command } from 'commander';
|
|
2
|
+
import { type TwistTokenStore } from '../../lib/auth-provider.js';
|
|
3
|
+
export declare function attachTwistLoginCommand(parent: Command, store: TwistTokenStore): Command;
|
|
4
4
|
//# sourceMappingURL=login.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"login.d.ts","sourceRoot":"","sources":["../../../src/commands/auth/login.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"login.d.ts","sourceRoot":"","sources":["../../../src/commands/auth/login.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAExC,OAAO,EAIH,KAAK,eAAe,EACvB,MAAM,4BAA4B,CAAA;AAKnC,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,eAAe,GAAG,OAAO,CA0BxF"}
|
|
@@ -1,67 +1,29 @@
|
|
|
1
|
+
import { attachLoginCommand } from '@doist/cli-core/auth';
|
|
1
2
|
import chalk from 'chalk';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { startCallbackServer } from '../../lib/oauth-server.js';
|
|
5
|
-
import { buildAuthorizationUrl, exchangeCodeForToken, READ_ONLY_SCOPES, READ_WRITE_SCOPES, registerDynamicClient, } from '../../lib/oauth.js';
|
|
6
|
-
import { generateCodeChallenge, generateCodeVerifier, generateState } from '../../lib/pkce.js';
|
|
3
|
+
import { renderError, renderSuccess } from '../../lib/auth-pages.js';
|
|
4
|
+
import { createTwistAuthProvider, READ_ONLY_SCOPES, READ_WRITE_SCOPES, } from '../../lib/auth-provider.js';
|
|
7
5
|
import { logTokenStorageResult } from './helpers.js';
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
const open = (await import('open')).default;
|
|
24
|
-
// Open browser in background after a delay
|
|
25
|
-
setTimeout(async () => {
|
|
26
|
-
try {
|
|
27
|
-
const authUrl = buildAuthorizationUrl(client.client_id, codeChallenge, state, {
|
|
28
|
-
readOnly: options.readOnly,
|
|
29
|
-
});
|
|
30
|
-
console.log(chalk.dim('Opening browser for authorization...'));
|
|
31
|
-
console.log(chalk.dim(`If the browser doesn't open, visit: ${authUrl}`));
|
|
32
|
-
await open(authUrl);
|
|
33
|
-
}
|
|
34
|
-
catch {
|
|
35
|
-
// Browser opening failure is not critical - user can use the URL manually
|
|
36
|
-
}
|
|
37
|
-
}, 1000);
|
|
38
|
-
// Wait for callback - this gives us both code and cleanup
|
|
39
|
-
const result = await startCallbackServer(state);
|
|
40
|
-
cleanup = result.cleanup;
|
|
41
|
-
console.log(chalk.dim('Exchanging authorization code for token...'));
|
|
42
|
-
const accessToken = await exchangeCodeForToken(result.code, codeVerifier, client);
|
|
43
|
-
const saveResult = await saveApiToken(accessToken, {
|
|
44
|
-
authMode: options.readOnly ? 'read-only' : 'read-write',
|
|
45
|
-
authScope: options.readOnly ? READ_ONLY_SCOPES : READ_WRITE_SCOPES,
|
|
46
|
-
});
|
|
47
|
-
console.log(chalk.green('✓'), 'OAuth authentication successful!');
|
|
48
|
-
logTokenStorageResult(saveResult, 'Token stored securely in the system credential manager');
|
|
49
|
-
}
|
|
50
|
-
finally {
|
|
51
|
-
// Always cleanup the server
|
|
52
|
-
if (cleanup) {
|
|
53
|
-
cleanup();
|
|
6
|
+
const PREFERRED_CALLBACK_PORT = 8766;
|
|
7
|
+
export function attachTwistLoginCommand(parent, store) {
|
|
8
|
+
const provider = createTwistAuthProvider();
|
|
9
|
+
return attachLoginCommand(parent, {
|
|
10
|
+
provider,
|
|
11
|
+
store,
|
|
12
|
+
preferredPort: PREFERRED_CALLBACK_PORT,
|
|
13
|
+
resolveScopes: ({ readOnly }) => (readOnly ? READ_ONLY_SCOPES : READ_WRITE_SCOPES),
|
|
14
|
+
renderSuccess,
|
|
15
|
+
renderError,
|
|
16
|
+
onSuccess({ view, account }) {
|
|
17
|
+
const isMachineOutput = view.json || view.ndjson;
|
|
18
|
+
if (!isMachineOutput) {
|
|
19
|
+
console.log(chalk.green('✓'), 'OAuth authentication successful!');
|
|
20
|
+
console.log(chalk.dim(`Logged in as ${account.label}`));
|
|
54
21
|
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
throw new CliError('AUTH_FAILED', `OAuth authentication failed${detail}`, [
|
|
62
|
-
'Try again: tw auth login',
|
|
63
|
-
'Or set TWIST_API_TOKEN environment variable',
|
|
64
|
-
]);
|
|
65
|
-
}
|
|
22
|
+
const result = store.getLastStorageResult();
|
|
23
|
+
if (result) {
|
|
24
|
+
logTokenStorageResult(result, 'Token stored securely in the system credential manager', isMachineOutput);
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
}).description('Authenticate using OAuth (opens browser)');
|
|
66
28
|
}
|
|
67
29
|
//# sourceMappingURL=login.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"login.js","sourceRoot":"","sources":["../../../src/commands/auth/login.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"login.js","sourceRoot":"","sources":["../../../src/commands/auth/login.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAA;AACzD,OAAO,KAAK,MAAM,OAAO,CAAA;AAEzB,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAA;AACpE,OAAO,EACH,uBAAuB,EACvB,gBAAgB,EAChB,iBAAiB,GAEpB,MAAM,4BAA4B,CAAA;AACnC,OAAO,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAA;AAEpD,MAAM,uBAAuB,GAAG,IAAI,CAAA;AAEpC,MAAM,UAAU,uBAAuB,CAAC,MAAe,EAAE,KAAsB;IAC3E,MAAM,QAAQ,GAAG,uBAAuB,EAAE,CAAA;IAE1C,OAAO,kBAAkB,CAAC,MAAM,EAAE;QAC9B,QAAQ;QACR,KAAK;QACL,aAAa,EAAE,uBAAuB;QACtC,aAAa,EAAE,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,iBAAiB,CAAC;QAClF,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,GAAG,CAAC,EAAE,kCAAkC,CAAC,CAAA;gBACjE,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,gBAAgB,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC,CAAA;YAC3D,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,CAAC,WAAW,CAAC,0CAA0C,CAAC,CAAA;AAC9D,CAAC"}
|
|
@@ -1,2 +1,11 @@
|
|
|
1
|
-
|
|
1
|
+
import type { Command } from 'commander';
|
|
2
|
+
import type { TwistTokenStore } from '../../lib/auth-provider.js';
|
|
3
|
+
/**
|
|
4
|
+
* Attach `tw auth logout` via cli-core's generic `attachLogoutCommand`. The
|
|
5
|
+
* registrar emits the success line (`✓ Logged out` / `{ok:true}` / silent
|
|
6
|
+
* ndjson); `onCleared` only surfaces the keyring-fallback warning carried by
|
|
7
|
+
* `TokenStorageResult` — cli-core's `TokenStore.clear: void` contract can't
|
|
8
|
+
* expose it directly, so we stash it on the adapter (`getLastClearResult`).
|
|
9
|
+
*/
|
|
10
|
+
export declare function attachTwistLogoutCommand(auth: Command, store: TwistTokenStore): Command;
|
|
2
11
|
//# sourceMappingURL=logout.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"logout.d.ts","sourceRoot":"","sources":["../../../src/commands/auth/logout.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"logout.d.ts","sourceRoot":"","sources":["../../../src/commands/auth/logout.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACxC,OAAO,KAAK,EAAgB,eAAe,EAAE,MAAM,4BAA4B,CAAA;AAG/E;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,eAAe,GAAG,OAAO,CAavF"}
|
|
@@ -1,9 +1,21 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { clearApiToken } from '../../lib/auth.js';
|
|
1
|
+
import { attachLogoutCommand } from '@doist/cli-core/auth';
|
|
3
2
|
import { logTokenStorageResult } from './helpers.js';
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
/**
|
|
4
|
+
* Attach `tw auth logout` via cli-core's generic `attachLogoutCommand`. The
|
|
5
|
+
* registrar emits the success line (`✓ Logged out` / `{ok:true}` / silent
|
|
6
|
+
* ndjson); `onCleared` only surfaces the keyring-fallback warning carried by
|
|
7
|
+
* `TokenStorageResult` — cli-core's `TokenStore.clear: void` contract can't
|
|
8
|
+
* expose it directly, so we stash it on the adapter (`getLastClearResult`).
|
|
9
|
+
*/
|
|
10
|
+
export function attachTwistLogoutCommand(auth, store) {
|
|
11
|
+
return attachLogoutCommand(auth, {
|
|
12
|
+
store,
|
|
13
|
+
onCleared: ({ view }) => {
|
|
14
|
+
const result = store.getLastClearResult();
|
|
15
|
+
if (!result)
|
|
16
|
+
return;
|
|
17
|
+
logTokenStorageResult(result, 'Stored token removed from the system credential manager', view.json || view.ndjson);
|
|
18
|
+
},
|
|
19
|
+
});
|
|
8
20
|
}
|
|
9
21
|
//# sourceMappingURL=logout.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"logout.js","sourceRoot":"","sources":["../../../src/commands/auth/logout.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"logout.js","sourceRoot":"","sources":["../../../src/commands/auth/logout.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAA;AAG1D,OAAO,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAA;AAEpD;;;;;;GAMG;AACH,MAAM,UAAU,wBAAwB,CAAC,IAAa,EAAE,KAAsB;IAC1E,OAAO,mBAAmB,CAAe,IAAI,EAAE;QAC3C,KAAK;QACL,SAAS,EAAE,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE;YACpB,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"}
|
|
@@ -1,4 +1,16 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import type { Command } from 'commander';
|
|
2
|
+
import type { TwistTokenStore } from '../../lib/auth-provider.js';
|
|
3
|
+
/**
|
|
4
|
+
* Attach `tw auth status` via cli-core's generic `attachStatusCommand`.
|
|
5
|
+
*
|
|
6
|
+
* `TwistTokenStore.active()` returns a snapshot whenever a token resolves
|
|
7
|
+
* (per the adapter's documented contract — see `auth-provider.ts`), so
|
|
8
|
+
* `fetchLive` covers every token-present path: secure-store, plaintext
|
|
9
|
+
* config fallback, env-token mode, and manual `tw auth token`. The
|
|
10
|
+
* snapshot's token is reused inside `gatherStatusData` so credentials are
|
|
11
|
+
* read once per invocation. `onNotAuthenticated` only fires when nothing
|
|
12
|
+
* is stored — it throws `NoTokenError` so the standard CliError envelope
|
|
13
|
+
* reaches the operator unchanged.
|
|
14
|
+
*/
|
|
15
|
+
export declare function attachTwistStatusCommand(auth: Command, store: TwistTokenStore): Command;
|
|
4
16
|
//# sourceMappingURL=status.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../../src/commands/auth/status.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../../src/commands/auth/status.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAExC,OAAO,KAAK,EAAgB,eAAe,EAAE,MAAM,4BAA4B,CAAA;AAiE/E;;;;;;;;;;;GAWG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,eAAe,GAAG,OAAO,CA+BvF"}
|
|
@@ -1,17 +1,34 @@
|
|
|
1
|
+
import { attachStatusCommand } from '@doist/cli-core/auth';
|
|
1
2
|
import { TwistRequestError } from '@doist/twist-sdk';
|
|
2
3
|
import chalk from 'chalk';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
4
|
+
import { createWrappedTwistClient } from '../../lib/api.js';
|
|
5
|
+
import { getAuthMetadata, NoTokenError } from '../../lib/auth.js';
|
|
5
6
|
import { CliError } from '../../lib/errors.js';
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
function formatAuthMode(authMode, authScope) {
|
|
8
|
+
if (authMode === 'read-only') {
|
|
9
|
+
return `read-only (scope: ${authScope ?? 'unknown'})`;
|
|
10
|
+
}
|
|
11
|
+
if (authMode === 'read-write') {
|
|
12
|
+
return 'read-write';
|
|
13
|
+
}
|
|
14
|
+
return 'unknown (manual token or env var; assuming write access)';
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Fetch the live session user (via the snapshot token) and the local auth
|
|
18
|
+
* metadata concurrently — the API call dominates and the metadata read is
|
|
19
|
+
* independent. 401-translation lives here so both the snapshot path and any
|
|
20
|
+
* future callers emit the same `NO_TOKEN` envelope when the token is
|
|
21
|
+
* rejected by the API.
|
|
22
|
+
*/
|
|
23
|
+
async function gatherStatusData(token) {
|
|
9
24
|
try {
|
|
10
|
-
user = await
|
|
25
|
+
const [user, metadata] = await Promise.all([
|
|
26
|
+
createWrappedTwistClient(token).users.getSessionUser(),
|
|
27
|
+
getAuthMetadata(),
|
|
28
|
+
]);
|
|
29
|
+
return { user, metadata };
|
|
11
30
|
}
|
|
12
31
|
catch (error) {
|
|
13
|
-
if (error instanceof NoTokenError)
|
|
14
|
-
throw error;
|
|
15
32
|
if (error instanceof TwistRequestError && error.httpStatusCode === 401) {
|
|
16
33
|
throw new CliError('NO_TOKEN', 'Not authenticated (token expired or invalid)', [
|
|
17
34
|
'Run `tw auth login` to re-authenticate',
|
|
@@ -19,19 +36,67 @@ export async function showStatus(options) {
|
|
|
19
36
|
}
|
|
20
37
|
throw error;
|
|
21
38
|
}
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
:
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
39
|
+
}
|
|
40
|
+
function buildStatusText({ user, metadata }) {
|
|
41
|
+
const modeLabel = formatAuthMode(metadata.authMode, metadata.authScope);
|
|
42
|
+
return [
|
|
43
|
+
`${chalk.green('✓')} Authenticated`,
|
|
44
|
+
` Email: ${user.email}`,
|
|
45
|
+
` Name: ${user.name}`,
|
|
46
|
+
` Mode: ${modeLabel}`,
|
|
47
|
+
];
|
|
48
|
+
}
|
|
49
|
+
function buildStatusJson({ user, metadata }) {
|
|
50
|
+
return {
|
|
51
|
+
id: user.id,
|
|
52
|
+
email: user.email,
|
|
53
|
+
name: user.name,
|
|
54
|
+
authMode: metadata.authMode,
|
|
55
|
+
authScope: metadata.authScope,
|
|
56
|
+
source: metadata.source,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Attach `tw auth status` via cli-core's generic `attachStatusCommand`.
|
|
61
|
+
*
|
|
62
|
+
* `TwistTokenStore.active()` returns a snapshot whenever a token resolves
|
|
63
|
+
* (per the adapter's documented contract — see `auth-provider.ts`), so
|
|
64
|
+
* `fetchLive` covers every token-present path: secure-store, plaintext
|
|
65
|
+
* config fallback, env-token mode, and manual `tw auth token`. The
|
|
66
|
+
* snapshot's token is reused inside `gatherStatusData` so credentials are
|
|
67
|
+
* read once per invocation. `onNotAuthenticated` only fires when nothing
|
|
68
|
+
* is stored — it throws `NoTokenError` so the standard CliError envelope
|
|
69
|
+
* reaches the operator unchanged.
|
|
70
|
+
*/
|
|
71
|
+
export function attachTwistStatusCommand(auth, store) {
|
|
72
|
+
let data = null;
|
|
73
|
+
return attachStatusCommand(auth, {
|
|
74
|
+
store,
|
|
75
|
+
description: 'Show current authentication status',
|
|
76
|
+
fetchLive: async ({ token }) => {
|
|
77
|
+
data = await gatherStatusData(token);
|
|
78
|
+
return {
|
|
79
|
+
id: String(data.user.id),
|
|
80
|
+
label: data.user.name,
|
|
81
|
+
authMode: data.metadata.authMode,
|
|
82
|
+
authScope: data.metadata.authScope ?? '',
|
|
83
|
+
};
|
|
84
|
+
},
|
|
85
|
+
renderText: () => {
|
|
86
|
+
if (!data) {
|
|
87
|
+
throw new CliError('INTERNAL_ERROR', 'status renderText called before fetchLive');
|
|
88
|
+
}
|
|
89
|
+
return buildStatusText(data);
|
|
90
|
+
},
|
|
91
|
+
renderJson: () => {
|
|
92
|
+
if (!data) {
|
|
93
|
+
throw new CliError('INTERNAL_ERROR', 'status renderJson called before fetchLive');
|
|
94
|
+
}
|
|
95
|
+
return buildStatusJson(data);
|
|
96
|
+
},
|
|
97
|
+
onNotAuthenticated: () => {
|
|
98
|
+
throw new NoTokenError();
|
|
99
|
+
},
|
|
100
|
+
});
|
|
36
101
|
}
|
|
37
102
|
//# sourceMappingURL=status.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"status.js","sourceRoot":"","sources":["../../../src/commands/auth/status.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"status.js","sourceRoot":"","sources":["../../../src/commands/auth/status.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,8 +1,3 @@
|
|
|
1
1
|
import type { Command } from 'commander';
|
|
2
|
-
interface ChangelogOptions {
|
|
3
|
-
count: string;
|
|
4
|
-
}
|
|
5
|
-
export declare function changelogAction(options: ChangelogOptions): Promise<void>;
|
|
6
2
|
export declare function registerChangelogCommand(program: Command): void;
|
|
7
|
-
export {};
|
|
8
3
|
//# sourceMappingURL=changelog.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"changelog.d.ts","sourceRoot":"","sources":["../../src/commands/changelog.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"changelog.d.ts","sourceRoot":"","sources":["../../src/commands/changelog.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAKxC,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAS/D"}
|
|
@@ -1,110 +1,16 @@
|
|
|
1
|
-
import { readFile } from 'node:fs/promises';
|
|
2
1
|
import { dirname, join } from 'node:path';
|
|
3
2
|
import { fileURLToPath } from 'node:url';
|
|
4
|
-
import
|
|
3
|
+
import { registerChangelogCommand as registerCoreChangelogCommand } from '@doist/cli-core/commands';
|
|
5
4
|
import packageJson from '../../package.json' with { type: 'json' };
|
|
6
|
-
import { CliError } from '../lib/errors.js';
|
|
7
5
|
const CHANGELOG_PATH = join(dirname(fileURLToPath(import.meta.url)), '..', '..', 'CHANGELOG.md');
|
|
8
|
-
const CHANGELOG_URL = `https://github.com/Doist/twist-cli/blob/v${packageJson.version}/CHANGELOG.md`;
|
|
9
|
-
function formatInline(text) {
|
|
10
|
-
return text
|
|
11
|
-
.replace(/\*\*([^*]+)\*\*/g, (_, content) => chalk.bold(content))
|
|
12
|
-
.replace(/`([^`]+)`/g, (_, code) => chalk.cyan(code));
|
|
13
|
-
}
|
|
14
|
-
function formatForTerminal(text) {
|
|
15
|
-
let inBullet = false;
|
|
16
|
-
return text
|
|
17
|
-
.split('\n')
|
|
18
|
-
.map((line) => {
|
|
19
|
-
// Version headers: # 1.25.0 or ## 1.25.0 (date) → bold green
|
|
20
|
-
if (/^#{1,2} \d/.test(line)) {
|
|
21
|
-
inBullet = false;
|
|
22
|
-
const content = line.replace(/^#{1,2} /, '');
|
|
23
|
-
return chalk.green.bold(content);
|
|
24
|
-
}
|
|
25
|
-
// Section headers: ### Features → bold
|
|
26
|
-
if (line.startsWith('### ')) {
|
|
27
|
-
inBullet = false;
|
|
28
|
-
return chalk.bold(line.slice(4));
|
|
29
|
-
}
|
|
30
|
-
// Bullet items: * description → dimmed bullet + text
|
|
31
|
-
if (line.startsWith('* ')) {
|
|
32
|
-
inBullet = true;
|
|
33
|
-
return ` ${chalk.dim('•')} ${formatInline(line.slice(2))}`;
|
|
34
|
-
}
|
|
35
|
-
// Continuation lines of a bullet item → indent to match
|
|
36
|
-
if (inBullet && line.length > 0) {
|
|
37
|
-
return ` ${formatInline(line)}`;
|
|
38
|
-
}
|
|
39
|
-
if (line.length === 0) {
|
|
40
|
-
inBullet = false;
|
|
41
|
-
}
|
|
42
|
-
return formatInline(line);
|
|
43
|
-
})
|
|
44
|
-
.join('\n');
|
|
45
|
-
}
|
|
46
|
-
function cleanChangelog(text) {
|
|
47
|
-
return (text
|
|
48
|
-
// Version headers: # [1.25.0](https://...) or ## [1.25.0](https://...) → keep heading level
|
|
49
|
-
.replace(/(#{1,2}) \[([^\]]+)\]\([^)]*\)/g, '$1 $2')
|
|
50
|
-
// Remove commit hash links: ([abc1234](https://...))
|
|
51
|
-
.replace(/ \([a-f0-9]{7}\)/g, '')
|
|
52
|
-
.replace(/ \(\[[a-f0-9]{7}\]\([^)]*\)\)/g, '')
|
|
53
|
-
// Issue/PR links: [#151](https://...) → #151
|
|
54
|
-
.replace(/\[#(\d+)\]\([^)]*\)/g, '#$1')
|
|
55
|
-
// Remove **deps:** dependency update lines (not useful to end users)
|
|
56
|
-
.replace(/^\* \*\*deps:\*\*.*$/gm, '')
|
|
57
|
-
// Remove **scope:** prefixes but keep the text: **task:** foo → foo
|
|
58
|
-
.replace(/\*\*[\w-]+:\*\* /g, '')
|
|
59
|
-
// Clean up blank lines left by removed dep lines
|
|
60
|
-
.replace(/\n{3,}/g, '\n\n')
|
|
61
|
-
// Remove section headers left empty after filtering (e.g. ### Bug Fixes with no items)
|
|
62
|
-
.replace(/### [\w ]+\n\n(?=#{1,3} |$)/gm, ''));
|
|
63
|
-
}
|
|
64
|
-
function isEmptyAfterClean(section) {
|
|
65
|
-
const cleaned = cleanChangelog(section);
|
|
66
|
-
// A section is empty if it only contains the version header line(s) and whitespace
|
|
67
|
-
const withoutHeader = cleaned.replace(/^#{1,2} .+$/m, '').trim();
|
|
68
|
-
return withoutHeader.length === 0;
|
|
69
|
-
}
|
|
70
|
-
function parseChangelog(content, count) {
|
|
71
|
-
const sections = content.split(/\n(?=#{1,2} \[)/);
|
|
72
|
-
// Skip preamble (non-version content) if present
|
|
73
|
-
const allVersions = /^#{1,2} \[/.test(sections[0]) ? sections : sections.slice(1);
|
|
74
|
-
// Filter out versions that become empty after cleaning (e.g. deps-only releases)
|
|
75
|
-
const versionSections = allVersions.filter((s) => !isEmptyAfterClean(s));
|
|
76
|
-
const selected = versionSections.slice(0, count);
|
|
77
|
-
if (selected.length === 0) {
|
|
78
|
-
return { text: 'No changelog entries found.', hasMore: false };
|
|
79
|
-
}
|
|
80
|
-
return {
|
|
81
|
-
text: cleanChangelog(selected.join('\n').trimEnd()),
|
|
82
|
-
hasMore: versionSections.length > count,
|
|
83
|
-
};
|
|
84
|
-
}
|
|
85
|
-
export async function changelogAction(options) {
|
|
86
|
-
const count = parseInt(options.count, 10);
|
|
87
|
-
if (Number.isNaN(count) || count < 1) {
|
|
88
|
-
throw new CliError('INVALID_TYPE', 'Count must be a positive number');
|
|
89
|
-
}
|
|
90
|
-
let content;
|
|
91
|
-
try {
|
|
92
|
-
content = await readFile(CHANGELOG_PATH, 'utf-8');
|
|
93
|
-
}
|
|
94
|
-
catch {
|
|
95
|
-
throw new CliError('FILE_READ_ERROR', 'Could not read changelog file');
|
|
96
|
-
}
|
|
97
|
-
const { text, hasMore } = parseChangelog(content, count);
|
|
98
|
-
console.log(formatForTerminal(text));
|
|
99
|
-
if (hasMore) {
|
|
100
|
-
console.log(chalk.dim(`\nView full changelog: ${CHANGELOG_URL}`));
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
6
|
export function registerChangelogCommand(program) {
|
|
104
|
-
program
|
|
105
|
-
|
|
106
|
-
.
|
|
107
|
-
.
|
|
108
|
-
|
|
7
|
+
registerCoreChangelogCommand(program, {
|
|
8
|
+
path: CHANGELOG_PATH,
|
|
9
|
+
repoUrl: 'https://github.com/Doist/twist-cli',
|
|
10
|
+
version: packageJson.version,
|
|
11
|
+
headingLevel: 'flexible',
|
|
12
|
+
continuationIndent: true,
|
|
13
|
+
filterEmptyVersions: true,
|
|
14
|
+
});
|
|
109
15
|
}
|
|
110
16
|
//# sourceMappingURL=changelog.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"changelog.js","sourceRoot":"","sources":["../../src/commands/changelog.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"changelog.js","sourceRoot":"","sources":["../../src/commands/changelog.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AACzC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AACxC,OAAO,EAAE,wBAAwB,IAAI,4BAA4B,EAAE,MAAM,0BAA0B,CAAA;AAEnG,OAAO,WAAW,MAAM,oBAAoB,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAA;AAElE,MAAM,cAAc,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,cAAc,CAAC,CAAA;AAEhG,MAAM,UAAU,wBAAwB,CAAC,OAAgB;IACrD,4BAA4B,CAAC,OAAO,EAAE;QAClC,IAAI,EAAE,cAAc;QACpB,OAAO,EAAE,oCAAoC;QAC7C,OAAO,EAAE,WAAW,CAAC,OAAO;QAC5B,YAAY,EAAE,UAAU;QACxB,kBAAkB,EAAE,IAAI;QACxB,mBAAmB,EAAE,IAAI;KAC5B,CAAC,CAAA;AACN,CAAC"}
|