Skip to content

Commit b4a53ae

Browse files
feat: Add support for --add-dir to exec and TypeScript SDK (#6565)
## Summary Adds support for specifying additional directories in the TypeScript SDK through a new `additionalDirectories` option in `ThreadOptions`. ## Changes - Added `additionalDirectories` parameter to `ThreadOptions` interface - Updated `CodexExec` to accept and pass through additional directories via the `--config` flag for `sandbox_workspace_write.writable_roots` - Added comprehensive test coverage for the new functionality ## Test plan - Added test case that verifies `additionalDirectories` is correctly passed as repeated flags - Existing tests continue to pass --------- Co-authored-by: Claude <[email protected]>
1 parent 439bc5d commit b4a53ae

File tree

8 files changed

+131
-1
lines changed

8 files changed

+131
-1
lines changed

codex-rs/exec/src/cli.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,10 @@ pub struct Cli {
5252
#[arg(long = "skip-git-repo-check", default_value_t = false)]
5353
pub skip_git_repo_check: bool,
5454

55+
/// Additional directories that should be writable alongside the primary workspace.
56+
#[arg(long = "add-dir", value_name = "DIR", value_hint = clap::ValueHint::DirPath)]
57+
pub add_dir: Vec<PathBuf>,
58+
5559
/// Path to a JSON Schema file describing the model's final response shape.
5660
#[arg(long = "output-schema", value_name = "FILE")]
5761
pub output_schema: Option<PathBuf>,

codex-rs/exec/src/lib.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ pub async fn run_main(cli: Cli, codex_linux_sandbox_exe: Option<PathBuf>) -> any
6262
dangerously_bypass_approvals_and_sandbox,
6363
cwd,
6464
skip_git_repo_check,
65+
add_dir,
6566
color,
6667
last_message_file,
6768
json: json_mode,
@@ -180,7 +181,7 @@ pub async fn run_main(cli: Cli, codex_linux_sandbox_exe: Option<PathBuf>) -> any
180181
show_raw_agent_reasoning: oss.then_some(true),
181182
tools_web_search_request: None,
182183
experimental_sandbox_command_assessment: None,
183-
additional_writable_roots: Vec::new(),
184+
additional_writable_roots: add_dir,
184185
};
185186
// Parse `-c` overrides.
186187
let cli_kv_overrides = match config_overrides.parse_overrides() {
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
#![cfg(not(target_os = "windows"))]
2+
#![allow(clippy::expect_used, clippy::unwrap_used)]
3+
4+
use core_test_support::responses;
5+
use core_test_support::test_codex_exec::test_codex_exec;
6+
7+
/// Verify that the --add-dir flag is accepted and the command runs successfully.
8+
/// This test confirms the CLI argument is properly wired up.
9+
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
10+
async fn accepts_add_dir_flag() -> anyhow::Result<()> {
11+
let test = test_codex_exec();
12+
13+
let server = responses::start_mock_server().await;
14+
let body = responses::sse(vec![
15+
responses::ev_response_created("response_1"),
16+
responses::ev_assistant_message("response_1", "Task completed"),
17+
responses::ev_completed("response_1"),
18+
]);
19+
responses::mount_sse_once(&server, body).await;
20+
21+
// Create temporary directories to use with --add-dir
22+
let temp_dir1 = tempfile::tempdir()?;
23+
let temp_dir2 = tempfile::tempdir()?;
24+
25+
test.cmd_with_server(&server)
26+
.arg("--skip-git-repo-check")
27+
.arg("--sandbox")
28+
.arg("workspace-write")
29+
.arg("--add-dir")
30+
.arg(temp_dir1.path())
31+
.arg("--add-dir")
32+
.arg(temp_dir2.path())
33+
.arg("test with additional directories")
34+
.assert()
35+
.code(0);
36+
37+
Ok(())
38+
}
39+
40+
/// Verify that multiple --add-dir flags can be specified.
41+
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
42+
async fn accepts_multiple_add_dir_flags() -> anyhow::Result<()> {
43+
let test = test_codex_exec();
44+
45+
let server = responses::start_mock_server().await;
46+
let body = responses::sse(vec![
47+
responses::ev_response_created("response_1"),
48+
responses::ev_assistant_message("response_1", "Multiple directories accepted"),
49+
responses::ev_completed("response_1"),
50+
]);
51+
responses::mount_sse_once(&server, body).await;
52+
53+
let temp_dir1 = tempfile::tempdir()?;
54+
let temp_dir2 = tempfile::tempdir()?;
55+
let temp_dir3 = tempfile::tempdir()?;
56+
57+
test.cmd_with_server(&server)
58+
.arg("--skip-git-repo-check")
59+
.arg("--sandbox")
60+
.arg("workspace-write")
61+
.arg("--add-dir")
62+
.arg(temp_dir1.path())
63+
.arg("--add-dir")
64+
.arg(temp_dir2.path())
65+
.arg("--add-dir")
66+
.arg(temp_dir3.path())
67+
.arg("test with three directories")
68+
.assert()
69+
.code(0);
70+
71+
Ok(())
72+
}

codex-rs/exec/tests/suite/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// Aggregates all former standalone integration tests as modules.
2+
mod add_dir;
23
mod apply_patch;
34
mod auth_env;
45
mod originator;

sdk/typescript/src/exec.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ export type CodexExecArgs = {
1818
sandboxMode?: SandboxMode;
1919
// --cd
2020
workingDirectory?: string;
21+
// --add-dir
22+
additionalDirectories?: string[];
2123
// --skip-git-repo-check
2224
skipGitRepoCheck?: boolean;
2325
// --output-schema
@@ -58,6 +60,12 @@ export class CodexExec {
5860
commandArgs.push("--cd", args.workingDirectory);
5961
}
6062

63+
if (args.additionalDirectories?.length) {
64+
for (const dir of args.additionalDirectories) {
65+
commandArgs.push("--add-dir", dir);
66+
}
67+
}
68+
6169
if (args.skipGitRepoCheck) {
6270
commandArgs.push("--skip-git-repo-check");
6371
}

sdk/typescript/src/thread.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ export class Thread {
9090
networkAccessEnabled: options?.networkAccessEnabled,
9191
webSearchEnabled: options?.webSearchEnabled,
9292
approvalPolicy: options?.approvalPolicy,
93+
additionalDirectories: options?.additionalDirectories,
9394
});
9495
try {
9596
for await (const item of generator) {

sdk/typescript/src/threadOptions.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,5 @@ export type ThreadOptions = {
1313
networkAccessEnabled?: boolean;
1414
webSearchEnabled?: boolean;
1515
approvalPolicy?: ApprovalMode;
16+
additionalDirectories?: string[];
1617
};

sdk/typescript/tests/run.test.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,48 @@ describe("Codex", () => {
348348
}
349349
});
350350

351+
it("passes additionalDirectories as repeated flags", async () => {
352+
const { url, close } = await startResponsesTestProxy({
353+
statusCode: 200,
354+
responseBodies: [
355+
sse(
356+
responseStarted("response_1"),
357+
assistantMessage("Additional directories applied", "item_1"),
358+
responseCompleted("response_1"),
359+
),
360+
],
361+
});
362+
363+
const { args: spawnArgs, restore } = codexExecSpy();
364+
365+
try {
366+
const client = new Codex({ codexPathOverride: codexExecPath, baseUrl: url, apiKey: "test" });
367+
368+
const thread = client.startThread({
369+
additionalDirectories: ["../backend", "/tmp/shared"],
370+
});
371+
await thread.run("test additional dirs");
372+
373+
const commandArgs = spawnArgs[0];
374+
expect(commandArgs).toBeDefined();
375+
if (!commandArgs) {
376+
throw new Error("Command args missing");
377+
}
378+
379+
// Find the --add-dir flags
380+
const addDirArgs: string[] = [];
381+
for (let i = 0; i < commandArgs.length; i += 1) {
382+
if (commandArgs[i] === "--add-dir") {
383+
addDirArgs.push(commandArgs[i + 1] ?? "");
384+
}
385+
}
386+
expect(addDirArgs).toEqual(["../backend", "/tmp/shared"]);
387+
} finally {
388+
restore();
389+
await close();
390+
}
391+
});
392+
351393
it("writes output schema to a temporary file and forwards it", async () => {
352394
const { url, close, requests } = await startResponsesTestProxy({
353395
statusCode: 200,

0 commit comments

Comments
 (0)