@4xeoz/re-entry 0.2.14 → 0.2.16

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 CHANGED
@@ -2,8 +2,9 @@
2
2
 
3
3
  > **Cloud Receiver dependency notice — 2026-09-02:** The former `re-entry-weld.vercel.app` Cloud
4
4
  > Receiver is deprecated and must not be used for new pairing, credentials, or production traffic.
5
- > This Connector package remains a reusable/local preview surface, but a new Receiver origin must
6
- > be supplied explicitly by an accepted replacement service.
5
+ > This Connector package remains a reusable/local preview surface and defaults to the accepted
6
+ > preview origin `https://cloud-receiver-delta.vercel.app`; pass `--receiver` for another accepted
7
+ > Receiver. This preview default is not a production deployment.
7
8
 
8
9
  Install it once on the Mac where Codex should open; after one dashboard pairing code, a macOS
9
10
  LaunchAgent keeps the outbound Connector running at login.
@@ -18,8 +19,7 @@ Requirements: macOS, Node.js 24+, Codex installed and signed in, and an absolute
18
19
  Codex may read and write.
19
20
 
20
21
  ```sh
21
- npx --yes --package=@4xeoz/re-entry re-entry install \
22
- --receiver https://your-accepted-receiver.example
22
+ npx --yes --package=@4xeoz/re-entry re-entry install
23
23
  ```
24
24
 
25
25
  `npx` runs a temporary copy, so it does not leave a permanent shell command behind. Install the
@@ -33,16 +33,16 @@ The package installs both `re-entry` and the older `reentry` spelling; `re-entry
33
33
  command. If you keep using the temporary `npx` form, run later commands with the same executable
34
34
  prefix, for example `npx --yes --package=@4xeoz/re-entry re-entry status`.
35
35
 
36
- The CLI no longer has a default hosted Receiver. For any new or historical test, pass an explicit
37
- accepted Receiver origin with `--receiver` or `REENTRY_RECEIVER_ORIGIN`; the former
38
- `https://re-entry-weld.vercel.app` alias is retired.
36
+ The CLI defaults to the current Cloud Receiver v2 preview at
37
+ `https://cloud-receiver-delta.vercel.app`. Override it with `--receiver` or
38
+ `REENTRY_RECEIVER_ORIGIN` when using another accepted Receiver. This preview default is not a
39
+ production deployment.
39
40
 
40
41
  The interactive CLI first offers Desktop, the current folder, or a small folder browser. Choose
41
42
  with ↑/↓ and Enter. If you want to skip the picker, pass the workspace directly:
42
43
 
43
44
  ```sh
44
45
  npx --yes --package=@4xeoz/re-entry re-entry install \
45
- --receiver https://your-accepted-receiver.example \
46
46
  --codex-cd /absolute/path/to/your/project
47
47
  ```
48
48
 
@@ -60,7 +60,6 @@ installation instead:
60
60
  ```sh
61
61
  npm install --global @4xeoz/re-entry
62
62
  re-entry install \
63
- --receiver https://your-accepted-receiver.example \
64
63
  --codex-cd /absolute/path/to/your/project
65
64
  ```
66
65
 
@@ -98,6 +97,7 @@ re-entry status
98
97
  re-entry listen
99
98
  re-entry --help
100
99
  re-entry stop
100
+ re-entry disconnect
101
101
  re-entry uninstall
102
102
  ```
103
103
 
@@ -108,14 +108,15 @@ npx --yes --package=@4xeoz/re-entry re-entry status
108
108
  npx --yes --package=@4xeoz/re-entry re-entry listen
109
109
  npx --yes --package=@4xeoz/re-entry re-entry --help
110
110
  npx --yes --package=@4xeoz/re-entry re-entry stop
111
+ npx --yes --package=@4xeoz/re-entry re-entry disconnect
111
112
  npx --yes --package=@4xeoz/re-entry re-entry uninstall
112
113
  ```
113
114
 
114
115
  The status view checks the local authorization, background job, Receiver reachability, Node, and
115
116
  Codex. If the Receiver rejects a previously saved device credential, the Connector pauses instead
116
- of retrying forever, and `status`/`listen` print an executable `connect` command. `listen` watches the
117
- already-running background Connector and displays new activity until you press Ctrl+C; it does not
118
- start a competing second poller. Useful development commands are:
117
+ of retrying forever, and `status`/`listen` tell you to disconnect before reconnecting. `listen` watches
118
+ the already-running background Connector and displays new activity until you press Ctrl+C; it does
119
+ not start a competing second poller. Useful development commands are:
119
120
 
