@4xeoz/re-entry 0.2.0

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.
Files changed (29) hide show
  1. package/README.md +337 -0
  2. package/node_modules/@webmcp-challenge/reentry-core/README.md +112 -0
  3. package/node_modules/@webmcp-challenge/reentry-core/package.json +38 -0
  4. package/node_modules/@webmcp-challenge/reentry-core/protocol/test-vectors/v0.1.json +47 -0
  5. package/node_modules/@webmcp-challenge/reentry-core/src/agent-adapter.mjs +438 -0
  6. package/node_modules/@webmcp-challenge/reentry-core/src/cloud-receiver-http.mjs +268 -0
  7. package/node_modules/@webmcp-challenge/reentry-core/src/host-sdk.mjs +278 -0
  8. package/node_modules/@webmcp-challenge/reentry-core/src/index.mjs +3 -0
  9. package/node_modules/@webmcp-challenge/reentry-core/src/local-connector-client.mjs +530 -0
  10. package/node_modules/@webmcp-challenge/reentry-core/src/managed-context-adapter.mjs +275 -0
  11. package/node_modules/@webmcp-challenge/reentry-core/src/protocol.mjs +845 -0
  12. package/node_modules/@webmcp-challenge/reentry-core/src/receiver-core.mjs +867 -0
  13. package/node_modules/@webmcp-challenge/reentry-core/src/receiver-delivery.mjs +613 -0
  14. package/node_modules/@webmcp-challenge/reentry-core/src/receiver-http-contract.mjs +24 -0
  15. package/node_modules/@webmcp-challenge/reentry-core/src/receiver-support.mjs +151 -0
  16. package/node_modules/@webmcp-challenge/reentry-core/src/sqlite-receiver-schema.mjs +184 -0
  17. package/node_modules/@webmcp-challenge/reentry-core/src/sqlite-receiver-store.mjs +573 -0
  18. package/package.json +44 -0
  19. package/src/browser-prompt.mjs +18 -0
  20. package/src/codex-discovery.mjs +246 -0
  21. package/src/codex-exec-adapter.mjs +197 -0
  22. package/src/codex-queue-adapter.mjs +214 -0
  23. package/src/credentials.mjs +87 -0
  24. package/src/index.mjs +6 -0
  25. package/src/local-connector.mjs +82 -0
  26. package/src/macos-service.mjs +184 -0
  27. package/src/main.mjs +733 -0
  28. package/src/pairing-client.mjs +545 -0
  29. package/src/terminal-ui.mjs +99 -0
