@indigoai-us/hq-cli 5.47.0 → 5.47.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.
@@ -0,0 +1,60 @@
1
+ /**
2
+ * `hq onboard join` planning helper.
3
+ *
4
+ * Modern HQ invites are email-keyed and TOKENLESS (hq-pro schemaVersion 2+):
5
+ * the membership row is claimed when the invitee signs into HQ with the
6
+ * invited email, and `hq sync` then discovers every company they belong to
7
+ * and pulls it locally. There is no token to redeem.
8
+ *
9
+ * The legacy path (schemaVersion ≤ 1) handed out a random `inviteToken` the
10
+ * invitee redeemed via `hq://accept/{token}`. `hq onboard join` historically
11
+ * declared `--invite-token` as a commander `requiredOption`, so a modern
12
+ * invitee — who has no token — could not even reach the action handler. They
13
+ * got `error: required option '--invite-token <token>' not specified`, which
14
+ * reads as "my HQ doesn't know I'm a member / I'm missing a token" when in
15
+ * fact the invite is fine and they simply need to sync.
16
+ *
17
+ * This module decides which path a given invocation is on and produces the
18
+ * tokenless guidance, as a pure function so it can be unit-tested without
19
+ * commander, the network, or a VaultClient (same shape as
20
+ * `onboard-identity-guard.ts`).
21
+ */
22
+ export interface JoinTokenless {
23
+ kind: "tokenless";
24
+ /** Human-facing guidance pointing at the real (sync) path. */
25
+ guidance: string;
26
+ }
27
+ export interface JoinLegacy {
28
+ kind: "legacy";
29
+ inviteToken: string;
30
+ email: string;
31
+ personName: string;
32
+ }
33
+ export interface JoinError {
34
+ kind: "error";
35
+ message: string;
36
+ }
37
+ export type JoinPlan = JoinTokenless | JoinLegacy | JoinError;
38
+ export interface JoinOptionsInput {
39
+ inviteToken?: string;
40
+ email?: string;
41
+ personName?: string;
42
+ }
43
+ /**
44
+ * Guidance shown when `hq onboard join` is run without a token. Covers both
45
+ * the CLI (`hq sync`) and the HQ Sync menubar app (the Sync button), since
46
+ * most invitees are on the app rather than the CLI.
47
+ */
48
+ export declare function tokenlessJoinGuidance(): string;
49
+ /**
50
+ * Decide how an `hq onboard join` invocation should proceed.
51
+ *
52
+ * - No token → tokenless: the invitee belongs on the `hq sync` path. Email
53
+ * and person-name are irrelevant here (sync uses the cached Cognito
54
+ * identity), so they are ignored rather than required.
55
+ * - Token → legacy: the historical redeem path. `--email` and
56
+ * `--person-name` are still required to bootstrap the person entity; a
57
+ * missing one yields an actionable error instead of a commander stack trace.
58
+ */
59
+ export declare function planOnboardJoin(opts: JoinOptionsInput): JoinPlan;
60
+ //# sourceMappingURL=onboard-join.d.ts.map
@@ -0,0 +1,76 @@
1
+ /**
2
+ * `hq onboard join` planning helper.
3
+ *
4
+ * Modern HQ invites are email-keyed and TOKENLESS (hq-pro schemaVersion 2+):
5
+ * the membership row is claimed when the invitee signs into HQ with the
6
+ * invited email, and `hq sync` then discovers every company they belong to
7
+ * and pulls it locally. There is no token to redeem.
8
+ *
9
+ * The legacy path (schemaVersion ≤ 1) handed out a random `inviteToken` the
10
+ * invitee redeemed via `hq://accept/{token}`. `hq onboard join` historically
11
+ * declared `--invite-token` as a commander `requiredOption`, so a modern
12
+ * invitee — who has no token — could not even reach the action handler. They
13
+ * got `error: required option '--invite-token <token>' not specified`, which
14
+ * reads as "my HQ doesn't know I'm a member / I'm missing a token" when in
15
+ * fact the invite is fine and they simply need to sync.
16
+ *
17
+ * This module decides which path a given invocation is on and produces the
18
+ * tokenless guidance, as a pure function so it can be unit-tested without
19
+ * commander, the network, or a VaultClient (same shape as
20
+ * `onboard-identity-guard.ts`).
21
+ */
22
+ /**
23
+ * Guidance shown when `hq onboard join` is run without a token. Covers both
24
+ * the CLI (`hq sync`) and the HQ Sync menubar app (the Sync button), since
25
+ * most invitees are on the app rather than the CLI.
26
+ */
27
+
28
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="38623485-912f-52db-bf3d-f1c911246026")}catch(e){}}();
29
+ export function tokenlessJoinGuidance() {
30
+ return [
31
+ "Modern HQ invites don't use a token — there's nothing to redeem.",
32
+ "",
33
+ "If you've accepted your invite (the email link or HQ Console), your",
34
+ "membership already exists. To pull it onto this machine, just sync:",
35
+ "",
36
+ " hq sync",
37
+ "",
38
+ "That signs you in, finds every company you've been added to, and pulls",
39
+ "it locally. In the HQ Sync menubar app, click Sync — same result.",
40
+ "",
41
+ "(--invite-token is only for legacy token invites and is no longer issued.)",
42
+ ].join("\n");
43
+ }
44
+ /**
45
+ * Decide how an `hq onboard join` invocation should proceed.
46
+ *
47
+ * - No token → tokenless: the invitee belongs on the `hq sync` path. Email
48
+ * and person-name are irrelevant here (sync uses the cached Cognito
49
+ * identity), so they are ignored rather than required.
50
+ * - Token → legacy: the historical redeem path. `--email` and
51
+ * `--person-name` are still required to bootstrap the person entity; a
52
+ * missing one yields an actionable error instead of a commander stack trace.
53
+ */
54
+ export function planOnboardJoin(opts) {
55
+ const inviteToken = (opts.inviteToken ?? "").trim();
56
+ if (!inviteToken) {
57
+ return { kind: "tokenless", guidance: tokenlessJoinGuidance() };
58
+ }
59
+ const email = (opts.email ?? "").trim();
60
+ const personName = (opts.personName ?? "").trim();
61
+ const missing = [];
62
+ if (!email)
63
+ missing.push("--email");
64
+ if (!personName)
65
+ missing.push("--person-name");
66
+ if (missing.length > 0) {
67
+ return {
68
+ kind: "error",
69
+ message: `Legacy token join (--invite-token) also requires ${missing.join(" and ")}. ` +
70
+ "Most invites are tokenless — omit --invite-token and run `hq sync` instead.",
71
+ };
72
+ }
73
+ return { kind: "legacy", inviteToken, email, personName };
74
+ }
75
+ //# sourceMappingURL=onboard-join.js.map
76
+ //# debugId=38623485-912f-52db-bf3d-f1c911246026
@@ -19,12 +19,13 @@
19
19
  * browser-OAuth flow opens automatically.
