@hasna/connectors 1.3.45 → 1.3.46

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.
Files changed (114) hide show
  1. package/README.md +25 -0
  2. package/bin/index.js +792 -130
  3. package/bin/mcp.js +438 -52
  4. package/bin/serve.js +438 -52
  5. package/connectors/cloudflare-workers/.env.example +5 -0
  6. package/connectors/cloudflare-workers/AGENTS.md +11 -0
  7. package/connectors/cloudflare-workers/CLAUDE.md +11 -0
  8. package/connectors/cloudflare-workers/README.md +45 -0
  9. package/connectors/cloudflare-workers/package.json +47 -0
  10. package/connectors/cloudflare-workers/src/api/client.test.ts +144 -0
  11. package/connectors/cloudflare-workers/src/api/client.ts +386 -0
  12. package/connectors/cloudflare-workers/src/api/index.ts +1 -0
  13. package/connectors/cloudflare-workers/src/cli/index.ts +206 -0
  14. package/connectors/cloudflare-workers/src/index.ts +16 -0
  15. package/connectors/cloudflare-workers/src/types/index.ts +53 -0
  16. package/connectors/cloudflare-workers/src/utils/config.ts +61 -0
  17. package/connectors/cloudflare-workers/src/utils/output.ts +26 -0
  18. package/connectors/cloudflare-workers/tsconfig.build.json +4 -0
  19. package/connectors/cloudflare-workers/tsconfig.json +14 -0
  20. package/connectors/tidio/.env.example +3 -2
  21. package/connectors/tidio/CLAUDE.md +21 -96
  22. package/connectors/tidio/README.md +22 -18
  23. package/connectors/tidio/package.json +4 -2
  24. package/connectors/tidio/src/api/client.ts +26 -15
  25. package/connectors/tidio/src/api/index.ts +185 -128
  26. package/connectors/tidio/src/api/tidio.test.ts +175 -44
  27. package/connectors/tidio/src/cli/index.ts +154 -388
  28. package/connectors/tidio/src/index.ts +5 -3
  29. package/connectors/tidio/src/types/index.ts +135 -82
  30. package/connectors/tidio/src/utils/config.ts +90 -44
  31. package/connectors/wandb/.env.example +7 -0
  32. package/connectors/wandb/AGENTS.md +43 -0
  33. package/connectors/wandb/CLAUDE.md +67 -0
  34. package/connectors/wandb/README.md +65 -0
  35. package/connectors/wandb/package.json +52 -0
  36. package/connectors/wandb/src/api/client.test.ts +116 -0
  37. package/connectors/wandb/src/api/client.ts +83 -0
  38. package/connectors/wandb/src/api/graphql.ts +9 -0
  39. package/connectors/wandb/src/api/index.ts +36 -0
  40. package/connectors/wandb/src/api/projects.ts +36 -0
  41. package/connectors/wandb/src/api/viewer.ts +20 -0
  42. package/connectors/wandb/src/cli/index.ts +265 -0
  43. package/connectors/wandb/src/index.ts +22 -0
  44. package/connectors/wandb/src/types/index.ts +75 -0
  45. package/connectors/wandb/src/utils/config.ts +164 -0
  46. package/connectors/wandb/src/utils/output.ts +119 -0
  47. package/connectors/wandb/tsconfig.json +16 -0
  48. package/connectors/weights-biases/.env.example +7 -0
  49. package/connectors/weights-biases/CLAUDE.md +65 -0
  50. package/connectors/weights-biases/README.md +62 -0
  51. package/connectors/weights-biases/package.json +53 -0
  52. package/connectors/weights-biases/src/api/client.test.ts +46 -0
  53. package/connectors/weights-biases/src/api/client.ts +119 -0
  54. package/connectors/weights-biases/src/api/events.ts +18 -0
  55. package/connectors/weights-biases/src/api/index.ts +56 -0
  56. package/connectors/weights-biases/src/api/runs.ts +28 -0
  57. package/connectors/weights-biases/src/api/search.ts +10 -0
  58. package/connectors/weights-biases/src/cli/index.ts +289 -0
  59. package/connectors/weights-biases/src/index.ts +19 -0
  60. package/connectors/weights-biases/src/types/index.ts +92 -0
  61. package/connectors/weights-biases/src/utils/config.ts +168 -0
  62. package/connectors/weights-biases/src/utils/output.ts +116 -0
  63. package/connectors/weights-biases/tsconfig.json +16 -0
  64. package/connectors/zoominfo/.env.example +4 -0
  65. package/connectors/zoominfo/AGENTS.md +26 -0
  66. package/connectors/zoominfo/CLAUDE.md +20 -0
  67. package/connectors/zoominfo/README.md +69 -0
  68. package/connectors/zoominfo/package.json +52 -0
  69. package/connectors/zoominfo/src/api/client.test.ts +140 -0
  70. package/connectors/zoominfo/src/api/client.ts +228 -0
  71. package/connectors/zoominfo/src/api/index.ts +1 -0
  72. package/connectors/zoominfo/src/cli/index.ts +179 -0
  73. package/connectors/zoominfo/src/index.ts +3 -0
  74. package/connectors/zoominfo/src/types/index.ts +39 -0
  75. package/connectors/zoominfo/src/utils/config.ts +61 -0
  76. package/connectors/zoominfo/src/utils/output.ts +27 -0
  77. package/connectors/zoominfo/tsconfig.json +20 -0
  78. package/connectors/zotero/.env.example +7 -0
  79. package/connectors/zotero/.npmrc.example +2 -0
  80. package/connectors/zotero/AGENTS.md +40 -0
  81. package/connectors/zotero/CLAUDE.md +69 -0
  82. package/connectors/zotero/README.md +91 -0
  83. package/connectors/zotero/package.json +43 -0
  84. package/connectors/zotero/src/api/attachments.ts +130 -0
  85. package/connectors/zotero/src/api/client.test.ts +166 -0
  86. package/connectors/zotero/src/api/client.ts +169 -0
  87. package/connectors/zotero/src/api/collections.ts +26 -0
  88. package/connectors/zotero/src/api/index.ts +45 -0
  89. package/connectors/zotero/src/api/items.ts +64 -0
  90. package/connectors/zotero/src/cli/index.ts +386 -0
  91. package/connectors/zotero/src/index.ts +28 -0
  92. package/connectors/zotero/src/types/index.ts +129 -0
  93. package/connectors/zotero/src/utils/config.ts +245 -0
  94. package/connectors/zotero/src/utils/output.ts +119 -0
  95. package/connectors/zotero/tsconfig.json +16 -0
  96. package/connectors/zymbly/.env.example +2 -0
  97. package/connectors/zymbly/AGENTS.md +11 -0
  98. package/connectors/zymbly/CLAUDE.md +5 -0
  99. package/connectors/zymbly/README.md +35 -0
  100. package/connectors/zymbly/package.json +52 -0
  101. package/connectors/zymbly/src/api/client.test.ts +110 -0
  102. package/connectors/zymbly/src/api/client.ts +166 -0
  103. package/connectors/zymbly/src/api/index.ts +1 -0
  104. package/connectors/zymbly/src/cli/index.ts +156 -0
  105. package/connectors/zymbly/src/index.ts +12 -0
  106. package/connectors/zymbly/src/types/index.ts +34 -0
  107. package/connectors/zymbly/src/utils/config.ts +52 -0
  108. package/connectors/zymbly/src/utils/output.ts +17 -0
  109. package/connectors/zymbly/tsconfig.json +13 -0
  110. package/dist/index.js +45 -2
  111. package/dist/lib/compact-output.d.ts +40 -0
  112. package/dist/mcp/tools/jobs.d.ts +32 -0
  113. package/dist/mcp/tools/jobs.test.d.ts +1 -0
  114. package/package.json +3 -2
