@4xeoz/re-entry 0.2.16 → 0.2.18

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
@@ -132,7 +132,14 @@ npx --yes --package=@4xeoz/re-entry re-entry test "Reply with: Re-entry is worki
132
132
  ```
133
133
 
134
134
  This starts one fresh local Codex process through the same adapter seam used by real deliveries. It
135
- does not create a Grant, claim Receiver work, or prove the browser/WebMCP return path.
135
+ forwards Codex output when run in an interactive terminal, so the smoke test behaves like the
136
+ underlying direct `codex exec` command. It does not create a Grant, claim Receiver work, or prove a
137
+ Desktop UI thread, browser, or WebMCP return path. If it times out, run the equivalent direct
138
+ `codex exec` command to inspect Codex's own output; the test does not contact the Receiver.
139
+
140
+ The one-shot test allows up to one hour by default. Override it with
141
+ `--activation-timeout <milliseconds>` when needed. Background delivery keeps its separate
142
+ 60-second adapter bound.
136
143
 
137
144
  To pause or remove the local Connector:
138
145
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@4xeoz/re-entry",
3
- "version": "0.2.16",
3
+ "version": "0.2.18",
4
4
  "type": "module",
5
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": {
@@ -10,6 +10,8 @@ import {
10
10
  } from "./codex-discovery.mjs";
11
11
 
12
12
  export const CODEX_EXEC_ADAPTER_ID = "codex_exec_local";
13
+ export const DEFAULT_CODEX_PROMPT_TIMEOUT_MS = 3_600_000;
14
+ export const MAX_CODEX_PROMPT_TIMEOUT_MS = 3_600_000;
13
15
 
14
16
  const OPTION_FIELDS = Object.freeze([
15
17
  "workingDirectory",
@@ -59,6 +61,7 @@ export function createCodexExecAdapter(options) {
59
61
  executable,
60
62
  workingDirectory,
61
63
  prompt: buildContinuationPrompt(activation),
64
+ stdio: ["ignore", "ignore", "ignore"],
62
65
  commandTimeoutMs,
63
66
  spawnCommand,
64
67
  });
@@ -79,7 +82,7 @@ export function createCodexExecAdapter(options) {
79
82
  export async function runCodexPrompt(options) {
80
83
  requireExactRecord(
81
84
  options,
82
- ["workingDirectory", "prompt", "executable", "commandTimeoutMs", "spawnCommand"],
85
+ ["workingDirectory", "prompt", "executable", "commandTimeoutMs", "spawnCommand", "stdio"],
83
86
  ["workingDirectory", "prompt"],
84
87
  "Codex prompt options",
85
88
  );
@@ -88,12 +91,16 @@ export async function runCodexPrompt(options) {
88
91
  ? discoverCodexExecutable()
89
92
  : requireReference(options.executable, "Codex executable");
90
93
  const prompt = requirePrompt(options.prompt);
91
- const timeoutMs = requireTimeout(options.commandTimeoutMs ?? 60_000);
94
+ const timeoutMs = requireTimeout(
95
+ options.commandTimeoutMs ?? DEFAULT_CODEX_PROMPT_TIMEOUT_MS,
96
+ MAX_CODEX_PROMPT_TIMEOUT_MS,
97
+ );
92
98
  const spawnCommand = options.spawnCommand ?? spawn;
93
99
  if (typeof spawnCommand !== "function") {
94
100
  throw new TypeError("Codex prompt spawnCommand must be a function");
95
101
  }
96
- await runCodexExec({ executable, workingDirectory, prompt, timeoutMs, spawnCommand });
102
+ const stdio = requireStdio(options.stdio ?? "inherit");
103
+ await runCodexExec({ executable, workingDirectory, prompt, timeoutMs, spawnCommand, stdio });
97
104
  }
98
105
 
99
106
  function buildContinuationPrompt(activation) {
@@ -114,7 +121,7 @@ function buildContinuationPrompt(activation) {
114
121
  ].join("\n");
115
122
  }
116
123
 
117
- function runCodexExec({ executable, workingDirectory, prompt, timeoutMs, spawnCommand }) {
124
+ function runCodexExec({ executable, workingDirectory, prompt, timeoutMs, spawnCommand, stdio }) {
118
125
  return new Promise((resolve, reject) => {
119
126
  let child;
120
127
  let settled = false;
@@ -131,15 +138,25 @@ function runCodexExec({ executable, workingDirectory, prompt, timeoutMs, spawnCo
131
138
  child = spawnCommand(
132
139
  executable,
133
140
  ["exec", "--cd", workingDirectory, prompt],
134
- { stdio: ["ignore", "ignore", "ignore"] },
141
+ { stdio },
135
142
  );
136
143
  } catch (error) {
137
- finish(reject, error);
144
+ finish(
145
+ reject,
146
+ codexExecError(
147
+ "connector_codex_exec_start_failed",
148
+ "Codex exec could not be started",
149
+ error,
150
+ ),
151
+ );
138
152
  return;
139
153
  }
140
154
 
141
155
  if (!child || typeof child.once !== "function") {
142
- finish(reject, new Error("Codex exec process is invalid"));
156
+ finish(
157
+ reject,
158
+ codexExecError("connector_codex_exec_invalid", "Codex exec returned an invalid process"),
159
+ );
143
160
  return;
144
161
  }
145
162
 
@@ -149,16 +166,36 @@ function runCodexExec({ executable, workingDirectory, prompt, timeoutMs, spawnCo
149
166
  } catch {
150
167
  // The activation remains unknown even if the process cannot be terminated.
151
168
  }
152
- finish(reject, new Error("Codex exec process timed out"));
169
+ finish(
170
+ reject,
171
+ codexExecError(
172
+ "connector_codex_exec_timeout",
173
+ `Codex exec process timed out after ${timeoutMs} milliseconds`,
174
+ ),
175
+ );
153
176
  }, timeoutMs);
154
177
 
155
- child.once("error", (error) => finish(reject, error));
178
+ child.once("error", (error) => finish(
179
+ reject,
180
+ codexExecError("connector_codex_exec_start_failed", "Codex exec process failed to start", error),
181
+ ));
156
182
  child.once("close", (code, signal) => {
157
183
  if (code === 0 && signal === null) {
158
184
  finish(resolve);
159
185
  return;
160
186
  }
161
- finish(reject, new Error("Codex exec process did not complete successfully"));
187
+ const detail = signal
188
+ ? ` (signal ${signal})`
189
+ : code === null
190
+ ? ""
191
+ : ` (exit code ${code})`;
192
+ finish(
193
+ reject,
194
+ codexExecError(
195
+ "connector_codex_exec_failed",
196
+ `Codex exec process did not complete successfully${detail}`,
197
+ ),
198
+ );
162
199
  });
163
200
  });
164
201
  }
@@ -184,11 +221,11 @@ function readClock(clock) {
184
221
  return new Date(value.getTime());
185
222
  }
186
223
 
187
- function requireTimeout(value) {
224
+ function requireTimeout(value, maximum = MAX_COMMAND_TIMEOUT_MS) {
188
225
  if (
189
226
  !Number.isSafeInteger(value) ||
190
227
  value < MIN_COMMAND_TIMEOUT_MS ||
191
- value > MAX_COMMAND_TIMEOUT_MS
228
+ value > maximum
192
229
  ) {
193
230
  throw new TypeError("Codex exec adapter commandTimeoutMs is invalid");
194
231
  }
@@ -221,6 +258,24 @@ function requirePrompt(value) {
221
258
  return value;
222
259
  }
223
260
 
261
+ function requireStdio(value) {
262
+ if (value === "inherit") return value;
263
+ if (
264
+ Array.isArray(value) &&
265
+ value.length === 3 &&
266
+ value.every((entry) => entry === "ignore" || entry === "inherit" || entry === "pipe")
267
+ ) {
268
+ return value;
269
+ }
270
+ throw new TypeError("Codex prompt stdio is invalid");
271
+ }
272
+
273
+ function codexExecError(code, message, cause = undefined) {
274
+ const error = cause === undefined ? new Error(message) : new Error(message, { cause });
275
+ error.code = code;
276
+ return error;
277
+ }
278
+
224
279
  function requireExactRecord(value, allowedFields, requiredFields, label) {
225
280
  if (!value || typeof value !== "object" || Array.isArray(value)) {
226
281
  throw new TypeError(`${label} must be an object`);
package/src/main.mjs CHANGED
@@ -10,7 +10,12 @@ import process from "node:process";
10
10
 
11
11
  import { LocalConnectorClient } from "@webmcp-challenge/reentry-core/local-connector-client";
12
12
  import { followConnectorActivity } from "./activity-monitor.mjs";
13
- import { createCodexExecAdapter, runCodexPrompt } from "./codex-exec-adapter.mjs";
13
+ import {
14
+ createCodexExecAdapter,
15
+ DEFAULT_CODEX_PROMPT_TIMEOUT_MS,
16
+ MAX_CODEX_PROMPT_TIMEOUT_MS,
17
+ runCodexPrompt,
18
+ } from "./codex-exec-adapter.mjs";
14
19
  import {
15
20
  discoverCodexExecutable,
16
21
  requireSupportedNode,
@@ -514,15 +519,19 @@ async function testCodex(flags, positionals, ui) {
514
519
  ui.info("Prompt", prompt);
515
520
  ui.wait("Starting a fresh Codex session…");
516
521
  }
522
+ if (ui.interactive) {
523
+ ui.stopWait("Codex", "running the local prompt; Codex output follows below", "info");
524
+ }
517
525
  await runCodexPrompt({
518
526
  workingDirectory: readiness.workingDirectory,
519
527
  executable: readiness.installation.executable,
520
528
  prompt,
529
+ stdio: ui.interactive ? "inherit" : ["ignore", "ignore", "ignore"],
521
530
  commandTimeoutMs: readBoundedNumber(
522
- flags["activation-timeout"] ?? 60_000,
531
+ flags["activation-timeout"] ?? DEFAULT_CODEX_PROMPT_TIMEOUT_MS,
523
532
  100,
524
- 60_000,
525
- "connector_activation_timeout_invalid",
533
+ MAX_CODEX_PROMPT_TIMEOUT_MS,
534
+ "connector_test_timeout_invalid",
526
535
  ),
527
536
  });
528
537
  if (ui.interactive) {
@@ -1114,6 +1123,10 @@ function errorHint(error) {
1114
1123
  pairing_expired: "ask the Host backend for a new pairing code",
1115
1124
  pairing_request_timeout: "the Receiver took too long to answer; check your connection and run the command again",
1116
1125
  pairing_network_error: "check your internet connection and the Receiver address, then try again",
1126
+ connector_codex_exec_timeout: "Codex did not finish in time; run the same codex exec command directly to inspect its output",
1127
+ connector_codex_exec_failed: "run the same codex exec command directly and confirm that Codex is signed in and the workspace is accessible",
1128
+ connector_codex_exec_start_failed: "open Codex, complete login if needed, then run the command again",
1129
+ connector_codex_exec_invalid: "the installed Codex executable returned an invalid process; run doctor and try again",
1117
1130
  workspace_directory_unavailable: "choose a readable folder or pass --codex-cd /absolute/path",
1118
1131
  workspace_selection_cancelled: "run the command again when you are ready to choose a workspace",
1119
1132
  device_authorization_expired: `run \`${cliCommand("connect")}\` again and approve within ten minutes`,
@@ -1125,9 +1138,10 @@ function errorHint(error) {
1125
1138
  connector_service_stop_failed: `check the Connector status and try \`${cliCommand("stop")}\` again`,
1126
1139
  connector_test_prompt_missing: `use \`${cliCommand('test "Reply with: Re-entry is working."')}\``,
1127
1140
  connector_test_prompt_invalid: "use one short, single-line prompt inside quotes",
1141
+ connector_test_timeout_invalid: "use a test timeout between 100 and 3600000 milliseconds",
1128
1142
  connector_activation_timeout_invalid: "use an activation timeout between 100 and 60000 milliseconds",
1129
1143
  };
1130
- return hints[error?.code] ?? "check the Receiver address and try again";
1144
+ return hints[error?.code] ?? "check the command output and try again";
1131
1145
  }
1132
1146
 
1133
1147
  await main();