@intercom/cli 0.8.1 → 0.8.3

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 CHANGED
@@ -7,6 +7,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  View it from the CLI with `intercom changelog`.
9
9
 
10
+ ## [0.8.3] - 2026-06-18
11
+
12
+ ### Fixed
13
+
14
+ - `fin workflows upload` now works with workflow YAML that doesn't declare a top-level `type:`. The upload endpoint is shared with procedures and routes by that field, so a workflow file without `type: workflow` was being validated as a procedure (`object at root is missing required properties: name, trigger_description, procedure`). The CLI now sets `type: workflow` for you when it's absent.
15
+ - `fin workflows enable` and `fin workflows disable` no longer fail with `Resource not found`. They re-upload the workflow after changing its state, and the re-uploaded YAML was missing `type: workflow`, so the server looked for a procedure with that id and 404'd. The state-change upload now carries `type: workflow`.
16
+ - `fin workflows get <id>` now prints the workflow as YAML (the format `upload` expects) instead of a lossy JSON summary, and accepts an optional output file: `intercom fin workflows get 42 workflow.yaml`. `--json` still returns parsed JSON.
17
+
18
+ ## [0.8.2] - 2026-06-17
19
+
20
+ ### Fixed
21
+
22
+ - EU- and AU-hosted workspaces now send API requests to their regional host (`api.eu.intercom.io` / `api.au.intercom.io`) automatically. `intercom auth login` detects the workspace's region from `/me` and stores it, so commands route correctly. This fixes `fin workflows upload` (and `enable`/`disable`) failing with `No file provided` for EU/AU workspaces, which happened because uploads to the US host were proxied cross-region in a way that dropped the file. Existing EU/AU users should run `intercom auth login --token <token>` once to record their region. You can still override the host with `INTERCOM_API_BASE_URL`, or set `INTERCOM_REGION=eu|au|us` when authenticating via the `INTERCOM_TOKEN` environment variable.
23
+
10
24
  ## [0.8.1] - 2026-06-16
11
25
 
12
26
  ### Fixed
@@ -106,4 +106,4 @@ export {
106
106
  formatJson,
107
107
  applyJq
108
108
  };
109
- //# sourceMappingURL=chunk-3V2ONMAU.js.map
109
+ //# sourceMappingURL=chunk-HMPO42TQ.js.map
package/dist/index.js CHANGED
@@ -9,7 +9,7 @@ import {
9
9
  applyJq,
10
10
  detectAuthType,
11
11
  formatJson
12
- } from "./chunk-3V2ONMAU.js";
12
+ } from "./chunk-HMPO42TQ.js";
13
13
 
14
14
  // src/index.ts
15
15
  import { Command } from "commander";
@@ -347,6 +347,43 @@ var IntercomClient = class _IntercomClient {
347
347
  }
348
348
  };
349
349
 
350
+ // src/api/regions.ts
351
+ var REGION_BASE_URLS = {
352
+ us: "https://api.intercom.io",
353
+ eu: "https://api.eu.intercom.io",
354
+ au: "https://api.au.intercom.io"
355
+ };
356
+ function regionBaseUrl(region) {
357
+ if (!region) return void 0;
358
+ return REGION_BASE_URLS[region];
359
+ }
360
+ function normalizeRegion(value) {
361
+ if (typeof value !== "string") return void 0;
362
+ switch (value.trim().toLowerCase()) {
363
+ case "us":
364
+ case "usa":
365
+ case "united states":
366
+ return "us";
367
+ case "eu":
368
+ case "europe":
369
+ return "eu";
370
+ case "au":
371
+ case "australia":
372
+ return "au";
373
+ default:
374
+ return void 0;
375
+ }
376
+ }
377
+ function detectRegion(me) {
378
+ if (!me || typeof me !== "object") return void 0;
379
+ const m = me;
380
+ const app = m.app;
381
+ return normalizeRegion(app?.region ?? m.region);
382
+ }
383
+ function resolveBaseUrl(region) {
384
+ return process.env.INTERCOM_API_BASE_URL || regionBaseUrl(region) || void 0;
385
+ }
386
+
350
387
  // src/auth/store.ts
351
388
  import { readFileSync, mkdirSync } from "fs";
352
389
 
