@parall/codex-agent 1.30.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/dist/config.d.ts.map +1 -1
- package/dist/config.js +34 -35
- package/dist/dispatch.d.ts +6 -5
- package/dist/dispatch.d.ts.map +1 -1
- package/dist/dispatch.js +102 -82
- package/dist/event-mapping.d.ts +1 -1
- package/dist/event-mapping.d.ts.map +1 -1
- package/dist/event-mapping.js +102 -82
- package/dist/index.js +169 -114
- package/dist/jsonrpc-client.d.ts +4 -4
- package/dist/jsonrpc-client.d.ts.map +1 -1
- package/dist/jsonrpc-client.js +15 -15
- package/dist/session-manager.d.ts +1 -1
- package/dist/session-manager.d.ts.map +1 -1
- package/dist/session-manager.js +7 -5
- package/dist/workspace.d.ts +1 -1
- package/dist/workspace.d.ts.map +1 -1
- package/dist/workspace.js +40 -39
- package/package.json +4 -4
- package/src/config.ts +41 -36
- package/src/dispatch.ts +140 -110
- package/src/event-mapping.ts +135 -109
- package/src/index.ts +199 -117
- package/src/jsonrpc-client.ts +22 -20
- package/src/session-manager.ts +10 -6
- package/src/workspace.ts +50 -43
package/src/workspace.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import * as fs from
|
|
2
|
-
import * as path from
|
|
3
|
-
import { parse as parseToml } from
|
|
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
|
|
12
|
-
import type { AgentIdentity } from
|
|
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,
|
|
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,
|
|
35
|
+
content = fs.readFileSync(configPath, 'utf8');
|
|
36
36
|
} catch (err) {
|
|
37
|
-
if ((err as NodeJS.ErrnoException).code !==
|
|
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(
|
|
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 ===
|
|
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,
|
|
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(
|
|
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 =
|
|
81
|
+
content =
|
|
82
|
+
content.substring(0, afterHeader) + trustLine + '\n' + content.substring(afterHeader);
|
|
80
83
|
}
|
|
81
84
|
}
|
|
82
|
-
fs.writeFileSync(configPath, content,
|
|
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(
|
|
88
|
-
|
|
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(
|
|
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`,
|
|
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,
|
|
135
|
-
const baseUrl = apiUrl.replace(/\/$/,
|
|
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,
|
|
147
|
+
content = fs.readFileSync(configPath, 'utf8');
|
|
141
148
|
} catch (err) {
|
|
142
|
-
if ((err as NodeJS.ErrnoException).code !==
|
|
149
|
+
if ((err as NodeJS.ErrnoException).code !== 'ENOENT') throw err;
|
|
143
150
|
}
|
|
144
151
|
|
|
145
|
-
const sectionHeader =
|
|
146
|
-
const authHeader =
|
|
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
|
-
|
|
153
|
-
|
|
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(
|
|
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() +
|
|
177
|
+
content = content.trimEnd() + '\n\n' + providerBlock + '\n';
|
|
171
178
|
}
|
|
172
179
|
|
|
173
180
|
fs.mkdirSync(codexHome, { recursive: true });
|
|
174
|
-
fs.writeFileSync(configPath, content,
|
|
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(
|
|
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(
|
|
205
|
+
].join('\n\n');
|
|
199
206
|
|
|
200
207
|
fs.mkdirSync(workspaceDir, { recursive: true });
|
|
201
208
|
|
|
202
|
-
const parallDir = path.join(workspaceDir,
|
|
209
|
+
const parallDir = path.join(workspaceDir, '.parall');
|
|
203
210
|
fs.mkdirSync(parallDir, { recursive: true });
|
|
204
|
-
fs.writeFileSync(path.join(parallDir,
|
|
211
|
+
fs.writeFileSync(path.join(parallDir, 'system-prompt.md'), systemPrompt, 'utf8');
|
|
205
212
|
|
|
206
|
-
writeSkillFiles(path.join(parallDir,
|
|
213
|
+
writeSkillFiles(path.join(parallDir, 'skills'));
|
|
207
214
|
|
|
208
|
-
const codexConfigDir = path.join(workspaceDir,
|
|
215
|
+
const codexConfigDir = path.join(workspaceDir, '.codex');
|
|
209
216
|
fs.mkdirSync(codexConfigDir, { recursive: true });
|
|
210
|
-
const toml = `developer_instructions = """\n${systemPrompt.replace(/\\/g,
|
|
211
|
-
fs.writeFileSync(path.join(codexConfigDir,
|
|
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
|
}
|