@circleback/cli 1.1.0 → 1.2.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/README.md +10 -2
- package/dist/auth/credentials.js +14 -1
- package/dist/client/jsonRpc.js +33 -4
- package/dist/commands/auth.js +6 -1
- package/dist/commands/folders.js +113 -0
- package/dist/commands/meetings.js +59 -41
- package/dist/commands/registry.js +42 -5
- package/dist/commands/tags.js +5 -17
- package/dist/constants.js +3 -1
- package/dist/helpers/command.js +18 -12
- package/dist/helpers/formatter.js +16 -5
- package/dist/helpers/parse.js +11 -1
- package/dist/helpers/tag.js +18 -1
- package/dist/helpers/toolList.js +44 -0
- package/dist/helpers/value.js +6 -1
- package/dist/index.js +15 -9
- package/dist/types.js +22 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Circleback CLI
|
|
2
2
|
|
|
3
|
-
Search and access your meetings, transcripts, emails, calendar events, and more from the terminal, and manage your action items, tags, and meeting access.
|
|
3
|
+
Search and access your meetings, transcripts, emails, calendar events, and more from the terminal, and manage your action items, tags, folders, and meeting access.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
@@ -67,6 +67,12 @@ cb meetings --json
|
|
|
67
67
|
| `cb tags create <name>` | Create a tag |
|
|
68
68
|
| `cb tags update <id>` | Update a tag |
|
|
69
69
|
| `cb tags delete <id>` | Delete a tag |
|
|
70
|
+
| `cb folders` | List folders |
|
|
71
|
+
| `cb folders read <ids...>` | Read folders |
|
|
72
|
+
| `cb folders create <name>` | Create a folder |
|
|
73
|
+
| `cb folders update <id>` | Update a folder |
|
|
74
|
+
| `cb folders delete <id>` | Delete a folder |
|
|
75
|
+
| `cb folders share <id>` | Share a folder |
|
|
70
76
|
|
|
71
77
|
Run `cb --help` or `cb <command> --help` for full usage details.
|
|
72
78
|
|
|
@@ -117,7 +123,9 @@ For stateless automation, set `CIRCLEBACK_API_KEY` instead of logging in:
|
|
|
117
123
|
CIRCLEBACK_API_KEY=cb_your_api_key cb meetings --json
|
|
118
124
|
```
|
|
119
125
|
|
|
120
|
-
Changing meetings, action items, tags, or sharing permissions needs write access. Browser logins from before v0.5.0 don't include it, so run `cb auth login` again; read-only API keys can't make changes.
|
|
126
|
+
Changing meetings, action items, tags, folders, or sharing permissions needs write access. Browser logins from before v0.5.0 don't include it, so run `cb auth login` again; read-only API keys can't make changes.
|
|
127
|
+
|
|
128
|
+
An API key limited to one folder only reaches that folder's meetings, transcripts, and action items, so `cb --help` lists just the commands it can run.
|
|
121
129
|
|
|
122
130
|
`CIRCLEBACK_API_KEY` takes precedence over stored credentials. Unset it before running `cb auth login`; `cb auth logout` cannot clear an environment variable from your shell.
|
|
123
131
|
|
package/dist/auth/credentials.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getCredentialSnapshot = exports.getLoginStatus = exports.getEnvironmentApiKey = void 0;
|
|
3
|
+
exports.getCredentialSnapshot = exports.getCredentialFingerprint = exports.getLoginStatus = exports.getEnvironmentApiKey = void 0;
|
|
4
|
+
const node_crypto_1 = require("node:crypto");
|
|
4
5
|
const node_util_1 = require("node:util");
|
|
5
6
|
const zod_1 = require("zod");
|
|
6
7
|
const constants_1 = require("../constants");
|
|
@@ -79,6 +80,18 @@ const requireCredentials = () => {
|
|
|
79
80
|
}
|
|
80
81
|
return { type: types_1.CredentialType.OAuth, tokens: storedCredentials };
|
|
81
82
|
};
|
|
83
|
+
const getCredentialFingerprint = () => {
|
|
84
|
+
try {
|
|
85
|
+
const credentials = requireCredentials();
|
|
86
|
+
return credentials.type === types_1.CredentialType.ApiKey
|
|
87
|
+
? (0, node_crypto_1.createHash)('sha256').update(credentials.apiKey).digest('hex')
|
|
88
|
+
: types_1.CredentialType.OAuth;
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
exports.getCredentialFingerprint = getCredentialFingerprint;
|
|
82
95
|
const getCredentialSnapshot = async () => {
|
|
83
96
|
const credentials = requireCredentials();
|
|
84
97
|
if (credentials.type === types_1.CredentialType.OAuth) {
|
package/dist/client/jsonRpc.js
CHANGED
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.callTool = void 0;
|
|
3
|
+
exports.listTools = exports.callTool = void 0;
|
|
4
4
|
const zod_1 = require("zod");
|
|
5
5
|
const constants_1 = require("../constants");
|
|
6
6
|
const types_1 = require("../types");
|
|
7
7
|
const credentials_1 = require("../auth/credentials");
|
|
8
|
+
const tokenStorage_1 = require("../auth/tokenStorage");
|
|
9
|
+
const value_1 = require("../helpers/value");
|
|
8
10
|
const TOOLS_CALL_METHOD = 'tools/call';
|
|
11
|
+
const TOOLS_LIST_METHOD = 'tools/list';
|
|
12
|
+
const TOOLS_LIST_TIMEOUT_MILLISECONDS = 3_000;
|
|
9
13
|
const errorResponseSchema = zod_1.z.object({
|
|
10
14
|
error: zod_1.z.string().optional(),
|
|
11
15
|
message: zod_1.z.string().optional(),
|
|
@@ -32,12 +36,13 @@ const HTTP_ERROR_MESSAGES = {
|
|
|
32
36
|
404: 'Resource not found.',
|
|
33
37
|
429: 'Rate limit exceeded. Please try again later.',
|
|
34
38
|
};
|
|
35
|
-
const sendRequest = async (method, params, accessToken) => fetch(`${constants_1.BASE_URL}/api/mcp`, {
|
|
39
|
+
const sendRequest = async (method, params, accessToken, signal) => fetch(`${constants_1.BASE_URL}/api/mcp`, {
|
|
36
40
|
method: 'POST',
|
|
41
|
+
signal,
|
|
37
42
|
headers: {
|
|
38
43
|
'Content-Type': 'application/json',
|
|
39
44
|
Accept: 'application/json, text/event-stream',
|
|
40
|
-
Authorization: `Bearer ${accessToken}
|
|
45
|
+
...((0, value_1.isPresent)(accessToken) && { Authorization: `Bearer ${accessToken}` }),
|
|
41
46
|
[constants_1.CLIENT_VERSION_HEADER]: constants_1.CLIENT_VERSION,
|
|
42
47
|
},
|
|
43
48
|
body: JSON.stringify({
|
|
@@ -136,8 +141,32 @@ const callTool = async (toolName, toolArguments) => {
|
|
|
136
141
|
const message = serverMessage ||
|
|
137
142
|
fallback ||
|
|
138
143
|
`Request failed with status ${response.status}.`;
|
|
139
|
-
throw new Error(response.status === 403
|
|
144
|
+
throw new Error(response.status === 403 && message.includes('insufficient scope')
|
|
145
|
+
? `${message} ${WRITE_ACCESS_HINT}`
|
|
146
|
+
: message);
|
|
140
147
|
}
|
|
141
148
|
return parseResponse(response);
|
|
142
149
|
};
|
|
143
150
|
exports.callTool = callTool;
|
|
151
|
+
const listTools = async () => {
|
|
152
|
+
const bearerCredential = (0, credentials_1.getEnvironmentApiKey)() || (0, tokenStorage_1.loadCredentials)()
|
|
153
|
+
? (await (0, credentials_1.getCredentialSnapshot)()).bearerCredential
|
|
154
|
+
: undefined;
|
|
155
|
+
const response = await sendRequest(TOOLS_LIST_METHOD, {}, bearerCredential, AbortSignal.timeout(TOOLS_LIST_TIMEOUT_MILLISECONDS));
|
|
156
|
+
if (!response.ok)
|
|
157
|
+
throw new Error(await extractErrorMessage(response));
|
|
158
|
+
const text = await response.text();
|
|
159
|
+
const body = response.headers
|
|
160
|
+
.get('content-type')
|
|
161
|
+
?.includes('text/event-stream')
|
|
162
|
+
? text
|
|
163
|
+
.split('\n')
|
|
164
|
+
.find((line) => line.startsWith('data: '))
|
|
165
|
+
?.slice(6)
|
|
166
|
+
: text;
|
|
167
|
+
return Object.fromEntries(types_1.ToolListResponseSchema.parse(JSON.parse(body ?? '')).result.tools.map(({ name, inputSchema }) => [
|
|
168
|
+
name,
|
|
169
|
+
Object.keys(inputSchema.properties ?? {}),
|
|
170
|
+
]));
|
|
171
|
+
};
|
|
172
|
+
exports.listTools = listTools;
|
package/dist/commands/auth.js
CHANGED
|
@@ -12,6 +12,7 @@ const oauth_1 = require("../auth/oauth");
|
|
|
12
12
|
const tokenStorage_1 = require("../auth/tokenStorage");
|
|
13
13
|
const constants_1 = require("../constants");
|
|
14
14
|
const output_1 = require("../helpers/output");
|
|
15
|
+
const toolList_1 = require("../helpers/toolList");
|
|
15
16
|
const types_1 = require("../types");
|
|
16
17
|
const loginCommand = new commander_1.Command('login')
|
|
17
18
|
.description('Log in to Circleback.')
|
|
@@ -33,11 +34,13 @@ const loginCommand = new commander_1.Command('login')
|
|
|
33
34
|
const apiKey = await (0, apiKey_1.promptForApiKey)();
|
|
34
35
|
await (0, apiKey_1.validateApiKey)(apiKey);
|
|
35
36
|
(0, tokenStorage_1.saveCredentials)({ type: types_1.CredentialType.ApiKey, apiKey });
|
|
37
|
+
await (0, toolList_1.refreshToolList)();
|
|
36
38
|
(0, output_1.printText)('Logged in with your API key.', output_1.TextType.Success);
|
|
37
39
|
return;
|
|
38
40
|
}
|
|
39
41
|
(0, output_1.printText)('Opening your browser to log in…', output_1.TextType.Default);
|
|
40
42
|
await (0, oauth_1.login)();
|
|
43
|
+
await (0, toolList_1.refreshToolList)();
|
|
41
44
|
(0, output_1.printText)('Logged in successfully.', output_1.TextType.Success);
|
|
42
45
|
}
|
|
43
46
|
catch (error) {
|
|
@@ -48,9 +51,11 @@ const loginCommand = new commander_1.Command('login')
|
|
|
48
51
|
});
|
|
49
52
|
const logoutCommand = new commander_1.Command('logout')
|
|
50
53
|
.description('Sign out on this device.')
|
|
51
|
-
.action(() => {
|
|
54
|
+
.action(async () => {
|
|
52
55
|
const environmentApiKey = (0, credentials_1.getEnvironmentApiKey)();
|
|
53
56
|
const didClearCredentials = (0, tokenStorage_1.clearCredentials)();
|
|
57
|
+
if (didClearCredentials)
|
|
58
|
+
await (0, toolList_1.refreshToolList)();
|
|
54
59
|
if (!didClearCredentials) {
|
|
55
60
|
if (environmentApiKey) {
|
|
56
61
|
(0, output_1.printText)(`No saved login to clear. ${constants_1.API_KEY_ENVIRONMENT_VARIABLE} is still active.`, output_1.TextType.Warning);
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.shareFolderCommand = exports.deleteFolderCommand = exports.updateFolderCommand = exports.createFolderCommand = exports.readFoldersCommand = exports.listFoldersCommand = void 0;
|
|
4
|
+
const commander_1 = require("commander");
|
|
5
|
+
const command_1 = require("../helpers/command");
|
|
6
|
+
const parse_1 = require("../helpers/parse");
|
|
7
|
+
const tag_1 = require("../helpers/tag");
|
|
8
|
+
const value_1 = require("../helpers/value");
|
|
9
|
+
const types_1 = require("../types");
|
|
10
|
+
exports.listFoldersCommand = (0, command_1.defineToolCommand)(new commander_1.Command('list').description('List folders.'), types_1.ToolName.ListFolders, () => ({}));
|
|
11
|
+
exports.readFoldersCommand = (0, command_1.defineToolCommand)(new commander_1.Command('read')
|
|
12
|
+
.description('Read folders with their members, teams, and rules.')
|
|
13
|
+
.argument('<ids...>', 'Folder IDs (max 20)'), types_1.ToolName.ReadFolders, (folderIds) => ({ intent: 'Reading folders.', folderIds }));
|
|
14
|
+
exports.createFolderCommand = (0, command_1.defineToolCommand)(new commander_1.Command('create')
|
|
15
|
+
.description('Create a folder.')
|
|
16
|
+
.argument('<name>', 'Folder name')
|
|
17
|
+
.requiredOption('--workspace <id>', 'Workspace ID')
|
|
18
|
+
.option('--private', 'Only show the folder to its members')
|
|
19
|
+
.option('--icon <icon>', 'Set the icon')
|
|
20
|
+
.addOption((0, tag_1.createColorOption)())
|
|
21
|
+
.option('--rules <json>', 'Set the rules (ruleGroups JSON)'), types_1.ToolName.CreateFolder, (name, options) => ({
|
|
22
|
+
name,
|
|
23
|
+
workspaceId: (0, parse_1.parseIntOption)(options.workspace, '--workspace'),
|
|
24
|
+
...(options.private && { visibility: 'Private' }),
|
|
25
|
+
...((0, value_1.isPresent)(options.icon) && { icon: options.icon }),
|
|
26
|
+
...((0, value_1.isPresent)(options.color) && { color: (0, tag_1.getTagColor)(options.color) }),
|
|
27
|
+
...((0, value_1.isPresent)(options.rules) && {
|
|
28
|
+
ruleGroups: (0, parse_1.parseJsonOption)(options.rules, '--rules'),
|
|
29
|
+
}),
|
|
30
|
+
}));
|
|
31
|
+
exports.updateFolderCommand = (0, command_1.defineToolCommand)(new commander_1.Command('update')
|
|
32
|
+
.description('Update a folder. Each option replaces the current value when provided.')
|
|
33
|
+
.argument('<id>', 'Folder ID')
|
|
34
|
+
.option('--name <name>', 'Update the name')
|
|
35
|
+
.option('--description <description>', 'Update the description, or "" to remove it')
|
|
36
|
+
.option('--icon <icon>', 'Update the icon, or "" to remove it')
|
|
37
|
+
.addOption(new commander_1.Option('--color <color>', 'Update the display color, or "" for the default').choices([...tag_1.TAG_COLOR_NAMES, '']))
|
|
38
|
+
.addOption(new commander_1.Option('--visibility <visibility>', 'Set who can find it').choices([
|
|
39
|
+
'Closed',
|
|
40
|
+
'Private',
|
|
41
|
+
]))
|
|
42
|
+
.option('--rules <json>', 'Replace the rules (ruleGroups JSON, or "[]" to remove all)'), types_1.ToolName.UpdateFolder, (folderId, options) => {
|
|
43
|
+
if (!Object.values(options).some(value_1.isPresent)) {
|
|
44
|
+
throw new Error('Provide at least one field to update.');
|
|
45
|
+
}
|
|
46
|
+
const { rules, color, description, icon, ...fields } = options;
|
|
47
|
+
return {
|
|
48
|
+
folderId,
|
|
49
|
+
...fields,
|
|
50
|
+
description: description === '' ? null : description,
|
|
51
|
+
icon: icon === '' ? null : icon,
|
|
52
|
+
color: color === '' ? null : (0, tag_1.getTagColor)(color),
|
|
53
|
+
...((0, value_1.isPresent)(rules) && {
|
|
54
|
+
ruleGroups: (0, parse_1.parseJsonOption)(rules, '--rules'),
|
|
55
|
+
}),
|
|
56
|
+
};
|
|
57
|
+
});
|
|
58
|
+
exports.deleteFolderCommand = (0, command_1.defineToolCommand)(new commander_1.Command('delete')
|
|
59
|
+
.description('Delete a folder.')
|
|
60
|
+
.argument('<id>', 'Folder ID'), types_1.ToolName.DeleteFolder, (folderId) => ({ folderId }));
|
|
61
|
+
exports.shareFolderCommand = (0, command_1.defineToolCommand)(new commander_1.Command('share')
|
|
62
|
+
.description('Share a folder, change access, or remove access.')
|
|
63
|
+
.argument('<id>', 'Folder ID')
|
|
64
|
+
.option('--email <emails>', 'Share with email addresses (comma-separated)')
|
|
65
|
+
.option('--profile <ids>', 'Share with people (comma-separated)')
|
|
66
|
+
.option('--team <ids>', 'Share with teams (comma-separated)')
|
|
67
|
+
.option('--workspace <ids>', 'Share with workspaces (comma-separated)')
|
|
68
|
+
.addOption(new commander_1.Option('--access <access>', "Set access to the folder's meetings, or none").choices([...Object.values(types_1.ShareableMeetingAccess), 'none']))
|
|
69
|
+
.option('--contributor', 'Let them add meetings')
|
|
70
|
+
.option('--no-contributor', 'Stop them adding meetings')
|
|
71
|
+
.option('--owner', 'Let people manage the folder')
|
|
72
|
+
.option('--no-owner', 'Stop people managing the folder')
|
|
73
|
+
.option('--remove', 'Remove them from the folder'), types_1.ToolName.ShareFolder, (folderId, options) => {
|
|
74
|
+
if ((0, value_1.isPresent)(options.owner) && (options.team || options.workspace)) {
|
|
75
|
+
throw new Error('--owner only applies to --email and --profile.');
|
|
76
|
+
}
|
|
77
|
+
const grant = {
|
|
78
|
+
...((0, value_1.isPresent)(options.access) && {
|
|
79
|
+
access: options.access === 'none' ? null : options.access,
|
|
80
|
+
}),
|
|
81
|
+
...((0, value_1.isPresent)(options.contributor) && {
|
|
82
|
+
isContributor: options.contributor,
|
|
83
|
+
}),
|
|
84
|
+
...(options.remove && { remove: true }),
|
|
85
|
+
};
|
|
86
|
+
const personGrant = {
|
|
87
|
+
...grant,
|
|
88
|
+
...((0, value_1.isPresent)(options.owner) && { isOwner: options.owner }),
|
|
89
|
+
};
|
|
90
|
+
const parseIds = (value, label) => value ? (0, parse_1.parseNumericIds)(value, label) : [];
|
|
91
|
+
const shares = [
|
|
92
|
+
...(options.email?.split(',') ?? []).map((email) => ({
|
|
93
|
+
email: email.trim(),
|
|
94
|
+
...personGrant,
|
|
95
|
+
})),
|
|
96
|
+
...parseIds(options.profile, 'profile ID').map((profileId) => ({
|
|
97
|
+
profileId,
|
|
98
|
+
...personGrant,
|
|
99
|
+
})),
|
|
100
|
+
...parseIds(options.team, 'team ID').map((teamId) => ({
|
|
101
|
+
teamId,
|
|
102
|
+
...grant,
|
|
103
|
+
})),
|
|
104
|
+
...parseIds(options.workspace, 'workspace ID').map((workspaceId) => ({
|
|
105
|
+
workspaceId,
|
|
106
|
+
...grant,
|
|
107
|
+
})),
|
|
108
|
+
];
|
|
109
|
+
if (shares.length === 0) {
|
|
110
|
+
throw new Error('Set at least one of --email, --profile, --team, or --workspace.');
|
|
111
|
+
}
|
|
112
|
+
return { folderId, shares };
|
|
113
|
+
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.createUpdateMeetingCommand = exports.shareMeetingCommand = exports.readMeetingsCommand = exports.createSearchMeetingsCommand = void 0;
|
|
4
4
|
const commander_1 = require("commander");
|
|
5
5
|
const constants_1 = require("../constants");
|
|
6
6
|
const command_1 = require("../helpers/command");
|
|
@@ -10,30 +10,37 @@ const tag_1 = require("../helpers/tag");
|
|
|
10
10
|
const value_1 = require("../helpers/value");
|
|
11
11
|
const types_1 = require("../types");
|
|
12
12
|
const DEFAULT_MEETINGS_SEARCH_LAST_DAYS = 7;
|
|
13
|
-
|
|
14
|
-
.
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
: undefined,
|
|
35
|
-
|
|
36
|
-
|
|
13
|
+
const createSearchMeetingsCommand = ({ canFilterByFolder, }) => {
|
|
14
|
+
const command = new commander_1.Command('search')
|
|
15
|
+
.description('Search meetings.')
|
|
16
|
+
.argument('[search]', 'Search term')
|
|
17
|
+
.option('-p, --page <index>', constants_1.PAGE_OPTION_DESCRIPTION, '0')
|
|
18
|
+
.option('--last <days>', 'Show meetings from the last N days')
|
|
19
|
+
.option('--from <date>', constants_1.START_DATE_OPTION_DESCRIPTION)
|
|
20
|
+
.option('--to <date>', constants_1.END_DATE_OPTION_DESCRIPTION)
|
|
21
|
+
.option('--tags <tags>', constants_1.TAGS_OPTION_DESCRIPTION)
|
|
22
|
+
.option('--profiles <ids>', constants_1.PROFILES_OPTION_DESCRIPTION)
|
|
23
|
+
.option('--domains <domains>', constants_1.DOMAINS_OPTION_DESCRIPTION);
|
|
24
|
+
return (0, command_1.defineToolCommand)(canFilterByFolder
|
|
25
|
+
? command.option('--folder <id>', constants_1.FOLDER_OPTION_DESCRIPTION)
|
|
26
|
+
: command, types_1.ToolName.SearchMeetings, async (query, options) => ({
|
|
27
|
+
intent: query ?? 'Listing recent meetings.',
|
|
28
|
+
pageIndex: (0, parse_1.parseIntOption)(options.page, '--page'),
|
|
29
|
+
searchTerm: query,
|
|
30
|
+
startDate: (0, date_1.getStartDate)(options) ??
|
|
31
|
+
(query || options.folder
|
|
32
|
+
? undefined
|
|
33
|
+
: (0, date_1.getStartDateFromDaysAgo)(DEFAULT_MEETINGS_SEARCH_LAST_DAYS)),
|
|
34
|
+
endDate: options.to || undefined,
|
|
35
|
+
tags: options.tags ? await (0, tag_1.resolveTagValues)(options.tags) : undefined,
|
|
36
|
+
profiles: options.profiles
|
|
37
|
+
? (0, parse_1.parseNumericIds)(options.profiles, 'profile ID')
|
|
38
|
+
: undefined,
|
|
39
|
+
domains: options.domains ? options.domains.split(',') : undefined,
|
|
40
|
+
folderId: options.folder,
|
|
41
|
+
}));
|
|
42
|
+
};
|
|
43
|
+
exports.createSearchMeetingsCommand = createSearchMeetingsCommand;
|
|
37
44
|
exports.readMeetingsCommand = (0, command_1.defineToolCommand)(new commander_1.Command('read')
|
|
38
45
|
.description('Get detailed meeting information.')
|
|
39
46
|
.argument('<ids...>', `Meeting IDs (max ${constants_1.MAX_READ_MEETING_IDENTIFIERS})`), types_1.ToolName.ReadMeetings, (ids) => {
|
|
@@ -99,19 +106,30 @@ exports.shareMeetingCommand = (0, command_1.defineToolCommand)(new commander_1.C
|
|
|
99
106
|
],
|
|
100
107
|
};
|
|
101
108
|
});
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
}
|
|
109
|
+
const createUpdateMeetingCommand = ({ canSetFolders, }) => {
|
|
110
|
+
const command = new commander_1.Command('update')
|
|
111
|
+
.description('Update a meeting. Each option replaces the current value when provided.')
|
|
112
|
+
.argument('<id>', 'Meeting ID')
|
|
113
|
+
.option('--name <name>', 'Update the meeting name')
|
|
114
|
+
.option('--notes <notes>', 'Update the meeting notes (supports Markdown)')
|
|
115
|
+
.option('--private-notes <notes>', constants_1.PRIVATE_NOTES_UPDATE_OPTION_DESCRIPTION)
|
|
116
|
+
.option('--tags <tags>', constants_1.TAGS_UPDATE_OPTION_DESCRIPTION);
|
|
117
|
+
return (0, command_1.defineToolCommand)(canSetFolders
|
|
118
|
+
? command.option('--folders <ids>', constants_1.FOLDERS_UPDATE_OPTION_DESCRIPTION)
|
|
119
|
+
: command, types_1.ToolName.UpdateMeeting, async (meetingId, { tags, folders, ...fields }) => {
|
|
120
|
+
if (!(0, value_1.isPresent)(tags) &&
|
|
121
|
+
!(0, value_1.isPresent)(folders) &&
|
|
122
|
+
!Object.values(fields).some(value_1.isPresent)) {
|
|
123
|
+
throw new Error('Provide at least one field to update.');
|
|
124
|
+
}
|
|
125
|
+
return {
|
|
126
|
+
meetingId,
|
|
127
|
+
...fields,
|
|
128
|
+
...((0, value_1.isPresent)(tags) && { tagIds: await (0, tag_1.resolveTagValues)(tags) }),
|
|
129
|
+
...((0, value_1.isPresent)(folders) && {
|
|
130
|
+
folderIds: folders.split(',').filter(Boolean),
|
|
131
|
+
}),
|
|
132
|
+
};
|
|
133
|
+
});
|
|
134
|
+
};
|
|
135
|
+
exports.createUpdateMeetingCommand = createUpdateMeetingCommand;
|
|
@@ -7,6 +7,7 @@ const auth_1 = require("./auth");
|
|
|
7
7
|
const calendar_1 = require("./calendar");
|
|
8
8
|
const companies_1 = require("./companies");
|
|
9
9
|
const emails_1 = require("./emails");
|
|
10
|
+
const folders_1 = require("./folders");
|
|
10
11
|
const meetings_1 = require("./meetings");
|
|
11
12
|
const profiles_1 = require("./profiles");
|
|
12
13
|
const support_1 = require("./support");
|
|
@@ -14,15 +15,31 @@ const tags_1 = require("./tags");
|
|
|
14
15
|
const teams_1 = require("./teams");
|
|
15
16
|
const transcripts_1 = require("./transcripts");
|
|
16
17
|
const update_1 = require("./update");
|
|
18
|
+
const command_1 = require("../helpers/command");
|
|
19
|
+
const toolList_1 = require("../helpers/toolList");
|
|
20
|
+
const value_1 = require("../helpers/value");
|
|
21
|
+
const types_1 = require("../types");
|
|
17
22
|
const standaloneCommands = [auth_1.authCommand, update_1.updateCommand];
|
|
18
|
-
const
|
|
23
|
+
const getCommandGroups = (toolList) => [
|
|
19
24
|
{
|
|
20
25
|
name: 'meetings',
|
|
21
26
|
description: 'Find, read, update, and share meetings.',
|
|
22
27
|
subcommands: [
|
|
23
|
-
meetings_1.
|
|
28
|
+
(0, meetings_1.createSearchMeetingsCommand)({
|
|
29
|
+
canFilterByFolder: (0, toolList_1.hasToolProperty)({
|
|
30
|
+
toolList,
|
|
31
|
+
toolName: types_1.ToolName.SearchMeetings,
|
|
32
|
+
property: 'folderId',
|
|
33
|
+
}),
|
|
34
|
+
}),
|
|
24
35
|
meetings_1.readMeetingsCommand,
|
|
25
|
-
meetings_1.
|
|
36
|
+
(0, meetings_1.createUpdateMeetingCommand)({
|
|
37
|
+
canSetFolders: (0, toolList_1.hasToolProperty)({
|
|
38
|
+
toolList,
|
|
39
|
+
toolName: types_1.ToolName.UpdateMeeting,
|
|
40
|
+
property: 'folderIds',
|
|
41
|
+
}),
|
|
42
|
+
}),
|
|
26
43
|
meetings_1.shareMeetingCommand,
|
|
27
44
|
],
|
|
28
45
|
},
|
|
@@ -61,6 +78,18 @@ const commandGroups = [
|
|
|
61
78
|
description: 'List your workspace and teams.',
|
|
62
79
|
subcommands: [teams_1.listTeamsCommand],
|
|
63
80
|
},
|
|
81
|
+
{
|
|
82
|
+
name: 'folders',
|
|
83
|
+
description: 'List, manage, and share folders.',
|
|
84
|
+
subcommands: [
|
|
85
|
+
folders_1.listFoldersCommand,
|
|
86
|
+
folders_1.readFoldersCommand,
|
|
87
|
+
folders_1.createFolderCommand,
|
|
88
|
+
folders_1.updateFolderCommand,
|
|
89
|
+
folders_1.deleteFolderCommand,
|
|
90
|
+
folders_1.shareFolderCommand,
|
|
91
|
+
],
|
|
92
|
+
},
|
|
64
93
|
{
|
|
65
94
|
name: 'action-items',
|
|
66
95
|
description: 'Search, create, update, and delete action items.',
|
|
@@ -83,8 +112,16 @@ const commandGroups = [
|
|
|
83
112
|
},
|
|
84
113
|
];
|
|
85
114
|
const buildCommandGroup = (definition) => definition.subcommands.reduce((group, command, index) => group.addCommand(command, index === 0 ? { isDefault: true } : undefined), new commander_1.Command(definition.name).description(definition.description));
|
|
86
|
-
const getAllCommands = () => [
|
|
115
|
+
const getAllCommands = (toolList) => [
|
|
87
116
|
...standaloneCommands,
|
|
88
|
-
...
|
|
117
|
+
...(0, value_1.filterMap)(getCommandGroups(toolList), (group) => {
|
|
118
|
+
const subcommands = group.subcommands.filter((command) => {
|
|
119
|
+
const toolName = (0, command_1.getCommandToolName)(command);
|
|
120
|
+
return (!(0, value_1.isPresent)(toolName) || !toolList || Object.hasOwn(toolList, toolName));
|
|
121
|
+
});
|
|
122
|
+
return subcommands.length > 0
|
|
123
|
+
? buildCommandGroup({ ...group, subcommands })
|
|
124
|
+
: undefined;
|
|
125
|
+
}),
|
|
89
126
|
];
|
|
90
127
|
exports.getAllCommands = getAllCommands;
|
package/dist/commands/tags.js
CHANGED
|
@@ -4,37 +4,25 @@ exports.deleteTagCommand = exports.updateTagCommand = exports.createTagCommand =
|
|
|
4
4
|
const commander_1 = require("commander");
|
|
5
5
|
const command_1 = require("../helpers/command");
|
|
6
6
|
const parse_1 = require("../helpers/parse");
|
|
7
|
+
const tag_1 = require("../helpers/tag");
|
|
7
8
|
const value_1 = require("../helpers/value");
|
|
8
9
|
const types_1 = require("../types");
|
|
9
|
-
const createColorOption = () => new commander_1.Option('--color <color>', 'Set the display color').choices([
|
|
10
|
-
'neutral',
|
|
11
|
-
'red',
|
|
12
|
-
'rose',
|
|
13
|
-
'orange',
|
|
14
|
-
'amber',
|
|
15
|
-
'emerald',
|
|
16
|
-
'sky',
|
|
17
|
-
'blue',
|
|
18
|
-
'indigo',
|
|
19
|
-
'purple',
|
|
20
|
-
]);
|
|
21
|
-
const getTagColor = (color) => color ? `bg-tag-${color}` : undefined;
|
|
22
10
|
exports.listTagsCommand = (0, command_1.defineToolCommand)(new commander_1.Command('list').description('List all tags.'), types_1.ToolName.ListTags, () => ({ intent: 'Listing all tags.' }));
|
|
23
11
|
exports.createTagCommand = (0, command_1.defineToolCommand)(new commander_1.Command('create')
|
|
24
12
|
.description('Create a tag.')
|
|
25
13
|
.argument('<name>', 'Tag name')
|
|
26
14
|
.option('--description <description>', 'Set the description')
|
|
27
|
-
.addOption(createColorOption()), types_1.ToolName.CreateTag, (name, options) => ({
|
|
15
|
+
.addOption((0, tag_1.createColorOption)()), types_1.ToolName.CreateTag, (name, options) => ({
|
|
28
16
|
name,
|
|
29
17
|
description: options.description,
|
|
30
|
-
color: getTagColor(options.color),
|
|
18
|
+
color: (0, tag_1.getTagColor)(options.color),
|
|
31
19
|
}));
|
|
32
20
|
exports.updateTagCommand = (0, command_1.defineToolCommand)(new commander_1.Command('update')
|
|
33
21
|
.description('Update a tag.')
|
|
34
22
|
.argument('<id>', 'Tag ID')
|
|
35
23
|
.option('--name <name>', 'Set the name')
|
|
36
24
|
.option('--description <description>', 'Set the description')
|
|
37
|
-
.addOption(createColorOption()), types_1.ToolName.UpdateTag, (identifier, options) => {
|
|
25
|
+
.addOption((0, tag_1.createColorOption)()), types_1.ToolName.UpdateTag, (identifier, options) => {
|
|
38
26
|
if (!Object.values(options).some(value_1.isPresent)) {
|
|
39
27
|
throw new Error('Provide at least one field to update.');
|
|
40
28
|
}
|
|
@@ -42,7 +30,7 @@ exports.updateTagCommand = (0, command_1.defineToolCommand)(new commander_1.Comm
|
|
|
42
30
|
tagId: (0, parse_1.parseIntOption)(identifier, 'id'),
|
|
43
31
|
name: options.name,
|
|
44
32
|
description: options.description,
|
|
45
|
-
color: getTagColor(options.color),
|
|
33
|
+
color: (0, tag_1.getTagColor)(options.color),
|
|
46
34
|
};
|
|
47
35
|
});
|
|
48
36
|
exports.deleteTagCommand = (0, command_1.defineToolCommand)(new commander_1.Command('delete').description('Delete a tag.').argument('<id>', 'Tag ID'), types_1.ToolName.DeleteTag, (identifier) => ({ tagId: (0, parse_1.parseIntOption)(identifier, 'id') }));
|
package/dist/constants.js
CHANGED
|
@@ -3,7 +3,7 @@ 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.DOMAINS_OPTION_DESCRIPTION = exports.PROFILES_OPTION_DESCRIPTION = exports.PRIVATE_NOTES_UPDATE_OPTION_DESCRIPTION = exports.TAGS_UPDATE_OPTION_DESCRIPTION = exports.TAGS_OPTION_DESCRIPTION = exports.END_DATE_OPTION_DESCRIPTION = exports.START_DATE_OPTION_DESCRIPTION = exports.PAGE_OPTION_DESCRIPTION = exports.UNASSIGNED_ACTION_ITEM_ASSIGNEE_LABEL = exports.DEFAULT_MEETING_NAME = exports.MAX_READ_MEETING_IDENTIFIERS = exports.CALLBACK_TIMEOUT_MILLISECONDS = exports.OAUTH_SCOPE = exports.TOKENS_FILE = exports.CONFIG_DIRECTORY = exports.ASCII_LOGO = exports.SECONDARY_COLOR = exports.BRAND_COLOR = exports.API_KEY_SETTINGS_URL = exports.API_KEY_ENVIRONMENT_VARIABLE = exports.API_KEY_PREFIX = exports.CLIENT_VERSION_HEADER = exports.CLIENT_VERSION = exports.CLIENT_PACKAGE_NAME = exports.CLIENT_NAME = exports.BASE_URL = void 0;
|
|
6
|
+
exports.FOLDERS_UPDATE_OPTION_DESCRIPTION = exports.FOLDER_OPTION_DESCRIPTION = exports.DOMAINS_OPTION_DESCRIPTION = exports.PROFILES_OPTION_DESCRIPTION = exports.PRIVATE_NOTES_UPDATE_OPTION_DESCRIPTION = exports.TAGS_UPDATE_OPTION_DESCRIPTION = exports.TAGS_OPTION_DESCRIPTION = exports.END_DATE_OPTION_DESCRIPTION = exports.START_DATE_OPTION_DESCRIPTION = exports.PAGE_OPTION_DESCRIPTION = exports.UNASSIGNED_ACTION_ITEM_ASSIGNEE_LABEL = exports.DEFAULT_MEETING_NAME = exports.MAX_READ_MEETING_IDENTIFIERS = exports.CALLBACK_TIMEOUT_MILLISECONDS = exports.OAUTH_SCOPE = exports.TOKENS_FILE = exports.CONFIG_DIRECTORY = exports.ASCII_LOGO = exports.SECONDARY_COLOR = exports.BRAND_COLOR = exports.API_KEY_SETTINGS_URL = exports.API_KEY_ENVIRONMENT_VARIABLE = exports.API_KEY_PREFIX = exports.CLIENT_VERSION_HEADER = exports.CLIENT_VERSION = exports.CLIENT_PACKAGE_NAME = exports.CLIENT_NAME = exports.BASE_URL = void 0;
|
|
7
7
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
8
8
|
const node_path_1 = __importDefault(require("node:path"));
|
|
9
9
|
const node_os_1 = __importDefault(require("node:os"));
|
|
@@ -56,3 +56,5 @@ exports.TAGS_UPDATE_OPTION_DESCRIPTION = 'Update the tags (comma-separated names
|
|
|
56
56
|
exports.PRIVATE_NOTES_UPDATE_OPTION_DESCRIPTION = 'Update your private notes (supports Markdown)';
|
|
57
57
|
exports.PROFILES_OPTION_DESCRIPTION = 'Comma-separated profile IDs';
|
|
58
58
|
exports.DOMAINS_OPTION_DESCRIPTION = 'Comma-separated domains';
|
|
59
|
+
exports.FOLDER_OPTION_DESCRIPTION = 'Folder ID';
|
|
60
|
+
exports.FOLDERS_UPDATE_OPTION_DESCRIPTION = 'Update the folders (comma-separated IDs, or "" to remove all)';
|
package/dist/helpers/command.js
CHANGED
|
@@ -1,19 +1,25 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.defineToolCommand = void 0;
|
|
3
|
+
exports.defineToolCommand = exports.getCommandToolName = void 0;
|
|
4
4
|
const jsonRpc_1 = require("../client/jsonRpc");
|
|
5
5
|
const error_1 = require("./error");
|
|
6
6
|
const formatter_1 = require("./formatter");
|
|
7
7
|
const output_1 = require("./output");
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
8
|
+
const commandToolNames = new WeakMap();
|
|
9
|
+
const getCommandToolName = (command) => commandToolNames.get(command);
|
|
10
|
+
exports.getCommandToolName = getCommandToolName;
|
|
11
|
+
const defineToolCommand = (command, toolName, getArguments) => {
|
|
12
|
+
commandToolNames.set(command, toolName);
|
|
13
|
+
return command.action(async (...args) => {
|
|
14
|
+
try {
|
|
15
|
+
const result = await (0, jsonRpc_1.callTool)(toolName, await getArguments(...args));
|
|
16
|
+
(0, formatter_1.formatOutput)(toolName, result, Boolean(command.optsWithGlobals().json));
|
|
17
|
+
}
|
|
18
|
+
catch (error) {
|
|
19
|
+
const message = error instanceof Error ? error.message : `${toolName} failed.`;
|
|
20
|
+
(0, output_1.printText)((0, error_1.formatMcpError)(message) ?? message, output_1.TextType.Error);
|
|
21
|
+
process.exit(1);
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
};
|
|
19
25
|
exports.defineToolCommand = defineToolCommand;
|
|
@@ -27,20 +27,20 @@ const formatTagChange = (verb, data) => {
|
|
|
27
27
|
const { id, name } = result.data;
|
|
28
28
|
return `${verb} tag ${id}: ${name}`;
|
|
29
29
|
};
|
|
30
|
-
const
|
|
30
|
+
const ShareChangeSchema = zod_1.z.object({
|
|
31
31
|
id: zod_1.z.string(),
|
|
32
32
|
name: zod_1.z.string().nullable(),
|
|
33
33
|
errors: zod_1.z
|
|
34
34
|
.array(zod_1.z.object({ index: zod_1.z.number(), error: zod_1.z.string() }))
|
|
35
35
|
.optional(),
|
|
36
36
|
});
|
|
37
|
-
const
|
|
38
|
-
const result =
|
|
37
|
+
const formatShareChange = (kind, data) => {
|
|
38
|
+
const result = ShareChangeSchema.safeParse(data);
|
|
39
39
|
if (!result.success)
|
|
40
40
|
return;
|
|
41
41
|
const { id, name, errors = [] } = result.data;
|
|
42
42
|
return [
|
|
43
|
-
`Updated sharing for
|
|
43
|
+
`Updated sharing for ${kind} ${id}${name ? `: ${name}` : ''}`,
|
|
44
44
|
...errors.map(({ index, error }) => `Could not share target ${index + 1}: ${error}`),
|
|
45
45
|
].join('\n');
|
|
46
46
|
};
|
|
@@ -59,7 +59,9 @@ const formatForTool = (toolName, data) => {
|
|
|
59
59
|
case types_1.ToolName.DeleteTag:
|
|
60
60
|
return formatTagChange('Deleted', data);
|
|
61
61
|
case types_1.ToolName.ShareMeeting:
|
|
62
|
-
return
|
|
62
|
+
return formatShareChange('meeting', data);
|
|
63
|
+
case types_1.ToolName.ShareFolder:
|
|
64
|
+
return formatShareChange('folder', data);
|
|
63
65
|
}
|
|
64
66
|
if (!Array.isArray(data))
|
|
65
67
|
return;
|
|
@@ -87,6 +89,11 @@ const formatForTool = (toolName, data) => {
|
|
|
87
89
|
case types_1.ToolName.ListTags:
|
|
88
90
|
return (0, table_1.formatTags)(data);
|
|
89
91
|
case types_1.ToolName.ListTeams:
|
|
92
|
+
case types_1.ToolName.ListFolders:
|
|
93
|
+
case types_1.ToolName.ReadFolders:
|
|
94
|
+
case types_1.ToolName.CreateFolder:
|
|
95
|
+
case types_1.ToolName.UpdateFolder:
|
|
96
|
+
case types_1.ToolName.DeleteFolder:
|
|
90
97
|
case types_1.ToolName.UpdateMeeting:
|
|
91
98
|
case types_1.ToolName.UpdateCalendarEvent:
|
|
92
99
|
return;
|
|
@@ -98,6 +105,10 @@ const formatForTool = (toolName, data) => {
|
|
|
98
105
|
};
|
|
99
106
|
const formatOutput = (toolName, rawText, jsonMode) => {
|
|
100
107
|
const parsed = (0, parse_1.extractAndValidateObjectFromString)(rawText, zod_1.z.json());
|
|
108
|
+
const isPartialShare = (toolName === types_1.ToolName.ShareMeeting || toolName === types_1.ToolName.ShareFolder) &&
|
|
109
|
+
(ShareChangeSchema.safeParse(parsed).data?.errors?.length ?? 0) > 0;
|
|
110
|
+
if (isPartialShare)
|
|
111
|
+
process.exitCode = 1;
|
|
101
112
|
if (jsonMode) {
|
|
102
113
|
(0, output_1.printText)(stringify(parsed), output_1.TextType.Default);
|
|
103
114
|
return;
|
package/dist/helpers/parse.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.validateReadMeetingIdentifierLimit = exports.validateMeetingIdentifiers = exports.parseNumericIds = exports.parseOptionalIntOption = exports.parseIntOption = exports.extractAndValidateObjectFromString = void 0;
|
|
3
|
+
exports.validateReadMeetingIdentifierLimit = exports.validateMeetingIdentifiers = exports.parseNumericIds = exports.parseOptionalIntOption = exports.parseJsonOption = exports.parseIntOption = exports.extractAndValidateObjectFromString = void 0;
|
|
4
4
|
const zod_1 = require("zod");
|
|
5
5
|
const constants_1 = require("../constants");
|
|
6
6
|
const output_1 = require("./output");
|
|
@@ -29,6 +29,16 @@ const parseIntOption = (value, flagName) => {
|
|
|
29
29
|
return result.data;
|
|
30
30
|
};
|
|
31
31
|
exports.parseIntOption = parseIntOption;
|
|
32
|
+
const parseJsonOption = (value, flagName) => {
|
|
33
|
+
try {
|
|
34
|
+
return JSON.parse(value);
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
(0, output_1.printText)(`Invalid JSON for ${flagName}.`, output_1.TextType.Error);
|
|
38
|
+
process.exit(1);
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
exports.parseJsonOption = parseJsonOption;
|
|
32
42
|
const parseOptionalIntOption = (value, flagName) => (value ? (0, exports.parseIntOption)(value, flagName) : undefined);
|
|
33
43
|
exports.parseOptionalIntOption = parseOptionalIntOption;
|
|
34
44
|
const parseNumericIds = (value, label) => {
|
package/dist/helpers/tag.js
CHANGED
|
@@ -1,11 +1,28 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.resolveTagValues = void 0;
|
|
3
|
+
exports.resolveTagValues = exports.getTagColor = exports.createColorOption = exports.TAG_COLOR_NAMES = void 0;
|
|
4
|
+
const commander_1 = require("commander");
|
|
4
5
|
const zod_1 = require("zod");
|
|
5
6
|
const jsonRpc_1 = require("../client/jsonRpc");
|
|
6
7
|
const output_1 = require("./output");
|
|
7
8
|
const parse_1 = require("./parse");
|
|
8
9
|
const types_1 = require("../types");
|
|
10
|
+
exports.TAG_COLOR_NAMES = [
|
|
11
|
+
'neutral',
|
|
12
|
+
'red',
|
|
13
|
+
'rose',
|
|
14
|
+
'orange',
|
|
15
|
+
'amber',
|
|
16
|
+
'emerald',
|
|
17
|
+
'sky',
|
|
18
|
+
'blue',
|
|
19
|
+
'indigo',
|
|
20
|
+
'purple',
|
|
21
|
+
];
|
|
22
|
+
const createColorOption = () => new commander_1.Option('--color <color>', 'Set the display color').choices(exports.TAG_COLOR_NAMES);
|
|
23
|
+
exports.createColorOption = createColorOption;
|
|
24
|
+
const getTagColor = (color) => color ? `bg-tag-${color}` : undefined;
|
|
25
|
+
exports.getTagColor = getTagColor;
|
|
9
26
|
const tagItemSchema = zod_1.z.object({
|
|
10
27
|
id: zod_1.z.number(),
|
|
11
28
|
name: zod_1.z.string(),
|
|
@@ -0,0 +1,44 @@
|
|
|
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.hasToolProperty = exports.refreshToolList = exports.readCachedToolList = void 0;
|
|
7
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
8
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
9
|
+
const credentials_1 = require("../auth/credentials");
|
|
10
|
+
const jsonRpc_1 = require("../client/jsonRpc");
|
|
11
|
+
const constants_1 = require("../constants");
|
|
12
|
+
const types_1 = require("../types");
|
|
13
|
+
const CACHE_FILE = node_path_1.default.join(constants_1.CONFIG_DIRECTORY, 'tool-list.json');
|
|
14
|
+
const REFRESH_INTERVAL_MILLISECONDS = 60 * 60 * 1000; // 1 hour
|
|
15
|
+
const readCachedToolList = () => {
|
|
16
|
+
try {
|
|
17
|
+
const { tools, checkedAt, credential } = types_1.ToolListCacheSchema.parse(JSON.parse(node_fs_1.default.readFileSync(CACHE_FILE, 'utf-8')));
|
|
18
|
+
if (credential !== (0, credentials_1.getCredentialFingerprint)())
|
|
19
|
+
return { isStale: true };
|
|
20
|
+
return {
|
|
21
|
+
toolList: tools,
|
|
22
|
+
isStale: Date.now() - checkedAt >= REFRESH_INTERVAL_MILLISECONDS,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
return { isStale: true };
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
exports.readCachedToolList = readCachedToolList;
|
|
30
|
+
const refreshToolList = async () => {
|
|
31
|
+
try {
|
|
32
|
+
const credential = (0, credentials_1.getCredentialFingerprint)();
|
|
33
|
+
const tools = await (0, jsonRpc_1.listTools)();
|
|
34
|
+
node_fs_1.default.mkdirSync(constants_1.CONFIG_DIRECTORY, { recursive: true, mode: 0o700 });
|
|
35
|
+
node_fs_1.default.writeFileSync(CACHE_FILE, JSON.stringify({ tools, checkedAt: Date.now(), credential }), { mode: 0o600 });
|
|
36
|
+
return tools;
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
exports.refreshToolList = refreshToolList;
|
|
43
|
+
const hasToolProperty = ({ toolList, toolName, property, }) => !toolList || toolList[toolName]?.includes(property) === true;
|
|
44
|
+
exports.hasToolProperty = hasToolProperty;
|
package/dist/helpers/value.js
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.isPresent = void 0;
|
|
3
|
+
exports.filterMap = exports.isPresent = void 0;
|
|
4
4
|
const isPresent = (value) => value !== null && value !== undefined;
|
|
5
5
|
exports.isPresent = isPresent;
|
|
6
|
+
const filterMap = (array, mappingFunction) => array.flatMap((value, index) => {
|
|
7
|
+
const mapped = mappingFunction(value, index);
|
|
8
|
+
return (0, exports.isPresent)(mapped) ? [mapped] : [];
|
|
9
|
+
});
|
|
10
|
+
exports.filterMap = filterMap;
|
package/dist/index.js
CHANGED
|
@@ -8,9 +8,16 @@ const constants_1 = require("./constants");
|
|
|
8
8
|
const help_1 = require("./helpers/help");
|
|
9
9
|
const output_1 = require("./helpers/output");
|
|
10
10
|
const style_1 = require("./helpers/style");
|
|
11
|
+
const toolList_1 = require("./helpers/toolList");
|
|
11
12
|
const update_1 = require("./helpers/update");
|
|
13
|
+
const value_1 = require("./helpers/value");
|
|
12
14
|
const program = new commander_1.Command();
|
|
13
15
|
const updateCheck = (0, update_1.getUpdateCheckResult)(constants_1.CLIENT_PACKAGE_NAME);
|
|
16
|
+
const isRootHelp = process.argv
|
|
17
|
+
.slice(2)
|
|
18
|
+
.every((argument) => ['--help', '-h', '--json', 'help', '--'].includes(argument));
|
|
19
|
+
const cachedToolList = (0, toolList_1.readCachedToolList)();
|
|
20
|
+
const toolListRefresh = isRootHelp || cachedToolList.isStale ? (0, toolList_1.refreshToolList)() : undefined;
|
|
14
21
|
const configureCommandOutput = (command) => {
|
|
15
22
|
command.helpOption('-h, --help', 'Display help for command');
|
|
16
23
|
if (command.commands.length > 0) {
|
|
@@ -27,11 +34,7 @@ program
|
|
|
27
34
|
.name('circleback')
|
|
28
35
|
.version(constants_1.CLIENT_VERSION, '-V, --version', 'Output the version number')
|
|
29
36
|
.option('--json', 'Output raw JSON instead of formatted text')
|
|
30
|
-
.allowExcessArguments(false)
|
|
31
|
-
.action(() => program.help())
|
|
32
37
|
.exitOverride();
|
|
33
|
-
(0, registry_1.getAllCommands)().reduce((p, command) => p.addCommand(command), program);
|
|
34
|
-
configureCommandOutput(program);
|
|
35
38
|
program.hook('postAction', async (_thisCommand, actionCommand) => {
|
|
36
39
|
if (actionCommand.name() === 'update')
|
|
37
40
|
return;
|
|
@@ -41,11 +44,13 @@ program.hook('postAction', async (_thisCommand, actionCommand) => {
|
|
|
41
44
|
catch { }
|
|
42
45
|
});
|
|
43
46
|
const run = async () => {
|
|
44
|
-
const
|
|
45
|
-
.
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
47
|
+
const [loginStatus, refreshedToolList] = await Promise.all([
|
|
48
|
+
isRootHelp ? (0, credentials_1.getLoginStatus)() : undefined,
|
|
49
|
+
toolListRefresh,
|
|
50
|
+
]);
|
|
51
|
+
(0, registry_1.getAllCommands)(refreshedToolList ?? cachedToolList.toolList).reduce((p, command) => p.addCommand(command), program);
|
|
52
|
+
configureCommandOutput(program);
|
|
53
|
+
if ((0, value_1.isPresent)(loginStatus)) {
|
|
49
54
|
program
|
|
50
55
|
.addHelpText('before', () => (0, style_1.formatWelcomeBanner)(process.stdout.columns ?? 80, loginStatus))
|
|
51
56
|
.addHelpText('before', [
|
|
@@ -57,6 +62,7 @@ const run = async () => {
|
|
|
57
62
|
' cb meetings --json Get JSON for scripts',
|
|
58
63
|
'',
|
|
59
64
|
].join('\n'));
|
|
65
|
+
program.help();
|
|
60
66
|
}
|
|
61
67
|
await program.parseAsync();
|
|
62
68
|
};
|
package/dist/types.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.isJsonRpcResponse = exports.jsonRpcResultSchema = exports.isOAuthTokenResponse = exports.isStoredApiKey = exports.parseStoredCredentials = exports.ShareableMeetingAccess = exports.ActionItemStatus = exports.ToolName = exports.CredentialSource = exports.CredentialType = void 0;
|
|
3
|
+
exports.isJsonRpcResponse = exports.jsonRpcResultSchema = exports.isOAuthTokenResponse = exports.isStoredApiKey = exports.parseStoredCredentials = exports.ShareableMeetingAccess = exports.ActionItemStatus = exports.ToolName = exports.ToolListCacheSchema = exports.ToolListResponseSchema = exports.CredentialSource = exports.CredentialType = void 0;
|
|
4
4
|
const zod_1 = require("zod");
|
|
5
5
|
var CredentialType;
|
|
6
6
|
(function (CredentialType) {
|
|
@@ -12,6 +12,21 @@ var CredentialSource;
|
|
|
12
12
|
CredentialSource["Environment"] = "environment";
|
|
13
13
|
CredentialSource["Stored"] = "stored";
|
|
14
14
|
})(CredentialSource || (exports.CredentialSource = CredentialSource = {}));
|
|
15
|
+
exports.ToolListResponseSchema = zod_1.z.object({
|
|
16
|
+
result: zod_1.z.object({
|
|
17
|
+
tools: zod_1.z.array(zod_1.z.object({
|
|
18
|
+
name: zod_1.z.string(),
|
|
19
|
+
inputSchema: zod_1.z.object({
|
|
20
|
+
properties: zod_1.z.record(zod_1.z.string(), zod_1.z.unknown()).optional(),
|
|
21
|
+
}),
|
|
22
|
+
})),
|
|
23
|
+
}),
|
|
24
|
+
});
|
|
25
|
+
exports.ToolListCacheSchema = zod_1.z.object({
|
|
26
|
+
tools: zod_1.z.record(zod_1.z.string(), zod_1.z.array(zod_1.z.string())),
|
|
27
|
+
checkedAt: zod_1.z.number(),
|
|
28
|
+
credential: zod_1.z.string().optional(),
|
|
29
|
+
});
|
|
15
30
|
var ToolName;
|
|
16
31
|
(function (ToolName) {
|
|
17
32
|
ToolName["SearchMeetings"] = "SearchMeetings";
|
|
@@ -26,12 +41,18 @@ var ToolName;
|
|
|
26
41
|
ToolName["SearchActionItems"] = "SearchActionItems";
|
|
27
42
|
ToolName["ListTags"] = "ListTags";
|
|
28
43
|
ToolName["ListTeams"] = "ListTeams";
|
|
44
|
+
ToolName["ListFolders"] = "ListFolders";
|
|
45
|
+
ToolName["ReadFolders"] = "ReadFolders";
|
|
29
46
|
ToolName["CreateActionItem"] = "CreateActionItem";
|
|
30
47
|
ToolName["UpdateActionItem"] = "UpdateActionItem";
|
|
31
48
|
ToolName["DeleteActionItem"] = "DeleteActionItem";
|
|
32
49
|
ToolName["CreateTag"] = "CreateTag";
|
|
33
50
|
ToolName["UpdateTag"] = "UpdateTag";
|
|
34
51
|
ToolName["DeleteTag"] = "DeleteTag";
|
|
52
|
+
ToolName["CreateFolder"] = "CreateFolder";
|
|
53
|
+
ToolName["UpdateFolder"] = "UpdateFolder";
|
|
54
|
+
ToolName["DeleteFolder"] = "DeleteFolder";
|
|
55
|
+
ToolName["ShareFolder"] = "ShareFolder";
|
|
35
56
|
ToolName["ShareMeeting"] = "ShareMeeting";
|
|
36
57
|
ToolName["UpdateMeeting"] = "UpdateMeeting";
|
|
37
58
|
ToolName["UpdateCalendarEvent"] = "UpdateCalendarEvent";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@circleback/cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "Circleback CLI. Search and access meetings, emails, calendar events, and more.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Circleback <support@circleback.ai> (https://circleback.ai)",
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"scripts": {
|
|
25
25
|
"prebuild": "rm -rf dist",
|
|
26
26
|
"build": "tsc && chmod +x dist/index.js",
|
|
27
|
-
"typecheck": "
|
|
27
|
+
"typecheck": "bun check --all",
|
|
28
28
|
"test:unit": "jest --runInBand",
|
|
29
29
|
"postinstall": "echo '\n Welcome to the Circleback CLI. Run `circleback` or `cb` to get started.\n'",
|
|
30
30
|
"prepublishOnly": "pnpm run build"
|