@4xeoz/re-entry 0.2.13 → 0.2.15
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 +67 -29
- package/package.json +2 -2
- package/src/macos-service.mjs +37 -7
- package/src/main.mjs +103 -41
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
|
|
6
|
-
>
|
|
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,7 +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
|
|
22
|
+
npx --yes --package=@4xeoz/re-entry re-entry install
|
|
22
23
|
```
|
|
23
24
|
|
|
24
25
|
`npx` runs a temporary copy, so it does not leave a permanent shell command behind. Install the
|
|
@@ -29,23 +30,25 @@ npm install --global @4xeoz/re-entry
|
|
|
29
30
|
```
|
|
30
31
|
|
|
31
32
|
The package installs both `re-entry` and the older `reentry` spelling; `re-entry` is the documented
|
|
32
|
-
command.
|
|
33
|
+
command. If you keep using the temporary `npx` form, run later commands with the same executable
|
|
34
|
+
prefix, for example `npx --yes --package=@4xeoz/re-entry re-entry status`.
|
|
33
35
|
|
|
34
|
-
The CLI
|
|
35
|
-
|
|
36
|
-
`
|
|
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.
|
|
37
40
|
|
|
38
41
|
The interactive CLI first offers Desktop, the current folder, or a small folder browser. Choose
|
|
39
42
|
with ↑/↓ and Enter. If you want to skip the picker, pass the workspace directly:
|
|
40
43
|
|
|
41
44
|
```sh
|
|
42
|
-
npx
|
|
45
|
+
npx --yes --package=@4xeoz/re-entry re-entry install \
|
|
43
46
|
--codex-cd /absolute/path/to/your/project
|
|
44
47
|
```
|
|
45
48
|
|
|
46
49
|
The guided screen stays intentionally small: **Workspace → System check → Connect Re-entry**. It
|
|
47
|
-
shows one
|
|
48
|
-
paths stay out of the normal success screen.
|
|
50
|
+
shows one executable next command when setup finishes; internal Connector IDs, credential paths, and
|
|
51
|
+
log paths stay out of the normal success screen.
|
|
49
52
|
|
|
50
53
|
Run this from the Host project directory, your home directory, or another normal working
|
|
51
54
|
directory—not from a checked-out `runtime/local-connector` package directory. npm can treat that
|
|
@@ -56,7 +59,8 @@ installation instead:
|
|
|
56
59
|
|
|
57
60
|
```sh
|
|
58
61
|
npm install --global @4xeoz/re-entry
|
|
59
|
-
re-entry install
|
|
62
|
+
re-entry install \
|
|
63
|
+
--codex-cd /absolute/path/to/your/project
|
|
60
64
|
```
|
|
61
65
|
|
|
62
66
|
Use `--receiver` to select an accepted replacement Receiver, such as a local historical preview:
|
|
@@ -86,29 +90,45 @@ preview.
|
|
|
86
90
|
|
|
87
91
|
## Check it
|
|
88
92
|
|
|
93
|
+
If you installed the package globally:
|
|
94
|
+
|
|
89
95
|
```sh
|
|
90
96
|
re-entry status
|
|
91
97
|
re-entry listen
|
|
92
98
|
re-entry --help
|
|
99
|
+
re-entry stop
|
|
100
|
+
re-entry disconnect
|
|
101
|
+
re-entry uninstall
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
If you used the temporary `npx` invocation:
|
|
105
|
+
|
|
106
|
+
```sh
|
|
107
|
+
npx --yes --package=@4xeoz/re-entry re-entry status
|
|
108
|
+
npx --yes --package=@4xeoz/re-entry re-entry listen
|
|
109
|
+
npx --yes --package=@4xeoz/re-entry re-entry --help
|
|
110
|
+
npx --yes --package=@4xeoz/re-entry re-entry stop
|
|
111
|
+
npx --yes --package=@4xeoz/re-entry re-entry disconnect
|
|
112
|
+
npx --yes --package=@4xeoz/re-entry re-entry uninstall
|
|
93
113
|
```
|
|
94
114
|
|
|
95
115
|
The status view checks the local authorization, background job, Receiver reachability, Node, and
|
|
96
116
|
Codex. If the Receiver rejects a previously saved device credential, the Connector pauses instead
|
|
97
|
-
of retrying forever, and `status`/`listen` tell you to
|
|
117
|
+
of retrying forever, and `status`/`listen` tell you to disconnect before reconnecting. `listen` watches
|
|
98
118
|
the already-running background Connector and displays new activity until you press Ctrl+C; it does
|
|
99
119
|
not start a competing second poller. Useful development commands are:
|
|
100
120
|
|
|
101
121
|
```sh
|
|
102
|
-
re-entry doctor --codex-cd /absolute/path/to/project
|
|
103
|
-
re-entry connect --receiver http://127.0.0.1:43224
|
|
104
|
-
re-entry claim-once --codex-cd /absolute/path/to/project
|
|
105
|
-
re-entry start --codex-cd /absolute/path/to/project
|
|
122
|
+
npx --yes --package=@4xeoz/re-entry re-entry doctor --codex-cd /absolute/path/to/project
|
|
123
|
+
npx --yes --package=@4xeoz/re-entry re-entry connect --receiver http://127.0.0.1:43224
|
|
124
|
+
npx --yes --package=@4xeoz/re-entry re-entry claim-once --codex-cd /absolute/path/to/project
|
|
125
|
+
npx --yes --package=@4xeoz/re-entry re-entry start --codex-cd /absolute/path/to/project
|
|
106
126
|
```
|
|
107
127
|
|
|
108
128
|
Test the local Codex handoff without waiting for Cloud work:
|
|
109
129
|
|
|
110
130
|
```sh
|
|
111
|
-
re-entry test "Reply with: Re-entry is working."
|
|
131
|
+
npx --yes --package=@4xeoz/re-entry re-entry test "Reply with: Re-entry is working."
|
|
112
132
|
```
|
|
113
133
|
|
|
114
134
|
This starts one fresh local Codex process through the same adapter seam used by real deliveries. It
|
|
@@ -117,8 +137,9 @@ does not create a Grant, claim Receiver work, or prove the browser/WebMCP return
|
|
|
117
137
|
To pause or remove the local Connector:
|
|
118
138
|
|
|
119
139
|
```sh
|
|
120
|
-
re-entry stop
|
|
121
|
-
re-entry
|
|
140
|
+
npx --yes --package=@4xeoz/re-entry re-entry stop
|
|
141
|
+
npx --yes --package=@4xeoz/re-entry re-entry disconnect
|
|
142
|
+
npx --yes --package=@4xeoz/re-entry re-entry uninstall
|
|
122
143
|
```
|
|
123
144
|
|
|
124
145
|
`stop` pauses the macOS background service and keeps the account connection. `uninstall` requires
|
|
@@ -126,9 +147,23 @@ typing `DELETE`, then removes only the LaunchAgent, saved Connector credential,
|
|
|
126
147
|
It does not recursively delete folders or uninstall the npm package. To remove a global npm
|
|
127
148
|
installation separately, run `npm uninstall --global @4xeoz/re-entry`.
|
|
128
149
|
|
|
129
|
-
`
|
|
130
|
-
|
|
131
|
-
|
|
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.
|
|
132
167
|
|
|
133
168
|
## Give this to a coding agent
|
|
134
169
|
|
|
@@ -139,8 +174,8 @@ Install the Re-entry Local Connector on this Mac. First read the package README.
|
|
|
139
174
|
or newer and locate the Connector executable. Run `npx --yes --package=@4xeoz/re-entry re-entry install` with an absolute
|
|
140
175
|
project directory. Let the human create or sign in to a Re-entry account, click Pair this Mac, and
|
|
141
176
|
enter the code in the CLI; never copy browser cookies, organization keys, Connector tokens, or private keys
|
|
142
|
-
into chat, logs, source files, or git. Finish by running `re-entry status`
|
|
143
|
-
results without claiming Browser/WebMCP or production deployment.
|
|
177
|
+
into chat, logs, source files, or git. Finish by running `npx --yes --package=@4xeoz/re-entry re-entry status`
|
|
178
|
+
and report the bounded results without claiming Browser/WebMCP or production deployment.
|
|
144
179
|
```
|
|
145
180
|
|
|
146
181
|
## Package map
|
|
@@ -203,20 +238,23 @@ the npm account that owns the `4xeoz` scope, publish from this directory:
|
|
|
203
238
|
```sh
|
|
204
239
|
npm login
|
|
205
240
|
npm whoami
|
|
241
|
+
npm view @4xeoz/re-entry version
|
|
206
242
|
npm version patch --no-git-tag-version
|
|
207
243
|
npm run verify
|
|
208
244
|
npm publish --access public
|
|
209
245
|
```
|
|
210
246
|
|
|
211
|
-
NPM versions are immutable: if the
|
|
212
|
-
publishing
|
|
247
|
+
NPM versions are immutable: if the target version has already been published, bump it before
|
|
248
|
+
publishing with `npm version patch --no-git-tag-version`.
|
|
213
249
|
|
|
214
250
|
After publishing, users run `npx --yes --package=@4xeoz/re-entry re-entry install`. The explicit
|
|
215
251
|
`--package` form works across npm versions when the package is scoped; the executable itself is
|
|
216
|
-
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.
|
|
217
254
|
|
|
218
|
-
|
|
219
|
-
@4xeoz/re-entry` once and then
|
|
255
|
+
A temporary `npx` invocation does not add `re-entry` to your shell `PATH`. If you prefer the shorter
|
|
256
|
+
bare commands, run `npm install --global @4xeoz/re-entry` once and then use `re-entry install`
|
|
257
|
+
directly.
|
|
220
258
|
|
|
221
259
|
## Check a Mac before pairing
|
|
222
260
|
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@4xeoz/re-entry",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.15",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"description": "Outbound Local Connector process for Re-entry Core;
|
|
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
|
},
|
package/src/macos-service.mjs
CHANGED
|
@@ -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",
|
|
175
|
+
throw serviceFailure("connector_service_remove_failed", `Connector data could not be removed during ${operation}`, error);
|
|
142
176
|
}
|
|
143
177
|
}
|
|
144
178
|
}
|
|
145
|
-
return
|
|
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,19 +28,23 @@ 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
|
-
//
|
|
38
|
-
//
|
|
39
|
-
const
|
|
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;
|
|
43
45
|
const CONNECTOR_VERSION = readConnectorVersion();
|
|
46
|
+
// The npx form is executable even when this CLI was launched from a temporary npx cache.
|
|
47
|
+
const TEMPORARY_CLI_COMMAND = "npx --yes --package=@4xeoz/re-entry re-entry";
|
|
44
48
|
|
|
45
49
|
async function main() {
|
|
46
50
|
let ui;
|
|
@@ -86,6 +90,10 @@ async function main() {
|
|
|
86
90
|
await stop(flags, ui);
|
|
87
91
|
return;
|
|
88
92
|
}
|
|
93
|
+
if (command === "disconnect") {
|
|
94
|
+
await disconnect(flags, ui);
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
89
97
|
if (command === "uninstall") {
|
|
90
98
|
await uninstall(flags, ui);
|
|
91
99
|
return;
|
|
@@ -127,14 +135,20 @@ Connect this Mac once, then receive approved work in Codex in the background.
|
|
|
127
135
|
Usage:
|
|
128
136
|
re-entry install Recommended first run
|
|
129
137
|
re-entry listen Watch live activity
|
|
138
|
+
re-entry disconnect Sign out this Mac locally
|
|
130
139
|
re-entry test "Reply with hello" Test Codex locally
|
|
131
140
|
|
|
141
|
+
Invocation:
|
|
142
|
+
Temporary npx: npx --yes --package=@4xeoz/re-entry re-entry <command>
|
|
143
|
+
Global install: npm install --global @4xeoz/re-entry, then re-entry <command>
|
|
144
|
+
|
|
132
145
|
Commands:
|
|
133
146
|
install Check this Mac, connect the Re-entry account, and start at login
|
|
134
147
|
status Show account, Receiver, service, Node.js, and Codex readiness
|
|
135
148
|
listen Watch the background Connector until you press Ctrl+C
|
|
136
149
|
test Start one fresh local Codex session with the supplied prompt
|
|
137
150
|
stop Stop the background Connector without removing its credential
|
|
151
|
+
disconnect Stop the Connector and clear this Mac's saved connection
|
|
138
152
|
uninstall Stop the Connector and remove its local service data
|
|
139
153
|
connect Open Re-entry, redeem a dashboard pairing code, and connect this Mac
|
|
140
154
|
start Poll for approved work until stopped
|
|
@@ -143,7 +157,7 @@ Commands:
|
|
|
143
157
|
pair Legacy Host-code pairing preview
|
|
144
158
|
|
|
145
159
|
Common options:
|
|
146
|
-
--receiver <url> Accepted Receiver origin (
|
|
160
|
+
--receiver <url> Accepted Receiver origin (overrides the preview default)
|
|
147
161
|
--codex-cd <path> Workspace for Codex; interactive mode offers a folder picker if omitted
|
|
148
162
|
--codex-binary <path> Explicit Codex executable
|
|
149
163
|
--json Machine-readable output
|
|
@@ -151,11 +165,19 @@ Common options:
|
|
|
151
165
|
--help Show this help
|
|
152
166
|
--version Show the installed version
|
|
153
167
|
|
|
168
|
+
Default Receiver:
|
|
169
|
+
https://cloud-receiver-delta.vercel.app
|
|
170
|
+
Override with --receiver or REENTRY_RECEIVER_ORIGIN for another accepted Receiver.
|
|
171
|
+
|
|
154
172
|
Both commands are installed: re-entry and reentry.
|
|
155
173
|
The Connector opens no inbound port. Host keys never belong on this Mac.
|
|
156
174
|
`);
|
|
157
175
|
}
|
|
158
176
|
|
|
177
|
+
function cliCommand(command) {
|
|
178
|
+
return `${TEMPORARY_CLI_COMMAND} ${command}`;
|
|
179
|
+
}
|
|
180
|
+
|
|
159
181
|
function readConnectorVersion() {
|
|
160
182
|
const packageJson = JSON.parse(
|
|
161
183
|
readFileSync(new URL("../package.json", import.meta.url), "utf8"),
|
|
@@ -229,6 +251,8 @@ async function pair(flags, ui, options = {}) {
|
|
|
229
251
|
}
|
|
230
252
|
const receiver = requireFlag(flags, "receiver");
|
|
231
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");
|
|
232
256
|
if (ui.interactive) {
|
|
233
257
|
ui.step("Receiver", receiver);
|
|
234
258
|
}
|
|
@@ -255,7 +279,6 @@ async function pair(flags, ui, options = {}) {
|
|
|
255
279
|
ui.warning("Browser", "did not open automatically; use the URL above");
|
|
256
280
|
}
|
|
257
281
|
}
|
|
258
|
-
const store = new LocalConnectorCredentialStore({ filename: credentialFile });
|
|
259
282
|
await store.save({
|
|
260
283
|
version: 1,
|
|
261
284
|
receiver_origin: receiver,
|
|
@@ -265,7 +288,7 @@ async function pair(flags, ui, options = {}) {
|
|
|
265
288
|
});
|
|
266
289
|
if (ui.interactive) {
|
|
267
290
|
ui.complete("Pairing complete", "Re-entry can now deliver approved work to this Mac.");
|
|
268
|
-
ui.next("
|
|
291
|
+
ui.next(cliCommand("start"), "Wait for approved work in this terminal.");
|
|
269
292
|
} else {
|
|
270
293
|
process.stdout.write(`${JSON.stringify({ event: "connector_paired", connector_id: credentials.connector_id })}\n`);
|
|
271
294
|
}
|
|
@@ -288,7 +311,7 @@ async function connect(flags, ui, options = {}) {
|
|
|
288
311
|
if (ui.interactive) {
|
|
289
312
|
ui.success("Account", "already connected");
|
|
290
313
|
if (!options.guidedInstall) {
|
|
291
|
-
ui.next("
|
|
314
|
+
ui.next(cliCommand("start"), "Wait for approved work in this terminal.");
|
|
292
315
|
}
|
|
293
316
|
} else {
|
|
294
317
|
process.stdout.write(`${JSON.stringify({
|
|
@@ -300,9 +323,7 @@ async function connect(flags, ui, options = {}) {
|
|
|
300
323
|
return current;
|
|
301
324
|
}
|
|
302
325
|
|
|
303
|
-
if (
|
|
304
|
-
ui.warning("Receiver changed", `${current.receiver_origin} → ${receiver}; approve this Mac again`);
|
|
305
|
-
}
|
|
326
|
+
if (current) throw cliFailure("connector_account_already_connected");
|
|
306
327
|
|
|
307
328
|
if (ui.interactive) {
|
|
308
329
|
if (!options.guidedInstall) ui.info("Re-entry", displayReceiver(receiver));
|
|
@@ -347,7 +368,7 @@ async function connect(flags, ui, options = {}) {
|
|
|
347
368
|
if (ui.interactive) {
|
|
348
369
|
if (!options.guidedInstall) {
|
|
349
370
|
ui.complete("This Mac is connected", "Re-entry can now route approved work here.");
|
|
350
|
-
ui.next("
|
|
371
|
+
ui.next(cliCommand("start"), "Wait for approved work in this terminal.");
|
|
351
372
|
}
|
|
352
373
|
} else {
|
|
353
374
|
process.stdout.write(`${JSON.stringify({
|
|
@@ -371,11 +392,15 @@ async function status(flags, ui) {
|
|
|
371
392
|
Date.parse(credentials.connector_expires_at) > Date.now() &&
|
|
372
393
|
!reauthorizationRequired,
|
|
373
394
|
);
|
|
395
|
+
const credentialExpired = Boolean(
|
|
396
|
+
credentials && Date.parse(credentials.connector_expires_at) <= Date.now(),
|
|
397
|
+
);
|
|
374
398
|
if (ui.interactive) {
|
|
375
399
|
ui.section("SYSTEM", "This Mac");
|
|
376
400
|
showReadiness(readiness, ui);
|
|
377
401
|
ui.section("CONNECTION", "Re-entry");
|
|
378
|
-
if (reauthorizationRequired) ui.warning("Account", "
|
|
402
|
+
if (reauthorizationRequired) ui.warning("Account", "disconnect required before reconnecting");
|
|
403
|
+
else if (credentialExpired) ui.warning("Account", "connection expired; disconnect before reconnecting");
|
|
379
404
|
else if (connected) ui.success("Account", "connected");
|
|
380
405
|
else ui.warning("Account", "not connected");
|
|
381
406
|
if (reauthorizationRequired && service.running) ui.warning("Background", "paused until this Mac is reconnected");
|
|
@@ -385,15 +410,15 @@ async function status(flags, ui) {
|
|
|
385
410
|
if (receiverReady) ui.success("Cloud", "online");
|
|
386
411
|
else if (credentials) ui.warning("Cloud", "unavailable");
|
|
387
412
|
|
|
388
|
-
if (reauthorizationRequired) {
|
|
389
|
-
ui.next("
|
|
413
|
+
if (reauthorizationRequired || credentialExpired) {
|
|
414
|
+
ui.next(cliCommand("disconnect"), "Clear this Mac's saved connection, then run install to connect again.");
|
|
390
415
|
} else if (!connected || !service.running) {
|
|
391
|
-
ui.next("
|
|
416
|
+
ui.next(cliCommand("install"), "Finish setup and start Re-entry in the background.");
|
|
392
417
|
} else if (!receiverReady) {
|
|
393
|
-
ui.next("
|
|
418
|
+
ui.next(cliCommand("status"), "Check again when the Re-entry Cloud service is available.");
|
|
394
419
|
} else {
|
|
395
420
|
ui.complete("Everything looks good", "Re-entry is ready for approved work.");
|
|
396
|
-
ui.next("
|
|
421
|
+
ui.next(cliCommand("listen"), "Watch live activity. Press Ctrl+C when you are done.");
|
|
397
422
|
}
|
|
398
423
|
} else {
|
|
399
424
|
process.stdout.write(`${JSON.stringify({
|
|
@@ -403,6 +428,7 @@ async function status(flags, ui) {
|
|
|
403
428
|
connector_id: credentials?.connector_id ?? null,
|
|
404
429
|
receiver_origin: credentials?.receiver_origin ?? null,
|
|
405
430
|
receiver_ready: receiverReady,
|
|
431
|
+
credential_expired: credentialExpired,
|
|
406
432
|
service_installed: service.installed,
|
|
407
433
|
service_running: service.running,
|
|
408
434
|
codex_binary: readiness.installation.executable,
|
|
@@ -421,20 +447,28 @@ async function listen(flags, ui) {
|
|
|
421
447
|
filename: defaultCredentialFile(),
|
|
422
448
|
}).load();
|
|
423
449
|
const reauthorizationRequired = await hasConnectorReauthorizationRequired(defaultCredentialFile());
|
|
424
|
-
|
|
450
|
+
const credentialExpired = Boolean(
|
|
451
|
+
credentials && Date.parse(credentials.connector_expires_at) <= Date.now(),
|
|
452
|
+
);
|
|
453
|
+
if (reauthorizationRequired || credentialExpired || !service.running || !credentials) {
|
|
425
454
|
if (ui.interactive) {
|
|
426
455
|
if (reauthorizationRequired) {
|
|
427
|
-
ui.warning("Account", "
|
|
428
|
-
ui.next("
|
|
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.");
|
|
429
462
|
}
|
|
430
463
|
if (!credentials) ui.warning("Account", "not connected");
|
|
431
464
|
if (!service.running) ui.warning("Background", "not running");
|
|
432
|
-
ui.next("
|
|
465
|
+
ui.next(cliCommand("install"), "Finish setup and start the background Connector.");
|
|
433
466
|
} else {
|
|
434
467
|
process.stdout.write(`${JSON.stringify({
|
|
435
468
|
event: "connector_listener_unavailable",
|
|
436
469
|
connected: Boolean(credentials),
|
|
437
470
|
reauthorization_required: reauthorizationRequired,
|
|
471
|
+
credential_expired: credentialExpired,
|
|
438
472
|
service_running: service.running,
|
|
439
473
|
})}\n`);
|
|
440
474
|
}
|
|
@@ -494,7 +528,7 @@ async function testCodex(flags, positionals, ui) {
|
|
|
494
528
|
if (ui.interactive) {
|
|
495
529
|
ui.stopWait("Codex", "completed the local test");
|
|
496
530
|
ui.complete("Test passed", "The same fresh-session process seam is ready for Re-entry work.");
|
|
497
|
-
ui.next("
|
|
531
|
+
ui.next(cliCommand("listen"), "Watch the background Connector for approved work.");
|
|
498
532
|
} else {
|
|
499
533
|
process.stdout.write('{"event":"connector_codex_test_passed"}\n');
|
|
500
534
|
}
|
|
@@ -511,7 +545,7 @@ function reportLiveActivity(event, ui) {
|
|
|
511
545
|
);
|
|
512
546
|
} else if (event.event === "connector_reauthorization_required" || event.code === "connector_identity_invalid") {
|
|
513
547
|
ui.stopWait("Reconnect required", "the Cloud Receiver rejected this Mac's saved connection", "warning");
|
|
514
|
-
ui.next("
|
|
548
|
+
ui.next(cliCommand("disconnect"), "Clear this Mac's saved connection, then run install again.");
|
|
515
549
|
return;
|
|
516
550
|
} else if (event.event === "connector_poll_failed" || event.event === "local_connector_failed") {
|
|
517
551
|
ui.stopWait("Connection interrupted", "Re-entry is retrying", "warning");
|
|
@@ -534,7 +568,7 @@ async function stop(flags, ui) {
|
|
|
534
568
|
if (!result.supported) ui.warning("Platform", "background service control currently supports macOS only");
|
|
535
569
|
else if (result.stopped) ui.complete("Re-entry is paused", "Your account connection is still saved.");
|
|
536
570
|
else ui.info("Already stopped", "no running background Connector was found");
|
|
537
|
-
if (result.supported) ui.next("
|
|
571
|
+
if (result.supported) ui.next(cliCommand("install"), "Start Re-entry in the background again.");
|
|
538
572
|
} else {
|
|
539
573
|
process.stdout.write(`${JSON.stringify({
|
|
540
574
|
event: "connector_stopped",
|
|
@@ -544,6 +578,32 @@ async function stop(flags, ui) {
|
|
|
544
578
|
}
|
|
545
579
|
}
|
|
546
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
|
+
|
|
547
607
|
async function uninstall(flags, ui) {
|
|
548
608
|
if (ui.interactive) {
|
|
549
609
|
ui.begin("Uninstall Re-entry", "Stop the Connector and remove only its local service data");
|
|
@@ -557,7 +617,7 @@ async function uninstall(flags, ui) {
|
|
|
557
617
|
});
|
|
558
618
|
if (ui.interactive) {
|
|
559
619
|
ui.complete("Removed from this Mac", "The local service, connection, and logs are gone.");
|
|
560
|
-
ui.next("
|
|
620
|
+
ui.next(cliCommand("install"), "Connect this Mac again whenever you are ready.");
|
|
561
621
|
} else {
|
|
562
622
|
process.stdout.write(`${JSON.stringify({
|
|
563
623
|
event: "connector_uninstalled",
|
|
@@ -589,7 +649,7 @@ async function install(flags, ui) {
|
|
|
589
649
|
if (ui.interactive) {
|
|
590
650
|
ui.stopWait("Background", "running at login");
|
|
591
651
|
ui.complete("You're all set", "Re-entry is connected and waiting for approved work.");
|
|
592
|
-
ui.next("
|
|
652
|
+
ui.next(cliCommand("listen"), "Watch live activity. Press Ctrl+C when you are done.");
|
|
593
653
|
} else {
|
|
594
654
|
process.stdout.write(`${JSON.stringify({
|
|
595
655
|
event: "connector_service_installed",
|
|
@@ -627,8 +687,8 @@ async function start(flags, ui) {
|
|
|
627
687
|
credentials = await connect(runtimeFlags, ui, { quietHeader: true });
|
|
628
688
|
} else if (await hasConnectorReauthorizationRequired(credentialFile)) {
|
|
629
689
|
if (ui.interactive) {
|
|
630
|
-
ui.warning("Account", "this Mac
|
|
631
|
-
ui.next("
|
|
690
|
+
ui.warning("Account", "this Mac must be disconnected before reconnecting");
|
|
691
|
+
ui.next(cliCommand("disconnect"), "Clear the saved connection, then run install again.");
|
|
632
692
|
} else {
|
|
633
693
|
process.stdout.write('{"event":"connector_reauthorization_required"}\n');
|
|
634
694
|
}
|
|
@@ -674,7 +734,7 @@ async function start(flags, ui) {
|
|
|
674
734
|
});
|
|
675
735
|
if (ui.interactive) {
|
|
676
736
|
ui.stopWait("Reconnect required", "the Cloud Receiver rejected this Mac's saved connection", "warning");
|
|
677
|
-
ui.next("
|
|
737
|
+
ui.next(cliCommand("disconnect"), "Clear this Mac's saved connection, then run install again.");
|
|
678
738
|
} else {
|
|
679
739
|
process.stdout.write('{"event":"connector_reauthorization_required"}\n');
|
|
680
740
|
}
|
|
@@ -866,6 +926,7 @@ function validateCommandFlags(command, flags, positionals) {
|
|
|
866
926
|
listen: new Set(["json"]),
|
|
867
927
|
test: new Set(["codex-cd", "codex-binary", "activation-timeout", "json"]),
|
|
868
928
|
stop: new Set(["json"]),
|
|
929
|
+
disconnect: new Set(["credential-file", "json"]),
|
|
869
930
|
uninstall: new Set(["credential-file", "yes", "json"]),
|
|
870
931
|
install: new Set([
|
|
871
932
|
"receiver",
|
|
@@ -1038,15 +1099,16 @@ function errorHint(error) {
|
|
|
1038
1099
|
connector_codex_cd_missing: "pass --codex-cd /absolute/path/to/your/Host-project",
|
|
1039
1100
|
connector_codex_not_found: "install Codex, add it to PATH, or pass --codex-binary /path/to/codex",
|
|
1040
1101
|
connector_codex_binary_not_found: "check --codex-binary or remove it to use automatic discovery",
|
|
1041
|
-
connector_codex_unusable:
|
|
1102
|
+
connector_codex_unusable: `open Codex, complete login if needed, then run \`${cliCommand("doctor")}\` again`,
|
|
1042
1103
|
connector_node_unsupported: "use Node.js 24 or newer, then run the command again",
|
|
1043
1104
|
connector_receiver_missing: "pass --receiver <replacement-receiver-origin> or set REENTRY_RECEIVER_ORIGIN",
|
|
1044
|
-
connector_credentials_missing:
|
|
1045
|
-
connector_credentials_expired:
|
|
1105
|
+
connector_credentials_missing: `connect this Mac once with \`${cliCommand("connect")}\``,
|
|
1106
|
+
connector_credentials_expired: `run \`${cliCommand("disconnect")}\`, then \`${cliCommand("install")}\` to authorize this Mac again`,
|
|
1046
1107
|
connector_credentials_already_exists: "use the existing Connector credential or ask for a new pairing code",
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
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`,
|
|
1111
|
+
connector_pairing_code_missing: `ask the Host backend for a new pairing code, then run \`${cliCommand("pair")}\` in a terminal`,
|
|
1050
1112
|
pairing_code_invalid: "use the 16-character code returned by the Host, for example ABCD-EFGH-IJKL-MNOP",
|
|
1051
1113
|
host_subject_already_paired: "use the existing Connector credential or revoke/reset the preview pairing",
|
|
1052
1114
|
pairing_expired: "ask the Host backend for a new pairing code",
|
|
@@ -1054,14 +1116,14 @@ function errorHint(error) {
|
|
|
1054
1116
|
pairing_network_error: "check your internet connection and the Receiver address, then try again",
|
|
1055
1117
|
workspace_directory_unavailable: "choose a readable folder or pass --codex-cd /absolute/path",
|
|
1056
1118
|
workspace_selection_cancelled: "run the command again when you are ready to choose a workspace",
|
|
1057
|
-
device_authorization_expired:
|
|
1058
|
-
device_authorization_denied:
|
|
1119
|
+
device_authorization_expired: `run \`${cliCommand("connect")}\` again and approve within ten minutes`,
|
|
1120
|
+
device_authorization_denied: `run \`${cliCommand("connect")}\` again when you are ready to approve this Mac`,
|
|
1059
1121
|
connector_poll_interval_invalid: "use a polling interval between 1000 and 60000 milliseconds",
|
|
1060
1122
|
connector_max_errors_invalid: "use a maximum error count between 1 and 20",
|
|
1061
|
-
connector_service_load_failed:
|
|
1062
|
-
connector_uninstall_confirmation_required:
|
|
1063
|
-
connector_service_stop_failed:
|
|
1064
|
-
connector_test_prompt_missing:
|
|
1123
|
+
connector_service_load_failed: `run \`${cliCommand("install")}\` again; if it still fails, inspect the Connector error log`,
|
|
1124
|
+
connector_uninstall_confirmation_required: `run \`${cliCommand("uninstall")}\` interactively and type DELETE, or pass \`--yes\` in a deliberate script`,
|
|
1125
|
+
connector_service_stop_failed: `check the Connector status and try \`${cliCommand("stop")}\` again`,
|
|
1126
|
+
connector_test_prompt_missing: `use \`${cliCommand('test "Reply with: Re-entry is working."')}\``,
|
|
1065
1127
|
connector_test_prompt_invalid: "use one short, single-line prompt inside quotes",
|
|
1066
1128
|
connector_activation_timeout_invalid: "use an activation timeout between 100 and 60000 milliseconds",
|
|
1067
1129
|
};
|