20
20
  */
21
21
 
22
- !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="99e9ce57-1051-59ce-8897-edcc83e83a16")}catch(e){}}();
22
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="05a49c9b-23bc-5b96-b5e1-cc2e573b11c3")}catch(e){}}();
23
23
  import chalk from "chalk";
24
24
  import { runOnboardCli, readCheckpoint } from "@indigoai-us/hq-onboarding";
25
25
  import { DEFAULT_HQ_ROOT, DEFAULT_VAULT_API_URL, ensureCognitoToken, buildVaultConfig, } from "../utils/cognito-session.js";
26
26
  import { createDefaultVaultClient } from "./cloud-provision.js";
27
27
  import { detectOnboardingIdentityMismatch } from "./onboard-identity-guard.js";
28
+ import { planOnboardJoin } from "./onboard-join.js";
28
29
  // ---------------------------------------------------------------------------
29
30
  // Command registration
30
31
  // ---------------------------------------------------------------------------
@@ -49,8 +50,8 @@ export function registerOnboardCommand(program) {
49
50
  console.log(` Person: ${options.personName} <${options.email}>`);
50
51
  console.log(` HQ root: ${options.hqRoot}\n`);
51
52
  console.log(chalk.gray(" This creates a NEW company you own. Joining a teammate's existing\n" +
52
- " company? Stop and run `hq onboard join --invite-token <token>` —\n" +
53
- " creating a same-named company leaves you alone in a separate one.\n"));
53
+ " company? Stop accept your invite, then run `hq sync` to pull it.\n" +
54
+ " Creating a same-named company leaves you alone in a separate one.\n"));
54
55
  const accessToken = await ensureCognitoToken();
