@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 +8 -1
- package/package.json +1 -1
- package/src/codex-exec-adapter.mjs +67 -12
- package/src/main.mjs +19 -5
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
|
-
|
|
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.
|
|
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(
|
|
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
|
-
|
|
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
|
|
141
|
+
{ stdio },
|
|
135
142
|
);
|
|
136
143
|
} catch (error) {
|
|
137
|
-
finish(
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
-
|
|
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 >
|
|
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 {
|
|
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"] ??
|
|
531
|
+
flags["activation-timeout"] ?? DEFAULT_CODEX_PROMPT_TIMEOUT_MS,
|
|
523
532
|
100,
|
|
524
|
-
|
|
525
|
-
"
|
|
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
|
|
1144
|
+
return hints[error?.code] ?? "check the command output and try again";
|
|
1131
1145
|
}
|
|
1132
1146
|
|
|
1133
1147
|
await main();
|