// ---------------------------------------------------------------------------// Shared helpers for the Codex plugin surfaces that run through `node_repl`.//// Two Codex plugins ship no MCP server of their own and are driven by// executing JavaScript in Codex's Node REPL: Computer Use (`@oai/sky`) and// Chrome (`browser-client.mjs`). Both surfaces project that REPL as typed MCP// tools, and both therefore have to embed caller arguments into a JS source// text and read one JSON value back out. That encoding is the genuinely// shared part, and it is the part that must be exactly right.// ---------------------------------------------------------------------------/** A JS string literal. For values that ARE strings and are ours (a resolved * module path), where the prototype hazard below cannot apply. *//** * An expression that evaluates to `value` inside the REPL. * * `JSON.parse` of a string, NOT a bare object literal. JSON is almost a subset * of JS, but the two differ in exactly the way that matters here: `__proto__` * as a key sets an object's PROTOTYPE in a literal while being an ordinary own * key under `JSON.parse`. Embedding caller arguments as a literal therefore let * a caller move data onto the prototype chain — an argument named `__proto__` * would vanish from `args.__proto__` and reappear as inherited properties on * every lookup the generated program makes. Parsing keeps arguments data. * * The JSON is then escaped as a JS string. U+2028 and U+2029 are legal raw * inside a JSON string but are literal line terminators in a JS source text, so * a value containing one would end the statement early. */export const jsString = (value: string): string => JSON.stringify(value).replaceAll("\u2028", "\\u2028").replaceAll("\u2029", "\\u2029");export const jsLiteral = (value: unknown): string => `JSON.parse(${JSON.stringify(JSON.stringify(value ?? null)) .replaceAll("\u2028", "\\u2028") .replaceAll("\u2029", "\\u2029")})`;/** * Wrap a program body so it runs in its own scope and reports one JSON value. * * The scope is not cosmetic. A REPL session is persistent and pooled * connections now reuse it across calls, so a program that declared its * working variables at top level would redeclare the same `const` on every * call — which the REPL answers with a warning per variable, prepended to the * result a caller then has to read around. Everything per-call lives in here; * only deliberate caches (the imported runtime) are left on `globalThis`. * * The REPL returns values only through `nodeRepl.write`, and only as text; * `undefined` (every action method) becomes `null` so a caller always gets a * well-formed JSON body rather than an empty string. */export const writeJsonResult = (body: readonly string[], expression: string): string => [ "await (async () => {", ...body.map((line) => ` ${line}`), ` const result = ${expression};`, " nodeRepl.write(JSON.stringify(result ?? null));", "})();", ].join("\n");
// ---------------------------------------------------------------------------
// Shared helpers for the Codex plugin surfaces that run through `node_repl`.
//
// Two Codex plugins ship no MCP server of their own and are driven by
// executing JavaScript in Codex's Node REPL: Computer Use (`@oai/sky`) and
// Chrome (`browser-client.mjs`). Both surfaces project that REPL as typed MCP
// tools, and both therefore have to embed caller arguments into a JS source
// text and read one JSON value back out. That encoding is the genuinely
// shared part, and it is the part that must be exactly right.
// ---------------------------------------------------------------------------
/** A JS string literal. For values that ARE strings and are ours (a resolved
* module path), where the prototype hazard below cannot apply. */
/**
* An expression that evaluates to `value` inside the REPL.
*
* `JSON.parse` of a string, NOT a bare object literal. JSON is almost a subset
* of JS, but the two differ in exactly the way that matters here: `__proto__`
* as a key sets an object's PROTOTYPE in a literal while being an ordinary own
* key under `JSON.parse`. Embedding caller arguments as a literal therefore let
* a caller move data onto the prototype chain — an argument named `__proto__`
* would vanish from `args.__proto__` and reappear as inherited properties on
* every lookup the generated program makes. Parsing keeps arguments data.
*
* The JSON is then escaped as a JS string. U+2028 and U+2029 are legal raw
* inside a JSON string but are literal line terminators in a JS source text, so
* a value containing one would end the statement early.
*/
export const jsString = (value: string): string =>
JSON.stringify(value).replaceAll("\u2028", "\\u2028").replaceAll("\u2029", "\\u2029");
export const jsLiteral = (value: unknown): string =>
`JSON.parse(${JSON.stringify(JSON.stringify(value ?? null))
.replaceAll("\u2028", "\\u2028")
.replaceAll("\u2029", "\\u2029")})`;
/**
* Wrap a program body so it runs in its own scope and reports one JSON value.
*
* The scope is not cosmetic. A REPL session is persistent and pooled
* connections now reuse it across calls, so a program that declared its
* working variables at top level would redeclare the same `const` on every
* call — which the REPL answers with a warning per variable, prepended to the
* result a caller then has to read around. Everything per-call lives in here;
* only deliberate caches (the imported runtime) are left on `globalThis`.
*
* The REPL returns values only through `nodeRepl.write`, and only as text;
* `undefined` (every action method) becomes `null` so a caller always gets a
* well-formed JSON body rather than an empty string.
*/
export const writeJsonResult = (body: readonly string[], expression: string): string =>
[
"await (async () => {",
...body.map((line) => ` ${line}`),
` const result = ${expression};`,
" nodeRepl.write(JSON.stringify(result ?? null));",
"})();",
].join("\n");