@aikdna/kdna-studio-cli 0.11.0 → 0.13.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/README.md +44 -269
- package/bin/kdna-studio.js +6 -3074
- package/docs/CREATION_COMMAND_CONTRACT.md +18 -223
- package/docs/TERMINAL_AGENT_CREATION.md +23 -206
- package/package.json +32 -29
- package/src/component-operations.js +13 -0
- package/src/public-bindings.json +8806 -0
- package/src/terminal-workspace.js +507 -0
- package/src/ai/distill.js +0 -96
- package/src/ai/filter.js +0 -70
- package/src/ai/index.js +0 -4
- package/src/ai/interview.js +0 -170
- package/src/application-host.js +0 -561
- package/src/creation-cli.js +0 -7721
- package/src/llm/config.js +0 -98
- package/src/llm/index.js +0 -50
- package/src/llm/providers/anthropic.js +0 -41
- package/src/llm/providers/openai.js +0 -42
- package/src/llm/transport.js +0 -196
package/src/application-host.js
DELETED
|
@@ -1,561 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const crypto = require('node:crypto');
|
|
4
|
-
const fs = require('node:fs');
|
|
5
|
-
const os = require('node:os');
|
|
6
|
-
const path = require('node:path');
|
|
7
|
-
const { spawnSync } = require('node:child_process');
|
|
8
|
-
|
|
9
|
-
const MAX_AGENT_INPUT_BYTES = 2 * 1024 * 1024;
|
|
10
|
-
const MAX_AGENT_SCHEMA_BYTES = 512 * 1024;
|
|
11
|
-
const MAX_AGENT_OUTPUT_BYTES = 8 * 1024 * 1024;
|
|
12
|
-
const MAX_AUTH_BYTES = 2 * 1024 * 1024;
|
|
13
|
-
const DISABLED_CODEX_FEATURES = Object.freeze([
|
|
14
|
-
'apps',
|
|
15
|
-
'browser_use',
|
|
16
|
-
'browser_use_external',
|
|
17
|
-
'browser_use_full_cdp_access',
|
|
18
|
-
'code_mode_host',
|
|
19
|
-
'computer_use',
|
|
20
|
-
'enable_fanout',
|
|
21
|
-
'enable_mcp_apps',
|
|
22
|
-
'hooks',
|
|
23
|
-
'image_generation',
|
|
24
|
-
'in_app_browser',
|
|
25
|
-
'multi_agent',
|
|
26
|
-
'multi_agent_v2',
|
|
27
|
-
'plugins',
|
|
28
|
-
'remote_plugin',
|
|
29
|
-
'shell_snapshot',
|
|
30
|
-
'shell_tool',
|
|
31
|
-
'shell_zsh_fork',
|
|
32
|
-
'standalone_web_search',
|
|
33
|
-
'tool_suggest',
|
|
34
|
-
'unified_exec',
|
|
35
|
-
'unified_exec_zsh_fork',
|
|
36
|
-
'workspace_dependencies',
|
|
37
|
-
]);
|
|
38
|
-
|
|
39
|
-
class ApplicationHostError extends Error {
|
|
40
|
-
constructor(code, message) {
|
|
41
|
-
super(message);
|
|
42
|
-
this.code = code;
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function sha256(value) {
|
|
47
|
-
return `sha256:${crypto
|
|
48
|
-
.createHash('sha256')
|
|
49
|
-
.update(value)
|
|
50
|
-
.digest('hex')}`;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
function sandboxLiteral(value) {
|
|
54
|
-
return JSON.stringify(path.resolve(value));
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
function sandboxProfile({ nativeCommand, roleRoot, deniedPaths }) {
|
|
58
|
-
const temporaryRoot = fs.realpathSync(os.tmpdir());
|
|
59
|
-
const explicitDenies = [...new Set(
|
|
60
|
-
deniedPaths
|
|
61
|
-
.filter(Boolean)
|
|
62
|
-
.map((entry) => path.resolve(entry))
|
|
63
|
-
.filter((entry) => !entry.startsWith(`${roleRoot}${path.sep}`)),
|
|
64
|
-
)].map((entry) => (
|
|
65
|
-
`(deny file-read* (subpath ${sandboxLiteral(entry)}))`
|
|
66
|
-
));
|
|
67
|
-
return [
|
|
68
|
-
'(version 1)',
|
|
69
|
-
'(deny default)',
|
|
70
|
-
// A model-generated subprocess cannot start. Only the already selected
|
|
71
|
-
// native Codex host executable may execute.
|
|
72
|
-
`(allow process-exec (literal ${sandboxLiteral(nativeCommand)}))`,
|
|
73
|
-
`(allow file-read* (literal ${sandboxLiteral(nativeCommand)}))`,
|
|
74
|
-
// The dynamic linker and system frameworks required to load the selected
|
|
75
|
-
// native executable.
|
|
76
|
-
'(allow file-read* (subpath "/usr/lib"))',
|
|
77
|
-
'(allow file-read* (subpath "/System/Library"))',
|
|
78
|
-
'(allow file-read* (subpath "/Library/Apple/System/Library"))',
|
|
79
|
-
'(allow file-read* (subpath "/opt/homebrew"))',
|
|
80
|
-
'(allow file-read-metadata)',
|
|
81
|
-
// The host resolves the filesystem root; only that root node is readable.
|
|
82
|
-
'(allow file-read-data (literal "/"))',
|
|
83
|
-
// Standard device and system configuration nodes the host may touch.
|
|
84
|
-
'(allow file-read* (subpath "/dev"))',
|
|
85
|
-
'(allow file-read* (subpath "/private/dev"))',
|
|
86
|
-
'(allow file-read* (subpath "/private/etc"))',
|
|
87
|
-
'(allow file-read* (subpath "/etc"))',
|
|
88
|
-
// The adapter may read and write only inside this role's disposable 0700
|
|
89
|
-
// root.
|
|
90
|
-
`(allow file-read* (subpath ${sandboxLiteral(roleRoot)}))`,
|
|
91
|
-
`(allow file-write* (subpath ${sandboxLiteral(roleRoot)}))`,
|
|
92
|
-
// The selected Host runtime writes transient session state under the
|
|
93
|
-
// system temporary directories; user data, project files, and credentials
|
|
94
|
-
// remain outside the write namespace.
|
|
95
|
-
'(allow file-write* (subpath "/private/var/folders"))',
|
|
96
|
-
'(allow file-write* (subpath "/private/tmp"))',
|
|
97
|
-
'(allow file-write* (subpath "/var"))',
|
|
98
|
-
// Process, system, network, IPC and logging primitives the isolated host
|
|
99
|
-
// needs to reach its declared service.
|
|
100
|
-
'(allow process-fork)',
|
|
101
|
-
'(allow sysctl-read)',
|
|
102
|
-
'(allow network-outbound)',
|
|
103
|
-
'(allow mach-lookup)',
|
|
104
|
-
'(allow ipc-posix-shm)',
|
|
105
|
-
'(allow ipc-posix-sem)',
|
|
106
|
-
'(allow signal)',
|
|
107
|
-
// User data, credentials, project files, and unrelated temporary runs are
|
|
108
|
-
// outside the role's readable namespace.
|
|
109
|
-
'(deny file-read* (subpath "/Users"))',
|
|
110
|
-
'(deny file-read* (subpath "/private/tmp"))',
|
|
111
|
-
`(deny file-read-data (require-all (subpath ${sandboxLiteral(temporaryRoot)}) (require-not (subpath ${sandboxLiteral(roleRoot)}))))`,
|
|
112
|
-
...explicitDenies,
|
|
113
|
-
].join('\n');
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
function makePrivateRoot(prefix) {
|
|
117
|
-
const root = fs.mkdtempSync(path.join(os.tmpdir(), prefix));
|
|
118
|
-
fs.chmodSync(root, 0o700);
|
|
119
|
-
return fs.realpathSync(root);
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
function writePrivateBytes(target, bytes) {
|
|
123
|
-
const descriptor = fs.openSync(
|
|
124
|
-
target,
|
|
125
|
-
fs.constants.O_CREAT |
|
|
126
|
-
fs.constants.O_EXCL |
|
|
127
|
-
fs.constants.O_WRONLY,
|
|
128
|
-
0o600,
|
|
129
|
-
);
|
|
130
|
-
try {
|
|
131
|
-
fs.writeFileSync(descriptor, bytes);
|
|
132
|
-
} finally {
|
|
133
|
-
fs.closeSync(descriptor);
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
function writePrivateJson(target, value) {
|
|
138
|
-
const bytes = Buffer.from(JSON.stringify(value));
|
|
139
|
-
try {
|
|
140
|
-
writePrivateBytes(target, bytes);
|
|
141
|
-
} finally {
|
|
142
|
-
bytes.fill(0);
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
function createPrivateOutput(target) {
|
|
147
|
-
const descriptor = fs.openSync(
|
|
148
|
-
target,
|
|
149
|
-
fs.constants.O_CREAT |
|
|
150
|
-
fs.constants.O_EXCL |
|
|
151
|
-
fs.constants.O_WRONLY,
|
|
152
|
-
0o600,
|
|
153
|
-
);
|
|
154
|
-
fs.closeSync(descriptor);
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
function readPrivateJson(target) {
|
|
158
|
-
const stat = fs.lstatSync(target);
|
|
159
|
-
if (!stat.isFile() || stat.isSymbolicLink()) {
|
|
160
|
-
throw new ApplicationHostError(
|
|
161
|
-
'application_host_output_invalid',
|
|
162
|
-
'The isolated Host did not produce a regular structured output file.',
|
|
163
|
-
);
|
|
164
|
-
}
|
|
165
|
-
if ((stat.mode & 0o077) !== 0 || stat.size > MAX_AGENT_OUTPUT_BYTES) {
|
|
166
|
-
throw new ApplicationHostError(
|
|
167
|
-
'application_host_output_invalid',
|
|
168
|
-
'The isolated Host output permissions or size are invalid.',
|
|
169
|
-
);
|
|
170
|
-
}
|
|
171
|
-
const bytes = fs.readFileSync(target);
|
|
172
|
-
try {
|
|
173
|
-
return JSON.parse(bytes.toString('utf8'));
|
|
174
|
-
} catch {
|
|
175
|
-
throw new ApplicationHostError(
|
|
176
|
-
'application_host_output_invalid',
|
|
177
|
-
'The isolated Host did not return the required structured JSON.',
|
|
178
|
-
);
|
|
179
|
-
} finally {
|
|
180
|
-
bytes.fill(0);
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
function executableFromPath(command, environmentPath = process.env.PATH) {
|
|
185
|
-
if (path.isAbsolute(command)) return path.resolve(command);
|
|
186
|
-
for (const directory of String(environmentPath || '').split(path.delimiter)) {
|
|
187
|
-
if (!directory) continue;
|
|
188
|
-
const candidate = path.join(directory, command);
|
|
189
|
-
try {
|
|
190
|
-
fs.accessSync(candidate, fs.constants.X_OK);
|
|
191
|
-
return fs.realpathSync(candidate);
|
|
192
|
-
} catch {
|
|
193
|
-
// Continue through PATH without invoking a shell.
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
throw new ApplicationHostError(
|
|
197
|
-
'application_host_unavailable',
|
|
198
|
-
'The Codex Host adapter is not installed.',
|
|
199
|
-
);
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
function nativeCodexExecutable(command) {
|
|
203
|
-
const executable = executableFromPath(command);
|
|
204
|
-
const stat = fs.lstatSync(executable);
|
|
205
|
-
if (!stat.isFile() || stat.isSymbolicLink()) {
|
|
206
|
-
throw new ApplicationHostError(
|
|
207
|
-
'application_host_unavailable',
|
|
208
|
-
'The Codex Host command is not a regular executable.',
|
|
209
|
-
);
|
|
210
|
-
}
|
|
211
|
-
const prefix = Buffer.alloc(4);
|
|
212
|
-
let descriptor;
|
|
213
|
-
try {
|
|
214
|
-
descriptor = fs.openSync(executable, 'r');
|
|
215
|
-
fs.readSync(descriptor, prefix, 0, prefix.length, 0);
|
|
216
|
-
} finally {
|
|
217
|
-
if (descriptor !== undefined) fs.closeSync(descriptor);
|
|
218
|
-
}
|
|
219
|
-
const isMachO = [
|
|
220
|
-
'feedface',
|
|
221
|
-
'cefaedfe',
|
|
222
|
-
'feedfacf',
|
|
223
|
-
'cffaedfe',
|
|
224
|
-
'cafebabe',
|
|
225
|
-
'bebafeca',
|
|
226
|
-
].includes(prefix.toString('hex'));
|
|
227
|
-
prefix.fill(0);
|
|
228
|
-
if (isMachO) return executable;
|
|
229
|
-
|
|
230
|
-
// The official npm command is a small JS launcher. Resolve its packaged
|
|
231
|
-
// native binary once, then execute only that binary inside the sandbox.
|
|
232
|
-
const packageRoot = path.resolve(path.dirname(executable), '..');
|
|
233
|
-
const modulesRoot = path.join(packageRoot, 'node_modules', '@openai');
|
|
234
|
-
let packages;
|
|
235
|
-
try {
|
|
236
|
-
packages = fs.readdirSync(modulesRoot);
|
|
237
|
-
} catch {
|
|
238
|
-
throw new ApplicationHostError(
|
|
239
|
-
'application_host_unavailable',
|
|
240
|
-
'The installed Codex command does not expose a packaged native executable.',
|
|
241
|
-
);
|
|
242
|
-
}
|
|
243
|
-
const candidates = [];
|
|
244
|
-
for (const packageName of packages) {
|
|
245
|
-
if (!packageName.startsWith('codex-')) continue;
|
|
246
|
-
const vendor = path.join(modulesRoot, packageName, 'vendor');
|
|
247
|
-
let triples = [];
|
|
248
|
-
try {
|
|
249
|
-
triples = fs.readdirSync(vendor);
|
|
250
|
-
} catch {
|
|
251
|
-
continue;
|
|
252
|
-
}
|
|
253
|
-
for (const triple of triples) {
|
|
254
|
-
const candidate = path.join(vendor, triple, 'bin', 'codex');
|
|
255
|
-
try {
|
|
256
|
-
const candidateStat = fs.lstatSync(candidate);
|
|
257
|
-
fs.accessSync(candidate, fs.constants.X_OK);
|
|
258
|
-
if (candidateStat.isFile() && !candidateStat.isSymbolicLink()) {
|
|
259
|
-
candidates.push(fs.realpathSync(candidate));
|
|
260
|
-
}
|
|
261
|
-
} catch {
|
|
262
|
-
// Ignore packages for another platform.
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
if (candidates.length !== 1) {
|
|
267
|
-
throw new ApplicationHostError(
|
|
268
|
-
'application_host_unavailable',
|
|
269
|
-
'The installed Codex command does not resolve to one native executable for this Host.',
|
|
270
|
-
);
|
|
271
|
-
}
|
|
272
|
-
return candidates[0];
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
function authSourcePath(options) {
|
|
276
|
-
if (options.authPath) return path.resolve(options.authPath);
|
|
277
|
-
const codexHome = process.env.CODEX_HOME
|
|
278
|
-
? path.resolve(process.env.CODEX_HOME)
|
|
279
|
-
: path.join(os.homedir(), '.codex');
|
|
280
|
-
return path.join(codexHome, 'auth.json');
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
function installEphemeralAuth(roleRoot, options) {
|
|
284
|
-
const source = authSourcePath(options);
|
|
285
|
-
let stat;
|
|
286
|
-
try {
|
|
287
|
-
stat = fs.lstatSync(source);
|
|
288
|
-
} catch {
|
|
289
|
-
throw new ApplicationHostError(
|
|
290
|
-
'application_host_auth_unavailable',
|
|
291
|
-
'The exact Codex Host has no installed authentication substrate.',
|
|
292
|
-
);
|
|
293
|
-
}
|
|
294
|
-
if (
|
|
295
|
-
!stat.isFile() ||
|
|
296
|
-
stat.isSymbolicLink() ||
|
|
297
|
-
stat.size <= 0 ||
|
|
298
|
-
stat.size > MAX_AUTH_BYTES
|
|
299
|
-
) {
|
|
300
|
-
throw new ApplicationHostError(
|
|
301
|
-
'application_host_auth_unavailable',
|
|
302
|
-
'The installed Codex authentication substrate is not a bounded regular file.',
|
|
303
|
-
);
|
|
304
|
-
}
|
|
305
|
-
const codexHome = path.join(roleRoot, 'codex-home');
|
|
306
|
-
fs.mkdirSync(codexHome, { mode: 0o700 });
|
|
307
|
-
const bytes = fs.readFileSync(source);
|
|
308
|
-
try {
|
|
309
|
-
writePrivateBytes(path.join(codexHome, 'auth.json'), bytes);
|
|
310
|
-
} finally {
|
|
311
|
-
bytes.fill(0);
|
|
312
|
-
}
|
|
313
|
-
return codexHome;
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
function codexCoordinate(nativeCommand, run = spawnSync) {
|
|
317
|
-
const result = run(nativeCommand, ['--version'], {
|
|
318
|
-
encoding: null,
|
|
319
|
-
timeout: 10_000,
|
|
320
|
-
maxBuffer: 64 * 1024,
|
|
321
|
-
env: {
|
|
322
|
-
HOME: os.tmpdir(),
|
|
323
|
-
PATH: '/usr/bin:/bin',
|
|
324
|
-
NO_COLOR: '1',
|
|
325
|
-
},
|
|
326
|
-
});
|
|
327
|
-
const stdout = Buffer.isBuffer(result.stdout)
|
|
328
|
-
? result.stdout
|
|
329
|
-
: Buffer.from(result.stdout || '');
|
|
330
|
-
const stderr = Buffer.isBuffer(result.stderr)
|
|
331
|
-
? result.stderr
|
|
332
|
-
: Buffer.from(result.stderr || '');
|
|
333
|
-
try {
|
|
334
|
-
if (result.status !== 0) {
|
|
335
|
-
throw new ApplicationHostError(
|
|
336
|
-
'application_host_unavailable',
|
|
337
|
-
'The Codex Host adapter could not report its version.',
|
|
338
|
-
);
|
|
339
|
-
}
|
|
340
|
-
return {
|
|
341
|
-
adapter: 'kdna.studio.application-host/codex-exec',
|
|
342
|
-
host: stdout.toString('utf8').trim(),
|
|
343
|
-
isolation: 'macos-sandbox-tool-less-role-root',
|
|
344
|
-
};
|
|
345
|
-
} finally {
|
|
346
|
-
stdout.fill(0);
|
|
347
|
-
stderr.fill(0);
|
|
348
|
-
}
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
function createCodexApplicationHost(options = {}) {
|
|
352
|
-
if (process.platform !== 'darwin') {
|
|
353
|
-
throw new ApplicationHostError(
|
|
354
|
-
'application_host_capability_isolation_unavailable',
|
|
355
|
-
'This Codex application adapter currently requires the macOS capability sandbox.',
|
|
356
|
-
);
|
|
357
|
-
}
|
|
358
|
-
const command = options.command || 'codex';
|
|
359
|
-
const run = options.spawnSync || spawnSync;
|
|
360
|
-
const nativeCommand =
|
|
361
|
-
options.nativeCommand || nativeCodexExecutable(command);
|
|
362
|
-
const deniedPaths = Array.isArray(options.deniedPaths)
|
|
363
|
-
? options.deniedPaths
|
|
364
|
-
: [];
|
|
365
|
-
const coordinate = codexCoordinate(nativeCommand, run);
|
|
366
|
-
const isolationPolicy = {
|
|
367
|
-
schema: 'kdna.studio.application-host-isolation/0.1.0',
|
|
368
|
-
tool_features_disabled: DISABLED_CODEX_FEATURES,
|
|
369
|
-
child_processes: 'deny',
|
|
370
|
-
role_filesystem: 'disposable-role-root-only',
|
|
371
|
-
inherited_environment: 'deny',
|
|
372
|
-
};
|
|
373
|
-
const runnerDigest = sha256(JSON.stringify({
|
|
374
|
-
coordinate,
|
|
375
|
-
isolation_policy_digest: sha256(JSON.stringify(isolationPolicy)),
|
|
376
|
-
}));
|
|
377
|
-
|
|
378
|
-
function runStructured({ role, prompt, schema }) {
|
|
379
|
-
const promptBytes = Buffer.from(prompt, 'utf8');
|
|
380
|
-
const schemaBytes = Buffer.from(JSON.stringify(schema), 'utf8');
|
|
381
|
-
if (
|
|
382
|
-
promptBytes.length > MAX_AGENT_INPUT_BYTES ||
|
|
383
|
-
schemaBytes.length > MAX_AGENT_SCHEMA_BYTES
|
|
384
|
-
) {
|
|
385
|
-
promptBytes.fill(0);
|
|
386
|
-
schemaBytes.fill(0);
|
|
387
|
-
throw new ApplicationHostError(
|
|
388
|
-
'application_host_input_too_large',
|
|
389
|
-
'The exact Runtime Capsule and task envelope exceed this Host adapter input budget; no input was truncated.',
|
|
390
|
-
);
|
|
391
|
-
}
|
|
392
|
-
const root = makePrivateRoot(`kdna-${role}-`);
|
|
393
|
-
const schemaPath = path.join(root, 'output.schema.json');
|
|
394
|
-
const outputPath = path.join(root, 'output.json');
|
|
395
|
-
let result = null;
|
|
396
|
-
try {
|
|
397
|
-
const roleHome = path.join(root, 'home');
|
|
398
|
-
const roleTmp = path.join(root, 'tmp');
|
|
399
|
-
fs.mkdirSync(roleHome, { mode: 0o700 });
|
|
400
|
-
fs.mkdirSync(roleTmp, { mode: 0o700 });
|
|
401
|
-
const codexHome = installEphemeralAuth(root, options);
|
|
402
|
-
writePrivateBytes(schemaPath, schemaBytes);
|
|
403
|
-
createPrivateOutput(outputPath);
|
|
404
|
-
const profile = sandboxProfile({
|
|
405
|
-
nativeCommand,
|
|
406
|
-
roleRoot: root,
|
|
407
|
-
deniedPaths: [
|
|
408
|
-
...deniedPaths,
|
|
409
|
-
options.workspacePath,
|
|
410
|
-
options.projectPath,
|
|
411
|
-
],
|
|
412
|
-
});
|
|
413
|
-
const args = [
|
|
414
|
-
'exec',
|
|
415
|
-
'--ephemeral',
|
|
416
|
-
'--ignore-user-config',
|
|
417
|
-
'--ignore-rules',
|
|
418
|
-
'--skip-git-repo-check',
|
|
419
|
-
'--sandbox',
|
|
420
|
-
'read-only',
|
|
421
|
-
'--cd',
|
|
422
|
-
root,
|
|
423
|
-
'--output-schema',
|
|
424
|
-
schemaPath,
|
|
425
|
-
'--output-last-message',
|
|
426
|
-
outputPath,
|
|
427
|
-
'--color',
|
|
428
|
-
'never',
|
|
429
|
-
...DISABLED_CODEX_FEATURES.flatMap(
|
|
430
|
-
(feature) => ['--disable', feature],
|
|
431
|
-
),
|
|
432
|
-
'-',
|
|
433
|
-
];
|
|
434
|
-
result = run(
|
|
435
|
-
'/usr/bin/sandbox-exec',
|
|
436
|
-
['-p', profile, nativeCommand, ...args],
|
|
437
|
-
{
|
|
438
|
-
cwd: root,
|
|
439
|
-
input: promptBytes,
|
|
440
|
-
encoding: null,
|
|
441
|
-
timeout: options.timeoutMs || 300_000,
|
|
442
|
-
maxBuffer: MAX_AGENT_OUTPUT_BYTES,
|
|
443
|
-
env: {
|
|
444
|
-
HOME: roleHome,
|
|
445
|
-
CODEX_HOME: codexHome,
|
|
446
|
-
TMPDIR: roleTmp,
|
|
447
|
-
PATH: '/usr/bin:/bin',
|
|
448
|
-
LANG: 'C.UTF-8',
|
|
449
|
-
LC_ALL: 'C.UTF-8',
|
|
450
|
-
NO_COLOR: '1',
|
|
451
|
-
},
|
|
452
|
-
},
|
|
453
|
-
);
|
|
454
|
-
if (
|
|
455
|
-
result.status !== 0 ||
|
|
456
|
-
result.signal ||
|
|
457
|
-
!fs.existsSync(outputPath)
|
|
458
|
-
) {
|
|
459
|
-
if (typeof options.onDiagnostic === 'function') {
|
|
460
|
-
const stderrText = Buffer.isBuffer(result.stderr)
|
|
461
|
-
? result.stderr.toString('utf8')
|
|
462
|
-
: '';
|
|
463
|
-
options.onDiagnostic({
|
|
464
|
-
phase: role,
|
|
465
|
-
status: result.status,
|
|
466
|
-
signal: result.signal || null,
|
|
467
|
-
failure_class: /operation not permitted|permission denied|deny\(/i.test(
|
|
468
|
-
stderrText,
|
|
469
|
-
)
|
|
470
|
-
? 'capability-sandbox'
|
|
471
|
-
: (
|
|
472
|
-
/output schema|schema/i.test(stderrText)
|
|
473
|
-
? 'structured-output'
|
|
474
|
-
: (
|
|
475
|
-
/auth|login|credential/i.test(stderrText)
|
|
476
|
-
? 'host-authentication'
|
|
477
|
-
: (
|
|
478
|
-
/config|feature|unknown field/i.test(stderrText)
|
|
479
|
-
? 'host-configuration'
|
|
480
|
-
: (
|
|
481
|
-
/network|connect|tls|http/i.test(stderrText)
|
|
482
|
-
? 'host-network'
|
|
483
|
-
: 'host-execution'
|
|
484
|
-
)
|
|
485
|
-
)
|
|
486
|
-
)
|
|
487
|
-
),
|
|
488
|
-
stdout_digest: Buffer.isBuffer(result.stdout)
|
|
489
|
-
? sha256(result.stdout)
|
|
490
|
-
: null,
|
|
491
|
-
stderr_digest: Buffer.isBuffer(result.stderr)
|
|
492
|
-
? sha256(result.stderr)
|
|
493
|
-
: null,
|
|
494
|
-
});
|
|
495
|
-
}
|
|
496
|
-
throw new ApplicationHostError(
|
|
497
|
-
'application_host_execution_failed',
|
|
498
|
-
`The isolated ${role} Host execution did not complete.`,
|
|
499
|
-
);
|
|
500
|
-
}
|
|
501
|
-
const output = readPrivateJson(outputPath);
|
|
502
|
-
const outputBytes = Buffer.from(JSON.stringify(output));
|
|
503
|
-
try {
|
|
504
|
-
return {
|
|
505
|
-
output,
|
|
506
|
-
output_digest: sha256(outputBytes),
|
|
507
|
-
run_digest: sha256(Buffer.concat([
|
|
508
|
-
Buffer.from(`${role}\0`),
|
|
509
|
-
promptBytes,
|
|
510
|
-
Buffer.from('\0'),
|
|
511
|
-
outputBytes,
|
|
512
|
-
])),
|
|
513
|
-
runner_digest: runnerDigest,
|
|
514
|
-
coordinate,
|
|
515
|
-
isolation_policy_digest:
|
|
516
|
-
sha256(JSON.stringify(isolationPolicy)),
|
|
517
|
-
};
|
|
518
|
-
} finally {
|
|
519
|
-
outputBytes.fill(0);
|
|
520
|
-
}
|
|
521
|
-
} finally {
|
|
522
|
-
promptBytes.fill(0);
|
|
523
|
-
schemaBytes.fill(0);
|
|
524
|
-
if (Buffer.isBuffer(result?.stdout)) result.stdout.fill(0);
|
|
525
|
-
if (Buffer.isBuffer(result?.stderr)) result.stderr.fill(0);
|
|
526
|
-
fs.rmSync(root, { recursive: true, force: true });
|
|
527
|
-
}
|
|
528
|
-
}
|
|
529
|
-
|
|
530
|
-
return Object.freeze({
|
|
531
|
-
coordinate,
|
|
532
|
-
runner_digest: runnerDigest,
|
|
533
|
-
isolation_policy_digest: sha256(JSON.stringify(isolationPolicy)),
|
|
534
|
-
generateOracle(input) {
|
|
535
|
-
return runStructured({
|
|
536
|
-
role: 'application-oracle',
|
|
537
|
-
prompt: input.prompt,
|
|
538
|
-
schema: input.schema,
|
|
539
|
-
});
|
|
540
|
-
},
|
|
541
|
-
runConsumer(input) {
|
|
542
|
-
return runStructured({
|
|
543
|
-
role: 'application-consumer',
|
|
544
|
-
prompt: input.prompt,
|
|
545
|
-
schema: input.schema,
|
|
546
|
-
});
|
|
547
|
-
},
|
|
548
|
-
runEvaluator(input) {
|
|
549
|
-
return runStructured({
|
|
550
|
-
role: 'application-evaluator',
|
|
551
|
-
prompt: input.prompt,
|
|
552
|
-
schema: input.schema,
|
|
553
|
-
});
|
|
554
|
-
},
|
|
555
|
-
});
|
|
556
|
-
}
|
|
557
|
-
|
|
558
|
-
module.exports = {
|
|
559
|
-
ApplicationHostError,
|
|
560
|
-
createCodexApplicationHost,
|
|
561
|
-
};
|