@parall/codex-agent 1.31.0 → 1.32.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.
package/src/workspace.ts CHANGED
@@ -1,6 +1,6 @@
1
- import * as fs from "node:fs";
2
- import * as path from "node:path";
3
- import { parse as parseToml } from "smol-toml";
1
+ import * as fs from 'node:fs';
2
+ import * as path from 'node:path';
3
+ import { parse as parseToml } from 'smol-toml';
4
4
  import {
5
5
  BRIDGE_WORKSPACE_INSTRUCTIONS,
6
6
  PRLL_BEHAVIOR,
@@ -8,8 +8,8 @@ import {
8
8
  buildIdentity,
9
9
  buildSkillReferences,
10
10
  writeSkillFiles,
11
- } from "@parall/agent-core";
12
- import type { AgentIdentity } from "@parall/agent-core";
11
+ } from '@parall/agent-core';
12
+ import type { AgentIdentity } from '@parall/agent-core';
13
13
 
14
14
  /**
15
15
  * Ensure the workspace directory is marked as trusted in the global Codex
@@ -26,15 +26,15 @@ export function ensureWorkspaceTrusted(
26
26
  workspaceDir: string,
27
27
  log?: { warn: (msg: string) => void },
28
28
  ): void {
29
- const configPath = path.join(codexHome, "config.toml");
29
+ const configPath = path.join(codexHome, 'config.toml');
30
30
  const normalizedPath = path.resolve(workspaceDir);
31
31
 
32
32
  try {
33
- let content = "";
33
+ let content = '';
34
34
  try {
35
- content = fs.readFileSync(configPath, "utf8");
35
+ content = fs.readFileSync(configPath, 'utf8');
36
36
  } catch (err) {
37
- if ((err as NodeJS.ErrnoException).code !== "ENOENT") throw err;
37
+ if ((err as NodeJS.ErrnoException).code !== 'ENOENT') throw err;
38
38
  }
39
39
 
40
40
  // Structured read — know exactly what state we're in.
@@ -44,16 +44,18 @@ export function ensureWorkspaceTrusted(
44
44
  parsed = parseToml(content) as Record<string, unknown>;
45
45
  } catch {
46
46
  // File is already broken TOML — don't make it worse.
47
- log?.warn(`Codex config.toml is not valid TOML; skipping trust write for ${normalizedPath}`);
47
+ log?.warn(
48
+ `Codex config.toml is not valid TOML; skipping trust write for ${normalizedPath}`,
49
+ );
48
50
  return;
49
51
  }
50
52
  }
51
53
 
52
54
  const projects = parsed?.projects as Record<string, Record<string, unknown>> | undefined;
53
- if (projects?.[normalizedPath]?.trust_level === "trusted") return;
55
+ if (projects?.[normalizedPath]?.trust_level === 'trusted') return;
54
56
 
55
57
  // TOML basic-string keys require backslash and double-quote escaping.
56
- const escapedPath = normalizedPath.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
58
+ const escapedPath = normalizedPath.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
57
59
  const sectionHeader = `[projects."${escapedPath}"]`;
58
60
  const trustLine = 'trust_level = "trusted"';
59
61
 
@@ -63,7 +65,7 @@ export function ensureWorkspaceTrusted(
63
65
  // within the section (up to the next `[` header or EOF) for an
64
66
  // existing trust_level key — it may not be the first line after
65
67
  // the header if the user added comments or other keys.
66
- const headerLineEnd = content.indexOf("\n", headerIdx);
68
+ const headerLineEnd = content.indexOf('\n', headerIdx);
67
69
  if (headerLineEnd === -1) {
68
70
  content = `${content}\n${trustLine}\n`;
69
71
  } else {
@@ -76,24 +78,29 @@ export function ensureWorkspaceTrusted(
76
78
  const matchEnd = matchStart + trustMatch[0].length;
77
79
  content = content.substring(0, matchStart) + trustLine + content.substring(matchEnd);
78
80
  } else {
79
- content = content.substring(0, afterHeader) + trustLine + "\n" + content.substring(afterHeader);
81
+ content =
82
+ content.substring(0, afterHeader) + trustLine + '\n' + content.substring(afterHeader);
80
83
  }
81
84
  }
82
- fs.writeFileSync(configPath, content, "utf8");
85
+ fs.writeFileSync(configPath, content, 'utf8');
83
86
  } else if (projects?.[normalizedPath] !== undefined) {
84
87
  // smol-toml found the section but indexOf missed it — the header
85
88
  // uses non-canonical TOML formatting. Appending would create a
86
89
  // duplicate table. Skip rather than corrupt the file.
87
- log?.warn(`Codex config.toml has non-canonical header for ${normalizedPath}; skipping trust write`);
88
- } else if (projects && !content.includes("[projects.")) {
90
+ log?.warn(
91
+ `Codex config.toml has non-canonical header for ${normalizedPath}; skipping trust write`,
92
+ );
93
+ } else if (projects && !content.includes('[projects.')) {
89
94
  // `projects` exists in parsed output but no `[projects.` table
90
95
  // headers in the raw text — it's an inline table. Appending a
91
96
  // standard table header would produce invalid TOML.
92
- log?.warn(`Codex config.toml uses inline table for projects; skipping trust write for ${normalizedPath}`);
97
+ log?.warn(
98
+ `Codex config.toml uses inline table for projects; skipping trust write for ${normalizedPath}`,
99
+ );
93
100
  } else {
94
101
  // Section doesn't exist — append.
95
102
  fs.mkdirSync(codexHome, { recursive: true });
96
- fs.appendFileSync(configPath, `\n${sectionHeader}\n${trustLine}\n`, "utf8");
103
+ fs.appendFileSync(configPath, `\n${sectionHeader}\n${trustLine}\n`, 'utf8');
97
104
  }
98
105
  } catch (err) {
99
106
  log?.warn(`failed to write Codex project trust for ${normalizedPath}: ${String(err)}`);
@@ -106,8 +113,8 @@ export function ensureWorkspaceTrusted(
106
113
  * whether OPENAI_BASE_URL points to the Parall API.
107
114
  */
