@intercom/cli 0.6.0 → 0.8.1
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/CHANGELOG.md +84 -0
- package/README.md +35 -5
- package/dist/{chunk-Y4JK5AHH.js → chunk-3V2ONMAU.js} +3 -3
- package/dist/index.js +245 -29
- package/dist/{json-MMBOB7YU.js → json-OEUXXCTX.js} +2 -2
- package/package.json +9 -3
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to the Intercom CLI are documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
View it from the CLI with `intercom changelog`.
|
|
9
|
+
|
|
10
|
+
## [0.8.1] - 2026-06-16
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- Show the server's actual error message on failed requests instead of a bare `HTTP <status>`. This covers Fin SDK endpoints like `fin workflows upload` and `fin procedures upload`, which report problems (e.g. `YAML syntax error`, `Invalid file content`, `File too large`) in a different format than the rest of the API. `--verbose` now also prints the response body on errors.
|
|
15
|
+
- `fin workflows enable` and `fin workflows disable` now send the workflow state as the name the server expects (`live`/`paused`) instead of an internal numeric value, which the upload endpoint rejected. Both commands work again.
|
|
16
|
+
|
|
17
|
+
## [0.7.0] - 2026-06-10
|
|
18
|
+
|
|
19
|
+
### Added
|
|
20
|
+
|
|
21
|
+
- `intercom changelog` command to view the CLI's release notes from the terminal
|
|
22
|
+
- `fin data-connectors versions <id>` to view a data connector's version history
|
|
23
|
+
- Auto-enable fin-sdk and retry when a fin command hits the gate (#102)
|
|
24
|
+
|
|
25
|
+
### Changed
|
|
26
|
+
|
|
27
|
+
- `fin data-connectors list` now shows each connector's state
|
|
28
|
+
- `fin data-connectors get` now shows the full connector definition (method, URL, inputs, outputs)
|
|
29
|
+
- `fin data-connectors create` now takes `--name` and returns the URL to finish configuring the connector in the UI
|
|
30
|
+
|
|
31
|
+
### Fixed
|
|
32
|
+
|
|
33
|
+
- Handle `login_verification` redirect in CLI sign-in (#99)
|
|
34
|
+
|
|
35
|
+
## [0.6.0] - 2026-05-25
|
|
36
|
+
|
|
37
|
+
### Added
|
|
38
|
+
|
|
39
|
+
- `fin workflows upload`, `fin workflows enable`, and `fin workflows disable` (#95)
|
|
40
|
+
- `fin monitor pull-flagged` and `fin conversation bulk-send` (#83)
|
|
41
|
+
|
|
42
|
+
### Changed
|
|
43
|
+
|
|
44
|
+
- Strip internal fields from conversation responses (#97)
|
|
45
|
+
|
|
46
|
+
### Fixed
|
|
47
|
+
|
|
48
|
+
- Target the correct workspace during bootstrap (#96)
|
|
49
|
+
- Parse `fin workflows get` output as YAML, not JSON (#90)
|
|
50
|
+
|
|
51
|
+
## [0.5.0] - 2026-04-27
|
|
52
|
+
|
|
53
|
+
### Added
|
|
54
|
+
|
|
55
|
+
- Emit a Claude Code plugin hint from `--help` and `auth login` (#91)
|
|
56
|
+
|
|
57
|
+
## [0.4.0] - 2026-04-20
|
|
58
|
+
|
|
59
|
+
### Added
|
|
60
|
+
|
|
61
|
+
- `articles search` subcommand (#85)
|
|
62
|
+
- `pricing` command to fetch live pricing information (#67)
|
|
63
|
+
- `fin guidance create` command (#69)
|
|
64
|
+
- `fin` as a binary alias for `intercom` (#66)
|
|
65
|
+
- `intercom config` command (#43)
|
|
66
|
+
- `auth list` and `auth switch` commands (#41)
|
|
67
|
+
- Global `--dry-run` / `-n` flag (#48)
|
|
68
|
+
- `--input` flag on the `api` command and search subcommands (#46)
|
|
69
|
+
- Color output and a `--wide` flag for tables (#44)
|
|
70
|
+
- 5xx retry with exponential backoff (#49)
|
|
71
|
+
|
|
72
|
+
### Changed
|
|
73
|
+
|
|
74
|
+
- Strip article bodies from list/search by default (#86)
|
|
75
|
+
- Default messenger snippets to JWT authentication (#81)
|
|
76
|
+
|
|
77
|
+
### Fixed
|
|
78
|
+
|
|
79
|
+
- Improve `setup` command reliability for greenfield workspaces (#64)
|
|
80
|
+
|
|
81
|
+
[0.7.0]: https://github.com/intercom/cli/releases/tag/v0.7.0
|
|
82
|
+
[0.6.0]: https://github.com/intercom/cli/releases/tag/v0.6.0
|
|
83
|
+
[0.5.0]: https://github.com/intercom/cli/releases/tag/v0.5.0
|
|
84
|
+
[0.4.0]: https://github.com/intercom/cli/releases/tag/v0.4.0
|
package/README.md
CHANGED
|
@@ -73,6 +73,7 @@ intercom setup --company-name "Acme" --articles-from ./docs
|
|
|
73
73
|
| `intercom setup` | Orchestrate full workspace setup |
|
|
74
74
|
| `intercom config get\|set\|list` | Manage CLI configuration |
|
|
75
75
|
| `intercom pricing` | Show current Intercom pricing |
|
|
76
|
+
| `intercom changelog` | Show the CLI's release notes / version history |
|
|
76
77
|
| `intercom completion bash\|zsh\|fish` | Generate shell completions |
|
|
77
78
|
|
|
78
79
|
## Global Flags
|
|
@@ -141,11 +142,11 @@ intercom api /contacts --jq '.data[].email'
|
|
|
141
142
|
```bash
|
|
142
143
|
git clone git@github.com:intercom/cli.git
|
|
143
144
|
cd cli
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
145
|
+
pnpm install
|
|
146
|
+
pnpm build # Build to dist/
|
|
147
|
+
pnpm test # Run tests (~5s)
|
|
148
|
+
pnpm lint # TypeScript typecheck
|
|
149
|
+
pnpm dev # Watch mode
|
|
149
150
|
|
|
150
151
|
# Try it locally
|
|
151
152
|
export INTERCOM_TOKEN="<your-token>"
|
|
@@ -154,6 +155,35 @@ node dist/index.js me
|
|
|
154
155
|
|
|
155
156
|
See [SETUP.md](SETUP.md) for full developer setup guide.
|
|
156
157
|
|
|
158
|
+
## Releasing
|
|
159
|
+
|
|
160
|
+
Publishing `@intercom/cli` to npm runs through `.github/workflows/publish.yml`, which fires when a GitHub Release is published. It uses OIDC trusted publishing (GitHub Actions mints a short-lived token per run, so there is no long-lived `NPM_TOKEN`) and npm staged publishing, which holds every version behind a human 2FA approval before it becomes installable. The org-wide reference is the [Engineering Wiki: Release new npm package versions](https://coda.io/d/Engineering-Wiki_duvlW1VbQXK/Release-new-npm-package-versions_suK1gai4).
|
|
161
|
+
|
|
162
|
+
### Prerequisites (one-time)
|
|
163
|
+
|
|
164
|
+
- **GitHub:** write access to this repo, to bump the version and create Releases.
|
|
165
|
+
- **npm:** to approve a staged release, log in to npmjs.com as `intercom-npm-publisher` (the package owner). Credentials are in the 1Password Engineering Vault.
|
|
166
|
+
|
|
167
|
+
### Cut a stable release
|
|
168
|
+
|
|
169
|
+
1. **Bump the version.** In a PR, set the new `version` in `package.json` and move the unreleased entries in `CHANGELOG.md` under a new `## [X.Y.Z] - YYYY-MM-DD` heading. Merge it to `main` — the workflow refuses any release that is not reachable from the default branch.
|
|
170
|
+
2. **Create a GitHub Release.** From the [Releases page](https://github.com/intercom/cli/releases/new), add a tag `vX.Y.Z` that matches `package.json` exactly (the `v` prefix is required; the workflow asserts `tag == version`). Leave the release label as **None**. Publishing the Release creates the tag and triggers `publish.yml`.
|
|
171
|
+
3. **Wait for the workflow.** It runs the tag and branch guards, builds, then runs `npm stage publish` over OIDC. When the Actions run is green, the version sits in npm's staged queue — uploaded but not yet installable.
|
|
172
|
+
4. **Approve in npm.** Logged in as `intercom-npm-publisher`, open [Staged Packages](https://www.npmjs.com/settings/intercom-npm-publisher/staged-packages), review the version, and **Approve** with 2FA. A stable version publishes to the `latest` dist-tag and is then installable via `npm install -g @intercom/cli`.
|
|
173
|
+
|
|
174
|
+
### Cut a prerelease
|
|
175
|
+
|
|
176
|
+
Same path and same approval gate; a prerelease lands on the `next` dist-tag instead of `latest`.
|
|
177
|
+
|
|
178
|
+
1. Bump `package.json` to a version with a `-` suffix (e.g. `X.Y.Z-beta.1`) and merge to `main` via PR. Prereleases come from the default branch too.
|
|
179
|
+
2. Create a GitHub Release tagged `vX.Y.Z-beta.1` from that commit (the tag must match the version) and tick **Set as a pre-release**.
|
|
180
|
+
3. The workflow detects the `-` suffix and stages to the `next` tag, never `latest`.
|
|
181
|
+
4. Approve the staged version with 2FA as above. Consumers opt in explicitly with `npm install -g @intercom/cli@next`; a plain install still resolves to stable `latest`.
|
|
182
|
+
|
|
183
|
+
### Reject a staged release
|
|
184
|
+
|
|
185
|
+
If a staged version should not ship, choose **Reject** in Staged Packages and it is removed without publishing. Rejecting leaves the git tag and GitHub Release behind, so also delete the Release and tag from the GitHub Releases UI. The version can then be re-staged — only published versions are immutable.
|
|
186
|
+
|
|
157
187
|
## License
|
|
158
188
|
|
|
159
189
|
MIT
|
|
@@ -26,8 +26,8 @@ var NotFoundError = class extends CLIError {
|
|
|
26
26
|
}
|
|
27
27
|
};
|
|
28
28
|
var PermissionError = class extends CLIError {
|
|
29
|
-
constructor(message) {
|
|
30
|
-
super(message, 5);
|
|
29
|
+
constructor(message, error) {
|
|
30
|
+
super(message, 5, error);
|
|
31
31
|
}
|
|
32
32
|
};
|
|
33
33
|
var TimeoutError = class extends CLIError {
|
|
@@ -106,4 +106,4 @@ export {
|
|
|
106
106
|
formatJson,
|
|
107
107
|
applyJq
|
|
108
108
|
};
|
|
109
|
-
//# sourceMappingURL=chunk-
|
|
109
|
+
//# sourceMappingURL=chunk-3V2ONMAU.js.map
|
package/dist/index.js
CHANGED
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
applyJq,
|
|
10
10
|
detectAuthType,
|
|
11
11
|
formatJson
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-3V2ONMAU.js";
|
|
13
13
|
|
|
14
14
|
// src/index.ts
|
|
15
15
|
import { Command } from "commander";
|
|
@@ -61,11 +61,30 @@ async function fetchWithTimeout(url, init, options) {
|
|
|
61
61
|
|
|
62
62
|
// src/api/client.ts
|
|
63
63
|
var DEFAULT_BASE_URL = "https://api.intercom.io";
|
|
64
|
+
var MAX_RAW_ERROR_LEN = 500;
|
|
65
|
+
function extractApiError(body) {
|
|
66
|
+
if (!body || typeof body !== "object") return {};
|
|
67
|
+
const b = body;
|
|
68
|
+
if (Array.isArray(b.errors) && b.errors.length > 0) {
|
|
69
|
+
const first = b.errors[0];
|
|
70
|
+
return {
|
|
71
|
+
message: typeof first?.message === "string" ? first.message : void 0,
|
|
72
|
+
code: typeof first?.code === "string" ? first.code : void 0
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
if (typeof b.error === "string") return { message: b.error };
|
|
76
|
+
if (b.errors && typeof b.errors === "object") {
|
|
77
|
+
const msgs = Object.values(b.errors).flat().filter((m) => typeof m === "string");
|
|
78
|
+
if (msgs.length > 0) return { message: msgs.join("; ") };
|
|
79
|
+
}
|
|
80
|
+
if (typeof b.message === "string") return { message: b.message };
|
|
81
|
+
return {};
|
|
82
|
+
}
|
|
64
83
|
var _dryRun = false;
|
|
65
84
|
function setDryRun(enabled) {
|
|
66
85
|
_dryRun = enabled;
|
|
67
86
|
}
|
|
68
|
-
var IntercomClient = class {
|
|
87
|
+
var IntercomClient = class _IntercomClient {
|
|
69
88
|
token;
|
|
70
89
|
authType;
|
|
71
90
|
appId;
|
|
@@ -88,7 +107,49 @@ var IntercomClient = class {
|
|
|
88
107
|
}
|
|
89
108
|
return `Bearer ${this.token}`;
|
|
90
109
|
}
|
|
91
|
-
|
|
110
|
+
// Dedicated error code the API returns when the `fin-sdk` feature flag gates
|
|
111
|
+
// a fin subcommand. It's specific to that self-fixable gate (not the generic
|
|
112
|
+
// plan-restriction code), so seeing it is a safe signal to auto-enable.
|
|
113
|
+
static FIN_SDK_GATE_CODE = "fin_sdk_not_enabled";
|
|
114
|
+
// Thin wrapper over requestOnce that transparently recovers from the fin-sdk
|
|
115
|
+
// gate: if a call is blocked because the flag isn't enabled, opt the
|
|
116
|
+
// workspace in via POST /fin-sdk/enable and retry the original request once.
|
|
117
|
+
// If enabling fails, the original gate error is surfaced unchanged.
|
|
118
|
+
async request(method, path, body, responseType = "json", extraHeaders, isFinSdkRetry = false) {
|
|
119
|
+
try {
|
|
120
|
+
return await this.requestOnce(
|
|
121
|
+
method,
|
|
122
|
+
path,
|
|
123
|
+
body,
|
|
124
|
+
responseType,
|
|
125
|
+
extraHeaders
|
|
126
|
+
);
|
|
127
|
+
} catch (err) {
|
|
128
|
+
if (!isFinSdkRetry && !_dryRun && err instanceof PermissionError && err.error?.code === _IntercomClient.FIN_SDK_GATE_CODE) {
|
|
129
|
+
try {
|
|
130
|
+
await this.enableFinSdk();
|
|
131
|
+
} catch {
|
|
132
|
+
throw err;
|
|
133
|
+
}
|
|
134
|
+
return this.request(
|
|
135
|
+
method,
|
|
136
|
+
path,
|
|
137
|
+
body,
|
|
138
|
+
responseType,
|
|
139
|
+
extraHeaders,
|
|
140
|
+
true
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
throw err;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
// Opt the current workspace into the fin-sdk feature flag. The notice goes to
|
|
147
|
+
// stderr so it never contaminates --json / piped stdout output.
|
|
148
|
+
async enableFinSdk() {
|
|
149
|
+
console.error("Fin SDK not enabled for this workspace \u2014 enabling it now...");
|
|
150
|
+
await this.requestOnce("POST", "/fin-sdk/enable", {}, "json");
|
|
151
|
+
}
|
|
152
|
+
async requestOnce(method, path, body, responseType = "json", extraHeaders) {
|
|
92
153
|
const url = `${this.baseUrl}${path}`;
|
|
93
154
|
if (_dryRun) {
|
|
94
155
|
console.error(`> ${method} ${url}`);
|
|
@@ -147,15 +208,32 @@ var IntercomClient = class {
|
|
|
147
208
|
return response.json();
|
|
148
209
|
}
|
|
149
210
|
}
|
|
150
|
-
const
|
|
151
|
-
|
|
211
|
+
const rawBody = await response.text().catch(() => "");
|
|
212
|
+
if (this.verbose && rawBody) {
|
|
213
|
+
console.error(`< Body: ${rawBody}`);
|
|
214
|
+
}
|
|
215
|
+
let parsedBody;
|
|
216
|
+
try {
|
|
217
|
+
parsedBody = rawBody ? JSON.parse(rawBody) : void 0;
|
|
218
|
+
} catch {
|
|
219
|
+
parsedBody = void 0;
|
|
220
|
+
}
|
|
221
|
+
const extracted = extractApiError(parsedBody);
|
|
222
|
+
const trimmedBody = rawBody.trim();
|
|
223
|
+
const rawFallback = trimmedBody.length > MAX_RAW_ERROR_LEN ? `${trimmedBody.slice(0, MAX_RAW_ERROR_LEN)}\u2026` : trimmedBody;
|
|
224
|
+
const errorMsg = extracted.message ?? (rawFallback || `HTTP ${response.status}`);
|
|
225
|
+
const errorCode = extracted.code ?? "";
|
|
152
226
|
if (response.status === 401) {
|
|
153
227
|
throw new AuthError(
|
|
154
228
|
`Authentication expired or invalid: ${errorMsg}. Run "intercom auth login" to re-authenticate.`
|
|
155
229
|
);
|
|
156
230
|
}
|
|
157
231
|
if (response.status === 403) {
|
|
158
|
-
throw new PermissionError(`Permission denied: ${errorMsg}
|
|
232
|
+
throw new PermissionError(`Permission denied: ${errorMsg}`, {
|
|
233
|
+
code: errorCode,
|
|
234
|
+
message: errorMsg,
|
|
235
|
+
status: 403
|
|
236
|
+
});
|
|
159
237
|
}
|
|
160
238
|
if (response.status === 404) {
|
|
161
239
|
throw new NotFoundError(errorMsg);
|
|
@@ -905,13 +983,18 @@ async function deviseSignIn(baseUrl, email, password, timeoutMs = API_TIMEOUT_MS
|
|
|
905
983
|
}
|
|
906
984
|
const location = signInResponse.headers.get("location") ?? "";
|
|
907
985
|
if (location.includes("/admins/sign_in")) {
|
|
908
|
-
throw new
|
|
986
|
+
throw new AuthError("Invalid credentials");
|
|
909
987
|
}
|
|
910
988
|
if (location.includes("two_factor")) {
|
|
911
|
-
throw new
|
|
989
|
+
throw new AuthError(
|
|
912
990
|
"Two-factor authentication is required. Please use an API key instead."
|
|
913
991
|
);
|
|
914
992
|
}
|
|
993
|
+
if (location.includes("/login_verification")) {
|
|
994
|
+
throw new AuthError(
|
|
995
|
+
"Login email verification is required.\n Please sign in to the Intercom web app to complete verification, then retry this command."
|
|
996
|
+
);
|
|
997
|
+
}
|
|
915
998
|
cookies = mergeCookies(cookies, extractCookies(signInResponse));
|
|
916
999
|
return cookies;
|
|
917
1000
|
}
|
|
@@ -2493,9 +2576,13 @@ function registerProceduresCommand(fin) {
|
|
|
2493
2576
|
}
|
|
2494
2577
|
|
|
2495
2578
|
// src/commands/fin/data-connectors.ts
|
|
2579
|
+
function summarizeKeys(obj) {
|
|
2580
|
+
if (!obj) return "";
|
|
2581
|
+
return Object.keys(obj).join(", ");
|
|
2582
|
+
}
|
|
2496
2583
|
function registerDataConnectorsCommand(fin) {
|
|
2497
2584
|
const dataConnectors = fin.command("data-connectors").description("Manage Fin SDK data connectors");
|
|
2498
|
-
dataConnectors.command("list").description("List data connectors from the Fin SDK manifest").option("--json", "Output as JSON").option("--verbose", "Show HTTP request/response details").action(async function() {
|
|
2585
|
+
dataConnectors.command("list").description("List data connectors from the Fin SDK manifest").option("--json", "Output as JSON").option("--jq <expr>", "Filter JSON output with jq expression").option("--verbose", "Show HTTP request/response details").action(async function() {
|
|
2499
2586
|
const opts = this.optsWithGlobals();
|
|
2500
2587
|
const client = await resolveClient(opts);
|
|
2501
2588
|
const result = await client.get("/fin-sdk/manifest");
|
|
@@ -2507,33 +2594,38 @@ function registerDataConnectorsCommand(fin) {
|
|
|
2507
2594
|
}
|
|
2508
2595
|
const rows = items.map((item) => ({
|
|
2509
2596
|
ID: String(item.id ?? ""),
|
|
2510
|
-
NAME: String(item.name ?? "")
|
|
2597
|
+
NAME: String(item.name ?? ""),
|
|
2598
|
+
STATE: String(item.state ?? "")
|
|
2511
2599
|
}));
|
|
2512
|
-
return table({ columns: ["ID", "NAME"], rows });
|
|
2600
|
+
return table({ columns: ["ID", "NAME", "STATE"], rows });
|
|
2513
2601
|
}, items);
|
|
2514
2602
|
});
|
|
2515
|
-
dataConnectors.command("create").description("Create a data connector").requiredOption("
|
|
2603
|
+
dataConnectors.command("create").description("Create a draft data connector (finish configuration in the UI)").requiredOption("--name <name>", "Name for the new data connector").option("--json", "Output as JSON").option("--jq <expr>", "Filter JSON output with jq expression").option("--verbose", "Show HTTP request/response details").addHelpText(
|
|
2604
|
+
"after",
|
|
2605
|
+
`
|
|
2606
|
+
The create endpoint only sets the name \u2014 it returns a web_url where you finish
|
|
2607
|
+
configuring the connector (request, inputs, outputs, auth) in the Intercom UI.
|
|
2608
|
+
|
|
2609
|
+
Example:
|
|
2610
|
+
$ intercom fin data-connectors create --name "Order lookup"`
|
|
2611
|
+
).action(async function() {
|
|
2516
2612
|
const opts = this.optsWithGlobals();
|
|
2517
2613
|
const client = await resolveClient(opts);
|
|
2518
|
-
const
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
const eq = f.indexOf("=");
|
|
2522
|
-
if (eq === -1) throw new Error(`Invalid field format: ${f}. Expected key=value`);
|
|
2523
|
-
body[f.slice(0, eq)] = f.slice(eq + 1);
|
|
2524
|
-
}
|
|
2525
|
-
const result = await client.post("/fin-sdk/data-connectors", body);
|
|
2614
|
+
const result = await client.post("/fin-sdk/data-connectors", {
|
|
2615
|
+
name: opts.name
|
|
2616
|
+
});
|
|
2526
2617
|
render(
|
|
2527
2618
|
result,
|
|
2528
2619
|
opts,
|
|
2529
2620
|
() => detail({
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
"
|
|
2621
|
+
ID: String(result.id ?? ""),
|
|
2622
|
+
Name: String(result.name ?? ""),
|
|
2623
|
+
"Web URL": result.web_url != null ? String(result.web_url) : void 0,
|
|
2624
|
+
"Next step": result.message != null ? String(result.message) : void 0
|
|
2533
2625
|
})
|
|
2534
2626
|
);
|
|
2535
2627
|
});
|
|
2536
|
-
dataConnectors.command("get <id>").description("Download a data connector by ID").option("--json", "Output as JSON").option("--jq <expr>", "Filter JSON output with jq expression").option("--verbose", "Show HTTP request/response details").action(async function(id) {
|
|
2628
|
+
dataConnectors.command("get <id>").description("Download a data connector's full definition by ID").option("--json", "Output as JSON").option("--jq <expr>", "Filter JSON output with jq expression").option("--verbose", "Show HTTP request/response details").action(async function(id) {
|
|
2537
2629
|
const opts = this.optsWithGlobals();
|
|
2538
2630
|
const client = await resolveClient(opts);
|
|
2539
2631
|
const result = await client.get(`/fin-sdk/data-connectors/${id}/download`);
|
|
@@ -2541,11 +2633,36 @@ function registerDataConnectorsCommand(fin) {
|
|
|
2541
2633
|
result,
|
|
2542
2634
|
opts,
|
|
2543
2635
|
() => detail({
|
|
2544
|
-
|
|
2545
|
-
|
|
2636
|
+
ID: String(result.id ?? ""),
|
|
2637
|
+
Name: String(result.name ?? ""),
|
|
2638
|
+
State: result.state != null ? String(result.state) : void 0,
|
|
2639
|
+
Description: result.description != null ? String(result.description) : void 0,
|
|
2640
|
+
"Direct Fin usage": result.enable_direct_usage != null ? String(result.enable_direct_usage) : void 0,
|
|
2641
|
+
Method: result.request?.method != null ? String(result.request.method) : void 0,
|
|
2642
|
+
URL: result.request?.url != null ? String(result.request.url) : void 0,
|
|
2643
|
+
Inputs: summarizeKeys(result.inputs) || void 0,
|
|
2644
|
+
Outputs: summarizeKeys(result.outputs) || void 0
|
|
2546
2645
|
})
|
|
2547
2646
|
);
|
|
2548
2647
|
});
|
|
2648
|
+
dataConnectors.command("versions <id>").description("List a data connector's server-side version history").option("--json", "Output as JSON").option("--jq <expr>", "Filter JSON output with jq expression").option("--verbose", "Show HTTP request/response details").action(async function(id) {
|
|
2649
|
+
const opts = this.optsWithGlobals();
|
|
2650
|
+
const client = await resolveClient(opts);
|
|
2651
|
+
const result = await client.get(`/fin-sdk/data-connectors/${id}/versions`);
|
|
2652
|
+
const items = result.versions ?? [];
|
|
2653
|
+
render(items, opts, () => {
|
|
2654
|
+
if (items.length === 0) {
|
|
2655
|
+
return "No versions found.";
|
|
2656
|
+
}
|
|
2657
|
+
const rows = items.map((v) => ({
|
|
2658
|
+
STATUS: String(v.status ?? ""),
|
|
2659
|
+
CREATED: String(v.created_at ?? ""),
|
|
2660
|
+
AUTHOR: String(v.author?.name ?? ""),
|
|
2661
|
+
NOTE: String(v.note ?? "")
|
|
2662
|
+
}));
|
|
2663
|
+
return table({ columns: ["STATUS", "CREATED", "AUTHOR", "NOTE"], rows });
|
|
2664
|
+
}, items);
|
|
2665
|
+
});
|
|
2549
2666
|
}
|
|
2550
2667
|
|
|
2551
2668
|
// src/commands/fin/guidance.ts
|
|
@@ -2656,8 +2773,8 @@ function registerAudiencesCommand(fin) {
|
|
|
2656
2773
|
import { readFile as readFile3 } from "fs/promises";
|
|
2657
2774
|
import { basename as basename2 } from "path";
|
|
2658
2775
|
import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
|
|
2659
|
-
var STATE_LIVE =
|
|
2660
|
-
var STATE_PAUSED =
|
|
2776
|
+
var STATE_LIVE = "live";
|
|
2777
|
+
var STATE_PAUSED = "paused";
|
|
2661
2778
|
function registerWorkflowsCommand(fin) {
|
|
2662
2779
|
const workflows = fin.command("workflows").description("Manage Fin SDK workflows");
|
|
2663
2780
|
workflows.command("list").description("List Fin SDK workflows").option("--json", "Output as JSON").option("--verbose", "Show HTTP request/response details").action(async function() {
|
|
@@ -3973,7 +4090,7 @@ function registerPricingCommand(program) {
|
|
|
3973
4090
|
}
|
|
3974
4091
|
const markdown = await response.text();
|
|
3975
4092
|
if (opts.json) {
|
|
3976
|
-
const { formatJson: formatJson2 } = await import("./json-
|
|
4093
|
+
const { formatJson: formatJson2 } = await import("./json-OEUXXCTX.js");
|
|
3977
4094
|
console.log(formatJson2({ url: PRICING_URL, content: markdown }));
|
|
3978
4095
|
} else {
|
|
3979
4096
|
console.log(markdown);
|
|
@@ -4075,6 +4192,104 @@ function registerConfigCommand(program) {
|
|
|
4075
4192
|
});
|
|
4076
4193
|
}
|
|
4077
4194
|
|
|
4195
|
+
// src/commands/changelog.ts
|
|
4196
|
+
import pc2 from "picocolors";
|
|
4197
|
+
|
|
4198
|
+
// src/changelog.ts
|
|
4199
|
+
import { readFileSync as readFileSync5 } from "fs";
|
|
4200
|
+
import { fileURLToPath } from "url";
|
|
4201
|
+
var RELEASE_RE = /^##\s+\[?([^\]]+?)\]?(?:\s+-\s+(.+))?$/;
|
|
4202
|
+
var SECTION_RE = /^###\s+(.+)$/;
|
|
4203
|
+
var ENTRY_RE = /^[-*]\s+(.+)$/;
|
|
4204
|
+
function parseChangelog(markdown) {
|
|
4205
|
+
const releases = [];
|
|
4206
|
+
let release = null;
|
|
4207
|
+
let section = null;
|
|
4208
|
+
for (const raw of markdown.split("\n")) {
|
|
4209
|
+
const line = raw.trimEnd();
|
|
4210
|
+
const releaseMatch = line.match(RELEASE_RE);
|
|
4211
|
+
if (releaseMatch) {
|
|
4212
|
+
release = { version: releaseMatch[1].trim(), date: releaseMatch[2]?.trim() ?? "", sections: [] };
|
|
4213
|
+
section = null;
|
|
4214
|
+
releases.push(release);
|
|
4215
|
+
continue;
|
|
4216
|
+
}
|
|
4217
|
+
if (!release) continue;
|
|
4218
|
+
const sectionMatch = line.match(SECTION_RE);
|
|
4219
|
+
if (sectionMatch) {
|
|
4220
|
+
section = { title: sectionMatch[1].trim(), entries: [] };
|
|
4221
|
+
release.sections.push(section);
|
|
4222
|
+
continue;
|
|
4223
|
+
}
|
|
4224
|
+
const entryMatch = line.match(ENTRY_RE);
|
|
4225
|
+
if (entryMatch && section) {
|
|
4226
|
+
section.entries.push(entryMatch[1].trim());
|
|
4227
|
+
}
|
|
4228
|
+
}
|
|
4229
|
+
return releases;
|
|
4230
|
+
}
|
|
4231
|
+
var cached;
|
|
4232
|
+
function loadChangelog() {
|
|
4233
|
+
if (cached) return cached;
|
|
4234
|
+
const path = fileURLToPath(new URL("../CHANGELOG.md", import.meta.url));
|
|
4235
|
+
cached = parseChangelog(readFileSync5(path, "utf8"));
|
|
4236
|
+
return cached;
|
|
4237
|
+
}
|
|
4238
|
+
|
|
4239
|
+
// src/commands/changelog.ts
|
|
4240
|
+
function normalizeVersion(v) {
|
|
4241
|
+
return v.replace(/^v/i, "");
|
|
4242
|
+
}
|
|
4243
|
+
function colorEnabled() {
|
|
4244
|
+
return !!process.stdout.isTTY && !process.env.NO_COLOR;
|
|
4245
|
+
}
|
|
4246
|
+
function renderHuman(releases) {
|
|
4247
|
+
const color = colorEnabled();
|
|
4248
|
+
const out = [];
|
|
4249
|
+
for (const release of releases) {
|
|
4250
|
+
const heading = release.date ? `${release.version} \u2014 ${release.date}` : release.version;
|
|
4251
|
+
out.push(color ? pc2.bold(heading) : heading);
|
|
4252
|
+
for (const section of release.sections) {
|
|
4253
|
+
out.push(` ${color ? pc2.dim(section.title) : section.title}`);
|
|
4254
|
+
for (const entry of section.entries) {
|
|
4255
|
+
out.push(` \u2022 ${entry}`);
|
|
4256
|
+
}
|
|
4257
|
+
}
|
|
4258
|
+
out.push("");
|
|
4259
|
+
}
|
|
4260
|
+
return out.join("\n").trimEnd();
|
|
4261
|
+
}
|
|
4262
|
+
function registerChangelogCommand(program) {
|
|
4263
|
+
program.command("changelog").description("Show the CLI's release notes / version history").argument("[version]", "Show a single version (e.g. 0.7.0)").option("-n, --limit <count>", "Show only the most recent <count> releases").option("--json", "Output as JSON").option("--jq <expr>", "Filter JSON output with jq expression").addHelpText(
|
|
4264
|
+
"after",
|
|
4265
|
+
`
|
|
4266
|
+
Examples:
|
|
4267
|
+
$ intercom changelog Show the full release history
|
|
4268
|
+
$ intercom changelog --limit 1 Show only the latest release
|
|
4269
|
+
$ intercom changelog 0.7.0 Show a specific version
|
|
4270
|
+
$ intercom changelog --json Machine-readable release history`
|
|
4271
|
+
).action(async function(version) {
|
|
4272
|
+
const opts = this.optsWithGlobals();
|
|
4273
|
+
let releases = loadChangelog();
|
|
4274
|
+
if (version) {
|
|
4275
|
+
const wanted = normalizeVersion(version);
|
|
4276
|
+
const match = releases.find((r) => normalizeVersion(r.version) === wanted);
|
|
4277
|
+
if (!match) {
|
|
4278
|
+
const known = releases.map((r) => r.version).join(", ");
|
|
4279
|
+
throw new CLIError(`No changelog entry for version "${version}". Known versions: ${known}`);
|
|
4280
|
+
}
|
|
4281
|
+
releases = [match];
|
|
4282
|
+
} else if (opts.limit !== void 0) {
|
|
4283
|
+
const limit = parseInt(String(opts.limit), 10);
|
|
4284
|
+
if (isNaN(limit) || limit <= 0) {
|
|
4285
|
+
throw new CLIError("--limit must be a positive integer");
|
|
4286
|
+
}
|
|
4287
|
+
releases = releases.slice(0, limit);
|
|
4288
|
+
}
|
|
4289
|
+
render(releases, opts, () => renderHuman(releases), releases);
|
|
4290
|
+
});
|
|
4291
|
+
}
|
|
4292
|
+
|
|
4078
4293
|
// src/index.ts
|
|
4079
4294
|
function detectBinaryName() {
|
|
4080
4295
|
const argv1 = process.argv[1] ?? "";
|
|
@@ -4119,6 +4334,7 @@ Environment: Set INTERCOM_TOKEN to skip login entirely.`;
|
|
|
4119
4334
|
registerPricingCommand(program);
|
|
4120
4335
|
registerCompletionCommand(program);
|
|
4121
4336
|
registerConfigCommand(program);
|
|
4337
|
+
registerChangelogCommand(program);
|
|
4122
4338
|
program.hook("preAction", (thisCommand, actionCommand) => {
|
|
4123
4339
|
const rootOpts = thisCommand.opts();
|
|
4124
4340
|
if (rootOpts.agent && rootOpts.human) {
|
package/package.json
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@intercom/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.1",
|
|
4
4
|
"description": "CLI tool for managing Intercom workspaces",
|
|
5
5
|
"type": "module",
|
|
6
|
+
"packageManager": "pnpm@10.23.0",
|
|
6
7
|
"bin": {
|
|
7
8
|
"intercom": "dist/index.js",
|
|
8
9
|
"fin": "dist/index.js"
|
|
@@ -20,7 +21,8 @@
|
|
|
20
21
|
},
|
|
21
22
|
"files": [
|
|
22
23
|
"dist",
|
|
23
|
-
"!dist/**/*.map"
|
|
24
|
+
"!dist/**/*.map",
|
|
25
|
+
"CHANGELOG.md"
|
|
24
26
|
],
|
|
25
27
|
"license": "MIT",
|
|
26
28
|
"dependencies": {
|
|
@@ -38,6 +40,10 @@
|
|
|
38
40
|
"msw": "^2.12.13",
|
|
39
41
|
"tsup": "^8.5.1",
|
|
40
42
|
"typescript": "^5.9.3",
|
|
41
|
-
"vitest": "^
|
|
43
|
+
"vitest": "^4.1.0"
|
|
44
|
+
},
|
|
45
|
+
"pnpm": {
|
|
46
|
+
"overrides": {},
|
|
47
|
+
"onlyBuiltDependencies": []
|
|
42
48
|
}
|
|
43
49
|
}
|