120
121
  ```sh
121
122
  npx --yes --package=@4xeoz/re-entry re-entry doctor --codex-cd /absolute/path/to/project
@@ -137,6 +138,7 @@ To pause or remove the local Connector:
137
138
 
138
139
  ```sh
139
140
  npx --yes --package=@4xeoz/re-entry re-entry stop
141
+ npx --yes --package=@4xeoz/re-entry re-entry disconnect
140
142
  npx --yes --package=@4xeoz/re-entry re-entry uninstall
141
143
  ```
142
144
 
@@ -145,9 +147,23 @@ typing `DELETE`, then removes only the LaunchAgent, saved Connector credential,
145
147
  It does not recursively delete folders or uninstall the npm package. To remove a global npm
146
148
  installation separately, run `npm uninstall --global @4xeoz/re-entry`.
147
149
 
148
- `connect` repeats only account authorization. `install` is the normal product path because it also
149
- installs the background job. `claim-once` is the smallest manual delivery test. The legacy
150
- Host-code `pair` command remains only for compatibility tests.
150
+ `disconnect` is the simple local sign-out: it stops the LaunchAgent, removes the saved Connector
151
+ credential, and leaves the log files in place. It is safe to run again when already disconnected.
152
+ The current CLI does not have a server-side revoke route, so this command does not remove the Mac
153
+ from the account's remote device list; use the account dashboard's revoke/decommission action when
154
+ that Receiver operation is available, or wait for the remote credential to expire. `uninstall` is
155
+ the stronger local cleanup and also removes Connector logs.
156
+
157
+ Each credential file accepts one connection at a time. Repeating `connect` or `install` with the
158
+ same saved connection returns `already connected`; an expired, rejected, or different-Receiver
159
+ connection is not silently replaced. Run `disconnect`, then `install`, to connect this Mac to a
160
+ different account or Receiver. `connect` repeats only account authorization, while `install` is the
161
+ normal product path because it also installs the background job. `claim-once` is the smallest manual
162
+ delivery test. The legacy Host-code `pair` command follows the same one-connection rule and remains
163
+ only for compatibility tests.
164
+
165
+ `Ctrl+C` stops a foreground `start` process or closes the `listen` view. It does not remove the
166
+ saved connection; use `stop` to pause the background service or `disconnect` to clear it.
151
167
 
152
168
  ## Give this to a coding agent
153
169
 
@@ -222,6 +238,7 @@ the npm account that owns the `4xeoz` scope, publish from this directory:
222
238
  ```sh
223
239
  npm login
224
240
  npm whoami
241
+ npm view @4xeoz/re-entry version
225
242
  npm version patch --no-git-tag-version
226
243
  npm run verify
227
244
  npm publish --access public
@@ -232,7 +249,8 @@ publishing with `npm version patch --no-git-tag-version`.
232
249
 
233
250
  After publishing, users run `npx --yes --package=@4xeoz/re-entry re-entry install`. The explicit
234
251
  `--package` form works across npm versions when the package is scoped; the executable itself is
235
- still named `re-entry`.
252
+ still named `re-entry`. The command uses the built-in preview Receiver unless `--receiver` or
253
+ `REENTRY_RECEIVER_ORIGIN` overrides it.
236
254
 
237
255
  A temporary `npx` invocation does not add `re-entry` to your shell `PATH`. If you prefer the shorter
238
256
  bare commands, run `npm install --global @4xeoz/re-entry` once and then use `re-entry install`
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@4xeoz/re-entry",
3
- "version": "0.2.14",
3
+ "version": "0.2.16",
4
4
  "type": "module",
5
- "description": "Outbound Local Connector process for Re-entry Core; new Receiver origins must be explicit.",
5
+ "description": "Outbound Local Connector process for Re-entry Core; uses the Cloud Receiver v2 preview by default with an explicit override for other accepted origins.",
6
6
  "publishConfig": {
7
7
  "access": "public"
8
8
  },
@@ -113,6 +113,30 @@ export async function stopMacConnectorService(options = {}) {
113
113
  return Object.freeze({ ...status, stopped: true, running: false });
114
114
  }
115
115
 