@@ -2,23 +2,22 @@
2
2
  import { Command } from 'commander';
3
3
  import chalk from 'chalk';
4
4
  import { Tidio } from '../api';
5
- import type { ConversationStatus, MessageType, WebhookEvent } from '../types';
5
+ import type { OutputFormat } from '../types';
6
6
  import {
7
- getApiKey,
8
- setApiKey,
9
7
  clearConfig,
8
+ createProfile,
9
+ deleteProfile,
10
+ getClientCredentials,
10
11
  getConfigDir,
11
- setProfileOverride,
12
12
  getCurrentProfile,
13
- setCurrentProfile,
14
13
  listProfiles,
15
- createProfile,
16
- deleteProfile,
17
- profileExists,
18
14
  loadProfile,
15
+ profileExists,
16
+ setClientCredentials,
17
+ setCurrentProfile,
18
+ setProfileOverride,
19
19
  } from '../utils/config';
20
- import type { OutputFormat } from '../utils/output';
21
- import { success, error, info, print } from '../utils/output';
20
+ import { error, info, print, success } from '../utils/output';
22
21
 
23
22
  const CONNECTOR_NAME = 'connect-tidio';
24
23
  const VERSION = '0.0.1';
@@ -27,12 +26,13 @@ const program = new Command();
27
26
 
