@intercom/cli 0.2.0 → 0.3.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/README.md +15 -10
- package/dist/{chunk-QEXGKXTU.js → chunk-Y4JK5AHH.js} +1 -1
- package/dist/chunk-Y4JK5AHH.js.map +1 -0
- package/dist/index.js +510 -113
- package/dist/index.js.map +1 -1
- package/dist/{json-TDEGGEUF.js → json-MMBOB7YU.js} +2 -2
- package/package.json +2 -1
- package/dist/chunk-QEXGKXTU.js.map +0 -1
- /package/dist/{json-TDEGGEUF.js.map → json-MMBOB7YU.js.map} +0 -0
package/dist/index.js
CHANGED
|
@@ -9,11 +9,14 @@ import {
|
|
|
9
9
|
applyJq,
|
|
10
10
|
detectAuthType,
|
|
11
11
|
formatJson
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-Y4JK5AHH.js";
|
|
13
13
|
|
|
14
14
|
// src/index.ts
|
|
15
15
|
import { Command } from "commander";
|
|
16
16
|
|
|
17
|
+
// src/commands/api.ts
|
|
18
|
+
import { readFileSync as readFileSync3 } from "fs";
|
|
19
|
+
|
|
17
20
|
// src/api/timeouts.ts
|
|
18
21
|
var API_TIMEOUT_MS = 3e4;
|
|
19
22
|
var SIMULATION_POLL_TIMEOUT_MS = 6e5;
|
|
@@ -163,6 +166,22 @@ var IntercomClient = class {
|
|
|
163
166
|
retryAfter
|
|
164
167
|
);
|
|
165
168
|
}
|
|
169
|
+
const RETRYABLE_5XX = [502, 503, 504];
|
|
170
|
+
if (RETRYABLE_5XX.includes(response.status)) {
|
|
171
|
+
if (attempt < this.maxRetries) {
|
|
172
|
+
const backoffMs = Math.pow(2, attempt) * 1e3;
|
|
173
|
+
if (this.verbose) {
|
|
174
|
+
console.error(
|
|
175
|
+
`Server error ${response.status}. Retrying in ${backoffMs / 1e3}s (attempt ${attempt + 1}/${this.maxRetries})...`
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
await new Promise((r) => setTimeout(r, backoffMs));
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
throw new CLIError(
|
|
182
|
+
`Server error ${response.status} after ${this.maxRetries} retries: ${errorMsg}`
|
|
183
|
+
);
|
|
184
|
+
}
|
|
166
185
|
throw new CLIError(errorMsg);
|
|
167
186
|
}
|
|
168
187
|
throw new CLIError("Request failed after retries");
|
|
@@ -392,9 +411,9 @@ var FallbackBackend = class {
|
|
|
392
411
|
}
|
|
393
412
|
}
|
|
394
413
|
};
|
|
395
|
-
function createDefaultBackend(
|
|
396
|
-
if (
|
|
397
|
-
return new EncryptedFileBackend(
|
|
414
|
+
function createDefaultBackend(configDir2) {
|
|
415
|
+
if (configDir2) {
|
|
416
|
+
return new EncryptedFileBackend(configDir2);
|
|
398
417
|
}
|
|
399
418
|
return new FallbackBackend(
|
|
400
419
|
new NativeKeychainBackend(),
|
|
@@ -433,8 +452,8 @@ var CONFIG_FILE = "config.json";
|
|
|
433
452
|
function defaultConfigDir() {
|
|
434
453
|
return join2(homedir2(), ".intercom");
|
|
435
454
|
}
|
|
436
|
-
async function readConfig(
|
|
437
|
-
const dir =
|
|
455
|
+
async function readConfig(configDir2) {
|
|
456
|
+
const dir = configDir2 ?? defaultConfigDir();
|
|
438
457
|
try {
|
|
439
458
|
const raw = await readFile(join2(dir, CONFIG_FILE), "utf-8");
|
|
440
459
|
return JSON.parse(raw);
|
|
@@ -442,27 +461,27 @@ async function readConfig(configDir) {
|
|
|
442
461
|
return { workspaces: {} };
|
|
443
462
|
}
|
|
444
463
|
}
|
|
445
|
-
async function writeConfig(config,
|
|
446
|
-
const dir =
|
|
464
|
+
async function writeConfig(config, configDir2) {
|
|
465
|
+
const dir = configDir2 ?? defaultConfigDir();
|
|
447
466
|
await mkdir(dir, { recursive: true });
|
|
448
467
|
await atomicWriteFile(join2(dir, CONFIG_FILE), JSON.stringify(config, null, 2) + "\n");
|
|
449
468
|
}
|
|
450
|
-
async function addWorkspace(
|
|
451
|
-
const config = await readConfig(
|
|
469
|
+
async function addWorkspace(configDir2, workspace) {
|
|
470
|
+
const config = await readConfig(configDir2);
|
|
452
471
|
config.workspaces[workspace.app_id] = workspace;
|
|
453
472
|
if (!config.default) {
|
|
454
473
|
config.default = workspace.app_id;
|
|
455
474
|
}
|
|
456
|
-
await writeConfig(config,
|
|
475
|
+
await writeConfig(config, configDir2);
|
|
457
476
|
}
|
|
458
|
-
async function removeWorkspace(
|
|
459
|
-
const config = await readConfig(
|
|
477
|
+
async function removeWorkspace(configDir2, appId) {
|
|
478
|
+
const config = await readConfig(configDir2);
|
|
460
479
|
delete config.workspaces[appId];
|
|
461
480
|
if (config.default === appId) {
|
|
462
481
|
const remaining = Object.keys(config.workspaces);
|
|
463
482
|
config.default = remaining.length > 0 ? remaining[0] : void 0;
|
|
464
483
|
}
|
|
465
|
-
await writeConfig(config,
|
|
484
|
+
await writeConfig(config, configDir2);
|
|
466
485
|
}
|
|
467
486
|
|
|
468
487
|
// src/commands/helpers.ts
|
|
@@ -479,8 +498,8 @@ async function resolveClient(options = {}) {
|
|
|
479
498
|
timeoutMs: options.timeout ? parseInt(options.timeout, 10) * 1e3 : void 0
|
|
480
499
|
});
|
|
481
500
|
}
|
|
482
|
-
const
|
|
483
|
-
const config = await readConfig(
|
|
501
|
+
const configDir2 = process.env.INTERCOM_CONFIG_DIR;
|
|
502
|
+
const config = await readConfig(configDir2);
|
|
484
503
|
const appId = options.workspace ?? config.default;
|
|
485
504
|
if (!appId) {
|
|
486
505
|
throw new AuthError(
|
|
@@ -492,7 +511,7 @@ async function resolveClient(options = {}) {
|
|
|
492
511
|
`Default workspace "${appId}" is not in configured workspaces. Run "intercom auth bootstrap" or "intercom auth login --token <token>" to re-authenticate.`
|
|
493
512
|
);
|
|
494
513
|
}
|
|
495
|
-
const store = new CredentialStore(createDefaultBackend(
|
|
514
|
+
const store = new CredentialStore(createDefaultBackend(configDir2));
|
|
496
515
|
const cred = await store.get(appId);
|
|
497
516
|
if (!cred) {
|
|
498
517
|
throw new AuthError(
|
|
@@ -603,6 +622,20 @@ function parseTypedFields(fields, typed) {
|
|
|
603
622
|
}
|
|
604
623
|
|
|
605
624
|
// src/commands/api.ts
|
|
625
|
+
function readInput(input) {
|
|
626
|
+
let raw;
|
|
627
|
+
if (input === "-") {
|
|
628
|
+
const fd = readFileSync3(0, "utf-8");
|
|
629
|
+
raw = fd;
|
|
630
|
+
} else {
|
|
631
|
+
raw = readFileSync3(input, "utf-8");
|
|
632
|
+
}
|
|
633
|
+
try {
|
|
634
|
+
return JSON.parse(raw);
|
|
635
|
+
} catch {
|
|
636
|
+
throw new CLIError(`Failed to parse JSON from ${input === "-" ? "stdin" : input}`);
|
|
637
|
+
}
|
|
638
|
+
}
|
|
606
639
|
function registerApiCommand(program) {
|
|
607
640
|
program.command("api").description("Make an authenticated API request (like gh api)").argument("<endpoint>", "API endpoint path (e.g., /articles)").addHelpText(
|
|
608
641
|
"after",
|
|
@@ -615,16 +648,18 @@ Examples:
|
|
|
615
648
|
).option("-X, --method <method>", "HTTP method").option("-f, --field <key=value...>", "String field").option(
|
|
616
649
|
"-F, --typed-field <key=value...>",
|
|
617
650
|
"Typed field (bools, numbers, file reads)"
|
|
618
|
-
).option("--jq <expr>", "Filter response with jq expression").option("--json", "Output as JSON").option("--paginate", "Auto-paginate through all pages").option("--verbose", "Show full HTTP request/response").action(async function(endpoint) {
|
|
651
|
+
).option("--jq <expr>", "Filter response with jq expression").option("--json", "Output as JSON").option("--input <file>", "Read JSON body from file (use - for stdin)").option("--paginate", "Auto-paginate through all pages").option("--verbose", "Show full HTTP request/response").action(async function(endpoint) {
|
|
619
652
|
const options = this.optsWithGlobals();
|
|
620
653
|
const client = await resolveClient(options);
|
|
621
654
|
const stringFields = options.field ?? [];
|
|
622
655
|
const typedFields = options.typedField ?? [];
|
|
656
|
+
const inputBody = options.input ? readInput(options.input) : {};
|
|
623
657
|
const body = {
|
|
658
|
+
...inputBody,
|
|
624
659
|
...parseTypedFields(stringFields, false),
|
|
625
660
|
...parseTypedFields(typedFields, true)
|
|
626
661
|
};
|
|
627
|
-
const hasBody = stringFields.length > 0 || typedFields.length > 0;
|
|
662
|
+
const hasBody = stringFields.length > 0 || typedFields.length > 0 || !!options.input;
|
|
628
663
|
const method = options.method ?? (hasBody ? "POST" : "GET");
|
|
629
664
|
let result;
|
|
630
665
|
if (options.paginate && method.toUpperCase() === "GET") {
|
|
@@ -668,7 +703,14 @@ Examples:
|
|
|
668
703
|
|
|
669
704
|
// src/commands/auth/login.ts
|
|
670
705
|
function registerLoginCommand(auth) {
|
|
671
|
-
auth.command("login").description("Authenticate with Intercom").option("--token <token>", "Authenticate with an API key or token directly").option("--device", "Use device flow (for headless environments)").
|
|
706
|
+
auth.command("login").description("Authenticate with Intercom").option("--token <token>", "Authenticate with an API key or token directly").option("--device", "Use device flow (for headless environments)").addHelpText(
|
|
707
|
+
"after",
|
|
708
|
+
`
|
|
709
|
+
Examples:
|
|
710
|
+
$ intercom auth login --token <your-api-token>
|
|
711
|
+
|
|
712
|
+
Environment: Set INTERCOM_TOKEN to skip login entirely.`
|
|
713
|
+
).action(async (options) => {
|
|
672
714
|
if (options.token) {
|
|
673
715
|
await loginWithToken(options.token);
|
|
674
716
|
} else if (options.device) {
|
|
@@ -692,17 +734,17 @@ async function loginWithToken(token) {
|
|
|
692
734
|
const app = me.app;
|
|
693
735
|
const appId = app?.id_code ?? me.id;
|
|
694
736
|
const name = app?.name ?? "Unknown";
|
|
695
|
-
const
|
|
696
|
-
const store = new CredentialStore(createDefaultBackend(
|
|
737
|
+
const configDir2 = process.env.INTERCOM_CONFIG_DIR;
|
|
738
|
+
const store = new CredentialStore(createDefaultBackend(configDir2));
|
|
697
739
|
await store.set(appId, { token, auth_type: authType, app_id: appId });
|
|
698
|
-
await addWorkspace(
|
|
740
|
+
await addWorkspace(configDir2, { app_id: appId, name });
|
|
699
741
|
console.log(`Authenticated to ${name} (${appId})`);
|
|
700
742
|
}
|
|
701
743
|
|
|
702
744
|
// src/auth/cookies.ts
|
|
703
745
|
function extractCookies(response) {
|
|
704
746
|
const setCookies = typeof response.headers.getSetCookie === "function" ? response.headers.getSetCookie() : (response.headers.get("set-cookie") ?? "").split(/,(?=[^ ])/).filter(Boolean);
|
|
705
|
-
return setCookies.map((
|
|
747
|
+
return setCookies.map((c2) => c2.split(";")[0]).join("; ");
|
|
706
748
|
}
|
|
707
749
|
function mergeCookies(existing, newCookies) {
|
|
708
750
|
const map = /* @__PURE__ */ new Map();
|
|
@@ -1026,10 +1068,10 @@ function registerBootstrapCommand(auth) {
|
|
|
1026
1068
|
console.log(`Found app package: ${appPackage.name} (id: ${appPackage.id})`);
|
|
1027
1069
|
}
|
|
1028
1070
|
console.log("Access token acquired");
|
|
1029
|
-
const
|
|
1030
|
-
const store = new CredentialStore(createDefaultBackend(
|
|
1071
|
+
const configDir2 = process.env.INTERCOM_CONFIG_DIR;
|
|
1072
|
+
const store = new CredentialStore(createDefaultBackend(configDir2));
|
|
1031
1073
|
await store.set(appId, { token, auth_type: "oauth", app_id: appId });
|
|
1032
|
-
await addWorkspace(
|
|
1074
|
+
await addWorkspace(configDir2, { app_id: appId, name: appId });
|
|
1033
1075
|
console.log(`
|
|
1034
1076
|
Authenticated to workspace ${appId}`);
|
|
1035
1077
|
} catch (err) {
|
|
@@ -1041,15 +1083,85 @@ Authenticated to workspace ${appId}`);
|
|
|
1041
1083
|
}
|
|
1042
1084
|
|
|
1043
1085
|
// src/output/formatter.ts
|
|
1086
|
+
import pc from "picocolors";
|
|
1087
|
+
function isColorEnabled() {
|
|
1088
|
+
return !!process.stdout.isTTY && !process.env.NO_COLOR;
|
|
1089
|
+
}
|
|
1090
|
+
function c(fn, s) {
|
|
1091
|
+
return isColorEnabled() ? fn(s) : s;
|
|
1092
|
+
}
|
|
1093
|
+
var STATUS_COLORS = {
|
|
1094
|
+
// green
|
|
1095
|
+
active: pc.green,
|
|
1096
|
+
open: pc.green,
|
|
1097
|
+
published: pc.green,
|
|
1098
|
+
enabled: pc.green,
|
|
1099
|
+
verified: pc.green,
|
|
1100
|
+
true: pc.green,
|
|
1101
|
+
// red
|
|
1102
|
+
error: pc.red,
|
|
1103
|
+
closed: pc.red,
|
|
1104
|
+
deleted: pc.red,
|
|
1105
|
+
disabled: pc.red,
|
|
1106
|
+
false: pc.red,
|
|
1107
|
+
// yellow
|
|
1108
|
+
pending: pc.yellow,
|
|
1109
|
+
draft: pc.yellow,
|
|
1110
|
+
snoozed: pc.yellow
|
|
1111
|
+
};
|
|
1112
|
+
function colorizeStatus(value) {
|
|
1113
|
+
if (!isColorEnabled()) return value;
|
|
1114
|
+
const fn = STATUS_COLORS[value.toLowerCase()];
|
|
1115
|
+
return fn ? fn(value) : value;
|
|
1116
|
+
}
|
|
1117
|
+
function truncate(value, max) {
|
|
1118
|
+
if (value.length <= max) return value;
|
|
1119
|
+
return value.slice(0, max - 1) + "\u2026";
|
|
1120
|
+
}
|
|
1121
|
+
var DEFAULT_MAX_COLUMNS = 8;
|
|
1122
|
+
var DEFAULT_TRUNCATION = 50;
|
|
1123
|
+
var WIDE_TRUNCATION = 200;
|
|
1124
|
+
var STATUS_KEYS = /* @__PURE__ */ new Set([
|
|
1125
|
+
"STATE",
|
|
1126
|
+
"STATUS",
|
|
1127
|
+
"OPEN",
|
|
1128
|
+
"ROLE",
|
|
1129
|
+
"ENABLED"
|
|
1130
|
+
]);
|
|
1131
|
+
function isWide() {
|
|
1132
|
+
return process.argv.includes("--wide");
|
|
1133
|
+
}
|
|
1044
1134
|
function table({ columns, rows }) {
|
|
1045
1135
|
if (rows.length === 0) return "";
|
|
1046
|
-
const
|
|
1047
|
-
|
|
1136
|
+
const wide = isWide();
|
|
1137
|
+
const maxTrunc = wide ? WIDE_TRUNCATION : DEFAULT_TRUNCATION;
|
|
1138
|
+
const displayColumns = wide ? columns : columns.slice(0, DEFAULT_MAX_COLUMNS);
|
|
1139
|
+
const truncatedRows = rows.map((row) => {
|
|
1140
|
+
const out = {};
|
|
1141
|
+
for (const col of displayColumns) {
|
|
1142
|
+
out[col] = truncate(row[col] ?? "", maxTrunc);
|
|
1143
|
+
}
|
|
1144
|
+
return out;
|
|
1145
|
+
});
|
|
1146
|
+
const widths = displayColumns.map(
|
|
1147
|
+
(col) => Math.max(
|
|
1148
|
+
col.length,
|
|
1149
|
+
...truncatedRows.map((row) => (row[col] ?? "").length)
|
|
1150
|
+
)
|
|
1048
1151
|
);
|
|
1049
|
-
const formatRow = (values) => values.map((val, i) =>
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
(
|
|
1152
|
+
const formatRow = (values, isHeader) => values.map((val, i) => {
|
|
1153
|
+
const col = displayColumns[i];
|
|
1154
|
+
const padded = i < values.length - 1 ? val.padEnd(widths[i]) : val;
|
|
1155
|
+
if (isHeader) return c(pc.bold, padded);
|
|
1156
|
+
if (STATUS_KEYS.has(col)) return colorizeStatus(padded);
|
|
1157
|
+
return padded;
|
|
1158
|
+
}).join(" ");
|
|
1159
|
+
const header = formatRow(displayColumns, true);
|
|
1160
|
+
const body = truncatedRows.map(
|
|
1161
|
+
(row) => formatRow(
|
|
1162
|
+
displayColumns.map((col) => row[col] ?? ""),
|
|
1163
|
+
false
|
|
1164
|
+
)
|
|
1053
1165
|
);
|
|
1054
1166
|
return [header, ...body].join("\n");
|
|
1055
1167
|
}
|
|
@@ -1059,19 +1171,25 @@ function detail(data) {
|
|
|
1059
1171
|
);
|
|
1060
1172
|
if (entries.length === 0) return "";
|
|
1061
1173
|
const maxKeyLen = Math.max(...entries.map(([k]) => k.length));
|
|
1062
|
-
return entries.map(([key, value]) =>
|
|
1174
|
+
return entries.map(([key, value]) => {
|
|
1175
|
+
const label = c(pc.bold, key.padEnd(maxKeyLen));
|
|
1176
|
+
const strVal = String(value);
|
|
1177
|
+
const isStatus = STATUS_KEYS.has(key.toUpperCase());
|
|
1178
|
+
const colored = isStatus ? colorizeStatus(strVal) : strVal;
|
|
1179
|
+
return `${label}: ${colored}`;
|
|
1180
|
+
}).join("\n");
|
|
1063
1181
|
}
|
|
1064
1182
|
|
|
1065
1183
|
// src/commands/auth/status.ts
|
|
1066
1184
|
function registerStatusCommand(auth) {
|
|
1067
1185
|
auth.command("status").description("Show current authentication status").option("--json", "Output as JSON").action(async (options) => {
|
|
1068
|
-
const
|
|
1069
|
-
const config = await readConfig(
|
|
1186
|
+
const configDir2 = process.env.INTERCOM_CONFIG_DIR;
|
|
1187
|
+
const config = await readConfig(configDir2);
|
|
1070
1188
|
const appId = config.default;
|
|
1071
1189
|
if (!appId) {
|
|
1072
1190
|
throw new AuthError('Not authenticated. Run "intercom auth login" to get started.');
|
|
1073
1191
|
}
|
|
1074
|
-
const store = new CredentialStore(createDefaultBackend(
|
|
1192
|
+
const store = new CredentialStore(createDefaultBackend(configDir2));
|
|
1075
1193
|
const cred = await store.get(appId);
|
|
1076
1194
|
if (!cred) {
|
|
1077
1195
|
throw new AuthError('Not authenticated. Run "intercom auth login" to get started.');
|
|
@@ -1098,15 +1216,15 @@ function registerStatusCommand(auth) {
|
|
|
1098
1216
|
// src/commands/auth/logout.ts
|
|
1099
1217
|
function registerLogoutCommand(auth) {
|
|
1100
1218
|
auth.command("logout").description("Remove stored credentials").option("--workspace <id>", "Workspace app_id to log out of").action(async (options) => {
|
|
1101
|
-
const
|
|
1102
|
-
const config = await readConfig(
|
|
1219
|
+
const configDir2 = process.env.INTERCOM_CONFIG_DIR;
|
|
1220
|
+
const config = await readConfig(configDir2);
|
|
1103
1221
|
const appId = options.workspace ?? config.default;
|
|
1104
1222
|
if (!appId) {
|
|
1105
1223
|
throw new AuthError("Not authenticated.");
|
|
1106
1224
|
}
|
|
1107
|
-
const store = new CredentialStore(createDefaultBackend(
|
|
1225
|
+
const store = new CredentialStore(createDefaultBackend(configDir2));
|
|
1108
1226
|
await store.delete(appId);
|
|
1109
|
-
await removeWorkspace(
|
|
1227
|
+
await removeWorkspace(configDir2, appId);
|
|
1110
1228
|
const name = config.workspaces[appId]?.name ?? appId;
|
|
1111
1229
|
console.log(`Logged out of ${name} (${appId})`);
|
|
1112
1230
|
});
|
|
@@ -1145,10 +1263,10 @@ function registerSignupCommand(auth) {
|
|
|
1145
1263
|
password,
|
|
1146
1264
|
appId: workspace.id
|
|
1147
1265
|
});
|
|
1148
|
-
const
|
|
1149
|
-
const store = new CredentialStore(createDefaultBackend(
|
|
1266
|
+
const configDir2 = process.env.INTERCOM_CONFIG_DIR;
|
|
1267
|
+
const store = new CredentialStore(createDefaultBackend(configDir2));
|
|
1150
1268
|
await store.set(appId, { token, auth_type: "oauth", app_id: appId });
|
|
1151
|
-
await addWorkspace(
|
|
1269
|
+
await addWorkspace(configDir2, { app_id: appId, name: workspace.name });
|
|
1152
1270
|
console.log(`
|
|
1153
1271
|
Authenticated to workspace ${appId}`);
|
|
1154
1272
|
} catch {
|
|
@@ -1194,6 +1312,64 @@ function registerVerifyEmailCommand(auth) {
|
|
|
1194
1312
|
});
|
|
1195
1313
|
}
|
|
1196
1314
|
|
|
1315
|
+
// src/commands/auth/list.ts
|
|
1316
|
+
function registerListCommand(auth) {
|
|
1317
|
+
auth.command("list").description("List all configured workspaces").option("--json", "Output as JSON").option("--jq <expr>", "Filter JSON output with jq expression").action(async function() {
|
|
1318
|
+
const opts = this.optsWithGlobals();
|
|
1319
|
+
const configDir2 = process.env.INTERCOM_CONFIG_DIR;
|
|
1320
|
+
const config = await readConfig(configDir2);
|
|
1321
|
+
const store = new CredentialStore(createDefaultBackend(configDir2));
|
|
1322
|
+
const workspaces = await Promise.all(
|
|
1323
|
+
Object.values(config.workspaces).map(async (ws) => {
|
|
1324
|
+
const cred = await store.get(ws.app_id);
|
|
1325
|
+
return {
|
|
1326
|
+
app_id: ws.app_id,
|
|
1327
|
+
name: ws.name,
|
|
1328
|
+
auth_type: cred?.auth_type ?? "unknown",
|
|
1329
|
+
default: ws.app_id === config.default
|
|
1330
|
+
};
|
|
1331
|
+
})
|
|
1332
|
+
);
|
|
1333
|
+
render(
|
|
1334
|
+
workspaces,
|
|
1335
|
+
opts,
|
|
1336
|
+
() => {
|
|
1337
|
+
if (workspaces.length === 0) {
|
|
1338
|
+
return 'No workspaces configured. Run "intercom auth login" to get started.';
|
|
1339
|
+
}
|
|
1340
|
+
const rows = workspaces.map((ws) => ({
|
|
1341
|
+
DEFAULT: ws.default ? "*" : "",
|
|
1342
|
+
APP_ID: ws.app_id,
|
|
1343
|
+
NAME: ws.name,
|
|
1344
|
+
AUTH_TYPE: ws.auth_type
|
|
1345
|
+
}));
|
|
1346
|
+
return table({
|
|
1347
|
+
columns: ["DEFAULT", "APP_ID", "NAME", "AUTH_TYPE"],
|
|
1348
|
+
rows
|
|
1349
|
+
});
|
|
1350
|
+
},
|
|
1351
|
+
workspaces
|
|
1352
|
+
);
|
|
1353
|
+
});
|
|
1354
|
+
}
|
|
1355
|
+
|
|
1356
|
+
// src/commands/auth/switch.ts
|
|
1357
|
+
function registerSwitchCommand(auth) {
|
|
1358
|
+
auth.command("switch <app_id>").description("Switch the default workspace").action(async (appId) => {
|
|
1359
|
+
const configDir2 = process.env.INTERCOM_CONFIG_DIR;
|
|
1360
|
+
const config = await readConfig(configDir2);
|
|
1361
|
+
if (!config.workspaces[appId]) {
|
|
1362
|
+
const available = Object.keys(config.workspaces);
|
|
1363
|
+
const hint = available.length > 0 ? ` Available: ${available.join(", ")}` : ' Run "intercom auth login" to add a workspace.';
|
|
1364
|
+
throw new CLIError(`Workspace "${appId}" not found.${hint}`);
|
|
1365
|
+
}
|
|
1366
|
+
config.default = appId;
|
|
1367
|
+
await writeConfig(config, configDir2);
|
|
1368
|
+
const name = config.workspaces[appId].name;
|
|
1369
|
+
console.log(`Switched to ${name} (${appId})`);
|
|
1370
|
+
});
|
|
1371
|
+
}
|
|
1372
|
+
|
|
1197
1373
|
// src/commands/auth/index.ts
|
|
1198
1374
|
function registerAuthCommand(program) {
|
|
1199
1375
|
const auth = program.command("auth").description("Manage authentication credentials").addHelpText(
|
|
@@ -1202,7 +1378,9 @@ function registerAuthCommand(program) {
|
|
|
1202
1378
|
Examples:
|
|
1203
1379
|
intercom auth login --token <api-token>
|
|
1204
1380
|
intercom auth status
|
|
1205
|
-
intercom auth logout
|
|
1381
|
+
intercom auth logout
|
|
1382
|
+
|
|
1383
|
+
Environment: Set INTERCOM_TOKEN to skip login entirely.`
|
|
1206
1384
|
);
|
|
1207
1385
|
registerLoginCommand(auth);
|
|
1208
1386
|
registerBootstrapCommand(auth);
|
|
@@ -1210,6 +1388,8 @@ Examples:
|
|
|
1210
1388
|
registerLogoutCommand(auth);
|
|
1211
1389
|
registerSignupCommand(auth);
|
|
1212
1390
|
registerVerifyEmailCommand(auth);
|
|
1391
|
+
registerListCommand(auth);
|
|
1392
|
+
registerSwitchCommand(auth);
|
|
1213
1393
|
}
|
|
1214
1394
|
|
|
1215
1395
|
// src/commands/me.ts
|
|
@@ -1245,7 +1425,7 @@ function htmlToText(html) {
|
|
|
1245
1425
|
|
|
1246
1426
|
// src/commands/articles.ts
|
|
1247
1427
|
function registerArticlesCommand(program) {
|
|
1248
|
-
const articles = program.command("articles").description("Manage help center articles").addHelpText(
|
|
1428
|
+
const articles = program.command("articles").description("Manage help center articles (list, get, create, update, delete)").addHelpText(
|
|
1249
1429
|
"after",
|
|
1250
1430
|
`
|
|
1251
1431
|
Examples:
|
|
@@ -1255,7 +1435,13 @@ Examples:
|
|
|
1255
1435
|
intercom articles update 123 -f state=published
|
|
1256
1436
|
intercom articles delete 123`
|
|
1257
1437
|
);
|
|
1258
|
-
articles.command("list").description("List help center articles").option("--json", "Output as JSON").option("--jq <expr>", "Filter JSON output with jq expression").option("--verbose", "Show HTTP request/response details").option("--limit <n>", "Max results to return", "25").option("--all", "Auto-paginate all pages").
|
|
1438
|
+
articles.command("list").description("List help center articles").option("--json", "Output as JSON").option("--jq <expr>", "Filter JSON output with jq expression").option("--verbose", "Show HTTP request/response details").option("--limit <n>", "Max results to return", "25").option("--all", "Auto-paginate all pages").addHelpText(
|
|
1439
|
+
"after",
|
|
1440
|
+
`
|
|
1441
|
+
Examples:
|
|
1442
|
+
$ intercom articles list --json
|
|
1443
|
+
$ intercom articles list --all --jq '.[].title'`
|
|
1444
|
+
).action(async function() {
|
|
1259
1445
|
const opts = this.optsWithGlobals();
|
|
1260
1446
|
const client = await resolveClient(opts);
|
|
1261
1447
|
let result;
|
|
@@ -1312,7 +1498,16 @@ ${body}` : meta;
|
|
|
1312
1498
|
"Typed field: bools, numbers, @file reads (repeatable)",
|
|
1313
1499
|
(v, acc) => [...acc, v],
|
|
1314
1500
|
[]
|
|
1315
|
-
).option("--json", "Output as JSON").option("--jq <expr>", "Filter JSON output with jq expression").option("--verbose", "Show HTTP request/response details").
|
|
1501
|
+
).option("--json", "Output as JSON").option("--jq <expr>", "Filter JSON output with jq expression").option("--verbose", "Show HTTP request/response details").addHelpText(
|
|
1502
|
+
"after",
|
|
1503
|
+
`
|
|
1504
|
+
Examples:
|
|
1505
|
+
$ intercom articles create -f title='Getting Started' -f body='<p>Hello</p>'
|
|
1506
|
+
$ intercom articles create -F body=@article.html -f title='Guide'
|
|
1507
|
+
|
|
1508
|
+
Common fields: title, body, author_id, state, parent_id, parent_type
|
|
1509
|
+
Use -F for typed values (numbers, booleans) and @file reads.`
|
|
1510
|
+
).action(async function() {
|
|
1316
1511
|
const opts = this.optsWithGlobals();
|
|
1317
1512
|
const client = await resolveClient(opts);
|
|
1318
1513
|
const body = {
|
|
@@ -1377,7 +1572,7 @@ ${body}` : meta;
|
|
|
1377
1572
|
|
|
1378
1573
|
// src/commands/contacts.ts
|
|
1379
1574
|
function registerContactsCommand(program) {
|
|
1380
|
-
const contacts = program.command("contacts").description("Manage contacts").addHelpText(
|
|
1575
|
+
const contacts = program.command("contacts").description("Manage contacts (list, get, create, update, delete, search)").addHelpText(
|
|
1381
1576
|
"after",
|
|
1382
1577
|
`
|
|
1383
1578
|
Examples:
|
|
@@ -1403,12 +1598,12 @@ Examples:
|
|
|
1403
1598
|
result,
|
|
1404
1599
|
opts,
|
|
1405
1600
|
() => {
|
|
1406
|
-
const rows = items.map((
|
|
1407
|
-
ID: String(
|
|
1408
|
-
EMAIL: String(
|
|
1409
|
-
NAME: String(
|
|
1410
|
-
ROLE: String(
|
|
1411
|
-
UPDATED: formatTimestamp(
|
|
1601
|
+
const rows = items.map((c2) => ({
|
|
1602
|
+
ID: String(c2.id ?? ""),
|
|
1603
|
+
EMAIL: String(c2.email ?? ""),
|
|
1604
|
+
NAME: String(c2.name ?? ""),
|
|
1605
|
+
ROLE: String(c2.role ?? ""),
|
|
1606
|
+
UPDATED: formatTimestamp(c2.updated_at)
|
|
1412
1607
|
}));
|
|
1413
1608
|
return table({
|
|
1414
1609
|
columns: ["ID", "EMAIL", "NAME", "ROLE", "UPDATED"],
|
|
@@ -1486,6 +1681,45 @@ Examples:
|
|
|
1486
1681
|
const deleted = result;
|
|
1487
1682
|
render(result, opts, () => `Deleted contact ${deleted.id ?? id}`);
|
|
1488
1683
|
});
|
|
1684
|
+
contacts.command("search").description("Search contacts (POST /contacts/search)").option(
|
|
1685
|
+
"-f, --field <key=value>",
|
|
1686
|
+
"String field (repeatable)",
|
|
1687
|
+
(v, acc) => [...acc, v],
|
|
1688
|
+
[]
|
|
1689
|
+
).option(
|
|
1690
|
+
"-F, --typed-field <key=value>",
|
|
1691
|
+
"Typed field (repeatable)",
|
|
1692
|
+
(v, acc) => [...acc, v],
|
|
1693
|
+
[]
|
|
1694
|
+
).option("--json", "Output as JSON").option("--jq <expr>", "Filter JSON output with jq expression").option("--verbose", "Show HTTP request/response details").action(async function() {
|
|
1695
|
+
const opts = this.optsWithGlobals();
|
|
1696
|
+
const client = await resolveClient(opts);
|
|
1697
|
+
const body = {
|
|
1698
|
+
...parseTypedFields(opts.field ?? [], false),
|
|
1699
|
+
...parseTypedFields(opts.typedField ?? [], true)
|
|
1700
|
+
};
|
|
1701
|
+
const result = await client.post("/contacts/search", body);
|
|
1702
|
+
const response = result;
|
|
1703
|
+
const items = response.data ?? [];
|
|
1704
|
+
render(
|
|
1705
|
+
result,
|
|
1706
|
+
opts,
|
|
1707
|
+
() => {
|
|
1708
|
+
const rows = items.map((c2) => ({
|
|
1709
|
+
ID: String(c2.id ?? ""),
|
|
1710
|
+
EMAIL: String(c2.email ?? ""),
|
|
1711
|
+
NAME: String(c2.name ?? ""),
|
|
1712
|
+
ROLE: String(c2.role ?? ""),
|
|
1713
|
+
UPDATED: formatTimestamp(c2.updated_at)
|
|
1714
|
+
}));
|
|
1715
|
+
return table({
|
|
1716
|
+
columns: ["ID", "EMAIL", "NAME", "ROLE", "UPDATED"],
|
|
1717
|
+
rows
|
|
1718
|
+
});
|
|
1719
|
+
},
|
|
1720
|
+
items
|
|
1721
|
+
);
|
|
1722
|
+
});
|
|
1489
1723
|
}
|
|
1490
1724
|
|
|
1491
1725
|
// src/commands/conversations.ts
|
|
@@ -1562,7 +1796,7 @@ function renderConversationStream(result) {
|
|
|
1562
1796
|
return parts.join("\n");
|
|
1563
1797
|
}
|
|
1564
1798
|
function registerConversationsCommand(program) {
|
|
1565
|
-
const conversations = program.command("conversations").description("Manage conversations").addHelpText(
|
|
1799
|
+
const conversations = program.command("conversations").description("Manage conversations (list, get, reply, search)").addHelpText(
|
|
1566
1800
|
"after",
|
|
1567
1801
|
`
|
|
1568
1802
|
Examples:
|
|
@@ -1591,12 +1825,12 @@ Examples:
|
|
|
1591
1825
|
result,
|
|
1592
1826
|
opts,
|
|
1593
1827
|
() => {
|
|
1594
|
-
const rows = items.map((
|
|
1595
|
-
ID: String(
|
|
1596
|
-
STATE: String(
|
|
1597
|
-
OPEN: String(
|
|
1598
|
-
CREATED: formatTimestamp(
|
|
1599
|
-
UPDATED: formatTimestamp(
|
|
1828
|
+
const rows = items.map((c2) => ({
|
|
1829
|
+
ID: String(c2.id ?? ""),
|
|
1830
|
+
STATE: String(c2.state ?? ""),
|
|
1831
|
+
OPEN: String(c2.open ?? ""),
|
|
1832
|
+
CREATED: formatTimestamp(c2.created_at),
|
|
1833
|
+
UPDATED: formatTimestamp(c2.updated_at)
|
|
1600
1834
|
}));
|
|
1601
1835
|
return table({
|
|
1602
1836
|
columns: ["ID", "STATE", "OPEN", "CREATED", "UPDATED"],
|
|
@@ -1620,7 +1854,14 @@ Examples:
|
|
|
1620
1854
|
"Field to set (repeatable)",
|
|
1621
1855
|
(v, acc) => [...acc, v],
|
|
1622
1856
|
[]
|
|
1623
|
-
).option("--json", "Output as JSON").option("--jq <expr>", "Filter JSON output with jq expression").option("--verbose", "Show HTTP request/response details").
|
|
1857
|
+
).option("--json", "Output as JSON").option("--jq <expr>", "Filter JSON output with jq expression").option("--verbose", "Show HTTP request/response details").addHelpText(
|
|
1858
|
+
"after",
|
|
1859
|
+
`
|
|
1860
|
+
Examples:
|
|
1861
|
+
$ intercom conversations reply 123 -f type=admin -f body='Thanks!'
|
|
1862
|
+
|
|
1863
|
+
Required fields: body, type (admin or user). Also: admin_id, message_type.`
|
|
1864
|
+
).action(async function(id) {
|
|
1624
1865
|
const opts = this.optsWithGlobals();
|
|
1625
1866
|
const client = await resolveClient(opts);
|
|
1626
1867
|
const body = parseFieldArgs(opts.field ?? []);
|
|
@@ -1636,6 +1877,45 @@ Examples:
|
|
|
1636
1877
|
})
|
|
1637
1878
|
);
|
|
1638
1879
|
});
|
|
1880
|
+
conversations.command("search").description("Search conversations (POST /conversations/search)").option(
|
|
1881
|
+
"-f, --field <key=value>",
|
|
1882
|
+
"String field (repeatable)",
|
|
1883
|
+
(v, acc) => [...acc, v],
|
|
1884
|
+
[]
|
|
1885
|
+
).option(
|
|
1886
|
+
"-F, --typed-field <key=value>",
|
|
1887
|
+
"Typed field (repeatable)",
|
|
1888
|
+
(v, acc) => [...acc, v],
|
|
1889
|
+
[]
|
|
1890
|
+
).option("--json", "Output as JSON").option("--jq <expr>", "Filter JSON output with jq expression").option("--verbose", "Show HTTP request/response details").action(async function() {
|
|
1891
|
+
const opts = this.optsWithGlobals();
|
|
1892
|
+
const client = await resolveClient(opts);
|
|
1893
|
+
const body = {
|
|
1894
|
+
...parseTypedFields(opts.field ?? [], false),
|
|
1895
|
+
...parseTypedFields(opts.typedField ?? [], true)
|
|
1896
|
+
};
|
|
1897
|
+
const result = await client.post("/conversations/search", body);
|
|
1898
|
+
const response = result;
|
|
1899
|
+
const items = response.conversations ?? [];
|
|
1900
|
+
render(
|
|
1901
|
+
result,
|
|
1902
|
+
opts,
|
|
1903
|
+
() => {
|
|
1904
|
+
const rows = items.map((c2) => ({
|
|
1905
|
+
ID: String(c2.id ?? ""),
|
|
1906
|
+
STATE: String(c2.state ?? ""),
|
|
1907
|
+
OPEN: String(c2.open ?? ""),
|
|
1908
|
+
CREATED: formatTimestamp(c2.created_at),
|
|
1909
|
+
UPDATED: formatTimestamp(c2.updated_at)
|
|
1910
|
+
}));
|
|
1911
|
+
return table({
|
|
1912
|
+
columns: ["ID", "STATE", "OPEN", "CREATED", "UPDATED"],
|
|
1913
|
+
rows
|
|
1914
|
+
});
|
|
1915
|
+
},
|
|
1916
|
+
items
|
|
1917
|
+
);
|
|
1918
|
+
});
|
|
1639
1919
|
}
|
|
1640
1920
|
|
|
1641
1921
|
// src/commands/help-center.ts
|
|
@@ -1669,11 +1949,11 @@ Examples:
|
|
|
1669
1949
|
result,
|
|
1670
1950
|
opts,
|
|
1671
1951
|
() => {
|
|
1672
|
-
const rows = items.map((
|
|
1673
|
-
ID: String(
|
|
1674
|
-
NAME: String(
|
|
1675
|
-
DESCRIPTION: String(
|
|
1676
|
-
UPDATED: formatTimestamp(
|
|
1952
|
+
const rows = items.map((c2) => ({
|
|
1953
|
+
ID: String(c2.id ?? ""),
|
|
1954
|
+
NAME: String(c2.name ?? ""),
|
|
1955
|
+
DESCRIPTION: String(c2.description ?? ""),
|
|
1956
|
+
UPDATED: formatTimestamp(c2.updated_at)
|
|
1677
1957
|
}));
|
|
1678
1958
|
return table({
|
|
1679
1959
|
columns: ["ID", "NAME", "DESCRIPTION", "UPDATED"],
|
|
@@ -1742,7 +2022,7 @@ export function IntercomWidget() {
|
|
|
1742
2022
|
}`;
|
|
1743
2023
|
}
|
|
1744
2024
|
function registerMessengerCommand(program) {
|
|
1745
|
-
const messenger = program.command("messenger").description("Manage Intercom Messenger settings").addHelpText(
|
|
2025
|
+
const messenger = program.command("messenger").description("Manage Intercom Messenger settings (get, update, snippet)").addHelpText(
|
|
1746
2026
|
"after",
|
|
1747
2027
|
`
|
|
1748
2028
|
Examples:
|
|
@@ -1750,7 +2030,13 @@ Examples:
|
|
|
1750
2030
|
intercom messenger update -f alignment=right -f theme_mode=dark
|
|
1751
2031
|
intercom messenger snippet --format react`
|
|
1752
2032
|
);
|
|
1753
|
-
messenger.command("get").description("Get Messenger settings").option("--json", "Output as JSON").option("--jq <expr>", "Filter JSON output with jq expression").option("--verbose", "Show HTTP request/response details").
|
|
2033
|
+
messenger.command("get").description("Get Messenger settings").option("--json", "Output as JSON").option("--jq <expr>", "Filter JSON output with jq expression").option("--verbose", "Show HTTP request/response details").addHelpText(
|
|
2034
|
+
"after",
|
|
2035
|
+
`
|
|
2036
|
+
Examples:
|
|
2037
|
+
$ intercom messenger get --json
|
|
2038
|
+
$ intercom messenger get --jq '.alignment'`
|
|
2039
|
+
).action(async function() {
|
|
1754
2040
|
const opts = this.optsWithGlobals();
|
|
1755
2041
|
const client = await resolveClient(opts);
|
|
1756
2042
|
const result = await client.get("/messenger", void 0, UNSTABLE_HEADERS);
|
|
@@ -1802,17 +2088,15 @@ Examples:
|
|
|
1802
2088
|
"--format <format>",
|
|
1803
2089
|
"Output format: html (default) or react",
|
|
1804
2090
|
"html"
|
|
1805
|
-
).option("--verbose", "Show HTTP request/response details").action(async function() {
|
|
2091
|
+
).option("--json", "Output as JSON").option("--jq <expr>", "Filter JSON output with jq expression").option("--verbose", "Show HTTP request/response details").action(async function() {
|
|
1806
2092
|
const opts = this.optsWithGlobals();
|
|
1807
2093
|
const client = await resolveClient(opts);
|
|
1808
2094
|
const me = await client.get("/me");
|
|
1809
2095
|
const app = me.app;
|
|
1810
2096
|
const appId = String(app?.id_code ?? "");
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
}
|
|
1814
|
-
console.log(generateHtmlSnippet(appId));
|
|
1815
|
-
}
|
|
2097
|
+
const snippet = opts.format === "react" ? generateReactSnippet(appId) : generateHtmlSnippet(appId);
|
|
2098
|
+
const format = opts.format ?? "html";
|
|
2099
|
+
render({ format, snippet }, opts, () => snippet);
|
|
1816
2100
|
});
|
|
1817
2101
|
}
|
|
1818
2102
|
|
|
@@ -1941,11 +2225,11 @@ function registerGuidanceCommand(fin) {
|
|
|
1941
2225
|
return table({ columns: ["ID", "NAME"], rows });
|
|
1942
2226
|
}, items);
|
|
1943
2227
|
});
|
|
1944
|
-
guidance.command("get <id>").description("Download a guidance item by ID and print its YAML content").option("--verbose", "Show HTTP request/response details").action(async function(id) {
|
|
2228
|
+
guidance.command("get <id>").description("Download a guidance item by ID and print its YAML content").option("--json", "Output as JSON").option("--verbose", "Show HTTP request/response details").action(async function(id) {
|
|
1945
2229
|
const opts = this.optsWithGlobals();
|
|
1946
2230
|
const client = await resolveClient(opts);
|
|
1947
2231
|
const text = await client.getText(`/fin-sdk/guidance/${id}/download`);
|
|
1948
|
-
|
|
2232
|
+
render({ format: "yaml", content: text }, opts, () => text);
|
|
1949
2233
|
});
|
|
1950
2234
|
}
|
|
1951
2235
|
|
|
@@ -2166,7 +2450,7 @@ async function emberRequest(session, path, options = {}) {
|
|
|
2166
2450
|
}
|
|
2167
2451
|
function extractCookies2(response) {
|
|
2168
2452
|
const setCookies = typeof response.headers.getSetCookie === "function" ? response.headers.getSetCookie() : (response.headers.get("set-cookie") ?? "").split(/,(?=[^ ])/).filter(Boolean);
|
|
2169
|
-
return setCookies.map((
|
|
2453
|
+
return setCookies.map((c2) => c2.split(";")[0]).join("; ");
|
|
2170
2454
|
}
|
|
2171
2455
|
function mergeCookies2(existing, newCookies) {
|
|
2172
2456
|
const map = /* @__PURE__ */ new Map();
|
|
@@ -2187,8 +2471,8 @@ function registerFinEnableCommand(fin) {
|
|
|
2187
2471
|
).requiredOption("--email <email>", "Admin email address").option("--password-stdin", "Read password from stdin (or set INTERCOM_PASSWORD)").option("--verify-email-url <url>", "Email verification URL (if email not yet verified)").option("--verbose", "Show detailed output").action(async function() {
|
|
2188
2472
|
const opts = this.optsWithGlobals();
|
|
2189
2473
|
const password = await resolvePassword({ passwordStdin: opts.passwordStdin });
|
|
2190
|
-
const
|
|
2191
|
-
const config = await readConfig(
|
|
2474
|
+
const configDir2 = process.env.INTERCOM_CONFIG_DIR;
|
|
2475
|
+
const config = await readConfig(configDir2);
|
|
2192
2476
|
const appId = opts.workspace ?? config.default;
|
|
2193
2477
|
if (!appId) {
|
|
2194
2478
|
throw new CLIError(
|
|
@@ -2290,7 +2574,13 @@ Examples:
|
|
|
2290
2574
|
intercom fin enable --email admin@company.com
|
|
2291
2575
|
intercom fin guidance list`
|
|
2292
2576
|
);
|
|
2293
|
-
fin.command("manifest").description("Show the Fin SDK manifest for this workspace").option("--json", "Output as JSON").option("--jq <expr>", "Filter JSON output with jq expression").option("--verbose", "Show HTTP request/response details").
|
|
2577
|
+
fin.command("manifest").description("Show the Fin SDK manifest for this workspace").option("--json", "Output as JSON").option("--jq <expr>", "Filter JSON output with jq expression").option("--verbose", "Show HTTP request/response details").addHelpText(
|
|
2578
|
+
"after",
|
|
2579
|
+
`
|
|
2580
|
+
Examples:
|
|
2581
|
+
$ intercom fin manifest
|
|
2582
|
+
$ intercom fin manifest --json`
|
|
2583
|
+
).action(async function() {
|
|
2294
2584
|
const opts = this.optsWithGlobals();
|
|
2295
2585
|
const client = await resolveClient(opts);
|
|
2296
2586
|
const result = await client.get("/fin-sdk/manifest");
|
|
@@ -2333,7 +2623,7 @@ Examples:
|
|
|
2333
2623
|
}
|
|
2334
2624
|
|
|
2335
2625
|
// src/commands/setup.ts
|
|
2336
|
-
import { readdirSync, readFileSync as
|
|
2626
|
+
import { readdirSync, readFileSync as readFileSync4, statSync } from "fs";
|
|
2337
2627
|
import { join as join3, basename as basename2 } from "path";
|
|
2338
2628
|
function isGreenfield(opts) {
|
|
2339
2629
|
return !!(opts.email && opts.name && (process.env.INTERCOM_PASSWORD || opts.passwordStdin));
|
|
@@ -2343,8 +2633,8 @@ function hasPartialGreenfieldFlags(opts) {
|
|
|
2343
2633
|
return !!(opts.email && hasPassword && !opts.name);
|
|
2344
2634
|
}
|
|
2345
2635
|
async function hasExistingAuth() {
|
|
2346
|
-
const
|
|
2347
|
-
const config = await readConfig(
|
|
2636
|
+
const configDir2 = process.env.INTERCOM_CONFIG_DIR;
|
|
2637
|
+
const config = await readConfig(configDir2);
|
|
2348
2638
|
return !!config.default;
|
|
2349
2639
|
}
|
|
2350
2640
|
function buildPlan(opts) {
|
|
@@ -2392,7 +2682,7 @@ function buildPlan(opts) {
|
|
|
2392
2682
|
}
|
|
2393
2683
|
if (mdFiles.length > 0) {
|
|
2394
2684
|
const articleCommands = mdFiles.map((file) => {
|
|
2395
|
-
const title = basename2(file, ".md").replace(/-/g, " ").replace(/\b\w/g, (
|
|
2685
|
+
const title = basename2(file, ".md").replace(/-/g, " ").replace(/\b\w/g, (c2) => c2.toUpperCase());
|
|
2396
2686
|
return `intercom articles create -f title="${title}" -F body=@${join3(opts.articlesFrom, file)} -f author_id=$(intercom me --json | jq -r .id)`;
|
|
2397
2687
|
});
|
|
2398
2688
|
steps.push({
|
|
@@ -2436,7 +2726,7 @@ async function runPostAuthSteps(client, opts, results, appId) {
|
|
|
2436
2726
|
const helpCenterName = opts.companyName ? `${opts.companyName} Help` : "Help Center";
|
|
2437
2727
|
const collections = await client.paginate("/help_center/collections");
|
|
2438
2728
|
const existingCollection = collections.find(
|
|
2439
|
-
(
|
|
2729
|
+
(c2) => String(c2.name ?? "") === helpCenterName
|
|
2440
2730
|
);
|
|
2441
2731
|
let collectionId;
|
|
2442
2732
|
if (existingCollection) {
|
|
@@ -2459,8 +2749,8 @@ async function runPostAuthSteps(client, opts, results, appId) {
|
|
|
2459
2749
|
let created = 0;
|
|
2460
2750
|
let skipped = 0;
|
|
2461
2751
|
for (const file of mdFiles) {
|
|
2462
|
-
const title = basename2(file, ".md").replace(/-/g, " ").replace(/\b\w/g, (
|
|
2463
|
-
const body =
|
|
2752
|
+
const title = basename2(file, ".md").replace(/-/g, " ").replace(/\b\w/g, (c2) => c2.toUpperCase());
|
|
2753
|
+
const body = readFileSync4(join3(opts.articlesFrom, file), "utf-8");
|
|
2464
2754
|
if (existingTitles.has(title)) {
|
|
2465
2755
|
skipped++;
|
|
2466
2756
|
} else {
|
|
@@ -2485,19 +2775,19 @@ async function runPostAuthSteps(client, opts, results, appId) {
|
|
|
2485
2775
|
const articleCount = (await client.paginate("/articles")).length;
|
|
2486
2776
|
if (articleCount === 0) {
|
|
2487
2777
|
results.push(`\u26A0 No help center articles found!`);
|
|
2488
|
-
console.
|
|
2489
|
-
console.
|
|
2490
|
-
console.
|
|
2491
|
-
console.
|
|
2492
|
-
console.
|
|
2493
|
-
console.
|
|
2494
|
-
console.
|
|
2495
|
-
console.
|
|
2496
|
-
console.
|
|
2497
|
-
console.
|
|
2498
|
-
console.
|
|
2499
|
-
console.
|
|
2500
|
-
console.
|
|
2778
|
+
console.error(results.join("\n"));
|
|
2779
|
+
console.error("");
|
|
2780
|
+
console.error("WARNING: Fin needs help center articles to answer questions.");
|
|
2781
|
+
console.error("Without articles, Fin will have nothing to draw from and cannot help visitors.\n");
|
|
2782
|
+
console.error("To add articles, either:");
|
|
2783
|
+
console.error(" 1. Re-run with: intercom setup --articles-from <directory-of-markdown-files>");
|
|
2784
|
+
console.error(' 2. Create articles manually: intercom articles create -f title="..." -f body="..." -F author_id=<id>');
|
|
2785
|
+
console.error("");
|
|
2786
|
+
console.error("Tip for AI agents: if no articles directory exists, consider creating one by");
|
|
2787
|
+
console.error(" analyzing the project's source code, README, documentation, or any available");
|
|
2788
|
+
console.error(" context about the product. Write markdown files covering key topics that");
|
|
2789
|
+
console.error(" customers would ask about, then re-run setup with --articles-from.\n");
|
|
2790
|
+
console.error("Skipping Fin enablement \u2014 enable Fin after adding articles with: intercom fin enable --email <email>");
|
|
2501
2791
|
return;
|
|
2502
2792
|
}
|
|
2503
2793
|
results.push(`\u2713 ${articleCount} article(s) in help center`);
|
|
@@ -2521,8 +2811,8 @@ async function runPostAuthSteps(client, opts, results, appId) {
|
|
|
2521
2811
|
results.push(`\u2713 Fin already opted in`);
|
|
2522
2812
|
} else {
|
|
2523
2813
|
results.push(`\u26A0 Fin opt-in failed (${optIn.status}) \u2014 enable manually with: intercom fin enable --email <email>`);
|
|
2524
|
-
console.
|
|
2525
|
-
console.
|
|
2814
|
+
console.error(results.join("\n"));
|
|
2815
|
+
console.error("Setup complete (Fin requires manual enablement).");
|
|
2526
2816
|
return;
|
|
2527
2817
|
}
|
|
2528
2818
|
const profile = await emberRequest(
|
|
@@ -2553,7 +2843,7 @@ async function runPostAuthSteps(client, opts, results, appId) {
|
|
|
2553
2843
|
}
|
|
2554
2844
|
} else if (opts.enableFin !== false) {
|
|
2555
2845
|
results.push(`\u26A0 Fin not enabled (--email required for Fin activation)`);
|
|
2556
|
-
console.
|
|
2846
|
+
console.error(" Tip: Re-run with --email to enable Fin, or use: intercom fin enable --email <email>");
|
|
2557
2847
|
}
|
|
2558
2848
|
try {
|
|
2559
2849
|
const manifest = await client.get("/fin-sdk/manifest");
|
|
@@ -2568,8 +2858,8 @@ async function runPostAuthSteps(client, opts, results, appId) {
|
|
|
2568
2858
|
throw err;
|
|
2569
2859
|
}
|
|
2570
2860
|
}
|
|
2571
|
-
console.
|
|
2572
|
-
console.
|
|
2861
|
+
console.error(results.join("\n"));
|
|
2862
|
+
console.error("Setup complete!");
|
|
2573
2863
|
}
|
|
2574
2864
|
function registerSetupCommand(program) {
|
|
2575
2865
|
program.command("setup").description("Set up a complete Intercom installation").addHelpText(
|
|
@@ -2583,7 +2873,9 @@ Examples:
|
|
|
2583
2873
|
INTERCOM_PASSWORD=*** intercom setup --email admin@acme.com --name "Admin" --company-name "Acme Inc"
|
|
2584
2874
|
|
|
2585
2875
|
# Preview steps without executing
|
|
2586
|
-
intercom setup --plan
|
|
2876
|
+
intercom setup --plan
|
|
2877
|
+
|
|
2878
|
+
Flow: signup \u2192 auth \u2192 messenger \u2192 help center \u2192 articles \u2192 fin`
|
|
2587
2879
|
).option("--company-name <name>", "Company name for Messenger branding").option("--articles-from <dir>", "Directory of markdown files to create as articles").option("--plan", "Output setup steps as CLI commands without executing").option("--dry-run", "Show what would happen without executing").option("--json", "Output as JSON (for --plan mode)").option("--verbose", "Show HTTP request/response details").option("--email <email>", "Admin email address (for new workspace signup)").option("--password-stdin", "Read password from stdin (or set INTERCOM_PASSWORD env var)").option("--name <name>", "Admin name (for new workspace signup)").option("--plan-tier <tier>", "Trial plan tier: essential, advanced, expert (default: advanced)").option("--enable-fin", "Enable Fin AI Agent (default: true for greenfield setup)", true).option("--no-enable-fin", "Skip Fin enablement").option("--verify-email-url <url>", "Email verification URL (needed for Fin on fresh workspaces)").action(async function() {
|
|
2588
2880
|
const opts = this.optsWithGlobals();
|
|
2589
2881
|
if (hasPartialGreenfieldFlags(opts)) {
|
|
@@ -2694,10 +2986,10 @@ Then re-run setup without signup flags:
|
|
|
2694
2986
|
);
|
|
2695
2987
|
}
|
|
2696
2988
|
results.push(`\u2713 Authentication token obtained`);
|
|
2697
|
-
const
|
|
2698
|
-
const store = new CredentialStore(createDefaultBackend(
|
|
2989
|
+
const configDir2 = process.env.INTERCOM_CONFIG_DIR;
|
|
2990
|
+
const store = new CredentialStore(createDefaultBackend(configDir2));
|
|
2699
2991
|
await store.set(workspace.id, { token, auth_type: "oauth", app_id: workspace.id });
|
|
2700
|
-
await addWorkspace(
|
|
2992
|
+
await addWorkspace(configDir2, { app_id: workspace.id, name: workspace.name });
|
|
2701
2993
|
const client2 = new IntercomClient({
|
|
2702
2994
|
token,
|
|
2703
2995
|
authType: "oauth",
|
|
@@ -2926,7 +3218,7 @@ function registerPricingCommand(program) {
|
|
|
2926
3218
|
}
|
|
2927
3219
|
const markdown = await response.text();
|
|
2928
3220
|
if (opts.json) {
|
|
2929
|
-
const { formatJson: formatJson2 } = await import("./json-
|
|
3221
|
+
const { formatJson: formatJson2 } = await import("./json-MMBOB7YU.js");
|
|
2930
3222
|
console.log(formatJson2({ url: PRICING_URL, content: markdown }));
|
|
2931
3223
|
} else {
|
|
2932
3224
|
console.log(markdown);
|
|
@@ -2934,6 +3226,100 @@ function registerPricingCommand(program) {
|
|
|
2934
3226
|
});
|
|
2935
3227
|
}
|
|
2936
3228
|
|
|
3229
|
+
// src/commands/config.ts
|
|
3230
|
+
import { join as join4 } from "path";
|
|
3231
|
+
var SUPPORTED_KEYS = ["default_workspace", "timeout"];
|
|
3232
|
+
function configDir() {
|
|
3233
|
+
return process.env.INTERCOM_CONFIG_DIR ?? defaultConfigDir();
|
|
3234
|
+
}
|
|
3235
|
+
function validateKey(key) {
|
|
3236
|
+
if (!SUPPORTED_KEYS.includes(key)) {
|
|
3237
|
+
throw new CLIError(
|
|
3238
|
+
`Unknown config key "${key}". Supported keys: ${SUPPORTED_KEYS.join(", ")}`
|
|
3239
|
+
);
|
|
3240
|
+
}
|
|
3241
|
+
return key;
|
|
3242
|
+
}
|
|
3243
|
+
async function getValue(key) {
|
|
3244
|
+
const config = await readConfig(configDir());
|
|
3245
|
+
switch (key) {
|
|
3246
|
+
case "default_workspace":
|
|
3247
|
+
return config.default;
|
|
3248
|
+
case "timeout":
|
|
3249
|
+
return config.timeout !== void 0 ? String(config.timeout) : void 0;
|
|
3250
|
+
}
|
|
3251
|
+
}
|
|
3252
|
+
function resolveSource(key) {
|
|
3253
|
+
switch (key) {
|
|
3254
|
+
case "default_workspace":
|
|
3255
|
+
return process.env.INTERCOM_WORKSPACE ? "INTERCOM_WORKSPACE" : "config";
|
|
3256
|
+
case "timeout":
|
|
3257
|
+
return "config";
|
|
3258
|
+
}
|
|
3259
|
+
}
|
|
3260
|
+
async function setValue(key, value) {
|
|
3261
|
+
const dir = configDir();
|
|
3262
|
+
const config = await readConfig(dir);
|
|
3263
|
+
switch (key) {
|
|
3264
|
+
case "default_workspace":
|
|
3265
|
+
config.default = value;
|
|
3266
|
+
break;
|
|
3267
|
+
case "timeout": {
|
|
3268
|
+
const n = parseInt(value, 10);
|
|
3269
|
+
if (isNaN(n) || n <= 0) {
|
|
3270
|
+
throw new CLIError("timeout must be a positive integer (seconds)");
|
|
3271
|
+
}
|
|
3272
|
+
config.timeout = n;
|
|
3273
|
+
break;
|
|
3274
|
+
}
|
|
3275
|
+
}
|
|
3276
|
+
await writeConfig(config, dir);
|
|
3277
|
+
}
|
|
3278
|
+
function registerConfigCommand(program) {
|
|
3279
|
+
const cmd = program.command("config").description("Manage CLI configuration");
|
|
3280
|
+
cmd.command("list").description("List all configuration settings").option("--json", "Output as JSON").option("--jq <expr>", "Filter JSON output with jq expression").action(async function() {
|
|
3281
|
+
const opts = this.optsWithGlobals();
|
|
3282
|
+
const config = await readConfig(configDir());
|
|
3283
|
+
const entries = {};
|
|
3284
|
+
for (const key of SUPPORTED_KEYS) {
|
|
3285
|
+
const val = await getValue(key);
|
|
3286
|
+
entries[key] = {
|
|
3287
|
+
value: val ?? "",
|
|
3288
|
+
source: val !== void 0 ? resolveSource(key) : ""
|
|
3289
|
+
};
|
|
3290
|
+
}
|
|
3291
|
+
render(
|
|
3292
|
+
entries,
|
|
3293
|
+
opts,
|
|
3294
|
+
() => table({
|
|
3295
|
+
columns: ["KEY", "VALUE", "SOURCE"],
|
|
3296
|
+
rows: SUPPORTED_KEYS.map((key) => ({
|
|
3297
|
+
KEY: key,
|
|
3298
|
+
VALUE: entries[key].value,
|
|
3299
|
+
SOURCE: entries[key].source
|
|
3300
|
+
}))
|
|
3301
|
+
})
|
|
3302
|
+
);
|
|
3303
|
+
});
|
|
3304
|
+
cmd.command("get <key>").description("Get a configuration value").option("--json", "Output as JSON").option("--jq <expr>", "Filter JSON output with jq expression").action(async function(key) {
|
|
3305
|
+
const opts = this.optsWithGlobals();
|
|
3306
|
+
const validKey = validateKey(key);
|
|
3307
|
+
const value = await getValue(validKey);
|
|
3308
|
+
if (value === void 0) {
|
|
3309
|
+
throw new CLIError(`Key "${key}" is not set`);
|
|
3310
|
+
}
|
|
3311
|
+
render({ key, value }, opts, () => value);
|
|
3312
|
+
});
|
|
3313
|
+
cmd.command("set <key> <value>").description("Set a configuration value").action(async function(key, value) {
|
|
3314
|
+
const validKey = validateKey(key);
|
|
3315
|
+
await setValue(validKey, value);
|
|
3316
|
+
console.log(`Set ${key} = ${value}`);
|
|
3317
|
+
});
|
|
3318
|
+
cmd.command("path").description("Show configuration file path").action(async function() {
|
|
3319
|
+
console.log(join4(configDir(), "config.json"));
|
|
3320
|
+
});
|
|
3321
|
+
}
|
|
3322
|
+
|
|
2937
3323
|
// src/index.ts
|
|
2938
3324
|
function detectBinaryName() {
|
|
2939
3325
|
const argv1 = process.argv[1] ?? "";
|
|
@@ -2954,7 +3340,17 @@ function fullCommandName(cmd) {
|
|
|
2954
3340
|
function createProgram() {
|
|
2955
3341
|
const program = new Command();
|
|
2956
3342
|
const binName2 = detectBinaryName();
|
|
2957
|
-
program.name(binName2).description("CLI tool for managing Intercom workspaces").version(VERSION).option("--json", "Output as JSON").option("--jq <expr>", "Filter JSON output").option("--workspace <id>", "Override default workspace").option("--verbose", "Show HTTP request/response details").option("--no-retry", "Disable automatic rate limit retry").option("--timeout <seconds>", "Request timeout in seconds").option("--agent", "Force machine-readable output (JSON/NDJSON)").option("--human", "Force human-readable output even when piped")
|
|
3343
|
+
program.name(binName2).description("CLI tool for managing Intercom workspaces").version(VERSION).option("--json", "Output as JSON").option("--jq <expr>", "Filter JSON output").option("--workspace <id>", "Override default workspace").option("--verbose", "Show HTTP request/response details").option("--no-retry", "Disable automatic rate limit retry").option("--timeout <seconds>", "Request timeout in seconds").option("--agent", "Force machine-readable output (JSON/NDJSON)").option("--human", "Force human-readable output even when piped").option("--wide", "Show all columns with wider truncation").addHelpText(
|
|
3344
|
+
"after",
|
|
3345
|
+
`
|
|
3346
|
+
Quick start:
|
|
3347
|
+
$ intercom auth login --token <your-api-token>
|
|
3348
|
+
$ intercom me
|
|
3349
|
+
$ intercom articles list --json
|
|
3350
|
+
$ intercom setup --company-name "Acme" --articles-from ./docs
|
|
3351
|
+
|
|
3352
|
+
Environment: Set INTERCOM_TOKEN to skip login entirely.`
|
|
3353
|
+
);
|
|
2958
3354
|
registerApiCommand(program);
|
|
2959
3355
|
registerAuthCommand(program);
|
|
2960
3356
|
registerMeCommand(program);
|
|
@@ -2967,6 +3363,7 @@ function createProgram() {
|
|
|
2967
3363
|
registerSetupCommand(program);
|
|
2968
3364
|
registerPricingCommand(program);
|
|
2969
3365
|
registerCompletionCommand(program);
|
|
3366
|
+
registerConfigCommand(program);
|
|
2970
3367
|
program.hook("preAction", (thisCommand, actionCommand) => {
|
|
2971
3368
|
const rootOpts = thisCommand.opts();
|
|
2972
3369
|
if (rootOpts.agent && rootOpts.human) {
|