116
+ export async function disconnectMacConnectorService(options = {}) {
117
+ const configuration = requireServiceConfiguration(options, "disconnect");
118
+ if (process.platform !== "darwin" && configuration.allowNonMacForTest !== true) {
119
+ return Object.freeze({ supported: false, disconnected: false, removedPaths: [] });
120
+ }
121
+ const service = await stopMacConnectorService(configuration);
122
+ const stateDirectory = configuration.stateDirectory ?? join(homedir(), ".webmcp-connector");
123
+ const credentialFile = requireAbsolutePath(
124
+ configuration.credentialFile ?? join(stateDirectory, "credentials.json"),
125
+ "Credential file",
126
+ );
127
+ const removedPaths = await removePaths([
128
+ service.plistPath,
129
+ credentialFile,
130
+ `${credentialFile}.reauthorization-required.json`,
131
+ ], "disconnect");
132
+ return Object.freeze({
133
+ supported: true,
134
+ disconnected: removedPaths.length > 0,
135
+ removedPaths,
136
+ plistPath: service.plistPath,
137
+ });
138
+ }
139
+
116
140
  export async function uninstallMacConnectorService(options = {}) {
117
141
  const configuration = requireServiceConfiguration(options, "uninstall");
118
142
  if (process.platform !== "darwin" && configuration.allowNonMacForTest !== true) {
@@ -131,22 +155,28 @@ export async function uninstallMacConnectorService(options = {}) {
131
155
  join(stateDirectory, "connector.log"),
132
156
  join(stateDirectory, "connector-error.log"),
133
157
  ])];
