@kyoji2/intercom-cli 0.1.4 → 0.1.6

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kyoji2/intercom-cli",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "description": "AI-native CLI for Intercom - manage customer conversations, contacts, messages, and support",
5
5
  "author": "kyoji2",
6
6
  "repository": {
@@ -430,13 +430,17 @@ export function registerCommands(program: Command, ctx: RegisterContext): void {
430
430
  { flags: "--admin <id>", description: "Admin ID sending the reply" },
431
431
  { flags: "--body <body>", description: "Reply message body" },
432
432
  ],
433
- options: [{ flags: "--json <json>", description: "Additional reply data as JSON" }],
433
+ options: [
434
+ { flags: "--type <type>", description: "Message type (comment, note)" },
435
+ { flags: "--json <json>", description: "Additional reply data as JSON" },
436
+ ],
434
437
  action: async ({ globals, args, options }) => {
435
438
  await cmdConversationReply({
436
439
  ...globals,
437
440
  id: String(args[0]),
438
441
  adminId: options.admin as string,
439
442
  body: options.body as string,
443
+ messageType: options.type as string | undefined,
440
444
  json: options.json as string | undefined,
441
445
  });
442
446
  },
@@ -845,7 +849,10 @@ export function registerCommands(program: Command, ctx: RegisterContext): void {
845
849
  { flags: "--admin <id>", description: "Admin ID sending the reply" },
846
850
  { flags: "--body <body>", description: "Reply message body" },
847
851
  ],
848
- options: [{ flags: "--type <type>", description: "Message type (comment, note)", defaultValue: "comment" }],
852
+ options: [
853
+ { flags: "--type <type>", description: "Message type (comment, note)" },
854
+ { flags: "--json <json>", description: "Additional reply data as JSON" },
855
+ ],
849
856
  action: async ({ globals, args, options }) => {
850
857
  await cmdTicketReply({
851
858
  ...globals,
@@ -853,6 +860,7 @@ export function registerCommands(program: Command, ctx: RegisterContext): void {
853
860
  adminId: options.admin as string,
854
861
  body: options.body as string,
855
862
  messageType: options.type as string | undefined,
863
+ json: options.json as string | undefined,
856
864
  });
857
865
  },
858
866
  },
package/src/client.ts CHANGED
@@ -109,6 +109,14 @@ function getDryRunResponse(method: string): unknown {
109
109
  }
110
110
 
111
111
  export function handleIntercomError(error: unknown): never {
112
+ if (error instanceof SyntaxError) {
113
+ throw new CLIError(
114
+ "Invalid JSON input provided to command.",
115
+ 400,
116
+ "Ensure your JSON data is valid and properly escaped for the shell.",
117
+ );
118
+ }
119
+
112
120
  if (error instanceof IntercomError) {
113
121
  let hint: string | undefined;
114
122
  if (error.statusCode === 401) {
@@ -1,6 +1,7 @@
1
1
  import ora from "ora";
2
2
  import { createClient, handleIntercomError } from "../client.ts";
3
3
  import { CLIError, type GlobalOptions, getTokenAsync, output } from "../utils/index.ts";
4
+ import { buildAdminReplyPayload } from "./replyPayload.ts";
4
5
 
5
6
  export interface ConversationListOptions extends GlobalOptions {
6
7
  limit?: string;
@@ -21,6 +22,7 @@ export interface ConversationReplyOptions extends GlobalOptions {
21
22
  id: string;
22
23
  adminId: string;
23
24
  body: string;
25
+ messageType?: string;
24
26
  json?: string;
25
27
  }
26
28
 
@@ -208,12 +210,12 @@ export async function cmdConversationReply(options: ConversationReplyOptions): P
208
210
 
209
211
  const result = await client.conversations.reply({
210
212
  conversation_id: options.id,
211
- body: {
212
- message_type: "comment",
213
- type: "admin",
214
- admin_id: options.adminId,
213
+ body: buildAdminReplyPayload({
214
+ adminId: options.adminId,
215
215
  body: options.body,
216
- },
216
+ messageType: options.messageType,
217
+ json: options.json,
218
+ }),
217
219
  });
218
220
 
219
221
  spinner.succeed("Reply sent");
@@ -118,7 +118,9 @@ export function cmdSchema(): void {
118
118
  create_contact: 'intercom contact create --email "user@example.com" --name "John Doe"',
119
119
  search_contacts: 'intercom contact search --email "user@example.com"',
120
120
  list_conversations: "intercom conversation list --limit 10",
121
- reply_conversation: 'intercom conversation reply <id> --admin <admin-id> --body "Thank you!"',
121
+ reply_conversation: 'intercom conversation reply <id> --admin <admin-id> --body "Internal note" --type note',
122
+ reply_ticket:
123
+ 'intercom ticket reply <id> --admin <admin-id> --body "Internal note" --json \'{"message_type":"note"}\'',
122
124
  create_tag: 'intercom tag create "VIP Customer"',
123
125
  search_articles: 'intercom article search "getting started"',
124
126
  },
@@ -0,0 +1,46 @@
1
+ import { CLIError } from "../utils/index.ts";
2
+
3
+ type ReplyPayloadInput = {
4
+ adminId: string;
5
+ body: string;
6
+ messageType?: string;
7
+ json?: string;
8
+ };
9
+
10
+ export type ReplyBodyPayload = {
11
+ message_type: "comment" | "note";
12
+ type: "admin";
13
+ admin_id: string;
14
+ body: string;
15
+ } & Record<string, unknown>;
16
+
17
+ function validateReplyType(value: unknown, source: "--type" | "--json"): "comment" | "note" {
18
+ if (value !== "comment" && value !== "note") {
19
+ throw new CLIError(`Invalid ${source} value for message type: ${String(value)}`, 400, "Use comment or note.");
20
+ }
21
+ return value;
22
+ }
23
+
24
+ export function buildAdminReplyPayload(input: ReplyPayloadInput): ReplyBodyPayload {
25
+ const parsed = input.json ? JSON.parse(input.json) : {};
26
+
27
+ if (parsed === null || Array.isArray(parsed) || typeof parsed !== "object") {
28
+ throw new CLIError("Invalid --json value. Expected a JSON object.", 400);
29
+ }
30
+
31
+ const messageTypeFromJson =
32
+ Object.hasOwn(parsed, "message_type") && (parsed as Record<string, unknown>).message_type !== undefined
33
+ ? validateReplyType((parsed as Record<string, unknown>).message_type, "--json")
34
+ : undefined;
35
+ const messageType = input.messageType
36
+ ? validateReplyType(input.messageType, "--type")
37
+ : (messageTypeFromJson ?? "comment");
38
+
39
+ return {
40
+ ...(parsed as Record<string, unknown>),
41
+ message_type: messageType,
42
+ type: "admin",
43
+ admin_id: input.adminId,
44
+ body: input.body,
45
+ };
46
+ }
@@ -1,6 +1,7 @@
1
1
  import ora from "ora";
2
2
  import { createClient, handleIntercomError } from "../client.ts";
3
3
  import { CLIError, type GlobalOptions, getTokenAsync, output } from "../utils/index.ts";
4
+ import { buildAdminReplyPayload } from "./replyPayload.ts";
4
5
 
5
6
  export interface TicketGetOptions extends GlobalOptions {
6
7
  id: string;
@@ -42,6 +43,7 @@ export interface TicketReplyOptions extends GlobalOptions {
42
43
  adminId: string;
43
44
  body: string;
44
45
  messageType?: string;
46
+ json?: string;
45
47
  }
46
48
 
47
49
  export interface TicketCloseOptions extends GlobalOptions {
@@ -306,16 +308,14 @@ export async function cmdTicketReply(options: TicketReplyOptions): Promise<void>
306
308
  try {
307
309
  const client = createClient({ token, dryRun: options.dryRun });
308
310
 
309
- const messageType = options.messageType === "note" ? "note" : "comment";
310
-
311
311
  const result = await client.tickets.reply({
312
312
  ticket_id: options.id,
313
- body: {
314
- message_type: messageType,
315
- type: "admin",
316
- admin_id: options.adminId,
313
+ body: buildAdminReplyPayload({
314
+ adminId: options.adminId,
317
315
  body: options.body,
318
- },
316
+ messageType: options.messageType,
317
+ json: options.json,
318
+ }),
319
319
  });
320
320
 
321
321
  spinner.succeed("Reply sent");