@intercom/cli 0.13.0 → 0.14.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 CHANGED
@@ -7,6 +7,22 @@ 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.14.1] - 2026-10-27
11
+
12
+ ### Fixed
13
+
14
+ - `intercom auth signup` no longer reads its CSRF token from the old signup page, which is moving to the new signup site. It now gets the token from the signup state endpoint, so signup keeps working after the old page redirects.
15
+
16
+ ## [0.14.0] - 2026-09-02
17
+
18
+ ### Added
19
+
20
+ - `intercom fin workflows upload --verify` compares the workflow the server saved against the file just uploaded and exits non-zero listing any field that did not survive the round-trip, matching `fin procedures upload --verify`.
21
+
22
+ ### Fixed
23
+
24
+ - `--verify` combined with `--dry-run` on a `fin procedures upload` no longer fails: dry-run sends no request, so there is nothing saved to compare against and the verification step is skipped.
25
+
10
26
  ## [0.13.0] - 2026-09-01
11
27
 
12
28
  ### Changed
package/dist/index.js CHANGED
@@ -933,6 +933,19 @@ async function fetchCsrfToken(url, timeoutMs = API_TIMEOUT_MS, init) {
933
933
  if (!match) throw new Error("Could not find CSRF token");
934
934
  return { token: match[1], cookies: extractCookies(response) };
935
935
  }
936
+ async function fetchSignupCsrfToken(baseUrl, timeoutMs) {
937
+ const url = `${baseUrl}/ember/signups/state`;
938
+ const response = await fetchWithTimeout(
939
+ url,
940
+ { headers: { Accept: "application/json" } },
941
+ { timeoutMs }
942
+ );
943
+ if (!response.ok)
944
+ throw new AuthError(`Failed to fetch ${url}: ${response.status}`);
945
+ const data = await response.json();
946
+ if (!data.csrf_token) throw new AuthError("Could not find CSRF token");
947
+ return { token: data.csrf_token, cookies: extractCookies(response) };
948
+ }
936
949
  async function signup(options) {
937
950
  const {
938
951
  baseUrl,
@@ -943,9 +956,8 @@ async function signup(options) {
943
956
  plan = "advanced",
944
957
  timeoutMs = API_TIMEOUT_MS
945
958
  } = options;
946
- const csrfUrl = `${baseUrl}/admins/sign_up/email`;
947
- const { token: csrfToken, cookies } = await fetchCsrfToken(
948
- csrfUrl,
959
+ const { token: csrfToken, cookies } = await fetchSignupCsrfToken(
960
+ baseUrl,
949
961
  timeoutMs
950
962
  );
951
963
  const solutionId = PLAN_SOLUTION_IDS[plan] ?? PLAN_SOLUTION_IDS["advanced"];
@@ -3075,7 +3087,7 @@ back missing or changed is reported.`
3075
3087
  }
3076
3088
  const result = await client.postFormData("/fin-sdk/upload", formData);
3077
3089
  render(result, opts, () => formatJson(result));
3078
- if (opts.verify) {
3090
+ if (opts.verify && !isDryRun()) {
3079
3091
  verifySaved(filePath, fileContent.toString("utf8"), result);
3080
3092
  }
3081
3093
  });
@@ -3570,17 +3582,23 @@ shows which version that is).`
3570
3582
  console.log(text.replace(/\n+$/, ""));
3571
3583
  }
3572
3584
  });
3573
- 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(
3585
+ workflows.command("upload <file>").description('Upload a workflow YAML file (creates if id is "new", updates if numeric)').option("--verify", "Fail if the saved workflow differs from the file uploaded").option("--json", "Output as JSON").option("--jq <expr>", "Filter JSON output with jq expression").option("--verbose", "Show HTTP request/response details").addHelpText(
3574
3586
  "after",
3575
3587
  `
3576
3588
  Examples:
3577
3589
  $ intercom fin workflows upload ./welcome.yaml
3578
3590
  $ intercom fin workflows get wf-42 > local.yaml; vim local.yaml; intercom fin workflows upload local.yaml
3579
3591
  $ intercom fin workflows upload ./new.yaml --json | jq .visual_builder_url
3592
+ $ intercom fin workflows upload ./welcome.yaml --verify
3580
3593
 
3581
3594
  The YAML file's id field determines whether this creates or updates:
3582
3595
  id: "new" \u2192 creates a new workflow
3583
- id: <int> \u2192 updates the existing workflow with that id`
3596
+ id: <int> \u2192 updates the existing workflow with that id
3597
+
3598
+ --verify compares the workflow the server saved against the file just uploaded
3599
+ and exits non-zero listing any field that did not survive the round-trip. Fields
3600
+ the server adds (ids, defaults) are ignored; only content that was sent and came
3601
+ back missing or changed is reported.`
3584
3602
  ).action(async function(filePath) {
3585
3603
  const opts = this.optsWithGlobals();
3586
3604
  const client = await resolveClient(opts);
@@ -3600,6 +3618,9 @@ The YAML file's id field determines whether this creates or updates:
3600
3618
  "Visual Builder URL": result.visual_builder_url != null ? String(result.visual_builder_url) : void 0
3601
3619
  })
3602
3620
  );
3621
+ if (opts.verify && !isDryRun()) {
3622
+ verifySaved2(filePath, fileContent, result);
3623
+ }
3603
3624
  });
3604
3625
  workflows.command("enable <id>").description("Set a workflow's state to live").option("--json", "Output as JSON").option("--jq <expr>", "Filter JSON output with jq expression").option("--verbose", "Show HTTP request/response details").addHelpText(
3605
3626
  "after",
@@ -3715,6 +3736,37 @@ async function getVersionYaml2(client, id, versionId) {
3715
3736
  }
3716
3737
  return typeof content === "string" ? content : stringifyYaml3(content);
3717
3738
  }
3739
+ function withoutExpectedChanges(sent) {
3740
+ if (sent == null || typeof sent !== "object" || Array.isArray(sent)) {
3741
+ return sent;
3742
+ }
3743
+ const rest = { ...sent };
3744
+ delete rest.type;
3745
+ delete rest.version;
3746
+ if (String(rest.id ?? "").toLowerCase() === "new") {
3747
+ delete rest.id;
3748
+ }
3749
+ return rest;
3750
+ }
3751
+ function verifySaved2(filePath, sentYaml, result) {
3752
+ if (typeof result.new_file_content !== "string") {
3753
+ throw new CLIError("Cannot verify: the server did not return the saved workflow");
3754
+ }
3755
+ const drift = findDrift(
3756
+ withoutExpectedChanges(parseYaml3(sentYaml)),
3757
+ parseYaml3(result.new_file_content)
3758
+ );
3759
+ if (drift.length === 0) {
3760
+ console.error(`Verified: the saved workflow matches ${filePath}`);
3761
+ return;
3762
+ }
3763
+ for (const path of drift) {
3764
+ console.error(` ${path}`);
3765
+ }
3766
+ throw new CLIError(
3767
+ `${drift.length} field(s) did not survive the upload (listed above). Compare with \`intercom fin workflows get ${result.id ?? "<id>"}\`.`
3768
+ );
3769
+ }
3718
3770
 
3719
3771
  // src/commands/fin/simulations.ts
3720
3772
  import { readFile as readFile5 } from "fs/promises";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@intercom/cli",
3
- "version": "0.13.0",
3
+ "version": "0.14.1",
4
4
  "description": "CLI tool for managing Intercom workspaces",
5
5
  "type": "module",
6
6
  "packageManager": "pnpm@10.23.0",