158
+ const removedPaths = await removePaths(paths, "uninstall");
159
+ return Object.freeze({
160
+ supported: true,
161
+ removedPaths,
162
+ plistPath: service.plistPath,
163
+ });
164
+ }
165
+
166
+ async function removePaths(paths, operation) {
134
167
  const removedPaths = [];
135
- for (const path of paths) {
168
+ for (const path of [...new Set(paths)]) {
169
+ if (typeof path !== "string") continue;
136
170
  try {
137
171
  await unlink(path);
138
172
  removedPaths.push(path);
139
173
  } catch (error) {
140
174
  if (error?.code !== "ENOENT") {
141
- throw serviceFailure("connector_service_remove_failed", "Connector data could not be removed", error);
175
+ throw serviceFailure("connector_service_remove_failed", `Connector data could not be removed during ${operation}`, error);
142
176
  }
143
177
  }
144
178
  }
145
- return Object.freeze({
146
- supported: true,
147
- removedPaths,
148
- plistPath: service.plistPath,
149
- });
179
+ return removedPaths;
150
180
  }
151
181
 
152
182
  export function renderLaunchAgent(options) {
package/src/main.mjs CHANGED
@@ -28,15 +28,17 @@ import { LocalConnectorPairingClient, openBrowser } from "./pairing-client.mjs";
28
28
  import { chooseWorkspaceDirectory } from "./workspace-picker.mjs";
29
29
  import {
30
30
  inspectMacConnectorService,
31
+ disconnectMacConnectorService,
31
32
  installMacConnectorService,
32
33
  stopMacConnectorService,
33
34
  uninstallMacConnectorService,
34
35
  } from "./macos-service.mjs";
35
36
  import { createTerminalUi } from "./terminal-ui.mjs";
36
37
 
37
- // The former hosted Cloud Receiver is retired. New pairing must name an accepted replacement
38
- // explicitly instead of silently sending credentials to the old deployment alias.
39
- const DEFAULT_RECEIVER_ORIGIN = process.env.REENTRY_RECEIVER_ORIGIN;
38
+ // This is the current Cloud Receiver v2 preview. A command-line --receiver override remains
39
+ // authoritative, and REENTRY_RECEIVER_ORIGIN can override this built-in preview default.
40
+ const BUILT_IN_RECEIVER_ORIGIN = "https://cloud-receiver-delta.vercel.app";
41
+ const DEFAULT_RECEIVER_ORIGIN = process.env.REENTRY_RECEIVER_ORIGIN?.trim() || BUILT_IN_RECEIVER_ORIGIN;
40
42
  const DEFAULT_POLL_INTERVAL_MS = 5_000;
41
43
  const DEFAULT_MAX_CONSECUTIVE_ERRORS = 5;
42
44
  const DEFAULT_PAIRING_REQUEST_TIMEOUT_MS = 20_000;
@@ -88,6 +90,10 @@ async function main() {
88
90
  await stop(flags, ui);
89
91
  return;
90
92
  }
93
+ if (command === "disconnect") {
94
+ await disconnect(flags, ui);
95
+ return;
96
+ }
91
97
  if (command === "uninstall") {
92
98
  await uninstall(flags, ui);
93
99
  return;
@@ -129,6 +135,7 @@ Connect this Mac once, then receive approved work in Codex in the background.
129
135
  Usage:
130
136
  re-entry install Recommended first run
131
137
  re-entry listen Watch live activity
138
+ re-entry disconnect Sign out this Mac locally
132
139
  re-entry test "Reply with hello" Test Codex locally
133
140
 
134
141
  Invocation:
@@ -141,6 +148,7 @@ Commands:
141
148
  listen Watch the background Connector until you press Ctrl+C
142
149
  test Start one fresh local Codex session with the supplied prompt
143
150
  stop Stop the background Connector without removing its credential
151
+ disconnect Stop the Connector and clear this Mac's saved connection
144
152
  uninstall Stop the Connector and remove its local service data
145
153
  connect Open Re-entry, redeem a dashboard pairing code, and connect this Mac
146
154
  start Poll for approved work until stopped
@@ -149,7 +157,7 @@ Commands:
149
157
  pair Legacy Host-code pairing preview
150
158
 
151
159
  Common options:
152
- --receiver <url> Accepted Receiver origin (required when pairing a new Mac)
160
+ --receiver <url> Accepted Receiver origin (overrides the preview default)
153
161
  --codex-cd <path> Workspace for Codex; interactive mode offers a folder picker if omitted
154
162
  --codex-binary <path> Explicit Codex executable
155
163
  --json Machine-readable output
@@ -157,6 +165,10 @@ Common options:
157
165
  --help Show this help
158
166
  --version Show the installed version
159
167
 
168
+ Default Receiver:
169
+ https://cloud-receiver-delta.vercel.app
170
+ Override with --receiver or REENTRY_RECEIVER_ORIGIN for another accepted Receiver.
171
+
160
172
  Both commands are installed: re-entry and reentry.
161
173
  The Connector opens no inbound port. Host keys never belong on this Mac.
162
174
  `);
@@ -239,6 +251,8 @@ async function pair(flags, ui, options = {}) {
239
251
  }
240
252
  const receiver = requireFlag(flags, "receiver");
241
253
  const credentialFile = flags["credential-file"] ?? defaultCredentialFile();
254
+ const store = new LocalConnectorCredentialStore({ filename: credentialFile });
255
+ if (await store.load()) throw cliFailure("connector_account_already_connected");
242
256
  if (ui.interactive) {
243
257
  ui.step("Receiver", receiver);
244
258
  }
@@ -265,7 +279,6 @@ async function pair(flags, ui, options = {}) {
265
279
  ui.warning("Browser", "did not open automatically; use the URL above");
266
280
  }
267
281
  }
268
- const store = new LocalConnectorCredentialStore({ filename: credentialFile });
269
282
  await store.save({
270
283
  version: 1,
271
284
  receiver_origin: receiver,
@@ -310,9 +323,7 @@ async function connect(flags, ui, options = {}) {
310
323
  return current;
311
324
  }
312
325
 
313
- if (currentIsValid && ui.interactive) {
314
- ui.warning("Receiver changed", `${current.receiver_origin} → ${receiver}; approve this Mac again`);
315
- }
326
+ if (current) throw cliFailure("connector_account_already_connected");
316
327
 
317
328
  if (ui.interactive) {
318
329
  if (!options.guidedInstall) ui.info("Re-entry", displayReceiver(receiver));
@@ -381,11 +392,15 @@ async function status(flags, ui) {
381
392
  Date.parse(credentials.connector_expires_at) > Date.now() &&
382
393
  !reauthorizationRequired,
383
394
  );
395
+ const credentialExpired = Boolean(
396
+ credentials && Date.parse(credentials.connector_expires_at) <= Date.now(),
397
+ );
384
398
  if (ui.interactive) {
385
399
  ui.section("SYSTEM", "This Mac");
386
400
  showReadiness(readiness, ui);
387
401
  ui.section("CONNECTION", "Re-entry");
388
- if (reauthorizationRequired) ui.warning("Account", "reconnect required");
402
+ if (reauthorizationRequired) ui.warning("Account", "disconnect required before reconnecting");
403
+ else if (credentialExpired) ui.warning("Account", "connection expired; disconnect before reconnecting");
389
404
  else if (connected) ui.success("Account", "connected");
390
405
  else ui.warning("Account", "not connected");
391
406
  if (reauthorizationRequired && service.running) ui.warning("Background", "paused until this Mac is reconnected");
@@ -395,8 +410,8 @@ async function status(flags, ui) {
395
410
  if (receiverReady) ui.success("Cloud", "online");
396
411
  else if (credentials) ui.warning("Cloud", "unavailable");
397
412
 
398
- if (reauthorizationRequired) {
399
- ui.next(cliCommand("connect"), "Approve this Mac again in your browser; the old credential was rejected.");
413
+ if (reauthorizationRequired || credentialExpired) {
414
+ ui.next(cliCommand("disconnect"), "Clear this Mac's saved connection, then run install to connect again.");
400
415
  } else if (!connected || !service.running) {
401
416
  ui.next(cliCommand("install"), "Finish setup and start Re-entry in the background.");
402
417
  } else if (!receiverReady) {
@@ -413,6 +428,7 @@ async function status(flags, ui) {
413
428
  connector_id: credentials?.connector_id ?? null,
414
429
  receiver_origin: credentials?.receiver_origin ?? null,
415
430
  receiver_ready: receiverReady,
431
+ credential_expired: credentialExpired,
416
432
  service_installed: service.installed,
417
433
  service_running: service.running,
418
434
  codex_binary: readiness.installation.executable,
@@ -431,11 +447,18 @@ async function listen(flags, ui) {
431
447
  filename: defaultCredentialFile(),
432
448
  }).load();
433
449
  const reauthorizationRequired = await hasConnectorReauthorizationRequired(defaultCredentialFile());
434
- if (reauthorizationRequired || !service.running || !credentials) {
450
+ const credentialExpired = Boolean(
451
+ credentials && Date.parse(credentials.connector_expires_at) <= Date.now(),
452
+ );
453
+ if (reauthorizationRequired || credentialExpired || !service.running || !credentials) {
435
454
  if (ui.interactive) {
436
455
  if (reauthorizationRequired) {
437
- ui.warning("Account", "reconnect required; the Cloud Receiver rejected this Mac");
438
- ui.next(cliCommand("connect"), "Approve this Mac again in your browser.");
456
+ ui.warning("Account", "disconnect required; the Cloud Receiver rejected this Mac");
457
+ ui.next(cliCommand("disconnect"), "Clear this Mac's saved connection, then run install again.");
458
+ }
459
+ if (credentialExpired) {
460
+ ui.warning("Account", "connection expired");
461
+ ui.next(cliCommand("disconnect"), "Clear this Mac's saved connection, then run install again.");
439
462
  }
440
463
  if (!credentials) ui.warning("Account", "not connected");
441
464
  if (!service.running) ui.warning("Background", "not running");
@@ -445,6 +468,7 @@ async function listen(flags, ui) {
445
468
  event: "connector_listener_unavailable",
446
469
  connected: Boolean(credentials),
447
470
  reauthorization_required: reauthorizationRequired,
471
+ credential_expired: credentialExpired,
448
472
  service_running: service.running,
449
473
  })}\n`);
450
474
  }
@@ -521,7 +545,7 @@ function reportLiveActivity(event, ui) {
521
545
  );
522
546
  } else if (event.event === "connector_reauthorization_required" || event.code === "connector_identity_invalid") {
523
547
  ui.stopWait("Reconnect required", "the Cloud Receiver rejected this Mac's saved connection", "warning");
524
- ui.next(cliCommand("connect"), "Approve this Mac again in your browser.");
548
+ ui.next(cliCommand("disconnect"), "Clear this Mac's saved connection, then run install again.");
525
549
  return;
526
550
  } else if (event.event === "connector_poll_failed" || event.event === "local_connector_failed") {
527
551
  ui.stopWait("Connection interrupted", "Re-entry is retrying", "warning");
@@ -554,6 +578,32 @@ async function stop(flags, ui) {
554
578
  }
555
579
  }