28
27
  program
29
28
  .name(CONNECTOR_NAME)
30
- .description('Tidio connector - Live chat support, contacts, conversations, and operators')
29
+ .description('Tidio connector - contacts, contact messages, departments, operators, project, tickets, products, and Lyro')
31
30
  .version(VERSION)
32
- .option('-k, --api-key <key>', 'API key (overrides config)')
31
+ .option('--client-id <id>', 'OpenAPI client id (overrides config)')
32
+ .option('--client-secret <secret>', 'OpenAPI client secret (overrides config)')
33
33
  .option('-f, --format <format>', 'Output format (json, pretty)', 'pretty')
34
34
  .option('-p, --profile <profile>', 'Use a specific profile')
35
- .hook('preAction', (thisCommand) => {
35
+ .hook('preAction', thisCommand => {
36
36
  const opts = thisCommand.opts();
37
37
  if (opts.profile) {
38
38
  if (!profileExists(opts.profile)) {
@@ -41,26 +41,25 @@ program
41
41
  }
42
42
  setProfileOverride(opts.profile);
43
43
  }
44
- if (opts.apiKey) {
45
- process.env.TIDIO_API_KEY = opts.apiKey;
46
- }
44
+ if (opts.clientId) process.env.TIDIO_CLIENT_ID = opts.clientId;
45
+ if (opts.clientSecret) process.env.TIDIO_CLIENT_SECRET = opts.clientSecret;
47
46
  });
48
47
 
49
48
  function getFormat(cmd: Command): OutputFormat {
50
- const parent = cmd.parent;
51
- return (parent?.opts().format || 'pretty') as OutputFormat;
49
+ return (cmd.opts().format || cmd.parent?.opts().format || 'pretty') as OutputFormat;
52
50
  }
53
51
 
54
52
  function getClient(): Tidio {
55
- const apiKey = getApiKey();
56
- if (!apiKey) {
57
- error(`No API key configured. Run "${CONNECTOR_NAME} config set-key <key>" or set TIDIO_API_KEY environment variable.`);
53
+ const { clientId, clientSecret } = getClientCredentials();
54
+ if (!clientId || !clientSecret) {
55
+ error(
56
+ `No Tidio credentials configured. Run "${CONNECTOR_NAME} config set-credentials <client-id> <client-secret>" or set TIDIO_CLIENT_ID and TIDIO_CLIENT_SECRET.`,
57
+ );
58
58
  process.exit(1);
59
59
  }
60
- return new Tidio({ apiKey });
60
+ return new Tidio({ clientId, clientSecret });
61
61
  }
62
62
 
63
- // Profile Commands
64
63
  const profileCmd = program.command('profile').description('Manage configuration profiles');
65
64
 
66
65
  profileCmd.command('list').description('List all profiles').action(() => {
@@ -71,17 +70,13 @@ profileCmd.command('list').description('List all profiles').action(() => {
71
70
  return;
72
71
  }
73
72
  success('Profiles:');
74
- profiles.forEach(p => {
75
- const isActive = p === current ? chalk.green(' (active)') : '';
76
- console.log(` ${p}${isActive}`);
73
+ profiles.forEach(profile => {
74
+ const active = profile === current ? chalk.green(' (active)') : '';
75
+ console.log(` ${profile}${active}`);
77
76
  });
78
77
  });
79
78
 
80
79
  profileCmd.command('use <name>').description('Switch to a profile').action((name: string) => {
81
- if (!profileExists(name)) {
82
- error(`Profile "${name}" does not exist. Create it with "profile create ${name}"`);
83
- process.exit(1);
84
- }
85
80
  setCurrentProfile(name);
86
81
  success(`Switched to profile: ${name}`);
87
82
  });
@@ -89,14 +84,14 @@ profileCmd.command('use <name>').description('Switch to a profile').action((name
89
84
  profileCmd
90
85
  .command('create <name>')
91
86
  .description('Create a new profile')
92
- .option('--key <key>', 'API key')
87
+ .option('--client-id <id>', 'OpenAPI client id')
88
+ .option('--client-secret <secret>', 'OpenAPI client secret')
93
89
  .option('--use', 'Switch to this profile after creation')
94
90
  .action((name: string, opts) => {
95
- if (profileExists(name)) {
91
+ if (!createProfile(name, { clientId: opts.clientId, clientSecret: opts.clientSecret })) {
96
92
  error(`Profile "${name}" already exists`);
97
93
  process.exit(1);
98
94
  }
99
- createProfile(name, { apiKey: opts.key });
100
95
  success(`Profile "${name}" created`);
101
96
  if (opts.use) {
102
97
  setCurrentProfile(name);
@@ -105,16 +100,12 @@ profileCmd
105
100
  });
106
101
 
107
102
  profileCmd.command('delete <name>').description('Delete a profile').action((name: string) => {
108
- if (name === 'default') {
109
- error('Cannot delete the default profile');
110
- process.exit(1);
111
- }
112
103
  if (deleteProfile(name)) {
113
104
  success(`Profile "${name}" deleted`);
114
- } else {
115
- error(`Profile "${name}" not found`);
116
- process.exit(1);
105
+ return;
117
106
  }
107
+ error(`Profile "${name}" was not deleted`);
108
+ process.exit(1);
118
109
  });
119
110
 
120
111
  profileCmd.command('show [name]').description('Show profile configuration').action((name?: string) => {
@@ -122,23 +113,24 @@ profileCmd.command('show [name]').description('Show profile configuration').acti
122
113
  const config = loadProfile(profileName);
123
114
  const active = getCurrentProfile();
124
115
  console.log(chalk.bold(`Profile: ${profileName}${profileName === active ? chalk.green(' (active)') : ''}`));
125
- info(`API Key: ${config.apiKey ? `${config.apiKey.substring(0, 8)}...` : chalk.gray('not set')}`);
116
+ info(`Client ID: ${config.clientId ? `${config.clientId.substring(0, 8)}...` : chalk.gray('not set')}`);
117
+ info(`Client Secret: ${config.clientSecret ? chalk.gray('set') : chalk.gray('not set')}`);
126
118
  });
127
119
 
128
- // Config Commands
129
120
  const configCmd = program.command('config').description('Manage CLI configuration');
130
121
 
131
- configCmd.command('set-key <key>').description('Set API key').action((key: string) => {
132
- setApiKey(key);
133
- success(`API key saved to profile: ${getCurrentProfile()}`);
122
+ configCmd.command('set-credentials <client-id> <client-secret>').description('Set OpenAPI credentials').action((clientId: string, clientSecret: string) => {
123
+ setClientCredentials(clientId, clientSecret);
124
+ success(`Credentials saved to profile: ${getCurrentProfile()}`);
134
125
  });
135
126
 
136
127
  configCmd.command('show').description('Show current configuration').action(() => {
137
128
  const profileName = getCurrentProfile();
138
- const apiKey = getApiKey();
129
+ const { clientId, clientSecret } = getClientCredentials();
139
130
  console.log(chalk.bold(`Active Profile: ${profileName}`));
140
131
  info(`Config directory: ${getConfigDir()}`);
141
- info(`API Key: ${apiKey ? `${apiKey.substring(0, 8)}...` : chalk.gray('not set')}`);
132
+ info(`Client ID: ${clientId ? `${clientId.substring(0, 8)}...` : chalk.gray('not set')}`);
133
+ info(`Client Secret: ${clientSecret ? chalk.gray('set') : chalk.gray('not set')}`);
142
134
  });
143
135
 
144
136
  configCmd.command('clear').description('Clear configuration').action(() => {
@@ -146,391 +138,165 @@ configCmd.command('clear').description('Clear configuration').action(() => {
146
138
  success(`Configuration cleared for profile: ${getCurrentProfile()}`);
147
139
  });
148
140
 
149
- // Contact Commands
150
- const contactCmd = program.command('contact').description('Manage contacts');
141
+ const contactCmd = program.command('contact').description('Manage contacts and contact messages');
151
142
 
152
- contactCmd
153
- .command('list')
154
- .description('List contacts')
155
- .option('-n, --limit <number>', 'Results limit')
156
- .option('--cursor <cursor>', 'Pagination cursor')
157
- .option('--updated-after <iso>', 'Filter by updated after')
158
- .option('--updated-before <iso>', 'Filter by updated before')
159
- .action(async (opts) => {
160
- try {
161
- const client = getClient();
162
- const result = await client.listContacts({
163
- limit: opts.limit ? parseInt(opts.limit, 10) : undefined,
164
- cursor: opts.cursor,
165
- updatedAfter: opts.updatedAfter,
166
- updatedBefore: opts.updatedBefore,
167
- });
168
- print(result, getFormat(contactCmd));
169
- } catch (err) {
170
- error(String(err));
171
- process.exit(1);
172
- }
173
- });
143
+ contactCmd.command('list').description('List contacts').option('-n, --limit <number>').option('--cursor <cursor>').action(async opts => {
144
+ print(await getClient().listContacts({ limit: opts.limit ? Number(opts.limit) : undefined, cursor: opts.cursor }), getFormat(contactCmd));
145
+ });
174
146
 
175
- contactCmd.command('get <id>').description('Get a contact by ID').action(async (id: string) => {
176
- try {
177
- print(await getClient().getContact(id), getFormat(contactCmd));
178
- } catch (err) {
179
- error(String(err));
180
- process.exit(1);
181
- }
147
+ contactCmd.command('get <id>').description('Get a contact').action(async (id: string) => {
148
+ print(await getClient().getContact(id), getFormat(contactCmd));
182
149
  });
183
150
 
184
151
  contactCmd
185
152
  .command('create')
186
153
  .description('Create a contact')
187
- .option('--email <email>', 'Email address')
188
- .option('--phone <phone>', 'Phone number')
189
- .option('--name <name>', 'Contact name')
190
- .option('--external-id <id>', 'External ID')
191
- .option('--tags <tags>', 'Comma-separated tags')
192
- .option('--subscriber', 'Mark as subscriber')
193
- .option('--consent', 'Marketing consent')
194
- .action(async (opts) => {
195
- try {
196
- const result = await getClient().createContact({
154
+ .option('--email <email>')
155
+ .option('--phone <phone>')
156
+ .option('--first-name <name>')
157
+ .option('--last-name <name>')
158
+ .option('--distinct-id <id>')
159
+ .action(async opts => {
160
+ print(
161
+ await getClient().createContact({
197
162
  email: opts.email,
198
163
  phone: opts.phone,
199
- name: opts.name,
200
- externalId: opts.externalId,
201
- tags: opts.tags ? opts.tags.split(',').map((t: string) => t.trim()) : undefined,
202
- subscriber: opts.subscriber,
203
- consent: opts.consent,
204
- });
205
- success('Contact created!');
206
- print(result, getFormat(contactCmd));
207
- } catch (err) {
208
- error(String(err));
209
- process.exit(1);
210
- }
164
+ firstName: opts.firstName,
165
+ lastName: opts.lastName,
166
+ distinctId: opts.distinctId,
167
+ }),
168
+ getFormat(contactCmd),
169
+ );
211
170
  });
212
171
 
213
172
  contactCmd
214
173
  .command('update <id>')
215
174
  .description('Update a contact')
216
- .option('--email <email>', 'Email address')
217
- .option('--phone <phone>', 'Phone number')
218
- .option('--name <name>', 'Contact name')
219
- .option('--external-id <id>', 'External ID')
220
- .option('--tags <tags>', 'Comma-separated tags')
175
+ .option('--email <email>')
176
+ .option('--phone <phone>')
177
+ .option('--first-name <name>')
178
+ .option('--last-name <name>')
179
+ .option('--distinct-id <id>')
221
180
  .action(async (id: string, opts) => {
222
- try {
223
- const result = await getClient().updateContact(id, {
181
+ print(
182
+ await getClient().updateContact(id, {
224
183
  email: opts.email,
225
184
  phone: opts.phone,
226
- name: opts.name,
227
- externalId: opts.externalId,
228
- tags: opts.tags ? opts.tags.split(',').map((t: string) => t.trim()) : undefined,
229
- });
230
- success('Contact updated!');
231
- print(result, getFormat(contactCmd));
232
- } catch (err) {
233
- error(String(err));
234
- process.exit(1);
235
- }
185
+ firstName: opts.firstName,
186
+ lastName: opts.lastName,
187
+ distinctId: opts.distinctId,
188
+ }),
189
+ getFormat(contactCmd),
190
+ );
236
191
  });
237
192
 
238
193
  contactCmd.command('delete <id>').description('Delete a contact').action(async (id: string) => {
239
- try {
240
- await getClient().deleteContact(id);
241
- success('Contact deleted!');
242
- } catch (err) {
243
- error(String(err));
244
- process.exit(1);
245
- }
194
+ await getClient().deleteContact(id);
195
+ success('Contact deleted');
246
196
  });
247
197
 
248
- // Conversation Commands
249
- const conversationCmd = program.command('conversation').description('Manage conversations');
250
-
251
- conversationCmd
252
- .command('list')
253
- .description('List conversations')
254
- .option('-n, --limit <number>', 'Results limit')
255
- .option('--cursor <cursor>', 'Pagination cursor')
256
- .option('--status <status>', 'Filter by status (open, closed, snoozed)')
257
- .option('--channel <channel>', 'Filter by channel')
258
- .option('--updated-after <iso>', 'Filter by updated after')
259
- .action(async (opts) => {
260
- try {
261
- const result = await getClient().listConversations({
262
- limit: opts.limit ? parseInt(opts.limit, 10) : undefined,
263
- cursor: opts.cursor,
264
- status: opts.status as ConversationStatus | undefined,
265
- channel: opts.channel,
266
- updatedAfter: opts.updatedAfter,
267
- });
268
- print(result, getFormat(conversationCmd));
269
- } catch (err) {
270
- error(String(err));
271
- process.exit(1);
272
- }
273
- });
274
-
275
- conversationCmd.command('get <id>').description('Get a conversation by ID').action(async (id: string) => {
276
- try {
277
- print(await getClient().getConversation(id), getFormat(conversationCmd));
278
- } catch (err) {
279
- error(String(err));
280
- process.exit(1);
281
- }
198
+ contactCmd.command('properties').description('Get contact property definitions').action(async () => {
199
+ print(await getClient().getContactProperties(), getFormat(contactCmd));
282
200
  });
283
201
 
284
- conversationCmd
285
- .command('messages <id>')
286
- .description('List messages in a conversation')
287
- .option('-n, --limit <number>', 'Results limit')
288
- .option('--cursor <cursor>', 'Pagination cursor')
289
- .action(async (id: string, opts) => {
290
- try {
291
- const result = await getClient().listConversationMessages(id, {
292
- limit: opts.limit ? parseInt(opts.limit, 10) : undefined,
293
- cursor: opts.cursor,
294
- });
295
- print(result, getFormat(conversationCmd));
296
- } catch (err) {
297
- error(String(err));
298
- process.exit(1);
299
- }
300
- });
301
-
302
- conversationCmd
303
- .command('send <id>')
304
- .description('Send a message in a conversation')
305
- .requiredOption('--type <type>', 'Message type (text, image, file, note)')
306
- .requiredOption('--content <content>', 'Message content')
307
- .option('--media-url <url>', 'Media URL for image/file')
308
- .option('--private', 'Send as private note')
309
- .option('--operator-id <id>', 'Operator ID')
310
- .action(async (id: string, opts) => {
311
- try {
312
- const result = await getClient().sendConversationMessage(id, {
313
- type: opts.type as MessageType,
314
- content: opts.content,
315
- mediaUrl: opts.mediaUrl,
316
- private: opts.private,
317
- operatorId: opts.operatorId,
318
- });
319
- success('Message sent!');
320
- print(result, getFormat(conversationCmd));
321
- } catch (err) {
322
- error(String(err));
323
- process.exit(1);
324
- }
325
- });
326
-
327
- conversationCmd
328
- .command('status <id>')
329
- .description('Set conversation status')
330
- .requiredOption('--status <status>', 'Status (open, closed, snoozed)')
331
- .option('--snoozed-until <iso>', 'Snooze until (ISO datetime)')
332
- .action(async (id: string, opts) => {
333
- try {
334
- const result = await getClient().setConversationStatus(id, {
335
- status: opts.status as ConversationStatus,
336
- snoozedUntil: opts.snoozedUntil,
337
- });
338
- success('Conversation status updated!');
339
- print(result, getFormat(conversationCmd));
340
- } catch (err) {
341
- error(String(err));
342
- process.exit(1);
343
- }
344
- });
345
-
346
- conversationCmd
347
- .command('assign <id>')
348
- .description('Assign a conversation')
349
- .option('--operator-id <id>', 'Operator ID (omit with --unassign to clear)')
350
- .option('--unassign', 'Unassign operator')
351
- .option('--department-id <id>', 'Department ID')
352
- .action(async (id: string, opts) => {
353
- try {
354
- const result = await getClient().assignConversation(id, {
355
- operatorId: opts.unassign ? null : opts.operatorId ?? null,
356
- departmentId: opts.departmentId,
357
- });
358
- success('Conversation assigned!');
359
- print(result, getFormat(conversationCmd));
360
- } catch (err) {
361
- error(String(err));
362
- process.exit(1);
363
- }
364
- });
365
-
366
- // Operator Commands
367
- const operatorCmd = program.command('operator').description('Manage operators');
368
-
369
- operatorCmd.command('list').description('List operators').action(async () => {
370
- try {
371
- print(await getClient().listOperators(), getFormat(operatorCmd));
372
- } catch (err) {
373
- error(String(err));
374
- process.exit(1);
375
- }
202
+ contactCmd.command('viewed-pages <id>').description('Get viewed pages history').action(async (id: string) => {
203
+ print(await getClient().getContactViewedPages(id), getFormat(contactCmd));
376
204
  });
377
205
 
378
- operatorCmd.command('get <id>').description('Get an operator by ID').action(async (id: string) => {
379
- try {
380
- print(await getClient().getOperator(id), getFormat(operatorCmd));
381
- } catch (err) {
382
- error(String(err));
383
- process.exit(1);
384
- }
206
+ contactCmd.command('messages <id>').description('List contact messages').option('--cursor <cursor>').action(async (id: string, opts) => {
207
+ print(await getClient().listContactMessages(id, { cursor: opts.cursor }), getFormat(contactCmd));
385
208
  });
386
209
 
387
- // Department Commands
388
- const departmentCmd = program.command('department').description('Manage departments');
389
-
390
- departmentCmd.command('list').description('List departments').action(async () => {
391
- try {
392
- print(await getClient().listDepartments(), getFormat(departmentCmd));
393
- } catch (err) {
394
- error(String(err));
395
- process.exit(1);
396
- }
210
+ contactCmd.command('send-message <id>').description('Send a message on behalf of a contact').requiredOption('-m, --message <message>').action(async (id: string, opts) => {
211
+ print(await getClient().sendContactMessage(id, { message: opts.message }), getFormat(contactCmd));
397
212
  });
398
213
 
399
- // Tag Commands
400
- const tagCmd = program.command('tag').description('Manage tags');
401
-
402
- tagCmd.command('list').description('List tags').action(async () => {
403
- try {
404
- print(await getClient().listTags(), getFormat(tagCmd));
405
- } catch (err) {
406
- error(String(err));
407
- process.exit(1);
408
- }
214
+ program.command('operator').description('List operators').action(async () => {
215
+ print(await getClient().listOperators(), getFormat(program));
409
216
  });
410
217
 
411
- tagCmd
412
- .command('create <name>')
413
- .description('Create a tag')
414
- .option('--color <color>', 'Tag color')
415
- .action(async (name: string, opts) => {
416
- try {
417
- const result = await getClient().createTag({ name, color: opts.color });
418
- success('Tag created!');
419
- print(result, getFormat(tagCmd));
420
- } catch (err) {
421
- error(String(err));
422
- process.exit(1);
423
- }
424
- });
425
-
426
- tagCmd.command('delete <id>').description('Delete a tag').action(async (id: string) => {
427
- try {
428
- await getClient().deleteTag(id);
429
- success('Tag deleted!');
430
- } catch (err) {
431
- error(String(err));
432
- process.exit(1);
433
- }
218
+ program.command('department').description('List departments').action(async () => {
219
+ print(await getClient().listDepartments(), getFormat(program));
434
220
  });
435
221
 
436
- // Automation Commands
437
- const automationCmd = program.command('automation').description('Manage automations');
438
-
439
- automationCmd.command('list').description('List automations').action(async () => {
440
- try {
441
- print(await getClient().listAutomations(), getFormat(automationCmd));
442
- } catch (err) {
443
- error(String(err));
444
- process.exit(1);
445
- }
222
+ program.command('project').description('Get project info').action(async () => {
223
+ print(await getClient().getProject(), getFormat(program));
446
224
  });
447
225
 
448
- // Canned Response Commands
449
- const cannedCmd = program.command('canned-response').description('Manage canned responses');
450
-
451
- cannedCmd.command('list').description('List canned responses').action(async () => {
452
- try {
453
- print(await getClient().listCannedResponses(), getFormat(cannedCmd));
454
- } catch (err) {
455
- error(String(err));
456
- process.exit(1);
457
- }
226
+ const ticketCmd = program.command('ticket').description('Manage tickets');
227
+
228
+ ticketCmd.command('list').option('-n, --limit <number>').option('--cursor <cursor>').option('--status <status>').option('--priority <priority>').action(async opts => {
229
+ print(
230
+ await getClient().listTickets({
231
+ limit: opts.limit ? Number(opts.limit) : undefined,
232
+ cursor: opts.cursor,
233
+ status: opts.status,
234
+ priority: opts.priority,
235
+ }),
236
+ getFormat(ticketCmd),
237
+ );
458
238
  });
459
239
 
460
- cannedCmd
461
- .command('create')
462
- .description('Create a canned response')
463
- .requiredOption('--shortcut <shortcut>', 'Shortcut keyword')
464
- .requiredOption('--content <content>', 'Response content')
465
- .option('--department-id <id>', 'Department ID')
466
- .action(async (opts) => {
467
- try {
468
- const result = await getClient().createCannedResponse({
469
- shortcut: opts.shortcut,
470
- content: opts.content,
471
- departmentId: opts.departmentId,
472
- });
473
- success('Canned response created!');
474
- print(result, getFormat(cannedCmd));
475
- } catch (err) {
476
- error(String(err));
477
- process.exit(1);
478
- }
479
- });
480
-
481
- // Webhook Commands
482
- const webhookCmd = program.command('webhook').description('Manage webhooks');
483
-
484
- webhookCmd.command('list').description('List webhooks').action(async () => {
485
- try {
486
- print(await getClient().listWebhooks(), getFormat(webhookCmd));
487
- } catch (err) {
488
- error(String(err));
489
- process.exit(1);
490
- }
240
+ ticketCmd.command('get <id>').action(async (id: string) => {
241
+ print(await getClient().getTicket(id), getFormat(ticketCmd));
491
242
  });
492
243
 
493
- webhookCmd
494
- .command('create')
495
- .description('Create a webhook')
496
- .requiredOption('--url <url>', 'Webhook URL')
497
- .requiredOption('--events <events>', 'Comma-separated events')
498
- .option('--secret <secret>', 'Webhook secret')
499
- .action(async (opts) => {
500
- try {
501
- const result = await getClient().createWebhook({
502
- url: opts.url,
503
- events: opts.events.split(',').map((e: string) => e.trim()) as WebhookEvent[],
504
- secret: opts.secret,
505
- });
506
- success('Webhook created!');
507
- print(result, getFormat(webhookCmd));
508
- } catch (err) {
509
- error(String(err));
510
- process.exit(1);
511
- }
512
- });
244
+ ticketCmd.command('reply <id>').requiredOption('-m, --message <message>').option('--author-type <type>').action(async (id: string, opts) => {
245
+ print(await getClient().replyToTicket(id, { message: opts.message, authorType: opts.authorType }), getFormat(ticketCmd));
246
+ });
513
247
 
514
- webhookCmd.command('delete <id>').description('Delete a webhook').action(async (id: string) => {
515
- try {
516
- await getClient().deleteWebhook(id);
517
- success('Webhook deleted!');
518
- } catch (err) {
519
- error(String(err));
520
- process.exit(1);
521
- }
248
+ ticketCmd.command('tags').description('Get ticket tags').action(async () => {
249
+ print(await getClient().getTicketTags(), getFormat(ticketCmd));
522
250
  });
523
251
 
524
- // Project Commands
525
- const projectCmd = program.command('project').description('Project information');
252
+ ticketCmd.command('custom-fields').description('Get ticket custom fields').action(async () => {
253
+ print(await getClient().getTicketCustomFields(), getFormat(ticketCmd));
254
+ });
526
255
 
527
- projectCmd.command('get').description('Get project details').action(async () => {
528
- try {
529
- print(await getClient().getProject(), getFormat(projectCmd));
530
- } catch (err) {
531
- error(String(err));
532
- process.exit(1);
533
- }
256
+ const lyroCmd = program.command('lyro').description('Manage Lyro data sources');
257
+
258
+ lyroCmd.command('sources').option('-n, --limit <number>').option('--cursor <cursor>').option('--kind <kind>').action(async opts => {
259
+ print(
260
+ await getClient().listLyroDataSources({
261
+ limit: opts.limit ? Number(opts.limit) : undefined,
262
+ cursor: opts.cursor,
263
+ kind: opts.kind,
264
+ }),
265
+ getFormat(lyroCmd),
266
+ );
534
267
  });
535
268
 
269
+ lyroCmd
270
+ .command('ask-ticket')
271
+ .description('Ask Lyro to answer a ticket from a single contact message')
272
+ .requiredOption('--ticket-id <id>')
273
+ .requiredOption('--subject <subject>')
274
+ .requiredOption('--contact-email <email>')
275
+ .requiredOption('--contact-name <name>')
276
+ .requiredOption('--recipient-email <email>')
277
+ .requiredOption('--message-id <id>')
278
+ .requiredOption('--message <message>')
279
+ .option('--created-at <iso>', 'Message creation timestamp', new Date().toISOString())
280
+ .action(async opts => {
281
+ print(
282
+ await getClient().askLyroToAnswerTicket({
283
+ ticketId: opts.ticketId,
284
+ subject: opts.subject,
285
+ contactEmail: opts.contactEmail,
286
+ contactName: opts.contactName,
287
+ recipientEmail: opts.recipientEmail,
288
+ messages: [
289
+ {
290
+ created_at: opts.createdAt,
291
+ message_id: opts.messageId,
292
+ author_type: 'contact',
293
+ message_type: 'public',
294
+ message_content: opts.message,
295
+ },
296
+ ],
297
+ }),
298
+ getFormat(lyroCmd),
299
+ );
300
+ });
301
+
536
302
  program.parse();