@circleback/cli 0.6.0 → 0.8.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 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.
3
+ Search and access your meetings, transcripts, emails, calendar events, and more from the terminal, and manage your action items, tags, and meeting access.
4
4
 
5
5
  ## Installation
6
6
 
@@ -22,8 +22,8 @@ cb meetings
22
22
  # Search by keyword
23
23
  cb meetings "product review"
24
24
 
25
- # Read by ID (from the meeting URL, e.g. circleback.ai/meetings/Vd6Pz8WqLm3xY2cRhTn7K)
26
- cb meetings read Vd6Pz8WqLm3xY2cRhTn7K
25
+ # Read by ID (from the meeting URL, e.g. circleback.ai/meetings/abc123)
26
+ cb meetings read abc123
27
27
 
28
28
  # Search transcripts
29
29
  cb transcripts "pricing discussion"
@@ -48,9 +48,12 @@ cb meetings --json
48
48
  | `cb update` | Update CLI to the latest version |
49
49
  | `cb meetings [search]` | Search meetings |
50
50
  | `cb meetings read <ids...>` | Read detailed meeting info, notes, and action items (max 50) |
51
+ | `cb meetings update <id>` | Update a meeting's name, notes, or private notes |
52
+ | `cb meetings share <id>` | Share a meeting or remove access |
51
53
  | `cb transcripts <search>` | Search transcript content |
52
54
  | `cb transcripts read <ids...>` | Read full transcripts (max 50) |
53
55
  | `cb calendar` | Search calendar events |
56
+ | `cb calendar update <id>` | Update private notes for a meeting before it starts |
54
57
  | `cb people <names...>` | Search people by name |
55
58
  | `cb companies <terms...>` | Search companies by name or domain |
56
59
  | `cb emails [search]` | Search connected email accounts |
@@ -115,7 +118,7 @@ For stateless automation, set `CIRCLEBACK_API_KEY` instead of logging in:
115
118
  CIRCLEBACK_API_KEY=cb_your_api_key cb meetings --json
116
119
  ```
117
120
 
118
- Changing action items or tags 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.
121
+ 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.
119
122
 
120
123
  `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.
121
124
 
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.searchCalendarCommand = void 0;
3
+ exports.updateCalendarEventCommand = exports.searchCalendarCommand = void 0;
4
4
  const commander_1 = require("commander");
5
5
  const constants_1 = require("../constants");
6
6
  const command_1 = require("../helpers/command");
@@ -16,3 +16,10 @@ exports.searchCalendarCommand = (0, command_1.defineToolCommand)(new commander_1
16
16
  startDate: (0, date_1.getStartDate)(options),
17
17
  endDate: options.to || undefined,
18
18
  }));
19
+ exports.updateCalendarEventCommand = (0, command_1.defineToolCommand)(new commander_1.Command('update')
20
+ .description('Update private notes for a meeting before it starts. Replaces the current notes.')
21
+ .argument('<id>', 'Event ID from `cb calendar`')
22
+ .requiredOption('--private-notes <notes>', 'Your private notes (Markdown)'), types_1.ToolName.UpdateCalendarEvent, (calendarMeetingId, { privateNotes }) => ({
23
+ calendarMeetingId,
24
+ privateNotes,
25
+ }));
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.readMeetingsCommand = exports.searchMeetingsCommand = void 0;
3
+ exports.updateMeetingCommand = exports.shareMeetingCommand = exports.readMeetingsCommand = exports.searchMeetingsCommand = void 0;
4
4
  const commander_1 = require("commander");
5
5
  const constants_1 = require("../constants");
6
6
  const command_1 = require("../helpers/command");
@@ -43,3 +43,74 @@ exports.readMeetingsCommand = (0, command_1.defineToolCommand)(new commander_1.C
43
43
  meetingIds,
44
44
  };
45
45
  });