55
56
  const result = await runOnboardCli({
56
57
  mode: "create-company",
@@ -74,21 +75,39 @@ export function registerOnboardCommand(program) {
74
75
  onboard
75
76
  .command("join")
76
77
  .description("Accept an invite and join an existing company")
77
- .requiredOption("--invite-token <token>", "Magic link token from your invite email")
78
- .requiredOption("--email <email>", "Your email (must match Cognito sign-in)")
79
- .requiredOption("--person-name <name>", "Your display name")
78
+ // Modern invites are tokenless (email-keyed); the token, email, and
79
+ // person-name are required ONLY on the legacy redeem path. Declaring them
80
+ // as plain options (not requiredOption) lets a tokenless invitee reach the
81
+ // action and be routed to `hq sync` instead of hitting a commander
82
+ // "required option not specified" wall that reads as "I have no token".
83
+ .option("--invite-token <token>", "Legacy magic-link token (modern invites don't need one)")
84
+ .option("--email <email>", "Your email (legacy token join only; must match Cognito sign-in)")
85
+ .option("--person-name <name>", "Your display name (legacy token join only)")
80
86
  .option("--hq-root <path>", `Local HQ tree root (default: ${DEFAULT_HQ_ROOT})`, DEFAULT_HQ_ROOT)
81
87
  .action(async (options) => {
82
88
  try {
83
89
  console.log(chalk.bold(`\nHQ Onboard — Join Company`));
84
- console.log(` Person: ${options.personName} <${options.email}>`);
90
+ const plan = planOnboardJoin({
91
+ inviteToken: options.inviteToken,
92
+ email: options.email,
93
+ personName: options.personName,
94
+ });
95
+ if (plan.kind === "tokenless") {
96
+ console.log(`\n${plan.guidance}\n`);
97
+ return;
98
+ }
99
+ if (plan.kind === "error") {
100
+ console.error(chalk.red(`\n✗ ${plan.message}`));
101
+ process.exit(1);
102
+ }
103
+ console.log(` Person: ${plan.personName} <${plan.email}>`);
85
104
  console.log(` HQ root: ${options.hqRoot}\n`);
86
105
  const accessToken = await ensureCognitoToken();
87
106
  const result = await runOnboardCli({
88
107
  mode: "join-company",
89
- personName: options.personName,
90
- personEmail: options.email,
91
- inviteToken: options.inviteToken,
108
+ personName: plan.personName,
109
+ personEmail: plan.email,
110
+ inviteToken: plan.inviteToken,
92
111
  vaultConfig: buildVaultConfig(accessToken),
93
112
  hqRoot: options.hqRoot,
94
113
  });
@@ -179,4 +198,4 @@ export function registerOnboardCommand(program) {
179
198
  });
180
199
  }
181
200
  //# sourceMappingURL=onboard.js.map
182
- //# debugId=99e9ce57-1051-59ce-8897-edcc83e83a16
201
+ //# debugId=05a49c9b-23bc-5b96-b5e1-cc2e573b11c3
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@indigoai-us/hq-cli",
3
- "version": "5.47.0",
3
+ "version": "5.47.1",
4
4
  "description": "HQ by Indigo management CLI — modules and cloud sync",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -0,0 +1,96 @@
1
+ /**
2
+ * Regression tests for the `hq onboard join` tokenless routing
3
+ * (invite-accept-local-attach).
4
+ *
5
+ * The bug: `hq onboard join` declared `--invite-token` as a commander
6
+ * requiredOption, so a modern (tokenless, email-keyed) invitee could not reach
7
+ * the action — they got "required option '--invite-token' not specified",
8
+ * which reads as "my HQ doesn't know I'm a member". The fix routes the
9
+ * no-token case to the real path (`hq sync`) and keeps the legacy token path
10
+ * intact. `planOnboardJoin` is a pure function — no network, no VaultClient.
11
+ */
12
+
13
+ import { describe, expect, it } from "vitest";
14
+
15
+ import { planOnboardJoin, tokenlessJoinGuidance } from "./onboard-join.js";
16
+
17
+ describe("planOnboardJoin", () => {
18
+ it("routes a no-token invocation to the tokenless (sync) path", () => {
19
+ const plan = planOnboardJoin({});
20
+ expect(plan.kind).toBe("tokenless");
21
+ if (plan.kind === "tokenless") {
22
+ expect(plan.guidance).toBe(tokenlessJoinGuidance());
23
+ }
24
+ });
25
+
26
+ it("treats an empty / whitespace-only token as tokenless", () => {
27
+ expect(planOnboardJoin({ inviteToken: "" }).kind).toBe("tokenless");
28
+ expect(planOnboardJoin({ inviteToken: " " }).kind).toBe("tokenless");
29
+ });
30
+
31
+ it("ignores email / person-name on the tokenless path (sync uses cached identity)", () => {
32
+ // A user who copy-pastes old instructions may still pass these — they must
33
+ // not flip the decision or be required.
34
+ const plan = planOnboardJoin({
35
+ email: "richard@sender.agency",
36
+ personName: "Richard",
37
+ });
38
+ expect(plan.kind).toBe("tokenless");
39
+ });
40
+
41
+ it("keeps the legacy path when a token + email + person-name are all present", () => {
42
+ const plan = planOnboardJoin({
43
+ inviteToken: "tok_abc123",
44
+ email: "richard@sender.agency",
45
+ personName: "Richard",
46
+ });
47
+ expect(plan.kind).toBe("legacy");
48
+ if (plan.kind === "legacy") {
49
+ expect(plan.inviteToken).toBe("tok_abc123");
50
+ expect(plan.email).toBe("richard@sender.agency");
51
+ expect(plan.personName).toBe("Richard");
52
+ }
53
+ });
54
+
55
+ it("trims the token before deciding", () => {
56
+ const plan = planOnboardJoin({
57
+ inviteToken: " tok_abc123 ",
58
+ email: "r@x.co",
59
+ personName: "R",
60
+ });
61
+ expect(plan.kind).toBe("legacy");
62
+ if (plan.kind === "legacy") {
63
+ expect(plan.inviteToken).toBe("tok_abc123");
64
+ }
65
+ });
66
+
67
+ it("errors actionably when a token is given but email/person-name are missing", () => {
68
+ const plan = planOnboardJoin({ inviteToken: "tok_abc123" });
69
+ expect(plan.kind).toBe("error");
70
+ if (plan.kind === "error") {
71
+ expect(plan.message).toContain("--email");
72
+ expect(plan.message).toContain("--person-name");
73
+ // Steers the likely-confused user back to the tokenless path.
74
+ expect(plan.message).toContain("hq sync");
75
+ }
76
+ });
77
+
78
+ it("names only the missing legacy field", () => {
79
+ const plan = planOnboardJoin({ inviteToken: "tok_abc123", email: "r@x.co" });
80
+ expect(plan.kind).toBe("error");
81
+ if (plan.kind === "error") {
82
+ expect(plan.message).toContain("--person-name");
83
+ expect(plan.message).not.toContain("--email and");
84
+ }
85
+ });
86
+ });
87
+
88
+ describe("tokenlessJoinGuidance", () => {
89
+ it("points at both the CLI sync and the menubar Sync button, and disclaims the token", () => {
90
+ const g = tokenlessJoinGuidance();
91
+ expect(g).toContain("hq sync");
92
+ expect(g).toMatch(/menubar|Sync/);
93
+ expect(g).toContain("--invite-token");
94
+ expect(g.toLowerCase()).toContain("don't use a token");
95
+ });
96
+ });
@@ -0,0 +1,101 @@
1
+ /**
2
+ * `hq onboard join` planning helper.
3
+ *
4
+ * Modern HQ invites are email-keyed and TOKENLESS (hq-pro schemaVersion 2+):
5
+ * the membership row is claimed when the invitee signs into HQ with the
6
+ * invited email, and `hq sync` then discovers every company they belong to
7
+ * and pulls it locally. There is no token to redeem.
8
+ *
9
+ * The legacy path (schemaVersion ≤ 1) handed out a random `inviteToken` the
10
+ * invitee redeemed via `hq://accept/{token}`. `hq onboard join` historically
11
+ * declared `--invite-token` as a commander `requiredOption`, so a modern
12
+ * invitee — who has no token — could not even reach the action handler. They
13
+ * got `error: required option '--invite-token <token>' not specified`, which
14
+ * reads as "my HQ doesn't know I'm a member / I'm missing a token" when in
15
+ * fact the invite is fine and they simply need to sync.
16
+ *
17
+ * This module decides which path a given invocation is on and produces the
18
+ * tokenless guidance, as a pure function so it can be unit-tested without
19
+ * commander, the network, or a VaultClient (same shape as
20
+ * `onboard-identity-guard.ts`).
21
+ */
22
+
23
+ export interface JoinTokenless {
24
+ kind: "tokenless";
25
+ /** Human-facing guidance pointing at the real (sync) path. */
26
+ guidance: string;
27
+ }
28
+
29
+ export interface JoinLegacy {
30
+ kind: "legacy";
31
+ inviteToken: string;
32
+ email: string;
33
+ personName: string;
34
+ }
35
+
36
+ export interface JoinError {
37
+ kind: "error";
38
+ message: string;
39
+ }
40
+
41
+ export type JoinPlan = JoinTokenless | JoinLegacy | JoinError;
42
+
43
+ export interface JoinOptionsInput {
44
+ inviteToken?: string;
45
+ email?: string;
46
+ personName?: string;
47
+ }
48
+
49
+ /**
50
+ * Guidance shown when `hq onboard join` is run without a token. Covers both
51
+ * the CLI (`hq sync`) and the HQ Sync menubar app (the Sync button), since
52
+ * most invitees are on the app rather than the CLI.
53
+ */
54
+ export function tokenlessJoinGuidance(): string {
55
+ return [
56
+ "Modern HQ invites don't use a token — there's nothing to redeem.",
57
+ "",
58
+ "If you've accepted your invite (the email link or HQ Console), your",
59
+ "membership already exists. To pull it onto this machine, just sync:",
60
+ "",
61
+ " hq sync",
62
+ "",
63
+ "That signs you in, finds every company you've been added to, and pulls",
64
+ "it locally. In the HQ Sync menubar app, click Sync — same result.",
65
+ "",
66
+ "(--invite-token is only for legacy token invites and is no longer issued.)",
67
+ ].join("\n");
68
+ }
69
+
70
+ /**
71
+ * Decide how an `hq onboard join` invocation should proceed.
72
+ *
73
+ * - No token → tokenless: the invitee belongs on the `hq sync` path. Email
74
+ * and person-name are irrelevant here (sync uses the cached Cognito
75
+ * identity), so they are ignored rather than required.
76
+ * - Token → legacy: the historical redeem path. `--email` and
77
+ * `--person-name` are still required to bootstrap the person entity; a
78
+ * missing one yields an actionable error instead of a commander stack trace.
79
+ */
80
+ export function planOnboardJoin(opts: JoinOptionsInput): JoinPlan {
81
+ const inviteToken = (opts.inviteToken ?? "").trim();
82
+ if (!inviteToken) {
83
+ return { kind: "tokenless", guidance: tokenlessJoinGuidance() };
84
+ }
85
+
86
+ const email = (opts.email ?? "").trim();
87
+ const personName = (opts.personName ?? "").trim();
88
+ const missing: string[] = [];
89
+ if (!email) missing.push("--email");
90
+ if (!personName) missing.push("--person-name");
91
+ if (missing.length > 0) {
92
+ return {
93
+ kind: "error",
94
+ message:
95
+ `Legacy token join (--invite-token) also requires ${missing.join(" and ")}. ` +
96
+ "Most invites are tokenless — omit --invite-token and run `hq sync` instead.",
97
+ };
98
+ }
99
+
100
+ return { kind: "legacy", inviteToken, email, personName };
101
+ }
@@ -31,6 +31,7 @@ import {
31
31
  } from "../utils/cognito-session.js";
32
32
  import { createDefaultVaultClient } from "./cloud-provision.js";
33
33
  import { detectOnboardingIdentityMismatch } from "./onboard-identity-guard.js";
34
+ import { planOnboardJoin } from "./onboard-join.js";
34
35
 
35
36
  // ---------------------------------------------------------------------------
36
37
  // Command registration
@@ -72,8 +73,8 @@ export function registerOnboardCommand(program: Command): void {
72
73
  console.log(
73
74
  chalk.gray(
74
75
  " This creates a NEW company you own. Joining a teammate's existing\n" +
75
- " company? Stop and run `hq onboard join --invite-token <token>` —\n" +
76
- " creating a same-named company leaves you alone in a separate one.\n",
76
+ " company? Stop accept your invite, then run `hq sync` to pull it.\n" +
77
+ " Creating a same-named company leaves you alone in a separate one.\n",
77
78
  ),
78
79
  );
79
80
 
@@ -104,31 +105,53 @@ export function registerOnboardCommand(program: Command): void {
104
105
  onboard
105
106
  .command("join")
106
107
  .description("Accept an invite and join an existing company")
107
- .requiredOption("--invite-token <token>", "Magic link token from your invite email")
108
- .requiredOption("--email <email>", "Your email (must match Cognito sign-in)")
109
- .requiredOption("--person-name <name>", "Your display name")
108
+ // Modern invites are tokenless (email-keyed); the token, email, and
109
+ // person-name are required ONLY on the legacy redeem path. Declaring them
110
+ // as plain options (not requiredOption) lets a tokenless invitee reach the
111
+ // action and be routed to `hq sync` instead of hitting a commander
112
+ // "required option not specified" wall that reads as "I have no token".
113
+ .option("--invite-token <token>", "Legacy magic-link token (modern invites don't need one)")
114
+ .option("--email <email>", "Your email (legacy token join only; must match Cognito sign-in)")
115
+ .option("--person-name <name>", "Your display name (legacy token join only)")
110
116
  .option(
111
117
  "--hq-root <path>",
112
118
  `Local HQ tree root (default: ${DEFAULT_HQ_ROOT})`,
113
119
  DEFAULT_HQ_ROOT,
114
120
  )
115
121
  .action(async (options: {
116
- inviteToken: string;
117
- email: string;
118
- personName: string;
122
+ inviteToken?: string;
123
+ email?: string;
124
+ personName?: string;
119
125
  hqRoot: string;
120
126
  }) => {
121
127
  try {
122
128
  console.log(chalk.bold(`\nHQ Onboard — Join Company`));
123
- console.log(` Person: ${options.personName} <${options.email}>`);
129
+
130
+ const plan = planOnboardJoin({
131
+ inviteToken: options.inviteToken,
132
+ email: options.email,
133
+ personName: options.personName,
134
+ });
135
+
136
+ if (plan.kind === "tokenless") {
137
+ console.log(`\n${plan.guidance}\n`);
138
+ return;
139
+ }
140
+
141
+ if (plan.kind === "error") {
142
+ console.error(chalk.red(`\n✗ ${plan.message}`));
143
+ process.exit(1);
144
+ }
145
+
146
+ console.log(` Person: ${plan.personName} <${plan.email}>`);
124
147
  console.log(` HQ root: ${options.hqRoot}\n`);
125
148
 
126
149
  const accessToken = await ensureCognitoToken();
127
150
  const result = await runOnboardCli({
128
151
  mode: "join-company",
129
- personName: options.personName,
130
- personEmail: options.email,
131
- inviteToken: options.inviteToken,
152
+ personName: plan.personName,
153
+ personEmail: plan.email,
154
+ inviteToken: plan.inviteToken,
132
155
  vaultConfig: buildVaultConfig(accessToken),
133
156
  hqRoot: options.hqRoot,
134
157
  });