108
115
  export function isParallProxyMode(env: NodeJS.ProcessEnv = process.env): boolean {
109
- const baseUrl = env.OPENAI_BASE_URL?.trim()?.replace(/\/+$/, "");
110
- const apiUrl = env.PRLL_API_URL?.trim()?.replace(/\/+$/, "");
116
+ const baseUrl = env.OPENAI_BASE_URL?.trim()?.replace(/\/+$/, '');
117
+ const apiUrl = env.PRLL_API_URL?.trim()?.replace(/\/+$/, '');
111
118
  if (!baseUrl || !apiUrl) return false;
112
119
  return baseUrl === apiUrl || baseUrl.startsWith(`${apiUrl}/`);
113
120
  }
@@ -131,47 +138,47 @@ export function ensureParallProvider(
131
138
  apiUrl: string,
132
139
  log?: { warn: (msg: string) => void },
133
140
  ): void {
134
- const configPath = path.join(codexHome, "config.toml");
135
- const baseUrl = apiUrl.replace(/\/$/, "") + "/api/llm/v1";
141
+ const configPath = path.join(codexHome, 'config.toml');
142
+ const baseUrl = apiUrl.replace(/\/$/, '') + '/api/llm/v1';
136
143
 
137
144
  try {
138
- let content = "";
145
+ let content = '';
139
146
  try {
140
- content = fs.readFileSync(configPath, "utf8");
147
+ content = fs.readFileSync(configPath, 'utf8');
141
148
  } catch (err) {
142
- if ((err as NodeJS.ErrnoException).code !== "ENOENT") throw err;
149
+ if ((err as NodeJS.ErrnoException).code !== 'ENOENT') throw err;
143
150
  }
144
151
 
145
- const sectionHeader = "[model_providers.parall]";
146
- const authHeader = "[model_providers.parall.auth]";
152
+ const sectionHeader = '[model_providers.parall]';
153
+ const authHeader = '[model_providers.parall.auth]';
147
154
  const providerBlock = [
148
155
  sectionHeader,
149
156
  'name = "Parall Proxy"',
150
157
  `base_url = "${baseUrl}"`,
151
158
  'wire_api = "responses"',
152
- "supports_websockets = false",
153
- "requires_openai_auth = false",
154
- "",
159
+ 'supports_websockets = false',
160
+ 'requires_openai_auth = false',
161
+ '',
155
162
  authHeader,
156
163
  'command = "printenv"',
157
164
  'args = ["OPENAI_API_KEY"]',
158
- ].join("\n");
165
+ ].join('\n');
159
166
 
160
167
  const headerIdx = content.indexOf(sectionHeader);
161
168
  if (headerIdx !== -1) {
162
169
  let blockEnd = findSectionEnd(content, headerIdx + sectionHeader.length + 1);
163
170
  let authIdx = content.indexOf(authHeader, blockEnd);
164
- while (authIdx !== -1 && content.substring(blockEnd, authIdx).trim() === "") {
171
+ while (authIdx !== -1 && content.substring(blockEnd, authIdx).trim() === '') {
165
172
  blockEnd = findSectionEnd(content, authIdx + authHeader.length + 1);
166
173
  authIdx = content.indexOf(authHeader, blockEnd);
167
174
  }
168
175
  content = content.substring(0, headerIdx) + providerBlock + content.substring(blockEnd);
169
176
  } else {
170
- content = content.trimEnd() + "\n\n" + providerBlock + "\n";
177
+ content = content.trimEnd() + '\n\n' + providerBlock + '\n';
171
178
  }
172
179
 
173
180
  fs.mkdirSync(codexHome, { recursive: true });
174
- fs.writeFileSync(configPath, content, "utf8");
181
+ fs.writeFileSync(configPath, content, 'utf8');
175
182
  } catch (err) {
176
183
  throw new Error(`failed to write Parall provider config: ${String(err)}`);
177
184
  }
@@ -180,7 +187,7 @@ export function ensureParallProvider(
180
187
  function findSectionEnd(content: string, fromIndex: number): number {
181
188
  // A TOML section ends at the next `[` that starts a new table header.
182
189
  // Match `[` at the beginning of a line (not inside a value).
183
- const nextHeader = content.indexOf("\n[", fromIndex);
190
+ const nextHeader = content.indexOf('\n[', fromIndex);
184
191
  return nextHeader === -1 ? content.length : nextHeader;
185
192
  }
186
193
 
@@ -195,18 +202,18 @@ export function ensureCodexWorkspace(
195
202
  PRLL_BEHAVIOR,
196
203
  PRLL_REFERENCE_GUIDE,
197
204
  buildSkillReferences(workspaceDir),
198
- ].join("\n\n");
205
+ ].join('\n\n');
199
206
 
200
207
  fs.mkdirSync(workspaceDir, { recursive: true });
201
208
 
202
- const parallDir = path.join(workspaceDir, ".parall");
209
+ const parallDir = path.join(workspaceDir, '.parall');
203
210
  fs.mkdirSync(parallDir, { recursive: true });
204
- fs.writeFileSync(path.join(parallDir, "system-prompt.md"), systemPrompt, "utf8");
211
+ fs.writeFileSync(path.join(parallDir, 'system-prompt.md'), systemPrompt, 'utf8');
205
212
 
206
- writeSkillFiles(path.join(parallDir, "skills"));
213
+ writeSkillFiles(path.join(parallDir, 'skills'));
207
214
 
208
- const codexConfigDir = path.join(workspaceDir, ".codex");
215
+ const codexConfigDir = path.join(workspaceDir, '.codex');
209
216
  fs.mkdirSync(codexConfigDir, { recursive: true });
210
- const toml = `developer_instructions = """\n${systemPrompt.replace(/\\/g, "\\\\").replace(/"""/g, '\\"""')}\n"""\n`;
211
- fs.writeFileSync(path.join(codexConfigDir, "config.toml"), toml, "utf8");
217
+ const toml = `developer_instructions = """\n${systemPrompt.replace(/\\/g, '\\\\').replace(/"""/g, '\\"""')}\n"""\n`;
218
+ fs.writeFileSync(path.join(codexConfigDir, 'config.toml'), toml, 'utf8');
212
219
  }