556
580
 
581
+ async function disconnect(flags, ui) {
582
+ if (ui.interactive) {
583
+ ui.begin("Disconnect Re-entry", "Stop background delivery and clear this Mac's saved connection");
584
+ }
585
+ const result = await disconnectMacConnectorService({
586
+ credentialFile: flags["credential-file"] ?? defaultCredentialFile(),
587
+ });
588
+ if (ui.interactive) {
589
+ if (!result.supported) {
590
+ ui.warning("Platform", "connection lifecycle control currently supports macOS only");
591
+ } else if (result.disconnected) {
592
+ ui.complete("This Mac is disconnected", "The LaunchAgent and local Connector credential are cleared.");
593
+ } else {
594
+ ui.info("Already disconnected", "No saved local connection or background service was found.");
595
+ }
596
+ if (result.supported) ui.next(cliCommand("install"), "Connect this Mac to one Re-entry account again.");
597
+ } else {
598
+ process.stdout.write(`${JSON.stringify({
599
+ event: "connector_disconnected",
600
+ supported: result.supported,
601
+ disconnected: result.disconnected,
602
+ removed_paths: result.removedPaths,
603
+ })}\n`);
604
+ }
605
+ }
606
+
557
607
  async function uninstall(flags, ui) {
558
608
  if (ui.interactive) {
559
609
  ui.begin("Uninstall Re-entry", "Stop the Connector and remove only its local service data");
@@ -637,8 +687,8 @@ async function start(flags, ui) {
637
687
  credentials = await connect(runtimeFlags, ui, { quietHeader: true });
638
688
  } else if (await hasConnectorReauthorizationRequired(credentialFile)) {
639
689
  if (ui.interactive) {
640
- ui.warning("Account", "this Mac needs to be connected again");
641
- ui.next(cliCommand("connect"), "Create a new dashboard pairing code and connect this Mac again.");
690
+ ui.warning("Account", "this Mac must be disconnected before reconnecting");
691
+ ui.next(cliCommand("disconnect"), "Clear the saved connection, then run install again.");
642
692
  } else {
643
693
  process.stdout.write('{"event":"connector_reauthorization_required"}\n');
644
694
  }
@@ -684,7 +734,7 @@ async function start(flags, ui) {
684
734
  });
685
735
  if (ui.interactive) {
686
736
  ui.stopWait("Reconnect required", "the Cloud Receiver rejected this Mac's saved connection", "warning");
687
- ui.next(cliCommand("connect"), "Approve this Mac again in your browser.");
737
+ ui.next(cliCommand("disconnect"), "Clear this Mac's saved connection, then run install again.");
688
738
  } else {
689
739
  process.stdout.write('{"event":"connector_reauthorization_required"}\n');
690
740
  }
@@ -876,6 +926,7 @@ function validateCommandFlags(command, flags, positionals) {
876
926
  listen: new Set(["json"]),
877
927
  test: new Set(["codex-cd", "codex-binary", "activation-timeout", "json"]),
878
928
  stop: new Set(["json"]),
929
+ disconnect: new Set(["credential-file", "json"]),
879
930
  uninstall: new Set(["credential-file", "yes", "json"]),
880
931
  install: new Set([
881
932
  "receiver",
@@ -1052,10 +1103,11 @@ function errorHint(error) {
1052
1103
  connector_node_unsupported: "use Node.js 24 or newer, then run the command again",
1053
1104
  connector_receiver_missing: "pass --receiver <replacement-receiver-origin> or set REENTRY_RECEIVER_ORIGIN",
1054
1105
  connector_credentials_missing: `connect this Mac once with \`${cliCommand("connect")}\``,
1055
- connector_credentials_expired: `run \`${cliCommand("connect")}\` to authorize this Mac again`,
1106
+ connector_credentials_expired: `run \`${cliCommand("disconnect")}\`, then \`${cliCommand("install")}\` to authorize this Mac again`,
1056
1107
  connector_credentials_already_exists: "use the existing Connector credential or ask for a new pairing code",
1057
- connector_identity_invalid: `run \`${cliCommand("connect")}\` and approve this Mac again`,
1058
- connector_reauthorization_required: `run \`${cliCommand("connect")}\` and approve this Mac again`,
1108
+ connector_account_already_connected: `run \`${cliCommand("disconnect")}\` before connecting a different account or Receiver`,
1109
+ connector_identity_invalid: `run \`${cliCommand("disconnect")}\`, then \`${cliCommand("install")}\` and approve this Mac again`,
1110
+ connector_reauthorization_required: `run \`${cliCommand("disconnect")}\`, then \`${cliCommand("install")}\` and approve this Mac again`,
1059
1111
  connector_pairing_code_missing: `ask the Host backend for a new pairing code, then run \`${cliCommand("pair")}\` in a terminal`,
1060
1112
  pairing_code_invalid: "use the 16-character code returned by the Host, for example ABCD-EFGH-IJKL-MNOP",
1061
1113
  host_subject_already_paired: "use the existing Connector credential or revoke/reset the preview pairing",