46
+ const ACCESS_LEVELS = [...Object.values(types_1.ShareableMeetingAccess), 'none'];
47
+ const getAccess = (access) => access === 'none' ? null : access;
48
+ exports.shareMeetingCommand = (0, command_1.defineToolCommand)(new commander_1.Command('share')
49
+ .description('Share a meeting, or remove access with --access none.')
50
+ .argument('<id>', 'Meeting ID')
51
+ .option('--email <emails>', 'Share with email addresses (comma-separated)')
52
+ .option('--profile <ids>', 'Share with people (comma-separated)')
53
+ .option('--workspace <ids>', 'Share with workspaces (comma-separated)')
54
+ .option('--team <ids>', 'Share with teams (comma-separated)')
55
+ .addOption(new commander_1.Option('--access <access>', 'Set the access level, or none to remove access (required with --email or --profile)').choices(ACCESS_LEVELS))
56
+ .addOption(new commander_1.Option('--link <access>', 'Set the meeting link access, or none to turn it off').choices(ACCESS_LEVELS))
57
+ .option('--send-email', 'Email the people the meeting is shared with'), types_1.ToolName.ShareMeeting, (meetingId, options) => {
58
+ const targets = [
59
+ options.email,
60
+ options.profile,
61
+ options.team,
62
+ options.workspace,
63
+ options.link,
64
+ ];
65
+ if (targets.every((target) => target === undefined)) {
66
+ throw new Error('Set at least one of --email, --profile, --team, --workspace, or --link.');
67
+ }
68
+ if ((options.email || options.profile) && !options.access) {
69
+ throw new Error('--access is required with --email or --profile.');
70
+ }
71
+ const access = getAccess(options.access);
72
+ const sendEmail = options.sendEmail;
73
+ const parseIds = (value, label) => value ? (0, parse_1.parseNumericIds)(value, label) : [];
74
+ return {
75
+ meetingId,
76
+ shares: [
77
+ ...(options.email?.split(',') ?? []).map((email) => ({
78
+ email: email.trim(),
79
+ access,
80
+ sendEmail,
81
+ })),
82
+ ...parseIds(options.profile, 'profile ID').map((profileId) => ({
83
+ profileId,
84
+ access,
85
+ sendEmail,
86
+ })),
87
+ ...parseIds(options.team, 'team ID').map((teamId) => ({
88
+ teamId,
89
+ access,
90
+ sendEmail,
91
+ })),
92
+ ...parseIds(options.workspace, 'workspace ID').map((workspaceId) => ({
93
+ workspaceId,
94
+ access,
95
+ sendEmail,
96
+ })),
97
+ ...(options.link ? [{ linkAccess: getAccess(options.link) }] : []),
98
+ ],
99
+ };
100
+ });
101
+ const addMeetingFieldOptions = (command) => command
102
+ .option('--name <name>', 'Set the name')
103
+ .option('--notes <notes>', 'Set the notes (Markdown)')
104
+ .option('--private-notes <notes>', 'Set your private notes (Markdown)');
105
+ const getMeetingFields = (fields) => {
106
+ if (!Object.values(fields).some((value) => value !== undefined)) {
107
+ throw new Error('Provide at least one field to update.');
108
+ }
109
+ return fields;
110
+ };
111
+ exports.updateMeetingCommand = (0, command_1.defineToolCommand)(addMeetingFieldOptions(new commander_1.Command('update')
112
+ .description('Update a meeting. Each option replaces the current value.')
113
+ .argument('<id>', 'Meeting ID')), types_1.ToolName.UpdateMeeting, (meetingId, fields) => ({
114
+ meetingId,
115
+ ...getMeetingFields(fields),
116
+ }));
@@ -17,8 +17,13 @@ const standaloneCommands = [auth_1.authCommand, update_1.updateCommand];
17
17
  const commandGroups = [
18
18
  {
19
19
  name: 'meetings',
20
- description: 'Find meetings and read notes.',
21
- subcommands: [meetings_1.searchMeetingsCommand, meetings_1.readMeetingsCommand],
20
+ description: 'Find, read, update, and share meetings.',
21
+ subcommands: [
22
+ meetings_1.searchMeetingsCommand,
23
+ meetings_1.readMeetingsCommand,
24
+ meetings_1.updateMeetingCommand,
25
+ meetings_1.shareMeetingCommand,
26
+ ],
22
27
  },
23
28
  {
24
29
  name: 'transcripts',
@@ -27,8 +32,8 @@ const commandGroups = [
27
32
  },
28
33
  {
29
34
  name: 'calendar',
30
- description: 'Search calendar events.',
31
- subcommands: [calendar_1.searchCalendarCommand],
35
+ description: 'Search and update calendar events.',
36
+ subcommands: [calendar_1.searchCalendarCommand, calendar_1.updateCalendarEventCommand],
32
37
  },
33
38
  {
34
39
  name: 'people',
@@ -27,6 +27,23 @@ const formatTagChange = (verb, data) => {
27
27
  const { id, name } = result.data;
28
28
  return `${verb} tag ${id}: ${name}`;
29
29
  };
30
+ const MeetingShareChangeSchema = zod_1.z.object({
31
+ id: zod_1.z.string(),
32
+ name: zod_1.z.string().nullable(),
33
+ errors: zod_1.z
34
+ .array(zod_1.z.object({ index: zod_1.z.number(), error: zod_1.z.string() }))
35
+ .optional(),
36
+ });
37
+ const formatMeetingShareChange = (data) => {
38
+ const result = MeetingShareChangeSchema.safeParse(data);
39
+ if (!result.success)
40
+ return;
41
+ const { id, name, errors = [] } = result.data;
42
+ return [
43
+ `Updated sharing for meeting ${id}${name ? `: ${name}` : ''}`,
44
+ ...errors.map(({ index, error }) => `Could not share target ${index + 1}: ${error}`),
45
+ ].join('\n');
46
+ };
30
47
  const formatForTool = (toolName, data) => {
31
48
  switch (toolName) {
32
49
  case types_1.ToolName.CreateActionItem:
@@ -41,6 +58,8 @@ const formatForTool = (toolName, data) => {
41
58
  return formatTagChange('Updated', data);
42
59
  case types_1.ToolName.DeleteTag:
43
60
  return formatTagChange('Deleted', data);
61
+ case types_1.ToolName.ShareMeeting:
62
+ return formatMeetingShareChange(data);
44
63
  }
45
64
  if (!Array.isArray(data))
46
65
  return;
@@ -69,6 +88,8 @@ const formatForTool = (toolName, data) => {
69
88
  return (0, table_1.formatTags)(data);
70
89
  case types_1.ToolName.AddTagToMeetings:
71
90
  case types_1.ToolName.RemoveTagFromMeetings:
91
+ case types_1.ToolName.UpdateMeeting:
92
+ case types_1.ToolName.UpdateCalendarEvent:
72
93
  return;
73
94
  default: {
74
95
  const _absurd = toolName;
@@ -140,17 +140,26 @@ const formatCalendarEvents = (events) => {
140
140
  const table = new cli_table3_1.default({
141
141
  head: [
142
142
  style_1.brand.bold('Title'),
143
+ style_1.brand.bold('ID'),
143
144
  style_1.brand.bold('Start'),
144
145
  style_1.brand.bold('End'),
145
146
  style_1.brand.bold('Platform'),
146
147
  style_1.brand.bold('Attendees'),
147
148
  ],
148
- colWidths: [30, 20, 20, 12, 35],
149
+ colWidths: [
150
+ 30,
151
+ idColumnWidth(events.map((event) => event.id)),
152
+ 20,
153
+ 20,
154
+ 12,
155
+ 35,
156
+ ],
149
157
  wordWrap: true,
150
158
  style: style_1.tableStyle,
151
159
  });
152
160
  table.push(...events.map((event) => [
153
161
  (0, string_1.truncate)(event.title, 28),
162
+ event.id,
154
163
  (0, date_1.formatDateTime)(event.startTime),
155
164
  (0, date_1.formatDateTime)(event.endTime),
156
165
  event.platform ?? '-',
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.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.CredentialSource = exports.CredentialType = void 0;
4
4
  const zod_1 = require("zod");
5
5
  var CredentialType;
6
6
  (function (CredentialType) {
@@ -33,12 +33,21 @@ var ToolName;
33
33
  ToolName["DeleteTag"] = "DeleteTag";
34
34
  ToolName["AddTagToMeetings"] = "AddTagToMeetings";
35
35
  ToolName["RemoveTagFromMeetings"] = "RemoveTagFromMeetings";
36
+ ToolName["ShareMeeting"] = "ShareMeeting";
37
+ ToolName["UpdateMeeting"] = "UpdateMeeting";
38
+ ToolName["UpdateCalendarEvent"] = "UpdateCalendarEvent";
36
39
  })(ToolName || (exports.ToolName = ToolName = {}));
37
40
  var ActionItemStatus;
38
41
  (function (ActionItemStatus) {
39
42
  ActionItemStatus["Pending"] = "PENDING";
40
43
  ActionItemStatus["Done"] = "DONE";
41
44
  })(ActionItemStatus || (exports.ActionItemStatus = ActionItemStatus = {}));
45
+ var ShareableMeetingAccess;
46
+ (function (ShareableMeetingAccess) {
47
+ ShareableMeetingAccess["Editor"] = "Editor";
48
+ ShareableMeetingAccess["Viewer"] = "Viewer";
49
+ ShareableMeetingAccess["LimitedViewer"] = "LimitedViewer";
50
+ })(ShareableMeetingAccess || (exports.ShareableMeetingAccess = ShareableMeetingAccess = {}));
42
51
  const storedTokensSchema = zod_1.z.object({
43
52
  accessToken: zod_1.z.string(),
44
53
  refreshToken: zod_1.z.string(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@circleback/cli",
3
- "version": "0.6.0",
3
+ "version": "0.8.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)",