@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.
- package/LICENSE +21 -0
- package/README.md +236 -0
- package/dist/index.js +25859 -0
- package/package.json +66 -0
- package/src/client.ts +132 -0
- package/src/commands/admins.ts +77 -0
- package/src/commands/articles.ts +229 -0
- package/src/commands/auth.ts +115 -0
- package/src/commands/companies.ts +176 -0
- package/src/commands/contacts.ts +410 -0
- package/src/commands/conversations.ts +350 -0
- package/src/commands/events.ts +91 -0
- package/src/commands/index.ts +82 -0
- package/src/commands/overview.ts +128 -0
- package/src/commands/tags.ts +112 -0
- package/src/index.ts +689 -0
- package/src/utils/config.ts +114 -0
- package/src/utils/index.ts +12 -0
- package/src/utils/output.ts +37 -0
package/package.json
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@kyoji2/intercom-cli",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "AI-native CLI for Intercom - manage customer conversations, contacts, messages, and support",
|
|
5
|
+
"author": "kyoji2",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/kyoji2/intercom-cli.git"
|
|
9
|
+
},
|
|
10
|
+
"bugs": {
|
|
11
|
+
"url": "https://github.com/kyoji2/intercom-cli/issues"
|
|
12
|
+
},
|
|
13
|
+
"homepage": "https://github.com/kyoji2/intercom-cli#readme",
|
|
14
|
+
"publishConfig": {
|
|
15
|
+
"access": "public"
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"dist",
|
|
19
|
+
"src",
|
|
20
|
+
"README.md",
|
|
21
|
+
"LICENSE"
|
|
22
|
+
],
|
|
23
|
+
"module": "src/index.ts",
|
|
24
|
+
"type": "module",
|
|
25
|
+
"exports": {
|
|
26
|
+
".": "./src/client.ts",
|
|
27
|
+
"./client": "./src/client.ts",
|
|
28
|
+
"./utils": "./src/utils/index.ts"
|
|
29
|
+
},
|
|
30
|
+
"bin": {
|
|
31
|
+
"intercom": "dist/index.js"
|
|
32
|
+
},
|
|
33
|
+
"scripts": {
|
|
34
|
+
"start": "bun run src/index.ts",
|
|
35
|
+
"dev": "bun --watch src/index.ts",
|
|
36
|
+
"test": "bun test",
|
|
37
|
+
"lint": "biome check ./src ./tests",
|
|
38
|
+
"lint:fix": "biome check --write ./src ./tests",
|
|
39
|
+
"format": "biome format --write ./src ./tests",
|
|
40
|
+
"build": "bun build src/index.ts --outdir dist --target bun && chmod +x dist/index.js",
|
|
41
|
+
"typecheck": "tsc --noEmit"
|
|
42
|
+
},
|
|
43
|
+
"dependencies": {
|
|
44
|
+
"intercom-client": "^7.0.1",
|
|
45
|
+
"@toon-format/toon": "^2.1.0",
|
|
46
|
+
"commander": "^14.0.2",
|
|
47
|
+
"ora": "^9.0.0",
|
|
48
|
+
"zod": "^4.3.5"
|
|
49
|
+
},
|
|
50
|
+
"devDependencies": {
|
|
51
|
+
"@biomejs/biome": "^2.3.11",
|
|
52
|
+
"@types/bun": "latest"
|
|
53
|
+
},
|
|
54
|
+
"peerDependencies": {
|
|
55
|
+
"typescript": "^5"
|
|
56
|
+
},
|
|
57
|
+
"keywords": [
|
|
58
|
+
"intercom",
|
|
59
|
+
"customer-support",
|
|
60
|
+
"conversations",
|
|
61
|
+
"contacts",
|
|
62
|
+
"cli",
|
|
63
|
+
"ai-native"
|
|
64
|
+
],
|
|
65
|
+
"license": "MIT"
|
|
66
|
+
}
|
package/src/client.ts
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import type { Intercom } from "intercom-client";
|
|
2
|
+
import { IntercomClient, IntercomError } from "intercom-client";
|
|
3
|
+
import { CLIError } from "./utils/output.ts";
|
|
4
|
+
|
|
5
|
+
export interface ClientOptions {
|
|
6
|
+
token: string;
|
|
7
|
+
dryRun?: boolean;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface Logger {
|
|
11
|
+
log: (message: string) => void;
|
|
12
|
+
warn: (message: string) => void;
|
|
13
|
+
error: (message: string) => void;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const defaultLogger: Logger = {
|
|
17
|
+
log: (msg) => console.log(msg),
|
|
18
|
+
warn: (msg) => console.warn(msg),
|
|
19
|
+
error: (msg) => console.error(msg),
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export function createClient(options: ClientOptions, logger: Logger = defaultLogger): IntercomClient {
|
|
23
|
+
const client = new IntercomClient({ token: options.token });
|
|
24
|
+
|
|
25
|
+
if (options.dryRun) {
|
|
26
|
+
return createDryRunProxy(client, logger);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return client;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function createDryRunProxy(client: IntercomClient, logger: Logger): IntercomClient {
|
|
33
|
+
const writeMethodPatterns = [
|
|
34
|
+
"create",
|
|
35
|
+
"update",
|
|
36
|
+
"delete",
|
|
37
|
+
"add",
|
|
38
|
+
"remove",
|
|
39
|
+
"attach",
|
|
40
|
+
"detach",
|
|
41
|
+
"assign",
|
|
42
|
+
"close",
|
|
43
|
+
"open",
|
|
44
|
+
"snooze",
|
|
45
|
+
"redact",
|
|
46
|
+
"convert",
|
|
47
|
+
"away",
|
|
48
|
+
"tag",
|
|
49
|
+
"untag",
|
|
50
|
+
];
|
|
51
|
+
|
|
52
|
+
const createNestedProxy = <T extends object>(target: T, path: string[]): T => {
|
|
53
|
+
return new Proxy(target, {
|
|
54
|
+
get(obj, prop: string) {
|
|
55
|
+
const value = (obj as Record<string, unknown>)[prop];
|
|
56
|
+
const currentPath = [...path, prop];
|
|
57
|
+
|
|
58
|
+
if (typeof value === "function") {
|
|
59
|
+
const isWriteMethod = writeMethodPatterns.some((pattern) =>
|
|
60
|
+
prop.toLowerCase().includes(pattern.toLowerCase()),
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
if (isWriteMethod) {
|
|
64
|
+
return (...args: unknown[]) => {
|
|
65
|
+
logger.log(`[DRY RUN] ${currentPath.join(".")}`);
|
|
66
|
+
if (args.length > 0 && typeof args[0] === "object") {
|
|
67
|
+
const filtered = Object.fromEntries(
|
|
68
|
+
Object.entries(args[0] as Record<string, unknown>).filter(
|
|
69
|
+
([k]) => !k.toLowerCase().includes("token"),
|
|
70
|
+
),
|
|
71
|
+
);
|
|
72
|
+
logger.log(`Payload: ${JSON.stringify(filtered, null, 2)}`);
|
|
73
|
+
}
|
|
74
|
+
return Promise.resolve(getDryRunResponse(prop));
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return value.bind(obj);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (value && typeof value === "object") {
|
|
82
|
+
return createNestedProxy(value, currentPath);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return value;
|
|
86
|
+
},
|
|
87
|
+
});
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
return createNestedProxy(client, ["client"]);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function getDryRunResponse(method: string): unknown {
|
|
94
|
+
if (method.includes("delete") || method.includes("remove")) {
|
|
95
|
+
return { deleted: true, id: "dry-run-id" };
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (method.includes("list")) {
|
|
99
|
+
return { data: [], pages: { next: null } };
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return {
|
|
103
|
+
id: "dry-run-id",
|
|
104
|
+
type: "dry-run",
|
|
105
|
+
created_at: Math.floor(Date.now() / 1000),
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export function handleIntercomError(error: unknown): never {
|
|
110
|
+
if (error instanceof IntercomError) {
|
|
111
|
+
let hint: string | undefined;
|
|
112
|
+
if (error.statusCode === 401) {
|
|
113
|
+
hint = "Authentication failed. Try running 'intercom login' again.";
|
|
114
|
+
} else if (error.statusCode === 404) {
|
|
115
|
+
hint = "The requested resource was not found. Verify the ID is correct.";
|
|
116
|
+
} else if (error.statusCode === 400) {
|
|
117
|
+
hint = "Invalid request. Check your input parameters.";
|
|
118
|
+
} else if (error.statusCode === 429) {
|
|
119
|
+
hint = "Rate limit exceeded. Wait a few minutes before trying again.";
|
|
120
|
+
}
|
|
121
|
+
throw new CLIError(error.message, error.statusCode ?? 500, hint);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (error instanceof CLIError) {
|
|
125
|
+
throw error;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
throw new CLIError(`Unexpected error: ${error}`, 500, "Check the CLI logs or report this issue.");
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export { IntercomClient, IntercomError };
|
|
132
|
+
export type { Intercom };
|
|
@@ -0,0 +1,77 @@
|
|
|
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 AdminGetOptions extends GlobalOptions {
|
|
6
|
+
id: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export async function cmdAdminList(options: GlobalOptions): Promise<void> {
|
|
10
|
+
const token = await getTokenAsync();
|
|
11
|
+
if (!token) {
|
|
12
|
+
throw new CLIError("Not logged in", 401, "Run 'intercom login' to authenticate.");
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const spinner = ora("Fetching admins...").start();
|
|
16
|
+
|
|
17
|
+
try {
|
|
18
|
+
const client = createClient({ token, dryRun: options.dryRun });
|
|
19
|
+
const result = await client.admins.list();
|
|
20
|
+
|
|
21
|
+
spinner.stop();
|
|
22
|
+
|
|
23
|
+
output(
|
|
24
|
+
{
|
|
25
|
+
admins:
|
|
26
|
+
result.admins?.map((admin) => ({
|
|
27
|
+
id: admin?.id,
|
|
28
|
+
name: admin?.name,
|
|
29
|
+
email: admin?.email,
|
|
30
|
+
type: admin?.type,
|
|
31
|
+
away_mode_enabled: admin?.away_mode_enabled,
|
|
32
|
+
})) ?? [],
|
|
33
|
+
},
|
|
34
|
+
options.format,
|
|
35
|
+
);
|
|
36
|
+
} catch (error) {
|
|
37
|
+
spinner.fail("Failed to fetch admins");
|
|
38
|
+
handleIntercomError(error);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export async function cmdAdminGet(options: AdminGetOptions): Promise<void> {
|
|
43
|
+
const token = await getTokenAsync();
|
|
44
|
+
if (!token) {
|
|
45
|
+
throw new CLIError("Not logged in", 401, "Run 'intercom login' to authenticate.");
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const spinner = ora("Fetching admin...").start();
|
|
49
|
+
|
|
50
|
+
try {
|
|
51
|
+
const client = createClient({ token, dryRun: options.dryRun });
|
|
52
|
+
const admin = await client.admins.find({ admin_id: Number(options.id) });
|
|
53
|
+
|
|
54
|
+
spinner.stop();
|
|
55
|
+
|
|
56
|
+
if (!admin) {
|
|
57
|
+
throw new CLIError("Admin not found", 404);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
output(
|
|
61
|
+
{
|
|
62
|
+
id: admin.id,
|
|
63
|
+
name: admin.name,
|
|
64
|
+
email: admin.email,
|
|
65
|
+
type: admin.type,
|
|
66
|
+
away_mode_enabled: admin.away_mode_enabled,
|
|
67
|
+
away_mode_reassign: admin.away_mode_reassign,
|
|
68
|
+
has_inbox_seat: admin.has_inbox_seat,
|
|
69
|
+
team_ids: admin.team_ids,
|
|
70
|
+
},
|
|
71
|
+
options.format,
|
|
72
|
+
);
|
|
73
|
+
} catch (error) {
|
|
74
|
+
spinner.fail("Failed to fetch admin");
|
|
75
|
+
handleIntercomError(error);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
@@ -0,0 +1,229 @@
|
|
|
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 ArticleListOptions extends GlobalOptions {
|
|
6
|
+
limit?: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface ArticleGetOptions extends GlobalOptions {
|
|
10
|
+
id: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface ArticleSearchOptions extends GlobalOptions {
|
|
14
|
+
query: string;
|
|
15
|
+
limit?: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface ArticleCreateOptions extends GlobalOptions {
|
|
19
|
+
title: string;
|
|
20
|
+
authorId: string;
|
|
21
|
+
body?: string;
|
|
22
|
+
description?: string;
|
|
23
|
+
state?: string;
|
|
24
|
+
parentId?: string;
|
|
25
|
+
parentType?: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface ArticleUpdateOptions extends GlobalOptions {
|
|
29
|
+
id: string;
|
|
30
|
+
json: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface ArticleDeleteOptions extends GlobalOptions {
|
|
34
|
+
id: string;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async function requireToken(): Promise<string> {
|
|
38
|
+
const token = await getTokenAsync();
|
|
39
|
+
if (!token) {
|
|
40
|
+
throw new CLIError("Not logged in", 401, "Run 'intercom login' to authenticate.");
|
|
41
|
+
}
|
|
42
|
+
return token;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export async function cmdArticleList(options: ArticleListOptions): Promise<void> {
|
|
46
|
+
const token = await requireToken();
|
|
47
|
+
const spinner = ora("Listing articles...").start();
|
|
48
|
+
|
|
49
|
+
try {
|
|
50
|
+
const client = createClient({ token, dryRun: options.dryRun });
|
|
51
|
+
const limit = options.limit ? Number.parseInt(options.limit, 10) : 25;
|
|
52
|
+
|
|
53
|
+
const result = await client.articles.list({ per_page: Math.min(limit, 50) });
|
|
54
|
+
|
|
55
|
+
spinner.stop();
|
|
56
|
+
|
|
57
|
+
const articles: unknown[] = [];
|
|
58
|
+
for await (const article of result) {
|
|
59
|
+
articles.push({
|
|
60
|
+
id: article.id,
|
|
61
|
+
title: article.title,
|
|
62
|
+
description: article.description,
|
|
63
|
+
state: article.state,
|
|
64
|
+
url: article.url,
|
|
65
|
+
created_at: article.created_at,
|
|
66
|
+
updated_at: article.updated_at,
|
|
67
|
+
});
|
|
68
|
+
if (articles.length >= limit) break;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
output({ total: articles.length, articles }, options.format);
|
|
72
|
+
} catch (error) {
|
|
73
|
+
spinner.fail("Failed to list articles");
|
|
74
|
+
handleIntercomError(error);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export async function cmdArticleGet(options: ArticleGetOptions): Promise<void> {
|
|
79
|
+
const token = await requireToken();
|
|
80
|
+
const spinner = ora("Fetching article...").start();
|
|
81
|
+
|
|
82
|
+
try {
|
|
83
|
+
const client = createClient({ token, dryRun: options.dryRun });
|
|
84
|
+
const article = await client.articles.find({ article_id: Number(options.id) });
|
|
85
|
+
|
|
86
|
+
spinner.stop();
|
|
87
|
+
|
|
88
|
+
output(
|
|
89
|
+
{
|
|
90
|
+
id: article.id,
|
|
91
|
+
title: article.title,
|
|
92
|
+
description: article.description,
|
|
93
|
+
body: article.body,
|
|
94
|
+
state: article.state,
|
|
95
|
+
url: article.url,
|
|
96
|
+
author_id: article.author_id,
|
|
97
|
+
parent_id: article.parent_id,
|
|
98
|
+
parent_type: article.parent_type,
|
|
99
|
+
created_at: article.created_at,
|
|
100
|
+
updated_at: article.updated_at,
|
|
101
|
+
statistics: article.statistics,
|
|
102
|
+
},
|
|
103
|
+
options.format,
|
|
104
|
+
);
|
|
105
|
+
} catch (error) {
|
|
106
|
+
spinner.fail("Failed to fetch article");
|
|
107
|
+
handleIntercomError(error);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export async function cmdArticleSearch(options: ArticleSearchOptions): Promise<void> {
|
|
112
|
+
const token = await requireToken();
|
|
113
|
+
const spinner = ora("Searching articles...").start();
|
|
114
|
+
|
|
115
|
+
try {
|
|
116
|
+
const client = createClient({ token, dryRun: options.dryRun });
|
|
117
|
+
const result = await client.articles.search({
|
|
118
|
+
phrase: options.query,
|
|
119
|
+
state: "published",
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
spinner.stop();
|
|
123
|
+
|
|
124
|
+
const limit = options.limit ? Number.parseInt(options.limit, 10) : 25;
|
|
125
|
+
const allArticles =
|
|
126
|
+
(result.data?.articles as Array<{
|
|
127
|
+
id?: string;
|
|
128
|
+
title?: string;
|
|
129
|
+
description?: string;
|
|
130
|
+
url?: string;
|
|
131
|
+
state?: string;
|
|
132
|
+
}>) ?? [];
|
|
133
|
+
const articles = allArticles.slice(0, limit).map((article) => ({
|
|
134
|
+
id: article.id,
|
|
135
|
+
title: article.title,
|
|
136
|
+
description: article.description,
|
|
137
|
+
url: article.url,
|
|
138
|
+
state: article.state,
|
|
139
|
+
}));
|
|
140
|
+
|
|
141
|
+
output({ total: articles.length, articles }, options.format);
|
|
142
|
+
} catch (error) {
|
|
143
|
+
spinner.fail("Failed to search articles");
|
|
144
|
+
handleIntercomError(error);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export async function cmdArticleCreate(options: ArticleCreateOptions): Promise<void> {
|
|
149
|
+
const token = await requireToken();
|
|
150
|
+
const spinner = ora("Creating article...").start();
|
|
151
|
+
|
|
152
|
+
try {
|
|
153
|
+
const client = createClient({ token, dryRun: options.dryRun });
|
|
154
|
+
|
|
155
|
+
const payload: Record<string, unknown> = {
|
|
156
|
+
title: options.title,
|
|
157
|
+
author_id: Number(options.authorId),
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
if (options.body) payload.body = options.body;
|
|
161
|
+
if (options.description) payload.description = options.description;
|
|
162
|
+
if (options.state) payload.state = options.state;
|
|
163
|
+
if (options.parentId) payload.parent_id = Number(options.parentId);
|
|
164
|
+
if (options.parentType) payload.parent_type = options.parentType;
|
|
165
|
+
|
|
166
|
+
const article = await client.articles.create(payload as Parameters<typeof client.articles.create>[0]);
|
|
167
|
+
|
|
168
|
+
spinner.succeed("Article created");
|
|
169
|
+
|
|
170
|
+
output(
|
|
171
|
+
{
|
|
172
|
+
id: article.id,
|
|
173
|
+
title: article.title,
|
|
174
|
+
state: article.state,
|
|
175
|
+
url: article.url,
|
|
176
|
+
created_at: article.created_at,
|
|
177
|
+
},
|
|
178
|
+
options.format,
|
|
179
|
+
);
|
|
180
|
+
} catch (error) {
|
|
181
|
+
spinner.fail("Failed to create article");
|
|
182
|
+
handleIntercomError(error);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
export async function cmdArticleUpdate(options: ArticleUpdateOptions): Promise<void> {
|
|
187
|
+
const token = await requireToken();
|
|
188
|
+
const spinner = ora("Updating article...").start();
|
|
189
|
+
|
|
190
|
+
try {
|
|
191
|
+
const client = createClient({ token, dryRun: options.dryRun });
|
|
192
|
+
|
|
193
|
+
const parsed = JSON.parse(options.json);
|
|
194
|
+
const payload = { article_id: Number(options.id), ...parsed };
|
|
195
|
+
|
|
196
|
+
const article = await client.articles.update(payload as Parameters<typeof client.articles.update>[0]);
|
|
197
|
+
|
|
198
|
+
spinner.succeed("Article updated");
|
|
199
|
+
|
|
200
|
+
output(
|
|
201
|
+
{
|
|
202
|
+
id: article.id,
|
|
203
|
+
title: article.title,
|
|
204
|
+
updated_at: article.updated_at,
|
|
205
|
+
},
|
|
206
|
+
options.format,
|
|
207
|
+
);
|
|
208
|
+
} catch (error) {
|
|
209
|
+
spinner.fail("Failed to update article");
|
|
210
|
+
handleIntercomError(error);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
export async function cmdArticleDelete(options: ArticleDeleteOptions): Promise<void> {
|
|
215
|
+
const token = await requireToken();
|
|
216
|
+
const spinner = ora("Deleting article...").start();
|
|
217
|
+
|
|
218
|
+
try {
|
|
219
|
+
const client = createClient({ token, dryRun: options.dryRun });
|
|
220
|
+
const result = await client.articles.delete({ article_id: Number(options.id) });
|
|
221
|
+
|
|
222
|
+
spinner.succeed("Article deleted");
|
|
223
|
+
|
|
224
|
+
output({ deleted: true, id: result.id }, options.format);
|
|
225
|
+
} catch (error) {
|
|
226
|
+
spinner.fail("Failed to delete article");
|
|
227
|
+
handleIntercomError(error);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import ora from "ora";
|
|
2
|
+
import { createClient, handleIntercomError } from "../client.ts";
|
|
3
|
+
import { CLIError, deleteConfig, type GlobalOptions, getTokenAsync, output, saveConfig } from "../utils/index.ts";
|
|
4
|
+
|
|
5
|
+
export interface LoginOptions extends GlobalOptions {
|
|
6
|
+
token?: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export async function cmdLogin(options: LoginOptions): Promise<void> {
|
|
10
|
+
let token = options.token;
|
|
11
|
+
|
|
12
|
+
if (!token) {
|
|
13
|
+
const spinner = ora("Waiting for token input...").start();
|
|
14
|
+
spinner.stop();
|
|
15
|
+
|
|
16
|
+
process.stdout.write("Enter your Intercom Access Token: ");
|
|
17
|
+
const input = await new Promise<string>((resolve) => {
|
|
18
|
+
let data = "";
|
|
19
|
+
process.stdin.setEncoding("utf8");
|
|
20
|
+
process.stdin.on("data", (chunk) => {
|
|
21
|
+
data += chunk;
|
|
22
|
+
if (data.includes("\n")) {
|
|
23
|
+
process.stdin.pause();
|
|
24
|
+
resolve(data.trim());
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
process.stdin.resume();
|
|
28
|
+
});
|
|
29
|
+
token = input;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (!token || token.trim().length === 0) {
|
|
33
|
+
throw new CLIError("No token provided", 400, "Provide a token as argument or via prompt.");
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const spinner = ora("Verifying token...").start();
|
|
37
|
+
|
|
38
|
+
try {
|
|
39
|
+
const client = createClient({ token, dryRun: options.dryRun });
|
|
40
|
+
const admin = await client.admins.identify();
|
|
41
|
+
|
|
42
|
+
if (!admin) {
|
|
43
|
+
throw new CLIError("Could not verify token", 401, "The token may be invalid or expired.");
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
await saveConfig({ token });
|
|
47
|
+
spinner.succeed("Logged in successfully");
|
|
48
|
+
|
|
49
|
+
output(
|
|
50
|
+
{
|
|
51
|
+
status: "success",
|
|
52
|
+
admin: {
|
|
53
|
+
id: admin.id,
|
|
54
|
+
name: admin.name,
|
|
55
|
+
email: admin.email,
|
|
56
|
+
},
|
|
57
|
+
workspace: admin.app ? { id: admin.app.id_code, name: admin.app.name } : undefined,
|
|
58
|
+
},
|
|
59
|
+
options.format,
|
|
60
|
+
);
|
|
61
|
+
} catch (error) {
|
|
62
|
+
spinner.fail("Login failed");
|
|
63
|
+
handleIntercomError(error);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export async function cmdLogout(options: GlobalOptions): Promise<void> {
|
|
68
|
+
await deleteConfig();
|
|
69
|
+
output({ status: "success", message: "Logged out successfully" }, options.format);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export async function cmdWhoami(options: GlobalOptions): Promise<void> {
|
|
73
|
+
const token = await getTokenAsync();
|
|
74
|
+
if (!token) {
|
|
75
|
+
throw new CLIError("Not logged in", 401, "Run 'intercom login' to authenticate.");
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const spinner = ora("Fetching account info...").start();
|
|
79
|
+
|
|
80
|
+
try {
|
|
81
|
+
const client = createClient({ token, dryRun: options.dryRun });
|
|
82
|
+
const admin = await client.admins.identify();
|
|
83
|
+
|
|
84
|
+
if (!admin) {
|
|
85
|
+
throw new CLIError("Could not identify admin", 401, "The token may be invalid or expired.");
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
spinner.stop();
|
|
89
|
+
|
|
90
|
+
output(
|
|
91
|
+
{
|
|
92
|
+
admin: {
|
|
93
|
+
id: admin.id,
|
|
94
|
+
name: admin.name,
|
|
95
|
+
email: admin.email,
|
|
96
|
+
type: admin.type,
|
|
97
|
+
away_mode_enabled: admin.away_mode_enabled,
|
|
98
|
+
away_mode_reassign: admin.away_mode_reassign,
|
|
99
|
+
},
|
|
100
|
+
workspace: admin.app
|
|
101
|
+
? {
|
|
102
|
+
id: admin.app.id_code,
|
|
103
|
+
name: admin.app.name,
|
|
104
|
+
region: admin.app.region,
|
|
105
|
+
timezone: admin.app.timezone,
|
|
106
|
+
}
|
|
107
|
+
: undefined,
|
|
108
|
+
},
|
|
109
|
+
options.format,
|
|
110
|
+
);
|
|
111
|
+
} catch (error) {
|
|
112
|
+
spinner.fail("Failed to fetch account info");
|
|
113
|
+
handleIntercomError(error);
|
|
114
|
+
}
|
|
115
|
+
}
|