@circleback/cli 0.1.7 → 0.1.9

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
@@ -22,9 +22,12 @@ cb meetings
22
22
  # Search by keyword
23
23
  cb meetings "product review"
24
24
 
25
- # Read full meeting details
25
+ # Read full meeting details by numeric ID
26
26
  cb meetings read 12345
27
27
 
28
+ # Read by linkId (from the meeting URL, e.g. circleback.ai/meetings/Vd6Pz_kWqLm3xY-c8RhTn)
29
+ cb meetings read Vd6Pz_kWqLm3xY-c8RhTn
30
+
28
31
  # Search transcripts
29
32
  cb transcripts "pricing discussion"
30
33
 
@@ -37,23 +40,32 @@ cb meetings --json
37
40
 
38
41
  ## Commands
39
42
 
40
- | Command | Description |
41
- | ------------------------------ | --------------------------------------------------- |
42
- | `cb login` | Authenticate with Circleback via browser |
43
- | `cb logout` | Clear stored authentication tokens |
44
- | `cb update` | Update CLI to the latest version |
45
- | `cb meetings [search]` | Search meetings |
46
- | `cb meetings read <ids...>` | Read detailed meeting info, notes, and action items |
47
- | `cb transcripts <search>` | Search transcript content |
48
- | `cb transcripts read <ids...>` | Read full transcripts |
49
- | `cb calendar` | Search calendar events |
50
- | `cb people <names...>` | Search people by name |
51
- | `cb companies <terms...>` | Search companies by domain |
52
- | `cb emails [search]` | Search connected email accounts |
53
- | `cb support [search]` | Search Circleback support articles |
43
+ | Command | Description |
44
+ | ------------------------------ | ------------------------------------------------------------------------------------ |
45
+ | `cb login` | Authenticate with Circleback via browser |
46
+ | `cb logout` | Clear stored authentication tokens |
47
+ | `cb update` | Update CLI to the latest version |
48
+ | `cb meetings [search]` | Search meetings |
49
+ | `cb meetings read <ids...>` | Read detailed meeting info, notes, and action items (numeric IDs or linkIds, max 50) |
50
+ | `cb transcripts <search>` | Search transcript content |
51
+ | `cb transcripts read <ids...>` | Read full transcripts (numeric IDs or linkIds, max 50) |
52
+ | `cb calendar` | Search calendar events |
53
+ | `cb people <names...>` | Search people by name |
54
+ | `cb companies <terms...>` | Search companies by domain |
55
+ | `cb emails [search]` | Search connected email accounts |
56
+ | `cb support [search]` | Search Circleback support articles |
54
57
 
55
58
  Run `cb --help` or `cb <command> --help` for full usage details.
56
59
 
60
+ ## Meeting identifiers
61
+
62
+ `cb meetings read` and `cb transcripts read` accept up to 50 IDs in either form:
63
+
64
+ - **Numeric ID** — the integer ID returned in search results (e.g. `12345`)
65
+ - **linkId** — the short alphanumeric slug from a meeting URL (e.g. `Vd6Pz_kWqLm3xY-c8RhTn` from `circleback.ai/meetings/Vd6Pz_kWqLm3xY-c8RhTn`)
66
+
67
+ Both forms can be mixed in a single call: `cb meetings read 12345 Vd6Pz_kWqLm3xY-c8RhTn`
68
+
57
69
  ## Filtering
58
70
 
59
71
  Meeting, transcript, and calendar searches support filtering by date range. Meeting and transcript searches also support filtering by tag, person, and company:
@@ -90,7 +90,7 @@ const refreshAccessToken = async (refreshToken, clientId) => {
90
90
  return tokenData;
91
91
  };
92
92
  exports.refreshAccessToken = refreshAccessToken;
