@right-link/paperclip-plugin-codex-remote 0.3.1 → 0.3.2
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/dist/chunk-FULSN5VN.js +5348 -0
- package/dist/chunk-YJYVA7CY.js +99 -0
- package/dist/chunk-ZLN6QQMX.js +3267 -0
- package/dist/cli/index.js +190 -2
- package/dist/index.js +28 -84
- package/dist/server/adapter.js +140 -135
- package/dist/server/index.js +41 -56
- package/dist/server-utils-C4H4WJOG.js +104 -0
- package/dist/ui/index.js +318 -3
- package/package.json +7 -5
- package/dist/cli/format-event.js +0 -213
- package/dist/cli/format-event.js.map +0 -1
- package/dist/cli/index.js.map +0 -1
- package/dist/cli/quota-probe.js +0 -97
- package/dist/cli/quota-probe.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/server/adapter.js.map +0 -1
- package/dist/server/adapter.test.js +0 -84
- package/dist/server/adapter.test.js.map +0 -1
- package/dist/server/codex-args.js +0 -60
- package/dist/server/codex-args.js.map +0 -1
- package/dist/server/codex-args.test.js +0 -94
- package/dist/server/codex-args.test.js.map +0 -1
- package/dist/server/codex-home.js +0 -378
- package/dist/server/codex-home.js.map +0 -1
- package/dist/server/codex-home.test.js +0 -244
- package/dist/server/codex-home.test.js.map +0 -1
- package/dist/server/execute.js +0 -906
- package/dist/server/execute.js.map +0 -1
- package/dist/server/execute.remote.test.js +0 -487
- package/dist/server/execute.remote.test.js.map +0 -1
- package/dist/server/index.js.map +0 -1
- package/dist/server/parse.js +0 -213
- package/dist/server/parse.js.map +0 -1
- package/dist/server/parse.test.js +0 -107
- package/dist/server/parse.test.js.map +0 -1
- package/dist/server/quota-spawn-error.test.js +0 -77
- package/dist/server/quota-spawn-error.test.js.map +0 -1
- package/dist/server/quota.js +0 -432
- package/dist/server/quota.js.map +0 -1
- package/dist/server/sandbox-env.js +0 -23
- package/dist/server/sandbox-env.js.map +0 -1
- package/dist/server/skills.js +0 -24
- package/dist/server/skills.js.map +0 -1
- package/dist/server/tailscale.js +0 -95
- package/dist/server/tailscale.js.map +0 -1
- package/dist/server/test.js +0 -811
- package/dist/server/test.js.map +0 -1
- package/dist/server/test.remote.test.js +0 -257
- package/dist/server/test.remote.test.js.map +0 -1
- package/dist/ui/build-config.js +0 -113
- package/dist/ui/build-config.js.map +0 -1
- package/dist/ui/build-config.test.js +0 -49
- package/dist/ui/build-config.test.js.map +0 -1
- package/dist/ui/index.js.map +0 -1
- package/dist/ui/parse-stdout.js +0 -261
- package/dist/ui/parse-stdout.js.map +0 -1
- package/dist/ui/parse-stdout.test.js +0 -77
- package/dist/ui/parse-stdout.test.js.map +0 -1
- package/dist/ui-parser.js.map +0 -1
|
@@ -1,378 +0,0 @@
|
|
|
1
|
-
import fs from "node:fs/promises";
|
|
2
|
-
import os from "node:os";
|
|
3
|
-
import path from "node:path";
|
|
4
|
-
import { resolvePaperclipInstanceRootForAdapter } from "@paperclipai/adapter-utils/server-utils";
|
|
5
|
-
const TRUTHY_ENV_RE = /^(1|true|yes|on)$/i;
|
|
6
|
-
const COPIED_SHARED_FILES = ["config.json", "config.toml", "instructions.md"];
|
|
7
|
-
const SYMLINKED_SHARED_FILES = ["auth.json"];
|
|
8
|
-
// Allowlist for the config.toml shipped into a remote sandbox. We send ONLY
|
|
9
|
-
// what Codex needs to run against the configured model provider, and nothing
|
|
10
|
-
// else — no `mcp_servers` (whose host binaries don't exist in the sandbox and
|
|
11
|
-
// stall Codex for each server's startup_timeout_sec), no project trust lists,
|
|
12
|
-
// plugins, marketplaces, desktop/Windows settings, etc.
|
|
13
|
-
//
|
|
14
|
-
// Kept top-level keys (everything else in the preamble is dropped):
|
|
15
|
-
const REMOTE_KEPT_TOP_LEVEL_KEYS = new Set([
|
|
16
|
-
"model",
|
|
17
|
-
"model_provider",
|
|
18
|
-
"model_reasoning_effort",
|
|
19
|
-
"model_reasoning_summary",
|
|
20
|
-
"model_verbosity",
|
|
21
|
-
"model_supports_reasoning_summaries",
|
|
22
|
-
"approval_policy",
|
|
23
|
-
"preferred_auth_method",
|
|
24
|
-
"check_for_update_on_startup",
|
|
25
|
-
"web_search",
|
|
26
|
-
"personality",
|
|
27
|
-
]);
|
|
28
|
-
// Kept sections: only the model provider definitions (e.g.
|
|
29
|
-
// `[model_providers.cliproxyapi]` and any sub-tables).
|
|
30
|
-
const REMOTE_KEPT_SECTION_ROOT = "model_providers";
|
|
31
|
-
/** Returns the dotted segments of a TOML table header, or null if not a header. */
|
|
32
|
-
function parseTomlSectionSegments(line) {
|
|
33
|
-
const match = /^\s*\[\[?\s*([^\]]+?)\s*\]\]?\s*$/.exec(line);
|
|
34
|
-
if (!match)
|
|
35
|
-
return null;
|
|
36
|
-
return match[1].split(".").map((segment) => segment.trim().replace(/^['"]|['"]$/g, ""));
|
|
37
|
-
}
|
|
38
|
-
function parseTomlStringValue(line) {
|
|
39
|
-
const match = /^\s*[A-Za-z0-9_-]+\s*=\s*(['"])(.*?)\1\s*(?:#.*)?$/.exec(line);
|
|
40
|
-
return match?.[2] ?? null;
|
|
41
|
-
}
|
|
42
|
-
function tomlString(value) {
|
|
43
|
-
return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
|
|
44
|
-
}
|
|
45
|
-
/**
|
|
46
|
-
* Reduces a Codex `config.toml` to the minimum needed inside a remote sandbox:
|
|
47
|
-
* the allowlisted top-level keys ({@link REMOTE_KEPT_TOP_LEVEL_KEYS}) plus the
|
|
48
|
-
* `[model_providers.*]` sections. Everything else — MCP servers, project trust
|
|
49
|
-
* lists, plugins, marketplaces, desktop/Windows host settings — is dropped, so
|
|
50
|
-
* Codex starts clean and fast and never tries to launch host-only tooling.
|
|
51
|
-
*
|
|
52
|
-
* Each provider also gets `supports_websockets = false` injected: Codex defaults
|
|
53
|
-
* to a `wss://` transport for the Responses API, but a sandbox reaching the
|
|
54
|
-
* provider through a userspace-Tailscale HTTP proxy cannot open that WebSocket,
|
|
55
|
-
* so Codex burns ~75s retrying it before falling back to HTTP on every run (the
|
|
56
|
-
* misleading "failed to refresh available models: timeout waiting for child
|
|
57
|
-
* process to exit"). Forcing HTTP removes that stall.
|
|
58
|
-
*/
|
|
59
|
-
export function sanitizeRemoteCodexConfigToml(toml) {
|
|
60
|
-
const lines = toml.split(/\r?\n/);
|
|
61
|
-
const preamble = new Map();
|
|
62
|
-
const sections = [];
|
|
63
|
-
const providerNames = [];
|
|
64
|
-
let configuredProvider = null;
|
|
65
|
-
let inPreamble = true;
|
|
66
|
-
let currentSection = null;
|
|
67
|
-
for (const line of lines) {
|
|
68
|
-
const segments = parseTomlSectionSegments(line);
|
|
69
|
-
if (segments) {
|
|
70
|
-
inPreamble = false;
|
|
71
|
-
currentSection = null;
|
|
72
|
-
if (segments[0]?.toLowerCase() === REMOTE_KEPT_SECTION_ROOT) {
|
|
73
|
-
currentSection = { header: line, segments, lines: [] };
|
|
74
|
-
sections.push(currentSection);
|
|
75
|
-
if (segments.length === 2 && segments[1]) {
|
|
76
|
-
providerNames.push(segments[1]);
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
continue;
|
|
80
|
-
}
|
|
81
|
-
if (inPreamble) {
|
|
82
|
-
const key = /^\s*([A-Za-z0-9_-]+)\s*=/.exec(line);
|
|
83
|
-
if (key && REMOTE_KEPT_TOP_LEVEL_KEYS.has(key[1].toLowerCase())) {
|
|
84
|
-
const normalizedKey = key[1].toLowerCase();
|
|
85
|
-
preamble.set(normalizedKey, line);
|
|
86
|
-
if (normalizedKey === "model_provider") {
|
|
87
|
-
configuredProvider = parseTomlStringValue(line);
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
continue;
|
|
91
|
-
}
|
|
92
|
-
if (currentSection) {
|
|
93
|
-
currentSection.lines.push(line);
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
const activeProvider = configuredProvider && configuredProvider.toLowerCase() !== "openai" && providerNames.includes(configuredProvider)
|
|
97
|
-
? configuredProvider
|
|
98
|
-
: providerNames.find((provider) => provider.toLowerCase() !== "openai") ?? configuredProvider ?? providerNames[0] ?? null;
|
|
99
|
-
if (activeProvider) {
|
|
100
|
-
preamble.set("model_provider", `model_provider = ${tomlString(activeProvider)}`);
|
|
101
|
-
}
|
|
102
|
-
preamble.set("preferred_auth_method", 'preferred_auth_method = "apikey"');
|
|
103
|
-
preamble.set("check_for_update_on_startup", "check_for_update_on_startup = false");
|
|
104
|
-
preamble.set("web_search", 'web_search = "disabled"');
|
|
105
|
-
const out = [...preamble.values()];
|
|
106
|
-
for (const section of sections) {
|
|
107
|
-
if (out.length > 0 && out[out.length - 1] !== "")
|
|
108
|
-
out.push("");
|
|
109
|
-
out.push(section.header);
|
|
110
|
-
const isProviderMainSection = section.segments.length === 2;
|
|
111
|
-
for (const line of section.lines) {
|
|
112
|
-
const key = /^\s*([A-Za-z0-9_-]+)\s*=/.exec(line);
|
|
113
|
-
if (isProviderMainSection &&
|
|
114
|
-
key &&
|
|
115
|
-
["requires_openai_auth", "supports_websockets"].includes(key[1].toLowerCase())) {
|
|
116
|
-
continue;
|
|
117
|
-
}
|
|
118
|
-
out.push(line);
|
|
119
|
-
}
|
|
120
|
-
if (isProviderMainSection) {
|
|
121
|
-
out.push("requires_openai_auth = false");
|
|
122
|
-
out.push("supports_websockets = false");
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
if (out.length > 0 && out[out.length - 1] !== "")
|
|
126
|
-
out.push("");
|
|
127
|
-
out.push("[features]");
|
|
128
|
-
out.push("browser_use = false");
|
|
129
|
-
out.push("in_app_browser = false");
|
|
130
|
-
out.push("computer_use = false");
|
|
131
|
-
return out.join("\n").replace(/\n{3,}/g, "\n\n").trimEnd() + "\n";
|
|
132
|
-
}
|
|
133
|
-
/**
|
|
134
|
-
* Injects `supports_websockets = false` into every `[model_providers.<name>]`
|
|
135
|
-
* section that does not already set the flag. Unlike
|
|
136
|
-
* {@link sanitizeRemoteCodexConfigToml}, this preserves all other config
|
|
137
|
-
* content — it is safe to apply to both local and remote config copies.
|
|
138
|
-
*/
|
|
139
|
-
export function injectWebsocketsDisabled(toml) {
|
|
140
|
-
const lines = toml.split(/\r?\n/);
|
|
141
|
-
const out = [];
|
|
142
|
-
let inProviderMainSection = false;
|
|
143
|
-
let providerHasWebsocketsFlag = false;
|
|
144
|
-
const closeProviderSection = () => {
|
|
145
|
-
if (inProviderMainSection && !providerHasWebsocketsFlag) {
|
|
146
|
-
out.push("supports_websockets = false");
|
|
147
|
-
}
|
|
148
|
-
inProviderMainSection = false;
|
|
149
|
-
providerHasWebsocketsFlag = false;
|
|
150
|
-
};
|
|
151
|
-
for (const line of lines) {
|
|
152
|
-
const segments = parseTomlSectionSegments(line);
|
|
153
|
-
if (segments) {
|
|
154
|
-
closeProviderSection();
|
|
155
|
-
inProviderMainSection = segments[0]?.toLowerCase() === REMOTE_KEPT_SECTION_ROOT && segments.length === 2;
|
|
156
|
-
out.push(line);
|
|
157
|
-
continue;
|
|
158
|
-
}
|
|
159
|
-
if (inProviderMainSection) {
|
|
160
|
-
const key = /^\s*([A-Za-z0-9_-]+)\s*=/.exec(line);
|
|
161
|
-
if (key && key[1].toLowerCase() === "supports_websockets") {
|
|
162
|
-
providerHasWebsocketsFlag = true;
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
out.push(line);
|
|
166
|
-
}
|
|
167
|
-
closeProviderSection();
|
|
168
|
-
return out.join("\n");
|
|
169
|
-
}
|
|
170
|
-
/**
|
|
171
|
-
* Builds a throwaway Codex home directory suitable for syncing into a remote
|
|
172
|
-
* sandbox: the same auth/instructions as the managed home, but with a
|
|
173
|
-
* sandbox-sanitized `config.toml` (see {@link sanitizeRemoteCodexConfigToml}).
|
|
174
|
-
* The caller must invoke `cleanup()` when the run finishes.
|
|
175
|
-
*/
|
|
176
|
-
export async function prepareRemoteCodexHomeAsset(sourceHome) {
|
|
177
|
-
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "paperclip-codex-remote-home-"));
|
|
178
|
-
const cleanup = async () => {
|
|
179
|
-
await fs.rm(dir, { recursive: true, force: true }).catch(() => { });
|
|
180
|
-
};
|
|
181
|
-
try {
|
|
182
|
-
const configSource = path.join(sourceHome, "config.toml");
|
|
183
|
-
if (await pathExists(configSource)) {
|
|
184
|
-
const raw = await fs.readFile(configSource, "utf8");
|
|
185
|
-
await fs.writeFile(path.join(dir, "config.toml"), sanitizeRemoteCodexConfigToml(raw), { mode: 0o600 });
|
|
186
|
-
}
|
|
187
|
-
// Do not copy auth.json into remote sandboxes. Remote runs should use the
|
|
188
|
-
// sanitized config.toml provider credentials; copying ChatGPT-mode auth can
|
|
189
|
-
// make Codex start account/plugin background transports that are irrelevant
|
|
190
|
-
// in a sandbox and can stall startup.
|
|
191
|
-
for (const name of ["config.json", "instructions.md"]) {
|
|
192
|
-
const src = path.join(sourceHome, name);
|
|
193
|
-
if (await pathExists(src)) {
|
|
194
|
-
await fs.copyFile(src, path.join(dir, name));
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
return { dir, cleanup };
|
|
198
|
-
}
|
|
199
|
-
catch (error) {
|
|
200
|
-
await cleanup();
|
|
201
|
-
throw error;
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
function nonEmpty(value) {
|
|
205
|
-
return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
|
|
206
|
-
}
|
|
207
|
-
export async function pathExists(candidate) {
|
|
208
|
-
return fs.access(candidate).then(() => true).catch(() => false);
|
|
209
|
-
}
|
|
210
|
-
export function resolveSharedCodexHomeDir(env = process.env) {
|
|
211
|
-
const fromEnv = nonEmpty(env.CODEX_HOME);
|
|
212
|
-
return fromEnv ? path.resolve(fromEnv) : path.join(os.homedir(), ".codex");
|
|
213
|
-
}
|
|
214
|
-
function isWorktreeMode(env) {
|
|
215
|
-
return TRUTHY_ENV_RE.test(env.PAPERCLIP_IN_WORKTREE ?? "");
|
|
216
|
-
}
|
|
217
|
-
export function resolveManagedCodexHomeDir(env, companyId) {
|
|
218
|
-
const instanceRoot = resolvePaperclipInstanceRootForAdapter({
|
|
219
|
-
homeDir: nonEmpty(env.PAPERCLIP_HOME) ?? undefined,
|
|
220
|
-
instanceId: nonEmpty(env.PAPERCLIP_INSTANCE_ID) ?? undefined,
|
|
221
|
-
env,
|
|
222
|
-
});
|
|
223
|
-
return companyId
|
|
224
|
-
? path.resolve(instanceRoot, "companies", companyId, "codex-home")
|
|
225
|
-
: path.resolve(instanceRoot, "codex-home");
|
|
226
|
-
}
|
|
227
|
-
async function ensureParentDir(target) {
|
|
228
|
-
await fs.mkdir(path.dirname(target), { recursive: true });
|
|
229
|
-
}
|
|
230
|
-
async function isExpectedSymlink(target, source) {
|
|
231
|
-
const existing = await fs.lstat(target).catch(() => null);
|
|
232
|
-
if (!existing?.isSymbolicLink())
|
|
233
|
-
return false;
|
|
234
|
-
const linkedPath = await fs.readlink(target).catch(() => null);
|
|
235
|
-
if (!linkedPath)
|
|
236
|
-
return false;
|
|
237
|
-
return path.resolve(path.dirname(target), linkedPath) === path.resolve(source);
|
|
238
|
-
}
|
|
239
|
-
async function createExpectedSymlink(target, source) {
|
|
240
|
-
try {
|
|
241
|
-
await fs.symlink(source, target);
|
|
242
|
-
}
|
|
243
|
-
catch (error) {
|
|
244
|
-
const code = error.code;
|
|
245
|
-
if (code === "EEXIST" && await isExpectedSymlink(target, source))
|
|
246
|
-
return;
|
|
247
|
-
throw error;
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
function isSymlinkPermissionError(error) {
|
|
251
|
-
const code = error.code;
|
|
252
|
-
return code === "EPERM" || code === "EACCES";
|
|
253
|
-
}
|
|
254
|
-
async function ensureSymlinkOrCopy(target, source) {
|
|
255
|
-
const existing = await fs.lstat(target).catch(() => null);
|
|
256
|
-
if (!existing) {
|
|
257
|
-
await ensureParentDir(target);
|
|
258
|
-
try {
|
|
259
|
-
await createExpectedSymlink(target, source);
|
|
260
|
-
return "symlink";
|
|
261
|
-
}
|
|
262
|
-
catch (error) {
|
|
263
|
-
if (!isSymlinkPermissionError(error))
|
|
264
|
-
throw error;
|
|
265
|
-
await fs.copyFile(source, target);
|
|
266
|
-
return "copy";
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
if (!existing.isSymbolicLink()) {
|
|
270
|
-
return "copy";
|
|
271
|
-
}
|
|
272
|
-
if (await isExpectedSymlink(target, source))
|
|
273
|
-
return "symlink";
|
|
274
|
-
await fs.unlink(target);
|
|
275
|
-
try {
|
|
276
|
-
await createExpectedSymlink(target, source);
|
|
277
|
-
return "symlink";
|
|
278
|
-
}
|
|
279
|
-
catch (error) {
|
|
280
|
-
if (!isSymlinkPermissionError(error))
|
|
281
|
-
throw error;
|
|
282
|
-
await fs.copyFile(source, target);
|
|
283
|
-
return "copy";
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
async function filesHaveSameContent(target, source) {
|
|
287
|
-
const [targetBuf, sourceBuf] = await Promise.all([
|
|
288
|
-
fs.readFile(target).catch(() => null),
|
|
289
|
-
fs.readFile(source).catch(() => null),
|
|
290
|
-
]);
|
|
291
|
-
if (!targetBuf || !sourceBuf)
|
|
292
|
-
return false;
|
|
293
|
-
return targetBuf.equals(sourceBuf);
|
|
294
|
-
}
|
|
295
|
-
/**
|
|
296
|
-
* Copies a shared Codex config file (e.g. `config.toml`) into the managed home,
|
|
297
|
-
* refreshing it whenever the source content changes. The managed copy is purely
|
|
298
|
-
* a mirror of the shared file — nothing else writes to it — so keeping it in
|
|
299
|
-
* sync prevents a stale config (e.g. an outdated provider `base_url`, bearer
|
|
300
|
-
* token, or `requires_openai_auth` flag) from being shipped into the sandbox
|
|
301
|
-
* after the host config is edited.
|
|
302
|
-
*/
|
|
303
|
-
async function ensureCopiedFile(target, source) {
|
|
304
|
-
const existing = await fs.lstat(target).catch(() => null);
|
|
305
|
-
if (existing && !existing.isSymbolicLink() && (await filesHaveSameContent(target, source))) {
|
|
306
|
-
return false;
|
|
307
|
-
}
|
|
308
|
-
await ensureParentDir(target);
|
|
309
|
-
await fs.rm(target, { force: true });
|
|
310
|
-
await fs.copyFile(source, target);
|
|
311
|
-
return true;
|
|
312
|
-
}
|
|
313
|
-
/**
|
|
314
|
-
* Writes an `auth.json` containing only `OPENAI_API_KEY` so the codex CLI can
|
|
315
|
-
* authenticate via API key. Overwrites any existing file or symlink at that
|
|
316
|
-
* path. Required because the codex CLI (>= 0.122) ignores the `OPENAI_API_KEY`
|
|
317
|
-
* environment variable and only reads credentials from `$CODEX_HOME/auth.json`.
|
|
318
|
-
*/
|
|
319
|
-
export async function writeApiKeyAuthJson(home, apiKey) {
|
|
320
|
-
await fs.mkdir(home, { recursive: true });
|
|
321
|
-
const target = path.join(home, "auth.json");
|
|
322
|
-
await fs.rm(target, { force: true });
|
|
323
|
-
await fs.writeFile(target, JSON.stringify({ OPENAI_API_KEY: apiKey }), { mode: 0o600 });
|
|
324
|
-
}
|
|
325
|
-
export async function prepareManagedCodexHome(env, onLog, companyId, options = {}) {
|
|
326
|
-
const targetHome = resolveManagedCodexHomeDir(env, companyId);
|
|
327
|
-
const apiKey = nonEmpty(options.apiKey ?? undefined);
|
|
328
|
-
const sourceHome = resolveSharedCodexHomeDir(env);
|
|
329
|
-
const seedFromShared = path.resolve(sourceHome) !== path.resolve(targetHome);
|
|
330
|
-
await fs.mkdir(targetHome, { recursive: true });
|
|
331
|
-
// If a previous run wrote an apikey-mode auth.json (regular file) and this
|
|
332
|
-
// run has no apiKey, remove it so the chatgpt-mode symlink can be restored.
|
|
333
|
-
// Without this cleanup, ensureSymlink bails on a non-symlink and Codex keeps
|
|
334
|
-
// authenticating with the stale key after it is removed from configuration.
|
|
335
|
-
if (!apiKey && seedFromShared) {
|
|
336
|
-
const authPath = path.join(targetHome, "auth.json");
|
|
337
|
-
const existing = await fs.lstat(authPath).catch(() => null);
|
|
338
|
-
if (existing && !existing.isSymbolicLink()) {
|
|
339
|
-
await fs.rm(authPath, { force: true });
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
if (seedFromShared) {
|
|
343
|
-
let copiedSharedAuth = false;
|
|
344
|
-
for (const name of SYMLINKED_SHARED_FILES) {
|
|
345
|
-
const source = path.join(sourceHome, name);
|
|
346
|
-
if (!(await pathExists(source)))
|
|
347
|
-
continue;
|
|
348
|
-
const mode = await ensureSymlinkOrCopy(path.join(targetHome, name), source);
|
|
349
|
-
copiedSharedAuth ||= mode === "copy";
|
|
350
|
-
}
|
|
351
|
-
const refreshedConfigFiles = [];
|
|
352
|
-
for (const name of COPIED_SHARED_FILES) {
|
|
353
|
-
const source = path.join(sourceHome, name);
|
|
354
|
-
if (!(await pathExists(source)))
|
|
355
|
-
continue;
|
|
356
|
-
if (await ensureCopiedFile(path.join(targetHome, name), source)) {
|
|
357
|
-
refreshedConfigFiles.push(name);
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
// Ensure Codex uses HTTP transport for model providers — avoids the ~75s
|
|
361
|
-
// WebSocket retry on every run when the provider doesn't support wss://.
|
|
362
|
-
const managedConfigToml = path.join(targetHome, "config.toml");
|
|
363
|
-
if (await pathExists(managedConfigToml)) {
|
|
364
|
-
const raw = await fs.readFile(managedConfigToml, "utf8");
|
|
365
|
-
const patched = injectWebsocketsDisabled(raw);
|
|
366
|
-
if (patched !== raw) {
|
|
367
|
-
await fs.writeFile(managedConfigToml, patched, { mode: 0o600 });
|
|
368
|
-
}
|
|
369
|
-
}
|
|
370
|
-
await onLog("stdout", `[paperclip] Using ${isWorktreeMode(env) ? "worktree-isolated" : "Paperclip-managed"} Codex home "${targetHome}" (seeded from "${sourceHome}"${copiedSharedAuth ? "; copied auth.json because symlinks are unavailable" : ""}${refreshedConfigFiles.length > 0 ? `; refreshed ${refreshedConfigFiles.join(", ")} from shared config` : ""}).\n`);
|
|
371
|
-
}
|
|
372
|
-
if (apiKey) {
|
|
373
|
-
await writeApiKeyAuthJson(targetHome, apiKey);
|
|
374
|
-
await onLog("stdout", `[paperclip] Wrote API-key auth.json into Codex home "${targetHome}" from configured OPENAI_API_KEY.\n`);
|
|
375
|
-
}
|
|
376
|
-
return targetHome;
|
|
377
|
-
}
|
|
378
|
-
//# sourceMappingURL=codex-home.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"codex-home.js","sourceRoot":"","sources":["../../src/server/codex-home.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,244 +0,0 @@
|
|
|
1
|
-
import fs from "node:fs/promises";
|
|
2
|
-
import os from "node:os";
|
|
3
|
-
import path from "node:path";
|
|
4
|
-
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
5
|
-
import { pathExists, prepareManagedCodexHome, prepareRemoteCodexHomeAsset, sanitizeRemoteCodexConfigToml } from "./codex-home.js";
|
|
6
|
-
describe("sanitizeRemoteCodexConfigToml", () => {
|
|
7
|
-
it("keeps only allowlisted top-level keys and model_providers sections", () => {
|
|
8
|
-
const toml = [
|
|
9
|
-
"windows_wsl_setup_acknowledged = true",
|
|
10
|
-
'model = "gpt-5.5"',
|
|
11
|
-
'model_provider = "cliproxyapi"',
|
|
12
|
-
'model_reasoning_effort = "medium"',
|
|
13
|
-
'personality = "pragmatic"',
|
|
14
|
-
'preferred_auth_method = "chatgpt"',
|
|
15
|
-
"check_for_update_on_startup = true",
|
|
16
|
-
'web_search = "cached"',
|
|
17
|
-
"",
|
|
18
|
-
"[marketplaces.openai-bundled]",
|
|
19
|
-
'source = "local"',
|
|
20
|
-
"",
|
|
21
|
-
"[windows]",
|
|
22
|
-
'sandbox = "elevated"',
|
|
23
|
-
"",
|
|
24
|
-
"[projects.'c:\\\\work']",
|
|
25
|
-
'trust_level = "trusted"',
|
|
26
|
-
"",
|
|
27
|
-
'[plugins."linear@openai-curated"]',
|
|
28
|
-
"enabled = true",
|
|
29
|
-
"",
|
|
30
|
-
"[model_providers.cliproxyapi]",
|
|
31
|
-
'base_url = "http://100.114.28.103:8317/v1"',
|
|
32
|
-
'experimental_bearer_token = "secret"',
|
|
33
|
-
'wire_api = "responses"',
|
|
34
|
-
"requires_openai_auth = true",
|
|
35
|
-
"supports_websockets = true",
|
|
36
|
-
"",
|
|
37
|
-
"[mcp_servers.node_repl]",
|
|
38
|
-
'command = "C:\\\\node_repl.exe"',
|
|
39
|
-
"startup_timeout_sec = 120",
|
|
40
|
-
].join("\n");
|
|
41
|
-
const out = sanitizeRemoteCodexConfigToml(toml);
|
|
42
|
-
// Required for Codex + the provider: kept.
|
|
43
|
-
expect(out).toContain('model = "gpt-5.5"');
|
|
44
|
-
expect(out).toContain('model_provider = "cliproxyapi"');
|
|
45
|
-
expect(out).toContain('model_reasoning_effort = "medium"');
|
|
46
|
-
expect(out).toContain('preferred_auth_method = "apikey"');
|
|
47
|
-
expect(out).toContain("check_for_update_on_startup = false");
|
|
48
|
-
expect(out).toContain('web_search = "disabled"');
|
|
49
|
-
expect(out).toContain("[model_providers.cliproxyapi]");
|
|
50
|
-
expect(out).toContain("experimental_bearer_token");
|
|
51
|
-
expect(out).toContain("requires_openai_auth = false");
|
|
52
|
-
// Force HTTP transport so the sandbox doesn't burn ~75s retrying wss://.
|
|
53
|
-
expect(out).toContain("supports_websockets = false");
|
|
54
|
-
expect(out).toContain("[features]");
|
|
55
|
-
expect(out).toContain("browser_use = false");
|
|
56
|
-
expect(out).toContain("in_app_browser = false");
|
|
57
|
-
expect(out).toContain("computer_use = false");
|
|
58
|
-
expect(out).not.toContain("requires_openai_auth = true");
|
|
59
|
-
expect(out).not.toContain("supports_websockets = true");
|
|
60
|
-
// Everything host-specific: dropped.
|
|
61
|
-
expect(out).not.toContain("windows_wsl_setup_acknowledged");
|
|
62
|
-
expect(out).not.toContain("[mcp_servers");
|
|
63
|
-
expect(out).not.toContain("node_repl.exe");
|
|
64
|
-
expect(out).not.toContain("startup_timeout_sec");
|
|
65
|
-
expect(out).not.toContain("[projects");
|
|
66
|
-
expect(out).not.toContain("[windows]");
|
|
67
|
-
expect(out).not.toContain("[marketplaces");
|
|
68
|
-
expect(out).not.toContain("[plugins");
|
|
69
|
-
});
|
|
70
|
-
it("forces sandbox startup flags onto the active non-openai provider", () => {
|
|
71
|
-
const toml = [
|
|
72
|
-
'model_provider = "openai"',
|
|
73
|
-
"",
|
|
74
|
-
"[model_providers.openai]",
|
|
75
|
-
'base_url = "https://api.openai.com/v1"',
|
|
76
|
-
"supports_websockets = true",
|
|
77
|
-
"",
|
|
78
|
-
"[model_providers.cliproxyapi]",
|
|
79
|
-
'base_url = "http://x/v1"',
|
|
80
|
-
"requires_openai_auth = true",
|
|
81
|
-
"supports_websockets = true",
|
|
82
|
-
].join("\n");
|
|
83
|
-
const out = sanitizeRemoteCodexConfigToml(toml);
|
|
84
|
-
expect(out).toContain('model_provider = "cliproxyapi"');
|
|
85
|
-
expect(out).toContain("[model_providers.cliproxyapi]");
|
|
86
|
-
expect(out).toContain("requires_openai_auth = false");
|
|
87
|
-
expect(out).toContain("supports_websockets = false");
|
|
88
|
-
expect(out).not.toContain('model_provider = "openai"');
|
|
89
|
-
expect(out).not.toContain("requires_openai_auth = true");
|
|
90
|
-
expect(out).not.toContain("supports_websockets = true");
|
|
91
|
-
});
|
|
92
|
-
});
|
|
93
|
-
describe("prepareRemoteCodexHomeAsset", () => {
|
|
94
|
-
it("omits auth.json so sandbox runs use config.toml provider credentials", async () => {
|
|
95
|
-
const root = await fs.mkdtemp(path.join(os.tmpdir(), "paperclip-codex-remote-home-"));
|
|
96
|
-
const sourceHome = path.join(root, "source");
|
|
97
|
-
await fs.mkdir(sourceHome, { recursive: true });
|
|
98
|
-
await fs.writeFile(path.join(sourceHome, "config.toml"), [
|
|
99
|
-
'model_provider = "cliproxyapi"',
|
|
100
|
-
"[model_providers.cliproxyapi]",
|
|
101
|
-
'base_url = "http://proxy/v1"',
|
|
102
|
-
"requires_openai_auth = false",
|
|
103
|
-
].join("\n"), "utf8");
|
|
104
|
-
await fs.writeFile(path.join(sourceHome, "auth.json"), '{"auth_mode":"chatgpt"}\n', "utf8");
|
|
105
|
-
const asset = await prepareRemoteCodexHomeAsset(sourceHome);
|
|
106
|
-
try {
|
|
107
|
-
await expect(pathExists(path.join(asset.dir, "auth.json"))).resolves.toBe(false);
|
|
108
|
-
await expect(fs.readFile(path.join(asset.dir, "config.toml"), "utf8")).resolves.toContain("[model_providers.cliproxyapi]");
|
|
109
|
-
const config = await fs.readFile(path.join(asset.dir, "config.toml"), "utf8");
|
|
110
|
-
expect(config).toContain('preferred_auth_method = "apikey"');
|
|
111
|
-
expect(config).toContain("check_for_update_on_startup = false");
|
|
112
|
-
expect(config).toContain('web_search = "disabled"');
|
|
113
|
-
expect(config).toContain("requires_openai_auth = false");
|
|
114
|
-
expect(config).toContain("supports_websockets = false");
|
|
115
|
-
expect(config).toContain("[features]");
|
|
116
|
-
expect(config).toContain("browser_use = false");
|
|
117
|
-
expect(config).toContain("in_app_browser = false");
|
|
118
|
-
expect(config).toContain("computer_use = false");
|
|
119
|
-
}
|
|
120
|
-
finally {
|
|
121
|
-
await asset.cleanup();
|
|
122
|
-
await fs.rm(root, { recursive: true, force: true });
|
|
123
|
-
}
|
|
124
|
-
});
|
|
125
|
-
});
|
|
126
|
-
describe("codex managed home", () => {
|
|
127
|
-
afterEach(() => {
|
|
128
|
-
vi.restoreAllMocks();
|
|
129
|
-
});
|
|
130
|
-
async function canCreateSymlink() {
|
|
131
|
-
const root = await fs.mkdtemp(path.join(os.tmpdir(), "paperclip-codex-home-symlink-check-"));
|
|
132
|
-
const source = path.join(root, "source.txt");
|
|
133
|
-
const target = path.join(root, "target.txt");
|
|
134
|
-
try {
|
|
135
|
-
await fs.writeFile(source, "probe", "utf8");
|
|
136
|
-
await fs.symlink(source, target);
|
|
137
|
-
return true;
|
|
138
|
-
}
|
|
139
|
-
catch {
|
|
140
|
-
return false;
|
|
141
|
-
}
|
|
142
|
-
finally {
|
|
143
|
-
await fs.rm(root, { recursive: true, force: true });
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
it("treats a concurrently-created expected auth symlink as success", async () => {
|
|
147
|
-
if (!(await canCreateSymlink()))
|
|
148
|
-
return;
|
|
149
|
-
const root = await fs.mkdtemp(path.join(os.tmpdir(), "paperclip-codex-home-"));
|
|
150
|
-
const sharedCodexHome = path.join(root, "shared-codex-home");
|
|
151
|
-
const paperclipHome = path.join(root, "paperclip-home");
|
|
152
|
-
const managedCodexHome = path.join(paperclipHome, "instances", "default", "companies", "company-1", "codex-home");
|
|
153
|
-
const sharedAuth = path.join(sharedCodexHome, "auth.json");
|
|
154
|
-
const managedAuth = path.join(managedCodexHome, "auth.json");
|
|
155
|
-
await fs.mkdir(sharedCodexHome, { recursive: true });
|
|
156
|
-
await fs.writeFile(sharedAuth, '{"token":"shared"}\n', "utf8");
|
|
157
|
-
const originalSymlink = fs.symlink.bind(fs);
|
|
158
|
-
vi.spyOn(fs, "symlink").mockImplementationOnce(async (source, target, type) => {
|
|
159
|
-
await originalSymlink(source, target, type);
|
|
160
|
-
const error = new Error("file already exists");
|
|
161
|
-
error.code = "EEXIST";
|
|
162
|
-
throw error;
|
|
163
|
-
});
|
|
164
|
-
try {
|
|
165
|
-
await expect(prepareManagedCodexHome({
|
|
166
|
-
CODEX_HOME: sharedCodexHome,
|
|
167
|
-
PAPERCLIP_HOME: paperclipHome,
|
|
168
|
-
PAPERCLIP_INSTANCE_ID: "default",
|
|
169
|
-
}, async () => { }, "company-1")).resolves.toBe(managedCodexHome);
|
|
170
|
-
expect((await fs.lstat(managedAuth)).isSymbolicLink()).toBe(true);
|
|
171
|
-
expect(await fs.realpath(managedAuth)).toBe(await fs.realpath(sharedAuth));
|
|
172
|
-
}
|
|
173
|
-
finally {
|
|
174
|
-
await fs.rm(root, { recursive: true, force: true });
|
|
175
|
-
}
|
|
176
|
-
});
|
|
177
|
-
it("refreshes a stale copied config.toml from the shared home", async () => {
|
|
178
|
-
const root = await fs.mkdtemp(path.join(os.tmpdir(), "paperclip-codex-home-"));
|
|
179
|
-
const sharedCodexHome = path.join(root, "shared-codex-home");
|
|
180
|
-
const paperclipHome = path.join(root, "paperclip-home");
|
|
181
|
-
const managedCodexHome = path.join(paperclipHome, "instances", "default", "companies", "company-1", "codex-home");
|
|
182
|
-
const sharedConfig = path.join(sharedCodexHome, "config.toml");
|
|
183
|
-
const managedConfig = path.join(managedCodexHome, "config.toml");
|
|
184
|
-
const logs = [];
|
|
185
|
-
await fs.mkdir(sharedCodexHome, { recursive: true });
|
|
186
|
-
await fs.writeFile(sharedConfig, 'requires_openai_auth = false\n', "utf8");
|
|
187
|
-
// Simulate an earlier run that copied a now-stale config into the managed home.
|
|
188
|
-
await fs.mkdir(managedCodexHome, { recursive: true });
|
|
189
|
-
await fs.writeFile(managedConfig, 'requires_openai_auth = true\n', "utf8");
|
|
190
|
-
const env = {
|
|
191
|
-
CODEX_HOME: sharedCodexHome,
|
|
192
|
-
PAPERCLIP_HOME: paperclipHome,
|
|
193
|
-
PAPERCLIP_INSTANCE_ID: "default",
|
|
194
|
-
};
|
|
195
|
-
try {
|
|
196
|
-
await prepareManagedCodexHome(env, async (_stream, chunk) => {
|
|
197
|
-
logs.push(chunk);
|
|
198
|
-
}, "company-1");
|
|
199
|
-
await expect(fs.readFile(managedConfig, "utf8")).resolves.toBe('requires_openai_auth = false\n');
|
|
200
|
-
expect(logs.join("")).toContain("refreshed config.toml from shared config");
|
|
201
|
-
// A second run with an unchanged source should not report another refresh.
|
|
202
|
-
logs.length = 0;
|
|
203
|
-
await prepareManagedCodexHome(env, async (_stream, chunk) => {
|
|
204
|
-
logs.push(chunk);
|
|
205
|
-
}, "company-1");
|
|
206
|
-
expect(logs.join("")).not.toContain("refreshed config.toml");
|
|
207
|
-
}
|
|
208
|
-
finally {
|
|
209
|
-
await fs.rm(root, { recursive: true, force: true });
|
|
210
|
-
}
|
|
211
|
-
});
|
|
212
|
-
it("copies auth.json when symlink creation is not permitted", async () => {
|
|
213
|
-
const root = await fs.mkdtemp(path.join(os.tmpdir(), "paperclip-codex-home-"));
|
|
214
|
-
const sharedCodexHome = path.join(root, "shared-codex-home");
|
|
215
|
-
const paperclipHome = path.join(root, "paperclip-home");
|
|
216
|
-
const managedCodexHome = path.join(paperclipHome, "instances", "default", "companies", "company-1", "codex-home");
|
|
217
|
-
const sharedAuth = path.join(sharedCodexHome, "auth.json");
|
|
218
|
-
const managedAuth = path.join(managedCodexHome, "auth.json");
|
|
219
|
-
const logs = [];
|
|
220
|
-
await fs.mkdir(sharedCodexHome, { recursive: true });
|
|
221
|
-
await fs.writeFile(sharedAuth, '{"token":"shared"}\n', "utf8");
|
|
222
|
-
vi.spyOn(fs, "symlink").mockImplementationOnce(async () => {
|
|
223
|
-
const error = new Error("operation not permitted");
|
|
224
|
-
error.code = "EPERM";
|
|
225
|
-
throw error;
|
|
226
|
-
});
|
|
227
|
-
try {
|
|
228
|
-
await expect(prepareManagedCodexHome({
|
|
229
|
-
CODEX_HOME: sharedCodexHome,
|
|
230
|
-
PAPERCLIP_HOME: paperclipHome,
|
|
231
|
-
PAPERCLIP_INSTANCE_ID: "default",
|
|
232
|
-
}, async (_stream, chunk) => {
|
|
233
|
-
logs.push(chunk);
|
|
234
|
-
}, "company-1")).resolves.toBe(managedCodexHome);
|
|
235
|
-
expect((await fs.lstat(managedAuth)).isSymbolicLink()).toBe(false);
|
|
236
|
-
await expect(fs.readFile(managedAuth, "utf8")).resolves.toBe('{"token":"shared"}\n');
|
|
237
|
-
expect(logs.join("")).toContain("copied auth.json because symlinks are unavailable");
|
|
238
|
-
}
|
|
239
|
-
finally {
|
|
240
|
-
await fs.rm(root, { recursive: true, force: true });
|
|
241
|
-
}
|
|
242
|
-
});
|
|
243
|
-
});
|
|
244
|
-
//# sourceMappingURL=codex-home.test.js.map
|