package/src/main.mjs ADDED
@@ -0,0 +1,733 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { homedir, hostname } from "node:os";
4
+ import { createInterface } from "node:readline/promises";
5
+ import { readFileSync } from "node:fs";
6
+ import { join } from "node:path";
7
+ import { fileURLToPath } from "node:url";
8
+ import { randomBytes } from "node:crypto";
9
+ import process from "node:process";
10
+
11
+ import { LocalConnectorClient } from "@webmcp-challenge/reentry-core/local-connector-client";
12
+ import { createCodexExecAdapter } from "./codex-exec-adapter.mjs";
13
+ import {
14
+ discoverCodexExecutable,
15
+ requireSupportedNode,
16
+ validateCodexWorkingDirectory,
17
+ verifyCodexExecutable,
18
+ } from "./codex-discovery.mjs";
19
+ import { LocalConnector } from "./local-connector.mjs";
20
+ import { LocalConnectorCredentialStore } from "./credentials.mjs";
21
+ import { LocalConnectorPairingClient } from "./pairing-client.mjs";
22
+ import { waitForEnterToOpenBrowser } from "./browser-prompt.mjs";
23
+ import {
24
+ inspectMacConnectorService,
25
+ installMacConnectorService,
26
+ } from "./macos-service.mjs";
27
+ import { createTerminalUi } from "./terminal-ui.mjs";
28
+
29
+ const DEFAULT_RECEIVER_ORIGIN = process.env.REENTRY_RECEIVER_ORIGIN ?? "https://reentry-cloud.vercel.app";
30
+ const DEFAULT_POLL_INTERVAL_MS = 5_000;
31
+ const DEFAULT_MAX_CONSECUTIVE_ERRORS = 5;
32
+ const CONNECTOR_VERSION = readConnectorVersion();
33
+
34
+ async function main() {
35
+ let ui;
36
+ try {
37
+ const argumentsList = process.argv.slice(2);
38
+ if (argumentsList.includes("--help") || argumentsList[0] === "help") {
39
+ printHelp();
40
+ return;
41
+ }
42
+ if (argumentsList.includes("--version") || argumentsList[0] === "version") {
43
+ process.stdout.write(`${CONNECTOR_VERSION}\n`);
44
+ return;
45
+ }
46
+ const { command, flags } = parseArguments(argumentsList);
47
+ validateCommandFlags(command, flags);
48
+ ui = createTerminalUi({ interactive: flags.json !== true && process.stdout.isTTY === true });
49
+ requireSupportedNode();
50
+ if (command === "doctor") {
51
+ doctor(flags, ui);
52
+ return;
53
+ }
54
+ if (command === "pair") {
55
+ await pair(flags, ui);
56
+ return;
57
+ }
58
+ if (command === "connect") {
59
+ await connect(flags, ui);
60
+ return;
61
+ }
62
+ if (command === "status") {
63
+ await status(flags, ui);
64
+ return;
65
+ }
66
+ if (command === "install") {
67
+ await install(flags, ui);
68
+ return;
69
+ }
70
+ if (command === "claim-once") {
71
+ await claimOnce(flags, ui);
72
+ return;
73
+ }
74
+ if (command === "start") {
75
+ await start(flags, ui);
76
+ return;
77
+ }
78
+ throw cliFailure("connector_command_invalid");
79
+ } catch (error) {
80
+ if (ui?.interactive) {
81
+ ui.error("Connector stopped", safeErrorMessage(error), errorHint(error));
82
+ } else {
83
+ process.stderr.write(`${JSON.stringify({
84
+ event: "local_connector_failed",
85
+ code: error?.code ?? "local_connector_failed",
86
+ message: safeErrorMessage(error),
87
+ })}\n`);
88
+ }
89
+ process.exitCode = 1;
90
+ } finally {
91
+ ui?.close();
92
+ }
93
+ }
94
+
95
+ function printHelp() {
96
+ process.stdout.write(`Re-entry Local Connector ${CONNECTOR_VERSION}
97
+
98
+ Connect this Mac once, then receive approved work in Codex in the background.
99
+
100
+ Usage:
101
+ re-entry install --receiver <url> --codex-cd <project> Recommended first run
102
+ re-entry status Check the connection
103
+ re-entry start --codex-cd <project> Run in this terminal
104
+
105
+ Commands:
106
+ install Check this Mac, connect the Re-entry account, and start at login
107
+ status Show account, Receiver, service, Node.js, and Codex readiness
108
+ connect Authorize this Mac without installing the background service
109
+ start Poll for approved work until stopped
110
+ doctor Check Node.js, Codex, and the optional project directory
111
+ claim-once Check once for approved work
112
+ pair Legacy Host-code pairing preview
113
+
114
+ Common options:
115
+ --receiver <url> Re-entry Receiver origin
116
+ --codex-cd <path> Project opened for the fresh Codex session
117
+ --codex-binary <path> Explicit Codex executable
118
+ --json Machine-readable output
119
+ --help Show this help
120
+ --version Show the installed version
121
+
122
+ The Connector opens no inbound port. Host keys never belong on this Mac.
123
+ `);
124
+ }
125
+
126
+ function readConnectorVersion() {
127
+ const packageJson = JSON.parse(
128
+ readFileSync(new URL("../package.json", import.meta.url), "utf8"),
129
+ );
130
+ if (typeof packageJson.version !== "string" || !/^\d+\.\d+\.\d+$/.test(packageJson.version)) {
131
+ throw new Error("Local Connector package version is invalid");
132
+ }
133
+ return packageJson.version;
134
+ }
135
+
136
+ function doctor(flags, ui) {
137
+ if (ui.interactive) ui.begin("Check this Mac", "Read-only readiness check");
138
+ const readiness = inspectReadiness(flags);
139
+ showReadiness(readiness, ui);
140
+ if (!ui.interactive) {
141
+ process.stdout.write(`${JSON.stringify({
142
+ event: "connector_ready",
143
+ node_version: process.versions.node,
144
+ codex_binary: readiness.installation.executable,
145
+ codex_version: readiness.installation.version,
146
+ codex_working_directory: readiness.workingDirectory,
147
+ })}\n`);
148
+ }
149
+ return readiness;
150
+ }
151
+
152
+ function inspectReadiness(flags) {
153
+ const workingDirectory = flags["codex-cd"] === undefined
154
+ ? null
155
+ : validateCodexWorkingDirectory(flags["codex-cd"]);
156
+ const executable = discoverCodexExecutable({
157
+ requested: flags["codex-binary"],
158
+ });
159
+ const installation = verifyCodexExecutable(executable);
160
+ return Object.freeze({ workingDirectory, installation });
161
+ }
162
+
163
+ function showReadiness(readiness, ui) {
164
+ if (!ui.interactive) return;
165
+ ui.success("Node.js", process.versions.node);
166
+ ui.success("Codex", `${readiness.installation.version} · ${readiness.installation.executable}`);
167
+ if (readiness.workingDirectory) {
168
+ ui.success("Host project", readiness.workingDirectory);
169
+ } else {
170
+ ui.info("Host project", "not selected; pass --codex-cd when starting Codex");
171
+ }
172
+ }
173
+
174
+ async function pair(flags, ui, options = {}) {
175
+ if (!options.quietHeader && ui.interactive) {
176
+ ui.begin("Pair this Mac", "One-time approval connects this Connector to a Host user");
177
+ }
178
+ const receiver = requireFlag(flags, "receiver");
179
+ const credentialFile = flags["credential-file"] ?? defaultCredentialFile();
180
+ if (ui.interactive) {
181
+ ui.step("Receiver", receiver);
182
+ }
183
+ const userCode = flags.code ?? await askForPairingCode(ui);
184
+ if (ui.interactive) ui.step("Pairing code", "received");
185
+ const client = new LocalConnectorPairingClient({ baseUrl: receiver });
186
+ const credentials = await client.pair({ userCode }, async ({ verificationUri }) => {
187
+ if (ui.interactive) {
188
+ ui.step("Browser", "press Enter to open the Re-entry approval page");
189
+ ui.info("Open this URL", verificationUri);
190
+ await waitForEnterToOpenBrowser();
191
+ ui.step("Browser", "opening the Re-entry approval page");
192
+ ui.wait("Waiting for you to approve this Mac…");
193
+ } else {
194
+ process.stdout.write(`${JSON.stringify({ event: "pairing_waiting", verification_uri: verificationUri })}\n`);
195
+ }
196
+ });
197
+ if (ui.interactive) {
198
+ ui.stopWait("Approval received", "the Receiver approved this Connector");
199
+ if (!credentials.browserOpened) {
200
+ ui.warning("Browser", "did not open automatically; use the URL above");
201
+ }
202
+ }
203
+ const store = new LocalConnectorCredentialStore({ filename: credentialFile });
204
+ await store.save({
205
+ version: 1,
206
+ receiver_origin: receiver,
207
+ connector_id: credentials.connector_id,
208
+ connector_token: credentials.connector_token,
209
+ connector_expires_at: credentials.connector_expires_at,
210
+ });
211
+ if (ui.interactive) {
212
+ ui.success("This Mac is paired", `credential saved at ${credentialFile}`);
213
+ } else {
214
+ process.stdout.write(`${JSON.stringify({ event: "connector_paired", connector_id: credentials.connector_id })}\n`);
215
+ }
216
+ }
217
+
218
+ async function connect(flags, ui, options = {}) {
219
+ if (!options.quietHeader && ui.interactive) {
220
+ ui.begin("Connect this Mac", "One browser approval, then Re-entry stays connected");
221
+ }
222
+ const receiver = flags.receiver ?? DEFAULT_RECEIVER_ORIGIN;
223
+ const credentialFile = flags["credential-file"] ?? defaultCredentialFile();
224
+ const store = new LocalConnectorCredentialStore({ filename: credentialFile });
225
+ const current = await store.load();
226
+ if (current && Date.parse(current.connector_expires_at) > Date.now()) {
227
+ if (ui.interactive) {
228
+ ui.success("Already connected", `${current.connector_id} · ${current.receiver_origin}`);
229
+ ui.info("Next", "run `re-entry start` to wait for approved work");
230
+ } else {
231
+ process.stdout.write(`${JSON.stringify({
232
+ event: "connector_already_connected",
233
+ connector_id: current.connector_id,
234
+ receiver_origin: current.receiver_origin,
235
+ })}\n`);
236
+ }
237
+ return current;
238
+ }
239
+
240
+ if (ui.interactive) {
241
+ ui.step("Re-entry", receiver);
242
+ ui.step("This Mac", flags["device-name"] ?? defaultDeviceName());
243
+ }
244
+ const client = new LocalConnectorPairingClient({ baseUrl: receiver });
245
+ const credentials = await client.connect(
246
+ { deviceName: flags["device-name"] ?? defaultDeviceName() },
247
+ async ({ verificationUri }) => {
248
+ if (ui.interactive) {
249
+ ui.step("Browser", "press Enter to open Re-entry sign-in and approval");
250
+ ui.info("If it does not open", verificationUri);
251
+ await waitForEnterToOpenBrowser();
252
+ ui.step("Browser", "opening Re-entry sign-in and approval");
253
+ ui.wait("Waiting for you to connect this Mac…");
254
+ } else {
255
+ process.stdout.write(`${JSON.stringify({
256
+ event: "connector_authorization_waiting",
257
+ verification_uri: verificationUri,
258
+ })}\n`);
259
+ }
260
+ },
261
+ );
262
+ if (ui.interactive) {
263
+ ui.stopWait("Approved", "Re-entry linked this Mac to your account");
264
+ if (!credentials.browserOpened) {
265
+ ui.warning("Browser", "did not open automatically; use the URL shown above");
266
+ }
267
+ }
268
+ const saved = {
269
+ version: 1,
270
+ receiver_origin: receiver,
271
+ connector_id: credentials.connector_id,
272
+ connector_token: credentials.connector_token,
273
+ connector_expires_at: credentials.connector_expires_at,
274
+ };
275
+ await store.save(saved);
276
+ if (ui.interactive) {
277
+ ui.success("Connected", `credential saved at ${credentialFile}`);
278
+ ui.info("Next", "run `re-entry start` once; it will keep waiting in the background");
279
+ } else {
280
+ process.stdout.write(`${JSON.stringify({
281
+ event: "connector_connected",
282
+ connector_id: credentials.connector_id,
283
+ })}\n`);
284
+ }
285
+ return saved;
286
+ }
287
+
288
+ async function status(flags, ui) {
289
+ if (ui.interactive) ui.begin("Connector status", "Account, background service, Receiver, and Codex");
290
+ const credentialFile = flags["credential-file"] ?? defaultCredentialFile();
291
+ const credentials = await new LocalConnectorCredentialStore({ filename: credentialFile }).load();
292
+ const readiness = inspectReadiness(flags);
293
+ const service = await inspectMacConnectorService();
294
+ const receiverReady = credentials ? await inspectReceiver(credentials.receiver_origin) : false;
295
+ showReadiness(readiness, ui);
296
+ const connected = Boolean(credentials && Date.parse(credentials.connector_expires_at) > Date.now());
297
+ if (ui.interactive) {
298
+ if (connected) ui.success("Account", `${credentials.connector_id} · authorized`);
299
+ else ui.warning("Re-entry", "not connected; run `re-entry connect`");
300
+ if (service.running) ui.success("Background", "running at login");
301
+ else if (service.installed) ui.warning("Background", "installed but not running; run `re-entry install`");
302
+ else ui.warning("Background", "not installed; run `re-entry install`");
303
+ if (connected && receiverReady) ui.success("Receiver", `${credentials.receiver_origin} · reachable`);
304
+ else if (connected) ui.warning("Receiver", `${credentials.receiver_origin} · unavailable`);
305
+ } else {
306
+ process.stdout.write(`${JSON.stringify({
307
+ event: "connector_status",
308
+ connected,
309
+ connector_id: credentials?.connector_id ?? null,
310
+ receiver_origin: credentials?.receiver_origin ?? null,
311
+ receiver_ready: receiverReady,
312
+ service_installed: service.installed,
313
+ service_running: service.running,
314
+ codex_binary: readiness.installation.executable,
315
+ codex_version: readiness.installation.version,
316
+ codex_working_directory: readiness.workingDirectory,
317
+ })}\n`);
318
+ }
319
+ }
320
+
321
+ async function install(flags, ui) {
322
+ if (ui.interactive) {
323
+ ui.begin("Install Re-entry", "Check Codex, connect your account, then start at login");
324
+ }
325
+ const runtimeFlags = {
326
+ ...flags,
327
+ "codex-cd": flags["codex-cd"] ?? process.cwd(),
328
+ };
329
+ const readiness = inspectReadiness(runtimeFlags);
330
+ showReadiness(readiness, ui);
331
+ const credentials = await connect(runtimeFlags, ui, { quietHeader: true });
332
+ if (ui.interactive) ui.wait("Installing the background Connector…");
333
+ const service = await installMacConnectorService({
334
+ nodeExecutable: process.execPath,
335
+ entrypoint: fileURLToPath(import.meta.url),
336
+ workingDirectory: readiness.workingDirectory,
337
+ credentialFile: flags["credential-file"] ?? defaultCredentialFile(),
338
+ });
339
+ if (ui.interactive) {
340
+ ui.stopWait("Installed", "Re-entry will start automatically when you log in");
341
+ ui.success("Account", credentials.connector_id);
342
+ ui.info("Logs", service.stdoutPath);
343
+ ui.info("You are done", "the Connector now waits in the background");
344
+ } else {
345
+ process.stdout.write(`${JSON.stringify({
346
+ event: "connector_service_installed",
347
+ connector_id: credentials.connector_id,
348
+ service_label: service.label,
349
+ plist_path: service.plistPath,
350
+ log_path: service.stdoutPath,
351
+ })}\n`);
352
+ }
353
+ }
354
+
355
+ async function start(flags, ui) {
356
+ if (ui.interactive) {
357
+ ui.begin("Re-entry is starting", "Connect once, then wait quietly for work you approve");
358
+ }
359
+ const runtimeFlags = {
360
+ ...flags,
361
+ "codex-cd": flags["codex-cd"] ?? process.cwd(),
362
+ };
363
+ const readiness = inspectReadiness(runtimeFlags);
364
+ showReadiness(readiness, ui);
365
+ if (!ui.interactive) {
366
+ process.stdout.write(`${JSON.stringify({
367
+ event: "connector_ready",
368
+ node_version: process.versions.node,
369
+ codex_binary: readiness.installation.executable,
370
+ codex_version: readiness.installation.version,
371
+ codex_working_directory: readiness.workingDirectory,
372
+ })}\n`);
373
+ }
374
+
375
+ const credentialFile = flags["credential-file"] ?? defaultCredentialFile();
376
+ const store = new LocalConnectorCredentialStore({ filename: credentialFile });
377
+ let credentials = await store.load();
378
+ if (!credentials) {
379
+ if (ui.interactive) ui.info("Re-entry", "first run — browser approval is required once");
380
+ credentials = await connect(runtimeFlags, ui, { quietHeader: true });
381
+ } else if (ui.interactive) {
382
+ ui.success("Re-entry", "existing account connection loaded");
383
+ }
384
+ if (Date.parse(credentials.connector_expires_at) <= Date.now()) {
385
+ throw cliFailure("connector_credentials_expired");
386
+ }
387
+
388
+ const connector = createRuntimeConnector(runtimeFlags, credentials, readiness);
389
+ const pollIntervalMs = readBoundedNumber(
390
+ flags["poll-interval"] ?? DEFAULT_POLL_INTERVAL_MS,
391
+ 1_000,
392
+ 60_000,
393
+ "connector_poll_interval_invalid",
394
+ );
395
+ const maximumErrors = readBoundedNumber(
396
+ flags["max-errors"] ?? DEFAULT_MAX_CONSECUTIVE_ERRORS,
397
+ 1,
398
+ 20,
399
+ "connector_max_errors_invalid",
400
+ );
401
+ const stop = createStopSignal();
402
+ let consecutiveErrors = 0;
403
+ if (ui.interactive) ui.wait("Connected. Waiting for approved work…");
404
+ else process.stdout.write('{"event":"connector_waiting"}\n');
405
+
406
+ try {
407
+ while (!stop.signal.aborted) {
408
+ try {
409
+ const result = await connector.runOnce();
410
+ consecutiveErrors = 0;
411
+ if (result.status === "activation_result") {
412
+ reportActivation(result, ui);
413
+ if (ui.interactive) ui.wait("Connected. Waiting for approved work…");
414
+ }
415
+ } catch (error) {
416
+ consecutiveErrors += 1;
417
+ if (ui.interactive) {
418
+ ui.stopWait("Receiver unavailable", `${safeErrorMessage(error)} · ${consecutiveErrors}/${maximumErrors}`, "warning");
419
+ } else {
420
+ process.stderr.write(`${JSON.stringify({
421
+ event: "connector_poll_failed",
422
+ code: error?.code ?? "connector_poll_failed",
423
+ attempt: consecutiveErrors,
424
+ })}\n`);
425
+ }
426
+ if (consecutiveErrors >= maximumErrors) throw error;
427
+ if (ui.interactive) ui.wait("Reconnecting to Re-entry…");
428
+ }
429
+ await waitForNextPoll(pollIntervalMs, stop.signal);
430
+ }
431
+ } finally {
432
+ stop.close();
433
+ if (ui.interactive) ui.stopWait("Connector stopped", "No local credential was removed", "info");
434
+ else process.stdout.write('{"event":"connector_stopped"}\n');
435
+ }
436
+ }
437
+
438
+ async function inspectReceiver(origin) {
439
+ const controller = new AbortController();
440
+ const timer = setTimeout(() => controller.abort(), 3_000);
441
+ try {
442
+ const response = await fetch(`${origin}/readyz`, {
443
+ method: "GET",
444
+ headers: { Accept: "application/json" },
445
+ redirect: "error",
446
+ signal: controller.signal,
447
+ });
448
+ return response.status === 200;
449
+ } catch {
450
+ return false;
451
+ } finally {
452
+ clearTimeout(timer);
453
+ }
454
+ }
455
+
456
+ async function claimOnce(flags, ui, options = {}) {
457
+ if (!options.quietHeader && ui.interactive) {
458
+ ui.begin("Check for approved work", "One-shot delivery check");
459
+ }
460
+ const credentialFile = flags["credential-file"] ?? defaultCredentialFile();
461
+ const store = new LocalConnectorCredentialStore({ filename: credentialFile });
462
+ const credentials = await store.load();
463
+ if (!credentials) throw cliFailure("connector_credentials_missing");
464
+ if (ui.interactive) ui.success("Credentials", "loaded from the local secure file");
465
+ const client = new LocalConnectorClient({
466
+ baseUrl: credentials.receiver_origin,
467
+ connectorToken: credentials.connector_token,
468
+ requestTimeoutMs: Number(flags.timeout ?? 5_000),
469
+ });
470
+ const codexDirectory = flags["codex-cd"];
471
+ if (flags["codex-thread"] !== undefined) {
472
+ throw cliFailure("connector_codex_thread_unsupported");
473
+ }
474
+ if (flags["codex-binary"] !== undefined && codexDirectory === undefined) {
475
+ throw cliFailure("connector_codex_cd_missing");
476
+ }
477
+ const readiness = options.readiness ?? (codexDirectory === undefined ? null : inspectReadiness(flags));
478
+ const workingDirectory = readiness?.workingDirectory;
479
+ if (ui.interactive && !workingDirectory) {
480
+ ui.warning("Codex", "no Host project was selected; this check will report activation as unsupported");
481
+ }
482
+ const connector = createRuntimeConnector(flags, credentials, readiness, client);
483
+ if (ui.interactive) ui.wait("Checking the Receiver and starting Codex if work is ready…");
484
+ const result = await connector.runOnce();
485
+ if (result.status === "idle") {
486
+ if (ui.interactive) {
487
+ ui.stopWait("No pending work", "the Connector is ready and will exit now", "info");
488
+ } else {
489
+ process.stdout.write('{"event":"connector_idle"}\n');
490
+ }
491
+ return;
492
+ }
493
+ if (ui.interactive) {
494
+ reportActivation(result, ui);
495
+ } else {
496
+ reportActivation(result, ui);
497
+ }
498
+ }
499
+
500
+ function createRuntimeConnector(flags, credentials, readiness, existingClient = undefined) {
501
+ const client = existingClient ?? new LocalConnectorClient({
502
+ baseUrl: credentials.receiver_origin,
503
+ connectorToken: credentials.connector_token,
504
+ requestTimeoutMs: Number(flags.timeout ?? 5_000),
505
+ });
506
+ const codexDirectory = flags["codex-cd"];
507
+ const workingDirectory = readiness?.workingDirectory;
508
+ const executable = readiness?.installation.executable;
509
+ const activationTimeoutMs = Number(flags["activation-timeout"] ?? 60_000);
510
+ return new LocalConnector({
511
+ client,
512
+ adapter: codexDirectory === undefined
513
+ ? unsupportedAdapter
514
+ : createCodexExecAdapter({
515
+ workingDirectory,
516
+ executable,
517
+ commandTimeoutMs: activationTimeoutMs,
518
+ }),
519
+ clock: () => new Date(),
520
+ activationTimeoutMs,
521
+ createClaimToken: () => randomBytes(32).toString("base64url"),
522
+ });
523
+ }
524
+
525
+ function reportActivation(result, ui) {
526
+ if (ui.interactive) {
527
+ const accepted = result.result.outcome === "accepted";
528
+ ui.stopWait(
529
+ accepted ? "Codex session started" : "Activation finished",
530
+ accepted ? "fresh session opened with the approved Re-entry context" : result.result.code,
531
+ accepted ? "success" : "warning",
532
+ );
533
+ ui.info("Delivery", `${result.delivery_id} · ${result.result.outcome}`);
534
+ return;
535
+ }
536
+ process.stdout.write(`${JSON.stringify({
537
+ event: "connector_activation_result",
538
+ delivery_id: result.delivery_id,
539
+ event_id: result.event_id,
540
+ outcome: result.result.outcome,
541
+ code: result.result.code,
542
+ })}\n`);
543
+ }
544
+
545
+ const unsupportedAdapter = {
546
+ activate(activation) {
547
+ return {
548
+ type: "webmcp.agent_activation_result",
549
+ protocol_version: "0.1",
550
+ delivery_id: activation.delivery_id,
551
+ event_id: activation.event_id,
552
+ attempt: activation.attempt,
553
+ outcome: "unsupported",
554
+ code: "required_capability_unavailable",
555
+ unavailable_capability: "managed_context_resume",
556
+ };
557
+ },
558
+ };
559
+
560
+ function parseArguments(argumentsList) {
561
+ const hasLeadingJson = argumentsList[0] === "--json";
562
+ const commandIndex = hasLeadingJson ? 1 : 0;
563
+ const hasExplicitCommand = argumentsList[commandIndex] !== undefined
564
+ && !argumentsList[commandIndex].startsWith("--");
565
+ const command = hasExplicitCommand ? argumentsList[commandIndex] : "start";
566
+ const rest = [
567
+ ...(hasLeadingJson ? ["--json"] : []),
568
+ ...(hasExplicitCommand ? argumentsList.slice(commandIndex + 1) : argumentsList.slice(commandIndex)),
569
+ ];
570
+ const flags = {};
571
+ for (let index = 0; index < rest.length; index += 1) {
572
+ const value = rest[index];
573
+ if (!value.startsWith("--")) throw cliFailure("connector_argument_invalid");
574
+ const name = value.slice(2);
575
+ if (name === "json") {
576
+ if (Object.hasOwn(flags, name)) throw cliFailure("connector_argument_invalid");
577
+ flags[name] = true;
578
+ continue;
579
+ }
580
+ const next = rest[index + 1];
581
+ if (!name || next === undefined || next.startsWith("--") || Object.hasOwn(flags, name)) throw cliFailure("connector_argument_invalid");
582
+ flags[name] = next;
583
+ index += 1;
584
+ }
585
+ return { command, flags };
586
+ }
587
+
588
+ function validateCommandFlags(command, flags) {
589
+ const allowedByCommand = {
590
+ doctor: new Set(["codex-binary", "codex-cd", "json"]),
591
+ pair: new Set(["receiver", "code", "credential-file", "json"]),
592
+ connect: new Set(["receiver", "device-name", "credential-file", "json"]),
593
+ status: new Set(["credential-file", "codex-cd", "codex-binary", "json"]),
594
+ install: new Set([
595
+ "receiver",
596
+ "device-name",
597
+ "credential-file",
598
+ "codex-cd",
599
+ "codex-binary",
600
+ "json",
601
+ ]),
602
+ "claim-once": new Set([
603
+ "credential-file",
604
+ "timeout",
605
+ "codex-cd",
606
+ "codex-binary",
607
+ "codex-thread",
608
+ "activation-timeout",
609
+ "json",
610
+ ]),
611
+ start: new Set([
612
+ "receiver",
613
+ "device-name",
614
+ "credential-file",
615
+ "timeout",
616
+ "codex-cd",
617
+ "codex-binary",
618
+ "codex-thread",
619
+ "activation-timeout",
620
+ "poll-interval",
621
+ "max-errors",
622
+ "json",
623
+ ]),
624
+ };
625
+ const allowed = allowedByCommand[command];
626
+ if (!allowed || Object.keys(flags).some((name) => !allowed.has(name))) {
627
+ throw cliFailure("connector_argument_invalid");
628
+ }
629
+ }
630
+
631
+ function requireFlag(flags, name) {
632
+ const value = flags[name];
633
+ if (typeof value !== "string" || value.length === 0) throw cliFailure(`connector_${name.replaceAll("-", "_")}_missing`);
634
+ return value;
635
+ }
636
+
637
+ function defaultCredentialFile() {
638
+ return join(homedir(), ".webmcp-connector", "credentials.json");
639
+ }
640
+
641
+ function defaultDeviceName() {
642
+ const value = hostname().trim();
643
+ return value.length >= 2 && Buffer.byteLength(value, "utf8") <= 80
644
+ ? value
645
+ : "This Mac";
646
+ }
647
+
648
+ function readBoundedNumber(value, minimum, maximum, code) {
649
+ const number = typeof value === "number" ? value : Number(value);
650
+ if (!Number.isSafeInteger(number) || number < minimum || number > maximum) {
651
+ throw cliFailure(code);
652
+ }
653
+ return number;
654
+ }
655
+
656
+ function createStopSignal() {
657
+ const controller = new AbortController();
658
+ const stop = () => controller.abort();
659
+ process.once("SIGINT", stop);
660
+ process.once("SIGTERM", stop);
661
+ return {
662
+ signal: controller.signal,
663
+ close() {
664
+ process.off("SIGINT", stop);
665
+ process.off("SIGTERM", stop);
666
+ },
667
+ };
668
+ }
669
+
670
+ function waitForNextPoll(milliseconds, signal) {
671
+ if (signal.aborted) return Promise.resolve();
672
+ return new Promise((resolve) => {
673
+ const timer = setTimeout(done, milliseconds);
674
+ function done() {
675
+ clearTimeout(timer);
676
+ signal.removeEventListener("abort", done);
677
+ resolve();
678
+ }
679
+ signal.addEventListener("abort", done, { once: true });
680
+ });
681
+ }
682
+
683
+ function cliFailure(code) {
684
+ const error = new Error(code);
685
+ error.code = code;
686
+ return error;
687
+ }
688
+
689
+ function safeErrorMessage(error) {
690
+ if (typeof error?.message !== "string" || error.message.length === 0) {
691
+ return "The Local Connector could not complete the command.";
692
+ }
693
+ return error.message.length > 240
694
+ ? `${error.message.slice(0, 237)}...`
695
+ : error.message;
696
+ }
697
+
698
+ async function askForPairingCode(ui) {
699
+ if (!ui.interactive || process.stdin.isTTY !== true) {
700
+ throw cliFailure("connector_pairing_code_missing");
701
+ }
702
+ const readline = createInterface({ input: process.stdin, output: process.stdout });
703
+ try {
704
+ const answer = await readline.question(" Enter the pairing code from your Host (XXXX-XXXX-XXXX-XXXX): ");
705
+ return answer.trim();
706
+ } finally {
707
+ readline.close();
708
+ }
709
+ }
710
+
711
+ function errorHint(error) {
712
+ const hints = {
713
+ connector_codex_cd_missing: "pass --codex-cd /absolute/path/to/your/Host-project",
714
+ connector_codex_not_found: "install Codex, add it to PATH, or pass --codex-binary /path/to/codex",
715
+ connector_codex_binary_not_found: "check --codex-binary or remove it to use automatic discovery",
716
+ connector_codex_unusable: "open Codex, complete login if needed, then run `npm run doctor` again",
717
+ connector_node_unsupported: "use Node.js 24 or newer, then run the command again",
718
+ connector_credentials_missing: "connect this Mac once with `re-entry connect`",
719
+ connector_credentials_expired: "run `re-entry connect` to authorize this Mac again",
720
+ connector_pairing_code_missing: "ask the Host backend for a new pairing code, then run pair in a terminal",
721
+ pairing_code_invalid: "use the 16-character code returned by the Host, for example ABCD-EFGH-IJKL-MNOP",
722
+ host_subject_already_paired: "use the existing Connector credential or revoke/reset the preview pairing",
723
+ pairing_expired: "ask the Host backend for a new pairing code",
724
+ device_authorization_expired: "run `re-entry connect` again and approve within ten minutes",
725
+ device_authorization_denied: "run `re-entry connect` again when you are ready to approve this Mac",
726
+ connector_poll_interval_invalid: "use a polling interval between 1000 and 60000 milliseconds",
727
+ connector_max_errors_invalid: "use a maximum error count between 1 and 20",
728
+ connector_service_load_failed: "run `re-entry install` again; if it still fails, inspect the Connector error log",
729
+ };
730
+ return hints[error?.code] ?? "check the Receiver address and try again";
731
+ }
732
+
733
+ await main();