93
- const login = async () => {
93
+ const login = async (options = {}) => {
94
94
  const { port, waitForCallback, shutdown } = await (0, server_1.startCallbackServer)();
95
95
  const redirectUri = `http://127.0.0.1:${port}`;
96
96
  try {
@@ -106,6 +106,7 @@ const login = async () => {
106
106
  authorizeUrl.searchParams.set('code_challenge', challenge);
107
107
  authorizeUrl.searchParams.set('code_challenge_method', 'S256');
108
108
  const url = authorizeUrl.toString();
109
+ options.onAuthorizeUrl?.(url);
109
110
  const isWindows = process.platform === 'win32';
110
111
  const escapedUrl = isWindows ? url.replace(/&/g, '^&') : url;
111
112
  const [command, ...arguments_] = isWindows
@@ -8,6 +8,24 @@ const commander_1 = require("commander");
8
8
  const chalk_1 = __importDefault(require("chalk"));
9
9
  const oauth_1 = require("../auth/oauth");
10
10
  const tokenStorage_1 = require("../auth/tokenStorage");
11
+ const AUTHORIZATION_URL_FALLBACK_DELAY_MILLISECONDS = 500;
12
+ const getOpenUrlInstruction = () => process.platform === 'darwin' ? '⌘-click' : 'Ctrl-click';
13
+ const createAuthorizationUrlFallback = () => {
14
+ const abortController = new AbortController();
15
+ const schedule = (url) => {
16
+ const timeout = setTimeout(() => {
17
+ if (abortController.signal.aborted)
18
+ return;
19
+ console.log(`Browser didn't open? ${getOpenUrlInstruction()} the URL below to log in.\n`);
20
+ console.log(chalk_1.default.cyan(url));
21
+ }, AUTHORIZATION_URL_FALLBACK_DELAY_MILLISECONDS);
22
+ abortController.signal.addEventListener('abort', () => clearTimeout(timeout), { once: true });
23
+ };
24
+ return {
25
+ schedule,
26
+ cleanup: () => abortController.abort(),
27
+ };
28
+ };
11
29
  exports.loginCommand = new commander_1.Command('login')
12
30
  .description('Authenticate with Circleback via browser.')
13
31
  .action(async () => {
@@ -17,8 +35,16 @@ exports.loginCommand = new commander_1.Command('login')
17
35
  console.log(chalk_1.default.yellow('Already authenticated. Run `circleback logout` first to re-authenticate.'));
18
36
  return;
19
37
  }
20
- console.log('Opening browser for authentication…');
21
- await (0, oauth_1.login)();
38
+ console.log('Opening your browser for authentication…\n');
39
+ const authorizationUrlFallback = createAuthorizationUrlFallback();
40
+ try {
41
+ await (0, oauth_1.login)({
42
+ onAuthorizeUrl: authorizationUrlFallback.schedule,
43
+ });
44
+ }
45
+ finally {
46
+ authorizationUrlFallback.cleanup();
47
+ }
22
48
  console.log(chalk_1.default.green('Authentication successful.'));
23
49
  }
24
50
  catch (error) {
@@ -67,13 +67,13 @@ exports.searchMeetingsCommand = new commander_1.Command('search')
67
67
  });
68
68
  exports.readMeetingsCommand = new commander_1.Command('read')
69
69
  .description('Get detailed meeting information.')
70
- .argument('<ids...>', 'Meeting IDs')
70
+ .argument('<ids...>', 'Meeting numeric IDs or linkIds (max 50)')
71
71
  .action(async (ids) => {
72
72
  try {
73
73
  const jsonMode = exports.readMeetingsCommand.parent?.parent?.opts().json ?? false;
74
- const meetingIds = (0, parse_1.validateNumericIds)(ids, 'meeting ID');
74
+ const meetingIds = (0, parse_1.validateMeetingIdentifiers)(ids);
75
75
  const result = await (0, jsonRpc_1.callTool)('ReadMeetings', {
76
- intent: `Reading meeting details for IDs: ${meetingIds.join(', ')}.`,
76
+ intent: `Reading meeting details for IDs or linkIds: ${meetingIds.join(', ')}.`,
77
77
  meetingIds,
78
78
  });
79
79
  (0, formatter_1.formatOutput)('ReadMeetings', result, jsonMode);
@@ -21,12 +21,12 @@ exports.standaloneCommands = [
21
21
  exports.commandGroups = [
22
22
  {
23
23
  name: 'meetings',
24
- description: 'Search and read meetings.',
24
+ description: 'Search meetings with `cb meetings search [term]` and read them with `cb meetings read <id>`.',
25
25
  subcommands: [meetings_1.searchMeetingsCommand, meetings_1.readMeetingsCommand],
26
26
  },
27
27
  {
28
28
  name: 'transcripts',
29
- description: 'Search and read transcripts.',
29
+ description: 'Search transcripts with `cb transcripts search <term>` and read them with `cb transcripts read <id>`.',
30
30
  subcommands: [transcripts_1.searchTranscriptsCommand, transcripts_1.getTranscriptsCommand],
31
31
  },
32
32
  {
@@ -44,14 +44,14 @@ exports.searchTranscriptsCommand = new commander_1.Command('search')
44
44
  });
45
45
  exports.getTranscriptsCommand = new commander_1.Command('read')
46
46
  .description('Get full meeting transcripts.')
47
- .argument('<meetingId...>', 'Meeting IDs (max 50)')
47
+ .argument('<meetingId...>', 'Meeting numeric IDs or linkIds (max 50)')
48
48
  .showHelpAfterError(true)
49
49
  .action(async (ids) => {
50
50
  try {
51
51
  const jsonMode = exports.getTranscriptsCommand.parent?.parent?.opts().json ?? false;
52
- const meetingIds = (0, parse_1.validateNumericIds)(ids, 'meeting ID');
52
+ const meetingIds = (0, parse_1.validateMeetingIdentifiers)(ids);
53
53
  const result = await (0, jsonRpc_1.callTool)('GetTranscriptsForMeetings', {
54
- intent: `Getting transcripts for meeting IDs: ${meetingIds.join(', ')}.`,
54
+ intent: `Getting transcripts for meetings with IDs or linkIds: ${meetingIds.join(', ')}.`,
55
55
  meetingIds,
56
56
  });
57
57
  (0, formatter_1.formatOutput)('GetTranscriptsForMeetings', result, jsonMode);
@@ -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.validateNumericIds = exports.parseNumericIds = exports.parseIntOption = exports.extractAndValidateObjectFromString = void 0;
6
+ exports.validateMeetingIdentifiers = exports.validateNumericIds = exports.parseNumericIds = exports.parseIntOption = exports.extractAndValidateObjectFromString = void 0;
7
7
  const chalk_1 = __importDefault(require("chalk"));
8
8
  const zod_1 = require("zod");
9
9
  const extractAndValidateObjectFromString = (rawText, schema) => {
@@ -49,3 +49,14 @@ const validateNumericIds = (values, label) => {
49
49
  return result.data;
50
50
  };
51
51
  exports.validateNumericIds = validateNumericIds;
52
+ const validateMeetingIdentifiers = (values) => {
53
+ const cleanedValues = values.map((value) => value.trim()).filter(Boolean);
54
+ const isValidIdentifier = /^[A-Za-z0-9_-]{1,64}$/;
55
+ if (cleanedValues.length === 0 ||
56
+ cleanedValues.some((value) => !isValidIdentifier.test(value))) {
57
+ console.error(chalk_1.default.red('Invalid meeting identifiers. Expected one or more numeric IDs or linkIds.'));
58
+ process.exit(1);
59
+ }
60
+ return cleanedValues;
61
+ };
62
+ exports.validateMeetingIdentifiers = validateMeetingIdentifiers;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@circleback/cli",
3
- "version": "0.1.7",
3
+ "version": "0.1.9",
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)",