@kyoji2/intercom-cli 0.1.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.
@@ -0,0 +1,350 @@
1
+ import ora from "ora";
2
+ import { createClient, handleIntercomError } from "../client.ts";
3
+ import { CLIError, type GlobalOptions, getTokenAsync, output } from "../utils/index.ts";
4
+
5
+ export interface ConversationListOptions extends GlobalOptions {
6
+ limit?: string;
7
+ }
8
+
9
+ export interface ConversationGetOptions extends GlobalOptions {
10
+ id: string;
11
+ }
12
+
13
+ export interface ConversationSearchOptions extends GlobalOptions {
14
+ json?: string;
15
+ state?: string;
16
+ assignee?: string;
17
+ limit?: string;
18
+ }
19
+
20
+ export interface ConversationReplyOptions extends GlobalOptions {
21
+ id: string;
22
+ adminId: string;
23
+ body: string;
24
+ json?: string;
25
+ }
26
+
27
+ export interface ConversationAssignOptions extends GlobalOptions {
28
+ id: string;
29
+ adminId: string;
30
+ assigneeId: string;
31
+ }
32
+
33
+ export interface ConversationCloseOptions extends GlobalOptions {
34
+ id: string;
35
+ adminId: string;
36
+ }
37
+
38
+ export interface ConversationOpenOptions extends GlobalOptions {
39
+ id: string;
40
+ adminId: string;
41
+ }
42
+
43
+ export interface ConversationSnoozeOptions extends GlobalOptions {
44
+ id: string;
45
+ adminId: string;
46
+ until: string;
47
+ }
48
+
49
+ async function requireToken(): Promise<string> {
50
+ const token = await getTokenAsync();
51
+ if (!token) {
52
+ throw new CLIError("Not logged in", 401, "Run 'intercom login' to authenticate.");
53
+ }
54
+ return token;
55
+ }
56
+
57
+ export async function cmdConversationList(options: ConversationListOptions): Promise<void> {
58
+ const token = await requireToken();
59
+ const spinner = ora("Listing conversations...").start();
60
+
61
+ try {
62
+ const client = createClient({ token, dryRun: options.dryRun });
63
+ const limit = options.limit ? Number.parseInt(options.limit, 10) : 25;
64
+
65
+ const result = await client.conversations.list({ per_page: Math.min(limit, 50) });
66
+
67
+ spinner.stop();
68
+
69
+ const conversations: unknown[] = [];
70
+ for await (const conv of result) {
71
+ conversations.push({
72
+ id: conv.id,
73
+ state: conv.state,
74
+ title: conv.title,
75
+ created_at: conv.created_at,
76
+ updated_at: conv.updated_at,
77
+ waiting_since: conv.waiting_since,
78
+ snoozed_until: conv.snoozed_until,
79
+ open: conv.open,
80
+ read: conv.read,
81
+ priority: conv.priority,
82
+ });
83
+ if (conversations.length >= limit) break;
84
+ }
85
+
86
+ output({ total: conversations.length, conversations }, options.format);
87
+ } catch (error) {
88
+ spinner.fail("Failed to list conversations");
89
+ handleIntercomError(error);
90
+ }
91
+ }
92
+
93
+ export async function cmdConversationGet(options: ConversationGetOptions): Promise<void> {
94
+ const token = await requireToken();
95
+ const spinner = ora("Fetching conversation...").start();
96
+
97
+ try {
98
+ const client = createClient({ token, dryRun: options.dryRun });
99
+ const conv = await client.conversations.find({ conversation_id: options.id });
100
+
101
+ spinner.stop();
102
+
103
+ output(
104
+ {
105
+ id: conv.id,
106
+ state: conv.state,
107
+ title: conv.title,
108
+ created_at: conv.created_at,
109
+ updated_at: conv.updated_at,
110
+ waiting_since: conv.waiting_since,
111
+ snoozed_until: conv.snoozed_until,
112
+ open: conv.open,
113
+ read: conv.read,
114
+ priority: conv.priority,
115
+ source: conv.source,
116
+ contacts: conv.contacts,
117
+ teammates: conv.teammates,
118
+ admin_assignee_id: conv.admin_assignee_id,
119
+ team_assignee_id: conv.team_assignee_id,
120
+ tags: conv.tags,
121
+ first_contact_reply: conv.first_contact_reply,
122
+ sla_applied: conv.sla_applied,
123
+ statistics: conv.statistics,
124
+ conversation_parts: conv.conversation_parts,
125
+ },
126
+ options.format,
127
+ );
128
+ } catch (error) {
129
+ spinner.fail("Failed to fetch conversation");
130
+ handleIntercomError(error);
131
+ }
132
+ }
133
+
134
+ export async function cmdConversationSearch(options: ConversationSearchOptions): Promise<void> {
135
+ const token = await requireToken();
136
+ const spinner = ora("Searching conversations...").start();
137
+
138
+ try {
139
+ const client = createClient({ token, dryRun: options.dryRun });
140
+
141
+ let query: Record<string, unknown>;
142
+ if (options.json) {
143
+ query = JSON.parse(options.json);
144
+ } else if (options.state) {
145
+ query = {
146
+ query: {
147
+ field: "state",
148
+ operator: "=",
149
+ value: options.state,
150
+ },
151
+ };
152
+ } else if (options.assignee) {
153
+ query = {
154
+ query: {
155
+ field: "admin_assignee_id",
156
+ operator: "=",
157
+ value: Number.parseInt(options.assignee, 10),
158
+ },
159
+ };
160
+ } else {
161
+ throw new CLIError("Search requires --json, --state, or --assignee", 400);
162
+ }
163
+
164
+ if (options.limit) {
165
+ query.pagination = { per_page: Number.parseInt(options.limit, 10) };
166
+ }
167
+
168
+ const searchPayload = { query: query.query, pagination: query.pagination };
169
+ const result = await client.conversations.search(
170
+ searchPayload as Parameters<typeof client.conversations.search>[0],
171
+ );
172
+
173
+ spinner.stop();
174
+
175
+ const conversations: unknown[] = [];
176
+ for await (const conv of result) {
177
+ conversations.push({
178
+ id: conv.id,
179
+ state: conv.state,
180
+ title: conv.title,
181
+ created_at: conv.created_at,
182
+ updated_at: conv.updated_at,
183
+ });
184
+ if (options.limit && conversations.length >= Number.parseInt(options.limit, 10)) break;
185
+ }
186
+
187
+ output({ total: conversations.length, conversations }, options.format);
188
+ } catch (error) {
189
+ spinner.fail("Failed to search conversations");
190
+ handleIntercomError(error);
191
+ }
192
+ }
193
+
194
+ export async function cmdConversationReply(options: ConversationReplyOptions): Promise<void> {
195
+ const token = await requireToken();
196
+ const spinner = ora("Sending reply...").start();
197
+
198
+ try {
199
+ const client = createClient({ token, dryRun: options.dryRun });
200
+
201
+ const result = await client.conversations.reply({
202
+ conversation_id: options.id,
203
+ body: {
204
+ message_type: "comment",
205
+ type: "admin",
206
+ admin_id: options.adminId,
207
+ body: options.body,
208
+ },
209
+ });
210
+
211
+ spinner.succeed("Reply sent");
212
+
213
+ output(
214
+ {
215
+ id: result.id,
216
+ state: result.state,
217
+ conversation_parts: result.conversation_parts,
218
+ },
219
+ options.format,
220
+ );
221
+ } catch (error) {
222
+ spinner.fail("Failed to send reply");
223
+ handleIntercomError(error);
224
+ }
225
+ }
226
+
227
+ export async function cmdConversationAssign(options: ConversationAssignOptions): Promise<void> {
228
+ const token = await requireToken();
229
+ const spinner = ora("Assigning conversation...").start();
230
+
231
+ try {
232
+ const client = createClient({ token, dryRun: options.dryRun });
233
+
234
+ const result = await client.conversations.manage({
235
+ conversation_id: options.id,
236
+ body: {
237
+ message_type: "assignment",
238
+ type: "admin",
239
+ admin_id: options.adminId,
240
+ assignee_id: options.assigneeId,
241
+ },
242
+ });
243
+
244
+ spinner.succeed("Conversation assigned");
245
+
246
+ output(
247
+ {
248
+ id: result.id,
249
+ admin_assignee_id: result.admin_assignee_id,
250
+ },
251
+ options.format,
252
+ );
253
+ } catch (error) {
254
+ spinner.fail("Failed to assign conversation");
255
+ handleIntercomError(error);
256
+ }
257
+ }
258
+
259
+ export async function cmdConversationClose(options: ConversationCloseOptions): Promise<void> {
260
+ const token = await requireToken();
261
+ const spinner = ora("Closing conversation...").start();
262
+
263
+ try {
264
+ const client = createClient({ token, dryRun: options.dryRun });
265
+
266
+ const result = await client.conversations.manage({
267
+ conversation_id: options.id,
268
+ body: {
269
+ message_type: "close",
270
+ type: "admin",
271
+ admin_id: options.adminId,
272
+ },
273
+ });
274
+
275
+ spinner.succeed("Conversation closed");
276
+
277
+ output(
278
+ {
279
+ id: result.id,
280
+ state: result.state,
281
+ },
282
+ options.format,
283
+ );
284
+ } catch (error) {
285
+ spinner.fail("Failed to close conversation");
286
+ handleIntercomError(error);
287
+ }
288
+ }
289
+
290
+ export async function cmdConversationOpen(options: ConversationOpenOptions): Promise<void> {
291
+ const token = await requireToken();
292
+ const spinner = ora("Opening conversation...").start();
293
+
294
+ try {
295
+ const client = createClient({ token, dryRun: options.dryRun });
296
+
297
+ const result = await client.conversations.manage({
298
+ conversation_id: options.id,
299
+ body: {
300
+ message_type: "open",
301
+ admin_id: options.adminId,
302
+ },
303
+ });
304
+
305
+ spinner.succeed("Conversation opened");
306
+
307
+ output(
308
+ {
309
+ id: result.id,
310
+ state: result.state,
311
+ },
312
+ options.format,
313
+ );
314
+ } catch (error) {
315
+ spinner.fail("Failed to open conversation");
316
+ handleIntercomError(error);
317
+ }
318
+ }
319
+
320
+ export async function cmdConversationSnooze(options: ConversationSnoozeOptions): Promise<void> {
321
+ const token = await requireToken();
322
+ const spinner = ora("Snoozing conversation...").start();
323
+
324
+ try {
325
+ const client = createClient({ token, dryRun: options.dryRun });
326
+
327
+ const result = await client.conversations.manage({
328
+ conversation_id: options.id,
329
+ body: {
330
+ message_type: "snoozed",
331
+ admin_id: options.adminId,
332
+ snoozed_until: Number.parseInt(options.until, 10),
333
+ },
334
+ });
335
+
336
+ spinner.succeed("Conversation snoozed");
337
+
338
+ output(
339
+ {
340
+ id: result.id,
341
+ state: result.state,
342
+ snoozed_until: result.snoozed_until,
343
+ },
344
+ options.format,
345
+ );
346
+ } catch (error) {
347
+ spinner.fail("Failed to snooze conversation");
348
+ handleIntercomError(error);
349
+ }
350
+ }
@@ -0,0 +1,91 @@
1
+ import ora from "ora";
2
+ import { createClient, handleIntercomError } from "../client.ts";
3
+ import { CLIError, type GlobalOptions, getTokenAsync, output } from "../utils/index.ts";
4
+
5
+ export interface EventTrackOptions extends GlobalOptions {
6
+ name: string;
7
+ userId?: string;
8
+ email?: string;
9
+ metadata?: string;
10
+ }
11
+
12
+ export interface EventListOptions extends GlobalOptions {
13
+ userId: string;
14
+ }
15
+
16
+ async function requireToken(): Promise<string> {
17
+ const token = await getTokenAsync();
18
+ if (!token) {
19
+ throw new CLIError("Not logged in", 401, "Run 'intercom login' to authenticate.");
20
+ }
21
+ return token;
22
+ }
23
+
24
+ export async function cmdEventTrack(options: EventTrackOptions): Promise<void> {
25
+ const token = await requireToken();
26
+ const spinner = ora("Tracking event...").start();
27
+
28
+ try {
29
+ const client = createClient({ token, dryRun: options.dryRun });
30
+
31
+ if (!options.userId && !options.email) {
32
+ throw new CLIError("Event requires --user-id or --email", 400);
33
+ }
34
+
35
+ const payload: Record<string, unknown> = {
36
+ event_name: options.name,
37
+ created_at: Math.floor(Date.now() / 1000),
38
+ };
39
+
40
+ if (options.userId) payload.user_id = options.userId;
41
+ if (options.email) payload.email = options.email;
42
+ if (options.metadata) payload.metadata = JSON.parse(options.metadata);
43
+
44
+ await client.events.create(payload as Parameters<typeof client.events.create>[0]);
45
+
46
+ spinner.succeed("Event tracked");
47
+
48
+ output(
49
+ {
50
+ status: "success",
51
+ event_name: options.name,
52
+ user_id: options.userId,
53
+ email: options.email,
54
+ },
55
+ options.format,
56
+ );
57
+ } catch (error) {
58
+ spinner.fail("Failed to track event");
59
+ handleIntercomError(error);
60
+ }
61
+ }
62
+
63
+ export async function cmdEventList(options: EventListOptions): Promise<void> {
64
+ const token = await requireToken();
65
+ const spinner = ora("Fetching events...").start();
66
+
67
+ try {
68
+ const client = createClient({ token, dryRun: options.dryRun });
69
+ const result = await client.events.list({
70
+ type: "user",
71
+ user_id: options.userId,
72
+ });
73
+
74
+ spinner.stop();
75
+
76
+ const events =
77
+ result.events
78
+ ?.filter((event): event is NonNullable<typeof event> => event != null)
79
+ .map((event) => ({
80
+ name: event.name,
81
+ first: event.first,
82
+ last: event.last,
83
+ count: event.count,
84
+ })) ?? [];
85
+
86
+ output({ total: events.length, events }, options.format);
87
+ } catch (error) {
88
+ spinner.fail("Failed to fetch events");
89
+ handleIntercomError(error);
90
+ }
91
+ }
@@ -0,0 +1,82 @@
1
+ // Auth commands
2
+
3
+ export type { AdminGetOptions } from "./admins.ts";
4
+ // Admin commands
5
+ export { cmdAdminGet, cmdAdminList } from "./admins.ts";
6
+ export type {
7
+ ArticleCreateOptions,
8
+ ArticleDeleteOptions,
9
+ ArticleGetOptions,
10
+ ArticleListOptions,
11
+ ArticleSearchOptions,
12
+ ArticleUpdateOptions,
13
+ } from "./articles.ts";
14
+ // Article commands
15
+ export {
16
+ cmdArticleCreate,
17
+ cmdArticleDelete,
18
+ cmdArticleGet,
19
+ cmdArticleList,
20
+ cmdArticleSearch,
21
+ cmdArticleUpdate,
22
+ } from "./articles.ts";
23
+ export type { LoginOptions } from "./auth.ts";
24
+ export { cmdLogin, cmdLogout, cmdWhoami } from "./auth.ts";
25
+ export type { CompanyCreateOptions, CompanyGetOptions, CompanyListOptions, CompanyUpdateOptions } from "./companies.ts";
26
+ // Company commands
27
+ export { cmdCompanyCreate, cmdCompanyGet, cmdCompanyList, cmdCompanyUpdate } from "./companies.ts";
28
+ export type {
29
+ ContactAttachCompanyOptions,
30
+ ContactCreateOptions,
31
+ ContactDeleteOptions,
32
+ ContactGetOptions,
33
+ ContactListOptions,
34
+ ContactNoteOptions,
35
+ ContactNotesListOptions,
36
+ ContactSearchOptions,
37
+ ContactTagOptions,
38
+ ContactUpdateOptions,
39
+ } from "./contacts.ts";
40
+ // Contact commands
41
+ export {
42
+ cmdContactAttachCompany,
43
+ cmdContactCreate,
44
+ cmdContactDelete,
45
+ cmdContactGet,
46
+ cmdContactList,
47
+ cmdContactNote,
48
+ cmdContactNotes,
49
+ cmdContactSearch,
50
+ cmdContactTag,
51
+ cmdContactUntag,
52
+ cmdContactUpdate,
53
+ } from "./contacts.ts";
54
+ export type {
55
+ ConversationAssignOptions,
56
+ ConversationCloseOptions,
57
+ ConversationGetOptions,
58
+ ConversationListOptions,
59
+ ConversationOpenOptions,
60
+ ConversationReplyOptions,
61
+ ConversationSearchOptions,
62
+ ConversationSnoozeOptions,
63
+ } from "./conversations.ts";
64
+ // Conversation commands
65
+ export {
66
+ cmdConversationAssign,
67
+ cmdConversationClose,
68
+ cmdConversationGet,
69
+ cmdConversationList,
70
+ cmdConversationOpen,
71
+ cmdConversationReply,
72
+ cmdConversationSearch,
73
+ cmdConversationSnooze,
74
+ } from "./conversations.ts";
75
+ export type { EventListOptions, EventTrackOptions } from "./events.ts";
76
+ // Event commands
77
+ export { cmdEventList, cmdEventTrack } from "./events.ts";
78
+ // Overview commands
79
+ export { cmdContext, cmdSchema } from "./overview.ts";
80
+ export type { TagCreateOptions, TagDeleteOptions, TagGetOptions } from "./tags.ts";
81
+ // Tag commands
82
+ export { cmdTagCreate, cmdTagDelete, cmdTagGet, cmdTagList } from "./tags.ts";
@@ -0,0 +1,128 @@
1
+ import ora from "ora";
2
+ import { createClient, handleIntercomError } from "../client.ts";
3
+ import { CLIError, type GlobalOptions, getTokenAsync, output } from "../utils/index.ts";
4
+
5
+ export async function cmdContext(options: GlobalOptions): Promise<void> {
6
+ const token = await getTokenAsync();
7
+ if (!token) {
8
+ throw new CLIError("Not logged in", 401, "Run 'intercom login' to authenticate.");
9
+ }
10
+
11
+ const spinner = ora("Fetching account context...").start();
12
+
13
+ try {
14
+ const client = createClient({ token, dryRun: options.dryRun });
15
+
16
+ const [admin, admins] = await Promise.all([client.admins.identify(), client.admins.list()]);
17
+
18
+ if (!admin) {
19
+ throw new CLIError("Could not identify admin", 401, "The token may be invalid or expired.");
20
+ }
21
+
22
+ spinner.stop();
23
+
24
+ output(
25
+ {
26
+ current_admin: {
27
+ id: admin.id,
28
+ name: admin.name,
29
+ email: admin.email,
30
+ },
31
+ workspace: admin.app
32
+ ? {
33
+ id: admin.app.id_code,
34
+ name: admin.app.name,
35
+ region: admin.app.region,
36
+ timezone: admin.app.timezone,
37
+ }
38
+ : undefined,
39
+ team: {
40
+ total_admins: admins.admins?.length ?? 0,
41
+ admins: admins.admins?.map((a) => ({
42
+ id: a?.id,
43
+ name: a?.name,
44
+ email: a?.email,
45
+ })),
46
+ },
47
+ },
48
+ options.format,
49
+ );
50
+ } catch (error) {
51
+ spinner.fail("Failed to fetch context");
52
+ handleIntercomError(error);
53
+ }
54
+ }
55
+
56
+ export function cmdSchema(): void {
57
+ const schema = {
58
+ api_version: "2.11",
59
+ sdk: "intercom-client@7.0.1",
60
+ resources: {
61
+ admins: {
62
+ methods: ["identify", "list", "find", "away", "listAllActivityLogs"],
63
+ description: "Manage workspace admins and their away status",
64
+ },
65
+ contacts: {
66
+ methods: ["create", "find", "update", "delete", "search", "list"],
67
+ subresources: {
68
+ notes: ["create", "list"],
69
+ tags: ["create", "list", "delete"],
70
+ companies: ["create", "list", "delete"],
71
+ },
72
+ description: "Manage customer contacts (users and leads)",
73
+ },
74
+ conversations: {
75
+ methods: ["create", "find", "update", "list", "search", "redact", "convert"],
76
+ subresources: {
77
+ parts: ["create"],
78
+ },
79
+ description: "Manage customer conversations and replies",
80
+ },
81
+ companies: {
82
+ methods: ["create", "update", "find", "list", "delete", "scroll"],
83
+ subresources: {
84
+ contacts: ["list"],
85
+ },
86
+ description: "Manage companies and their contacts",
87
+ },
88
+ tags: {
89
+ methods: ["list", "create", "find", "delete"],
90
+ description: "Manage tags for contacts and conversations",
91
+ },
92
+ articles: {
93
+ methods: ["list", "create", "find", "update", "delete", "search"],
94
+ description: "Manage help center articles",
95
+ },
96
+ events: {
97
+ methods: ["create", "list"],
98
+ description: "Track and list custom user events",
99
+ },
100
+ helpCenters: {
101
+ methods: ["find", "list"],
102
+ description: "Manage help centers",
103
+ },
104
+ collections: {
105
+ methods: ["create", "find", "update", "delete", "list"],
106
+ description: "Manage article collections",
107
+ },
108
+ teams: {
109
+ methods: ["list", "find"],
110
+ description: "Manage teams",
111
+ },
112
+ segments: {
113
+ methods: ["list", "find"],
114
+ description: "Manage user segments",
115
+ },
116
+ },
117
+ usage_examples: {
118
+ create_contact: 'intercom contact create --email "user@example.com" --name "John Doe"',
119
+ search_contacts: 'intercom contact search --email "user@example.com"',
120
+ list_conversations: "intercom conversation list --limit 10",
121
+ reply_conversation: 'intercom conversation reply <id> --admin <admin-id> --body "Thank you!"',
122
+ create_tag: 'intercom tag create "VIP Customer"',
123
+ search_articles: 'intercom article search "getting started"',
124
+ },
125
+ };
126
+
127
+ console.log(JSON.stringify(schema, null, 2));
128
+ }