@@ -583,7 +620,7 @@ async function resolveClient(options = {}) {
583
620
  token: envToken,
584
621
  authType: detectAuthType(envToken),
585
622
  appId: process.env.INTERCOM_APP_ID ?? "",
586
- baseUrl: process.env.INTERCOM_API_BASE_URL || void 0,
623
+ baseUrl: resolveBaseUrl(normalizeRegion(process.env.INTERCOM_REGION)),
587
624
  verbose: options.verbose,
588
625
  maxRetries: options.retry === false ? 0 : void 0,
589
626
  timeoutMs: options.timeout ? parseInt(options.timeout, 10) * 1e3 : void 0
@@ -613,7 +650,7 @@ async function resolveClient(options = {}) {
613
650
  token: cred.token,
614
651
  authType: cred.auth_type,
615
652
  appId: cred.app_id,
616
- baseUrl: process.env.INTERCOM_API_BASE_URL || void 0,
653
+ baseUrl: resolveBaseUrl(cred.region),
617
654
  verbose: options.verbose,
618
655
  maxRetries: options.retry === false ? 0 : void 0,
619
656
  timeoutMs: options.timeout ? parseInt(options.timeout, 10) * 1e3 : void 0
@@ -833,11 +870,13 @@ async function loginWithToken(token) {
833
870
  const app = me.app;
834
871
  const appId = app?.id_code ?? me.id;
835
872
  const name = app?.name ?? "Unknown";
873
+ const region = detectRegion(me);
836
874
  const configDir2 = process.env.INTERCOM_CONFIG_DIR;
837
875
  const store = new CredentialStore(createDefaultBackend(configDir2));
838
- await store.set(appId, { token, auth_type: authType, app_id: appId });
839
- await addWorkspace(configDir2, { app_id: appId, name });
840
- console.log(`Authenticated to ${name} (${appId})`);
876
+ await store.set(appId, { token, auth_type: authType, app_id: appId, region });
877
+ await addWorkspace(configDir2, { app_id: appId, name, region });
878
+ const regionLabel = region ? ` [${region.toUpperCase()}]` : "";
879
+ console.log(`Authenticated to ${name} (${appId})${regionLabel}`);
841
880
  emitClaudeCodeHint();
842
881
  }
843
882
 
@@ -2775,6 +2814,24 @@ import { basename as basename2 } from "path";
2775
2814
  import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
2776
2815
  var STATE_LIVE = "live";
2777
2816
  var STATE_PAUSED = "paused";
2817
+ var WORKFLOW_TYPE = "workflow";
2818
+ function ensureWorkflowType(raw) {
2819
+ let parsed;
2820
+ try {
2821
+ parsed = parseYaml(raw);
2822
+ } catch {
2823
+ return raw;
2824
+ }
2825
+ if (parsed == null || typeof parsed !== "object" || Array.isArray(parsed)) {
2826
+ return raw;
2827
+ }
2828
+ const obj = parsed;
2829
+ if (obj.type != null && obj.type !== "") {
2830
+ return raw;
2831
+ }
2832
+ obj.type = WORKFLOW_TYPE;
2833
+ return stringifyYaml(obj);
2834
+ }
2778
2835
  function registerWorkflowsCommand(fin) {
2779
2836
  const workflows = fin.command("workflows").description("Manage Fin SDK workflows");
2780
2837
  workflows.command("list").description("List Fin SDK workflows").option("--json", "Output as JSON").option("--verbose", "Show HTTP request/response details").action(async function() {
@@ -2795,22 +2852,31 @@ function registerWorkflowsCommand(fin) {
2795
2852
  return table({ columns: ["ID", "TITLE", "STATE", "TRIGGER"], rows });
2796
2853
  }, items);
2797
2854
  });
2798
- workflows.command("get <id>").description("Get a workflow 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) {
2855
+ workflows.command("get <id> [output]").description("Get a workflow by ID as YAML (writes to [output] file, or stdout)").option("--json", "Output as parsed JSON instead of YAML").option("--jq <expr>", "Filter parsed-JSON output with a jq expression").option("--verbose", "Show HTTP request/response details").addHelpText(
2856
+ "after",
2857
+ `
2858
+ Examples:
2859
+ $ intercom fin workflows get 42 workflow.yaml # write YAML to a file
2860
+ $ intercom fin workflows get 42 > workflow.yaml # or redirect stdout
2861
+ $ intercom fin workflows get 42 --json | jq .state
2862
+
2863
+ The YAML it prints is ready to edit and re-upload with
2864
+ \`intercom fin workflows upload\`.`
2865
+ ).action(async function(id, output) {
2799
2866
  const opts = this.optsWithGlobals();
2800
2867
  const client = await resolveClient(opts);
2801
2868
  const text = await client.getText(`/fin-sdk/workflows/${id}`);
2802
- const parsed = parseYaml(text);
2803
- render(
2804
- parsed,
2805
- opts,
2806
- () => detail({
2807
- "ID": String(parsed.id ?? ""),
2808
- "Title": String(parsed.title ?? ""),
2809
- "State": String(parsed.state ?? ""),
2810
- "Version": parsed.version != null ? String(parsed.version) : void 0,
2811
- "Trigger": parsed.trigger_type != null ? String(parsed.trigger_type) : void 0
2812
- })
2813
- );
2869
+ if (opts.json || opts.jq || opts.agent) {
2870
+ const parsed = parseYaml(text);
2871
+ render(parsed, opts, () => formatJson(parsed));
2872
+ return;
2873
+ }
2874
+ if (output) {
2875
+ await atomicWriteFile(output, text, 420);
2876
+ console.error(`Wrote workflow ${id} to ${output}`);
2877
+ } else {
2878
+ console.log(text.replace(/\n+$/, ""));
2879
+ }
2814
2880
  });
2815
2881
  workflows.command("upload <file>").description('Upload a workflow YAML file (creates if id is "new", updates if numeric)').option("--json", "Output as JSON").option("--jq <expr>", "Filter JSON output with jq expression").option("--verbose", "Show HTTP request/response details").addHelpText(
2816
2882
  "after",
@@ -2826,9 +2892,10 @@ The YAML file's id field determines whether this creates or updates:
2826
2892
  ).action(async function(filePath) {
2827
2893
  const opts = this.optsWithGlobals();
2828
2894
  const client = await resolveClient(opts);
2829
- const fileContent = await readFile3(filePath);
2895
+ const fileContent = await readFile3(filePath, "utf8");
2896
+ const yamlBody = ensureWorkflowType(fileContent);
2830
2897
  const formData = new FormData();
2831
- const blob = new Blob([fileContent], { type: "application/x-yaml" });
2898
+ const blob = new Blob([yamlBody], { type: "application/x-yaml" });
2832
2899
  formData.append("file_data", blob, basename2(filePath));
2833
2900
  const result = await client.postFormData("/fin-sdk/upload", formData);
2834
2901
  render(
@@ -2862,6 +2929,7 @@ overwritten (the server does not expose a concurrency token).`
2862
2929
  } catch (err) {
2863
2930
  throw new Error(`server returned unparseable YAML for workflow ${id}: ${err.message}`);
2864
2931
  }
2932
+ parsed.type = WORKFLOW_TYPE;
2865
2933
  parsed.state = STATE_LIVE;
2866
2934
  const yamlBody = stringifyYaml(parsed);
2867
2935
  const formData = new FormData();
@@ -2898,6 +2966,7 @@ overwritten (the server does not expose a concurrency token).`
2898
2966
  } catch (err) {
2899
2967
  throw new Error(`server returned unparseable YAML for workflow ${id}: ${err.message}`);
2900
2968
  }
2969
+ parsed.type = WORKFLOW_TYPE;
2901
2970
  parsed.state = STATE_PAUSED;
2902
2971
  const yamlBody = stringifyYaml(parsed);
2903
2972
  const formData = new FormData();
@@ -4090,7 +4159,7 @@ function registerPricingCommand(program) {
4090
4159
  }
4091
4160
  const markdown = await response.text();
4092
4161
  if (opts.json) {
4093
- const { formatJson: formatJson2 } = await import("./json-OEUXXCTX.js");
4162
+ const { formatJson: formatJson2 } = await import("./json-BJB55URV.js");
4094
4163
  console.log(formatJson2({ url: PRICING_URL, content: markdown }));
4095
4164
  } else {
4096
4165
  console.log(markdown);
@@ -2,9 +2,9 @@
2
2
  import {
3
3
  applyJq,
4
4
  formatJson
5
- } from "./chunk-3V2ONMAU.js";
5
+ } from "./chunk-HMPO42TQ.js";
6
6
  export {
7
7
  applyJq,
8
8
  formatJson
9
9
  };
10
- //# sourceMappingURL=json-OEUXXCTX.js.map
10
+ //# sourceMappingURL=json-BJB55URV.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@intercom/cli",
3
- "version": "0.8.1",
3
+ "version": "0.8.3",
4
4
  "description": "CLI tool for managing Intercom workspaces",
5
5
  "type": "module",
6
6
  "packageManager": "pnpm@10.23.0",