@circleback/cli 0.3.4 → 0.3.6
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/README.md +8 -8
- package/dist/auth/credentials.js +47 -2
- package/dist/auth/oauth.js +21 -16
- package/dist/auth/server.js +9 -12
- package/dist/commands/auth.js +15 -41
- package/dist/commands/registry.js +2 -2
- package/dist/helpers/help.js +2 -1
- package/dist/helpers/stdin.js +39 -0
- package/dist/helpers/style.js +30 -11
- package/dist/helpers/url.js +33 -0
- package/dist/index.js +31 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -13,7 +13,7 @@ Requires Node.js 18 or later. The package installs two binaries: `circleback` an
|
|
|
13
13
|
## Quick start
|
|
14
14
|
|
|
15
15
|
```bash
|
|
16
|
-
#
|
|
16
|
+
# Log in with your browser
|
|
17
17
|
cb auth login
|
|
18
18
|
|
|
19
19
|
# Search recent meetings
|
|
@@ -39,9 +39,9 @@ cb meetings --json
|
|
|
39
39
|
|
|
40
40
|
| Command | Description |
|
|
41
41
|
| ------------------------------ | ------------------------------------------------------------ |
|
|
42
|
-
| `cb auth login` |
|
|
43
|
-
| `cb auth login --api-key` |
|
|
44
|
-
| `cb auth logout` |
|
|
42
|
+
| `cb auth login` | Log in to Circleback with your browser |
|
|
43
|
+
| `cb auth login --api-key` | Log in with an API key |
|
|
44
|
+
| `cb auth logout` | Sign out on this device |
|
|
45
45
|
| `cb update` | Update CLI to the latest version |
|
|
46
46
|
| `cb meetings [search]` | Search meetings |
|
|
47
47
|
| `cb meetings read <ids...>` | Read detailed meeting info, notes, and action items (max 50) |
|
|
@@ -86,12 +86,12 @@ Pass `--json` to any command to get raw JSON instead of formatted tables. Useful
|
|
|
86
86
|
cb meetings "standup" --json | jq '.[].name'
|
|
87
87
|
```
|
|
88
88
|
|
|
89
|
-
##
|
|
89
|
+
## Login
|
|
90
90
|
|
|
91
91
|
```bash
|
|
92
|
-
cb auth login # Opens
|
|
93
|
-
cb auth login --api-key #
|
|
94
|
-
cb auth logout #
|
|
92
|
+
cb auth login # Opens your browser to log in
|
|
93
|
+
cb auth login --api-key # Asks for your API key securely
|
|
94
|
+
cb auth logout # Signs out on this device
|
|
95
95
|
```
|
|
96
96
|
|
|
97
97
|
API-key login links to [Settings → API keys](https://circleback.ai/settings?tab=api-access), verifies the key, and stores it locally on your device.
|
package/dist/auth/credentials.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getCredentialSnapshot = exports.getEnvironmentApiKey = void 0;
|
|
3
|
+
exports.getCredentialSnapshot = exports.getLoginStatus = exports.getEnvironmentApiKey = void 0;
|
|
4
|
+
const node_util_1 = require("node:util");
|
|
5
|
+
const zod_1 = require("zod");
|
|
4
6
|
const constants_1 = require("../constants");
|
|
5
7
|
const types_1 = require("../types");
|
|
6
8
|
const oauth_1 = require("./oauth");
|
|
@@ -10,6 +12,49 @@ const getEnvironmentApiKey = () => {
|
|
|
10
12
|
return apiKey || undefined;
|
|
11
13
|
};
|
|
12
14
|
exports.getEnvironmentApiKey = getEnvironmentApiKey;
|
|
15
|
+
const fetchLoginStatus = async (credentials) => {
|
|
16
|
+
if (credentials.type === types_1.CredentialType.OAuth &&
|
|
17
|
+
Math.floor(Date.now() / 1000) >= credentials.tokens.expiresAt) {
|
|
18
|
+
return 'Unable to check login';
|
|
19
|
+
}
|
|
20
|
+
try {
|
|
21
|
+
const bearerCredential = credentials.type === types_1.CredentialType.OAuth
|
|
22
|
+
? credentials.tokens.accessToken
|
|
23
|
+
: credentials.apiKey;
|
|
24
|
+
const response = await fetch(`${constants_1.BASE_URL}/api/oauth/test`, {
|
|
25
|
+
headers: { Authorization: `Bearer ${bearerCredential}` },
|
|
26
|
+
signal: AbortSignal.timeout(5_000),
|
|
27
|
+
});
|
|
28
|
+
if (response.status === 401)
|
|
29
|
+
return 'Not logged in';
|
|
30
|
+
if (!response.ok)
|
|
31
|
+
return 'Unable to check login';
|
|
32
|
+
const account = zod_1.z
|
|
33
|
+
.object({ email: zod_1.z.email(), apiKeyName: zod_1.z.string().optional() })
|
|
34
|
+
.parse(await response.json());
|
|
35
|
+
if (credentials.type === types_1.CredentialType.ApiKey) {
|
|
36
|
+
const name = (0, node_util_1.stripVTControlCharacters)(account.apiKeyName ?? '')
|
|
37
|
+
.replace(/[\p{Cc}\p{Cf}]/gu, '')
|
|
38
|
+
.trim();
|
|
39
|
+
return name
|
|
40
|
+
? `Logged in with API key “${name}”`
|
|
41
|
+
: 'Logged in with an API key';
|
|
42
|
+
}
|
|
43
|
+
return `Logged in as ${account.email}`;
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
return 'Unable to check login';
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
const getLoginStatus = async () => {
|
|
50
|
+
try {
|
|
51
|
+
return await fetchLoginStatus(requireCredentials());
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
return 'Not logged in';
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
exports.getLoginStatus = getLoginStatus;
|
|
13
58
|
const requireCredentials = () => {
|
|
14
59
|
const environmentApiKey = (0, exports.getEnvironmentApiKey)();
|
|
15
60
|
if (environmentApiKey) {
|
|
@@ -24,7 +69,7 @@ const requireCredentials = () => {
|
|
|
24
69
|
}
|
|
25
70
|
const storedCredentials = (0, tokenStorage_1.loadCredentials)();
|
|
26
71
|
if (!storedCredentials) {
|
|
27
|
-
throw new Error(`Not
|
|
72
|
+
throw new Error(`Not logged in. Run \`circleback auth login\` or set ${constants_1.API_KEY_ENVIRONMENT_VARIABLE}.`);
|
|
28
73
|
}
|
|
29
74
|
if ((0, types_1.isStoredApiKey)(storedCredentials)) {
|
|
30
75
|
return {
|
package/dist/auth/oauth.js
CHANGED
|
@@ -4,9 +4,10 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.forceRefreshOAuthToken = exports.getValidOAuthTokens = exports.login = void 0;
|
|
7
|
-
const node_child_process_1 = require("node:child_process");
|
|
8
7
|
const node_crypto_1 = __importDefault(require("node:crypto"));
|
|
9
8
|
const constants_1 = require("../constants");
|
|
9
|
+
const stdin_1 = require("../helpers/stdin");
|
|
10
|
+
const url_1 = require("../helpers/url");
|
|
10
11
|
const types_1 = require("../types");
|
|
11
12
|
const server_1 = require("./server");
|
|
12
13
|
const tokenStorage_1 = require("./tokenStorage");
|
|
@@ -89,9 +90,10 @@ const refreshAccessToken = async (refreshToken, clientId) => {
|
|
|
89
90
|
}
|
|
90
91
|
return tokenData;
|
|
91
92
|
};
|
|
92
|
-
const login = async (
|
|
93
|
+
const login = async () => {
|
|
93
94
|
const { port, waitForCallback, shutdown } = await (0, server_1.startCallbackServer)();
|
|
94
95
|
const redirectUri = `http://127.0.0.1:${port}`;
|
|
96
|
+
const promptAbort = new AbortController();
|
|
95
97
|
try {
|
|
96
98
|
const clientId = await registerClient(redirectUri);
|
|
97
99
|
const { verifier, challenge } = generatePkce();
|
|
@@ -105,21 +107,24 @@ const login = async (options = {}) => {
|
|
|
105
107
|
authorizeUrl.searchParams.set('code_challenge', challenge);
|
|
106
108
|
authorizeUrl.searchParams.set('code_challenge_method', 'S256');
|
|
107
109
|
const url = authorizeUrl.toString();
|
|
108
|
-
|
|
109
|
-
const
|
|
110
|
-
const
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
110
|
+
await (0, url_1.openUrlInBrowser)(url);
|
|
111
|
+
const callback = waitForCallback();
|
|
112
|
+
const retryBrowserOnEnter = async () => {
|
|
113
|
+
if (await (0, stdin_1.promptToOpenAuthorizeUrl)(url, promptAbort.signal)) {
|
|
114
|
+
await (0, url_1.openUrlInBrowser)(url);
|
|
115
|
+
}
|
|
116
|
+
return callback;
|
|
117
|
+
};
|
|
118
|
+
const receivedCallback = await Promise.race([
|
|
119
|
+
callback,
|
|
120
|
+
retryBrowserOnEnter(),
|
|
121
|
+
]);
|
|
122
|
+
promptAbort.abort();
|
|
123
|
+
if (receivedCallback.state !== state) {
|
|
119
124
|
throw new Error('State mismatch. Possible CSRF attack.');
|
|
120
125
|
}
|
|
121
126
|
const tokenResponse = await exchangeCode({
|
|
122
|
-
code:
|
|
127
|
+
code: receivedCallback.code,
|
|
123
128
|
redirectUri,
|
|
124
129
|
clientId,
|
|
125
130
|
codeVerifier: verifier,
|
|
@@ -128,9 +133,9 @@ const login = async (options = {}) => {
|
|
|
128
133
|
(0, tokenStorage_1.saveCredentials)(tokens);
|
|
129
134
|
return tokens;
|
|
130
135
|
}
|
|
131
|
-
|
|
136
|
+
finally {
|
|
137
|
+
promptAbort.abort();
|
|
132
138
|
shutdown();
|
|
133
|
-
throw error;
|
|
134
139
|
}
|
|
135
140
|
};
|
|
136
141
|
exports.login = login;
|
package/dist/auth/server.js
CHANGED
|
@@ -13,7 +13,7 @@ const SUCCESS_HTML = `
|
|
|
13
13
|
<head><title>Circleback CLI</title></head>
|
|
14
14
|
<body style="font-family: system-ui, sans-serif; display: flex; justify-content: center; align-items: center; height: 100vh; margin: 0;">
|
|
15
15
|
<div style="text-align: center;">
|
|
16
|
-
<h1>
|
|
16
|
+
<h1>Login complete</h1>
|
|
17
17
|
<p>You can close this tab and return to the terminal.</p>
|
|
18
18
|
</div>
|
|
19
19
|
</body>
|
|
@@ -44,7 +44,7 @@ const startCallbackServer = () => new Promise((resolve, reject) => {
|
|
|
44
44
|
.replace(/>/g, '>')
|
|
45
45
|
.replace(/"/g, '"')
|
|
46
46
|
.replace(/'/g, ''');
|
|
47
|
-
response.end(`<h1>
|
|
47
|
+
response.end(`<h1>Login failed: ${escapedError}</h1>`);
|
|
48
48
|
callbackReject(new Error(`OAuth error: ${error}.`));
|
|
49
49
|
return;
|
|
50
50
|
}
|
|
@@ -57,17 +57,9 @@ const startCallbackServer = () => new Promise((resolve, reject) => {
|
|
|
57
57
|
response.end(SUCCESS_HTML);
|
|
58
58
|
callbackResolve({ code, state });
|
|
59
59
|
});
|
|
60
|
-
const timeout = setTimeout(() => {
|
|
61
|
-
shutdown();
|
|
62
|
-
callbackReject(new Error('Authentication timed out. Please try again.'));
|
|
63
|
-
}, constants_1.CALLBACK_TIMEOUT_MILLISECONDS);
|
|
64
|
-
let isShutdown = false;
|
|
65
60
|
const shutdown = () => {
|
|
66
|
-
if (
|
|
67
|
-
|
|
68
|
-
isShutdown = true;
|
|
69
|
-
clearTimeout(timeout);
|
|
70
|
-
server.close();
|
|
61
|
+
if (server.listening)
|
|
62
|
+
server.close();
|
|
71
63
|
};
|
|
72
64
|
server.listen(0, '127.0.0.1', () => {
|
|
73
65
|
const address = server.address();
|
|
@@ -78,6 +70,11 @@ const startCallbackServer = () => new Promise((resolve, reject) => {
|
|
|
78
70
|
resolve({
|
|
79
71
|
port: address.port,
|
|
80
72
|
waitForCallback: async () => {
|
|
73
|
+
const timeout = setTimeout(() => {
|
|
74
|
+
shutdown();
|
|
75
|
+
callbackReject(new Error('Login timed out. Please try again.'));
|
|
76
|
+
}, constants_1.CALLBACK_TIMEOUT_MILLISECONDS);
|
|
77
|
+
server.once('close', () => clearTimeout(timeout));
|
|
81
78
|
const result = await callbackPromise;
|
|
82
79
|
shutdown();
|
|
83
80
|
return result;
|
package/dist/commands/auth.js
CHANGED
|
@@ -13,35 +13,17 @@ const tokenStorage_1 = require("../auth/tokenStorage");
|
|
|
13
13
|
const constants_1 = require("../constants");
|
|
14
14
|
const output_1 = require("../helpers/output");
|
|
15
15
|
const types_1 = require("../types");
|
|
16
|
-
const AUTHORIZATION_URL_FALLBACK_DELAY_MILLISECONDS = 500;
|
|
17
|
-
const getOpenUrlInstruction = () => process.platform === 'darwin' ? '⌘-click' : 'Ctrl-click';
|
|
18
|
-
const createAuthorizationUrlFallback = () => {
|
|
19
|
-
const abortController = new AbortController();
|
|
20
|
-
const schedule = (url) => {
|
|
21
|
-
const timeout = setTimeout(() => {
|
|
22
|
-
if (abortController.signal.aborted)
|
|
23
|
-
return;
|
|
24
|
-
(0, output_1.printText)(`Browser didn't open? ${getOpenUrlInstruction()} the URL below to log in.\n`, output_1.TextType.Default);
|
|
25
|
-
(0, output_1.printText)(chalk_1.default.cyan(url), output_1.TextType.Default);
|
|
26
|
-
}, AUTHORIZATION_URL_FALLBACK_DELAY_MILLISECONDS);
|
|
27
|
-
abortController.signal.addEventListener('abort', () => clearTimeout(timeout), { once: true });
|
|
28
|
-
};
|
|
29
|
-
return {
|
|
30
|
-
schedule,
|
|
31
|
-
cleanup: () => abortController.abort(),
|
|
32
|
-
};
|
|
33
|
-
};
|
|
34
16
|
const loginCommand = new commander_1.Command('login')
|
|
35
|
-
.description('
|
|
36
|
-
.option('--api-key', '
|
|
17
|
+
.description('Log in to Circleback.')
|
|
18
|
+
.option('--api-key', 'Log in with an API key')
|
|
37
19
|
.action(async (options) => {
|
|
38
20
|
try {
|
|
39
21
|
if ((0, credentials_1.getEnvironmentApiKey)()) {
|
|
40
|
-
(0, output_1.printText)(`${constants_1.API_KEY_ENVIRONMENT_VARIABLE} is active. Unset it before
|
|
22
|
+
(0, output_1.printText)(`${constants_1.API_KEY_ENVIRONMENT_VARIABLE} is active. Unset it before logging in with another account or API key.`, output_1.TextType.Warning);
|
|
41
23
|
return;
|
|
42
24
|
}
|
|
43
25
|
if ((0, tokenStorage_1.loadCredentials)()) {
|
|
44
|
-
(0, output_1.printText)('Already
|
|
26
|
+
(0, output_1.printText)('Already logged in. Run `cb auth logout` first to log in again.', output_1.TextType.Warning);
|
|
45
27
|
return;
|
|
46
28
|
}
|
|
47
29
|
if (options.apiKey) {
|
|
@@ -51,47 +33,39 @@ const loginCommand = new commander_1.Command('login')
|
|
|
51
33
|
const apiKey = await (0, apiKey_1.promptForApiKey)();
|
|
52
34
|
await (0, apiKey_1.validateApiKey)(apiKey);
|
|
53
35
|
(0, tokenStorage_1.saveCredentials)({ type: types_1.CredentialType.ApiKey, apiKey });
|
|
54
|
-
(0, output_1.printText)('API key
|
|
36
|
+
(0, output_1.printText)('Logged in with your API key.', output_1.TextType.Success);
|
|
55
37
|
return;
|
|
56
38
|
}
|
|
57
|
-
(0, output_1.printText)('Opening your browser
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
await (0, oauth_1.login)({
|
|
61
|
-
onAuthorizeUrl: authorizationUrlFallback.schedule,
|
|
62
|
-
});
|
|
63
|
-
}
|
|
64
|
-
finally {
|
|
65
|
-
authorizationUrlFallback.cleanup();
|
|
66
|
-
}
|
|
67
|
-
(0, output_1.printText)('Authentication successful.', output_1.TextType.Success);
|
|
39
|
+
(0, output_1.printText)('Opening your browser to log in…', output_1.TextType.Default);
|
|
40
|
+
await (0, oauth_1.login)();
|
|
41
|
+
(0, output_1.printText)('Logged in successfully.', output_1.TextType.Success);
|
|
68
42
|
}
|
|
69
43
|
catch (error) {
|
|
70
|
-
const message = error instanceof Error ? error.message : '
|
|
44
|
+
const message = error instanceof Error ? error.message : 'Login failed.';
|
|
71
45
|
(0, output_1.printText)(message, output_1.TextType.Error);
|
|
72
46
|
process.exit(1);
|
|
73
47
|
}
|
|
74
48
|
});
|
|
75
49
|
const logoutCommand = new commander_1.Command('logout')
|
|
76
|
-
.description('
|
|
50
|
+
.description('Sign out on this device.')
|
|
77
51
|
.action(() => {
|
|
78
52
|
const environmentApiKey = (0, credentials_1.getEnvironmentApiKey)();
|
|
79
53
|
const didClearCredentials = (0, tokenStorage_1.clearCredentials)();
|
|
80
54
|
if (!didClearCredentials) {
|
|
81
55
|
if (environmentApiKey) {
|
|
82
|
-
(0, output_1.printText)(`No
|
|
56
|
+
(0, output_1.printText)(`No saved login to clear. ${constants_1.API_KEY_ENVIRONMENT_VARIABLE} is still active.`, output_1.TextType.Warning);
|
|
83
57
|
return;
|
|
84
58
|
}
|
|
85
|
-
(0, output_1.printText)('Not
|
|
59
|
+
(0, output_1.printText)('Not logged in.', output_1.TextType.Warning);
|
|
86
60
|
return;
|
|
87
61
|
}
|
|
88
62
|
if (environmentApiKey) {
|
|
89
|
-
(0, output_1.printText)(`
|
|
63
|
+
(0, output_1.printText)(`Saved login cleared. ${constants_1.API_KEY_ENVIRONMENT_VARIABLE} is still active.`, output_1.TextType.Warning);
|
|
90
64
|
return;
|
|
91
65
|
}
|
|
92
|
-
(0, output_1.printText)('
|
|
66
|
+
(0, output_1.printText)('Signed out successfully.', output_1.TextType.Success);
|
|
93
67
|
});
|
|
94
68
|
exports.authCommand = new commander_1.Command('auth')
|
|
95
|
-
.description('
|
|
69
|
+
.description('Log in or sign out.')
|
|
96
70
|
.addCommand(loginCommand)
|
|
97
71
|
.addCommand(logoutCommand);
|
|
@@ -17,12 +17,12 @@ const standaloneCommands = [auth_1.authCommand, update_1.updateCommand];
|
|
|
17
17
|
const commandGroups = [
|
|
18
18
|
{
|
|
19
19
|
name: 'meetings',
|
|
20
|
-
description: '
|
|
20
|
+
description: 'Find meetings and read notes.',
|
|
21
21
|
subcommands: [meetings_1.searchMeetingsCommand, meetings_1.readMeetingsCommand],
|
|
22
22
|
},
|
|
23
23
|
{
|
|
24
24
|
name: 'transcripts',
|
|
25
|
-
description: '
|
|
25
|
+
description: 'Find and read meeting transcripts.',
|
|
26
26
|
subcommands: [transcripts_1.searchTranscriptsCommand, transcripts_1.getTranscriptsCommand],
|
|
27
27
|
},
|
|
28
28
|
{
|
package/dist/helpers/help.js
CHANGED
|
@@ -9,6 +9,7 @@ const SECTION_HEADERS = {
|
|
|
9
9
|
'Options:': 'options',
|
|
10
10
|
'Commands:': 'commands',
|
|
11
11
|
'Arguments:': 'arguments',
|
|
12
|
+
'Examples:': 'commands',
|
|
12
13
|
};
|
|
13
14
|
const styleLine = (line, section) => {
|
|
14
15
|
if (/^Usage:/.test(line)) {
|
|
@@ -25,7 +26,7 @@ const styleLine = (line, section) => {
|
|
|
25
26
|
const match = line.match(/^(\s+)(.+?)( +)(.*)$/);
|
|
26
27
|
if (match) {
|
|
27
28
|
return [
|
|
28
|
-
`${match[1]}${chalk_1.default.
|
|
29
|
+
`${match[1]}${chalk_1.default.cyan(match[2])}${match[3]}${chalk_1.default.dim(match[4])}`,
|
|
29
30
|
section,
|
|
30
31
|
];
|
|
31
32
|
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.promptToOpenAuthorizeUrl = void 0;
|
|
7
|
+
const node_readline_1 = require("node:readline");
|
|
8
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
9
|
+
const output_1 = require("./output");
|
|
10
|
+
const string_1 = require("./string");
|
|
11
|
+
const promptToOpenAuthorizeUrl = async (url, signal, stdin = process.stdin) => {
|
|
12
|
+
const isInteractive = Boolean(stdin.isTTY && process.stdout.isTTY);
|
|
13
|
+
if (isInteractive) {
|
|
14
|
+
(0, output_1.printText)(`Browser didn't open? Press Enter to log in…`, output_1.TextType.Default);
|
|
15
|
+
(0, output_1.printText)('', output_1.TextType.Default);
|
|
16
|
+
}
|
|
17
|
+
(0, output_1.printText)(process.stdout.isTTY ? (0, string_1.link)(chalk_1.default.cyan.underline(url), url) : url, output_1.TextType.Default);
|
|
18
|
+
if (!isInteractive || signal?.aborted)
|
|
19
|
+
return false;
|
|
20
|
+
const readlineInterface = (0, node_readline_1.createInterface)({
|
|
21
|
+
input: stdin,
|
|
22
|
+
terminal: false,
|
|
23
|
+
signal,
|
|
24
|
+
});
|
|
25
|
+
return new Promise((resolve, reject) => {
|
|
26
|
+
readlineInterface.once('line', () => {
|
|
27
|
+
resolve(true);
|
|
28
|
+
readlineInterface.close();
|
|
29
|
+
});
|
|
30
|
+
readlineInterface.once('close', () => {
|
|
31
|
+
if (signal?.aborted) {
|
|
32
|
+
resolve(false);
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
reject(new Error('Authentication canceled.'));
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
};
|
|
39
|
+
exports.promptToOpenAuthorizeUrl = promptToOpenAuthorizeUrl;
|
package/dist/helpers/style.js
CHANGED
|
@@ -3,19 +3,38 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.
|
|
6
|
+
exports.formatWelcomeBanner = exports.tableStyle = exports.brand = void 0;
|
|
7
7
|
const chalk_1 = __importDefault(require("chalk"));
|
|
8
8
|
const constants_1 = require("../constants");
|
|
9
9
|
exports.brand = chalk_1.default.hex(constants_1.BRAND_COLOR);
|
|
10
10
|
const secondary = chalk_1.default.hex(constants_1.SECONDARY_COLOR);
|
|
11
11
|
exports.tableStyle = { head: [], border: [] };
|
|
12
|
-
|
|
13
|
-
''
|
|
14
|
-
|
|
15
|
-
constants_1.
|
|
16
|
-
|
|
17
|
-
.
|
|
18
|
-
|
|
19
|
-
.
|
|
20
|
-
|
|
21
|
-
|
|
12
|
+
const formatWelcomeBanner = (width, loginStatus) => {
|
|
13
|
+
const logo = constants_1.ASCII_LOGO.trimEnd().split('\n').slice(1);
|
|
14
|
+
const logoWidth = Math.max(...logo.map((line) => line.length));
|
|
15
|
+
const title = `${constants_1.CLIENT_NAME} v${constants_1.CLIENT_VERSION}`;
|
|
16
|
+
const details = [
|
|
17
|
+
`${exports.brand.bold(constants_1.CLIENT_NAME)} ${chalk_1.default.dim(`v${constants_1.CLIENT_VERSION}`)}`,
|
|
18
|
+
'',
|
|
19
|
+
chalk_1.default.bold(loginStatus),
|
|
20
|
+
];
|
|
21
|
+
const isWide = width >= logoWidth + 4 + Math.max(title.length, loginStatus.length);
|
|
22
|
+
const detailsStart = Math.floor((logo.length - details.length) / 2);
|
|
23
|
+
const artwork = logo.map((line, index) => {
|
|
24
|
+
const colored = line
|
|
25
|
+
.replace(/=+/g, (match) => (0, exports.brand)(match))
|
|
26
|
+
.replace(/\++/g, (match) => secondary(match));
|
|
27
|
+
const detail = isWide ? details[index - detailsStart] : undefined;
|
|
28
|
+
return detail
|
|
29
|
+
? `${colored}${' '.repeat(logoWidth - line.length + 4)}${detail}`
|
|
30
|
+
: colored;
|
|
31
|
+
});
|
|
32
|
+
return [
|
|
33
|
+
'',
|
|
34
|
+
...artwork,
|
|
35
|
+
'',
|
|
36
|
+
...(isWide ? [] : details.map((line) => ` ${line}`)),
|
|
37
|
+
'',
|
|
38
|
+
].join('\n');
|
|
39
|
+
};
|
|
40
|
+
exports.formatWelcomeBanner = formatWelcomeBanner;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.openUrlInBrowser = void 0;
|
|
4
|
+
const node_child_process_1 = require("node:child_process");
|
|
5
|
+
const node_events_1 = require("node:events");
|
|
6
|
+
const output_1 = require("./output");
|
|
7
|
+
const openUrlInBrowser = async (url) => {
|
|
8
|
+
const isWindows = process.platform === 'win32';
|
|
9
|
+
const escapedUrl = isWindows ? url.replace(/&/g, '^&') : url;
|
|
10
|
+
const [command, ...arguments_] = isWindows
|
|
11
|
+
? ['start', '', escapedUrl]
|
|
12
|
+
: process.platform === 'darwin'
|
|
13
|
+
? ['open', url]
|
|
14
|
+
: ['xdg-open', url];
|
|
15
|
+
const warnAboutBrowserFailure = () => (0, output_1.printTextToStderr)('Unable to open your browser. Open the URL manually.', output_1.TextType.Warning);
|
|
16
|
+
try {
|
|
17
|
+
const child = (0, node_child_process_1.spawn)(command, arguments_, {
|
|
18
|
+
stdio: 'ignore',
|
|
19
|
+
shell: isWindows,
|
|
20
|
+
});
|
|
21
|
+
child.once('spawn', () => {
|
|
22
|
+
child.once('exit', (code) => {
|
|
23
|
+
if (code !== 0)
|
|
24
|
+
warnAboutBrowserFailure();
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
await (0, node_events_1.once)(child, 'spawn');
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
warnAboutBrowserFailure();
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
exports.openUrlInBrowser = openUrlInBrowser;
|
package/dist/index.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
"use strict";
|
|
3
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
4
|
const commander_1 = require("commander");
|
|
5
|
+
const credentials_1 = require("./auth/credentials");
|
|
5
6
|
const registry_1 = require("./commands/registry");
|
|
6
7
|
const constants_1 = require("./constants");
|
|
7
8
|
const help_1 = require("./helpers/help");
|
|
@@ -11,19 +12,23 @@ const update_1 = require("./helpers/update");
|
|
|
11
12
|
const program = new commander_1.Command();
|
|
12
13
|
const updateCheck = (0, update_1.getUpdateCheckResult)(constants_1.CLIENT_PACKAGE_NAME);
|
|
13
14
|
const configureCommandOutput = (command) => {
|
|
15
|
+
command.helpOption('-h, --help', 'Display help for command');
|
|
16
|
+
if (command.commands.length > 0) {
|
|
17
|
+
command.addHelpCommand('help [command]', 'Display help for command');
|
|
18
|
+
}
|
|
14
19
|
command.configureOutput({
|
|
15
|
-
writeOut: (text) =>
|
|
16
|
-
writeErr: (text) =>
|
|
20
|
+
writeOut: (text) => process.stdout.write((0, help_1.styleHelpOutput)(text)),
|
|
21
|
+
writeErr: (text) => process.stderr.write((0, help_1.styleHelpOutput)(text)),
|
|
17
22
|
outputError: (text) => (0, output_1.printText)((0, help_1.styleHelpOutput)(text).trim(), output_1.TextType.Error),
|
|
18
23
|
});
|
|
19
24
|
command.commands.forEach(configureCommandOutput);
|
|
20
25
|
};
|
|
21
26
|
program
|
|
22
27
|
.name('circleback')
|
|
23
|
-
.
|
|
24
|
-
.version(constants_1.CLIENT_VERSION)
|
|
28
|
+
.version(constants_1.CLIENT_VERSION, '-V, --version', 'Output the version number')
|
|
25
29
|
.option('--json', 'Output raw JSON instead of formatted text')
|
|
26
|
-
.
|
|
30
|
+
.allowExcessArguments(false)
|
|
31
|
+
.action(() => program.help())
|
|
27
32
|
.exitOverride();
|
|
28
33
|
(0, registry_1.getAllCommands)().reduce((p, command) => p.addCommand(command), program);
|
|
29
34
|
configureCommandOutput(program);
|
|
@@ -35,7 +40,27 @@ program.hook('postAction', async (_thisCommand, actionCommand) => {
|
|
|
35
40
|
}
|
|
36
41
|
catch { }
|
|
37
42
|
});
|
|
38
|
-
|
|
43
|
+
const run = async () => {
|
|
44
|
+
const isRootHelp = process.argv
|
|
45
|
+
.slice(2)
|
|
46
|
+
.every((argument) => ['--help', '-h', '--json', 'help', '--'].includes(argument));
|
|
47
|
+
if (isRootHelp) {
|
|
48
|
+
const loginStatus = await (0, credentials_1.getLoginStatus)();
|
|
49
|
+
program
|
|
50
|
+
.addHelpText('before', () => (0, style_1.formatWelcomeBanner)(process.stdout.columns ?? 80, loginStatus))
|
|
51
|
+
.addHelpText('before', [
|
|
52
|
+
'Examples:',
|
|
53
|
+
' cb auth login Log in to Circleback',
|
|
54
|
+
' cb meetings See recent meetings',
|
|
55
|
+
' cb meetings "product review" Find a meeting',
|
|
56
|
+
' cb transcripts "pricing" Search transcript text',
|
|
57
|
+
' cb meetings --json Get JSON for scripts',
|
|
58
|
+
'',
|
|
59
|
+
].join('\n'));
|
|
60
|
+
}
|
|
61
|
+
await program.parseAsync();
|
|
62
|
+
};
|
|
63
|
+
run().catch((error) => {
|
|
39
64
|
if (error instanceof commander_1.CommanderError)
|
|
40
65
|
process.exit(error.exitCode);
|
|
41
66
|
(0, output_1.printText)(error instanceof Error ? error.message : String(error), output_1.TextType.Error);
|
package/package.json
CHANGED