Skip to content

Commit 0544e31

Browse files
fix: add mode parameter to writeFile and appendFile across all SDKs
The RPC types for writeFile and appendFile include an optional mode field, but all 4 SDK languages were silently dropping it. This adds the mode parameter to the provider interfaces and adapters for Node.js, Python, Go, and .NET, consistent with how mkdir already forwards mode. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent b5f91e2 commit 0544e31

7 files changed

Lines changed: 48 additions & 26 deletions

File tree

dotnet/src/SessionFsProvider.cs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,16 @@ public abstract class SessionFsProvider : ISessionFsHandler
2323
/// <summary>Writes content to a file, creating it (and parent directories) if needed.</summary>
2424
/// <param name="path">SessionFs-relative path.</param>
2525
/// <param name="content">Content to write.</param>
26+
/// <param name="mode">Optional POSIX-style permission mode. Null means use OS default.</param>
2627
/// <param name="cancellationToken">Cancellation token.</param>
27-
protected abstract Task WriteFileAsync(string path, string content, CancellationToken cancellationToken);
28+
protected abstract Task WriteFileAsync(string path, string content, int? mode, CancellationToken cancellationToken);
2829

2930
/// <summary>Appends content to a file, creating it (and parent directories) if needed.</summary>
3031
/// <param name="path">SessionFs-relative path.</param>
3132
/// <param name="content">Content to append.</param>
33+
/// <param name="mode">Optional POSIX-style permission mode. Null means use OS default.</param>
3234
/// <param name="cancellationToken">Cancellation token.</param>
33-
protected abstract Task AppendFileAsync(string path, string content, CancellationToken cancellationToken);
35+
protected abstract Task AppendFileAsync(string path, string content, int? mode, CancellationToken cancellationToken);
3436

3537
/// <summary>Checks whether a path exists.</summary>
3638
/// <param name="path">SessionFs-relative path.</param>
@@ -92,7 +94,7 @@ async Task<SessionFsReadFileResult> ISessionFsHandler.ReadFileAsync(SessionFsRea
9294
{
9395
try
9496
{
95-
await WriteFileAsync(request.Path, request.Content, cancellationToken).ConfigureAwait(false);
97+
await WriteFileAsync(request.Path, request.Content, (int?)request.Mode, cancellationToken).ConfigureAwait(false);
9698
return null;
9799
}
98100
catch (Exception ex)
@@ -105,7 +107,7 @@ async Task<SessionFsReadFileResult> ISessionFsHandler.ReadFileAsync(SessionFsRea
105107
{
106108
try
107109
{
108-
await AppendFileAsync(request.Path, request.Content, cancellationToken).ConfigureAwait(false);
110+
await AppendFileAsync(request.Path, request.Content, (int?)request.Mode, cancellationToken).ConfigureAwait(false);
109111
return null;
110112
}
111113
catch (Exception ex)

dotnet/test/SessionFsTests.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -438,14 +438,14 @@ protected override async Task<string> ReadFileAsync(string path, CancellationTok
438438
return await File.ReadAllTextAsync(ResolvePath(path), cancellationToken);
439439
}
440440

441-
protected override async Task WriteFileAsync(string path, string content, CancellationToken cancellationToken)
441+
protected override async Task WriteFileAsync(string path, string content, int? mode, CancellationToken cancellationToken)
442442
{
443443
var fullPath = ResolvePath(path);
444444
Directory.CreateDirectory(Path.GetDirectoryName(fullPath)!);
445445
await File.WriteAllTextAsync(fullPath, content, cancellationToken);
446446
}
447447

448-
protected override async Task AppendFileAsync(string path, string content, CancellationToken cancellationToken)
448+
protected override async Task AppendFileAsync(string path, string content, int? mode, CancellationToken cancellationToken)
449449
{
450450
var fullPath = ResolvePath(path);
451451
Directory.CreateDirectory(Path.GetDirectoryName(fullPath)!);

go/internal/e2e/session_fs_test.go

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -350,20 +350,28 @@ func (h *testSessionFsHandler) ReadFile(path string) (string, error) {
350350
return string(content), nil
351351
}
352352

353-
func (h *testSessionFsHandler) WriteFile(path string, content string) error {
353+
func (h *testSessionFsHandler) WriteFile(path string, content string, mode *int) error {
354354
fullPath := providerPath(h.root, h.sessionID, path)
355355
if err := os.MkdirAll(filepath.Dir(fullPath), 0o755); err != nil {
356356
return err
357357
}
358-
return os.WriteFile(fullPath, []byte(content), 0o666)
358+
perm := os.FileMode(0o666)
359+
if mode != nil {
360+
perm = os.FileMode(*mode)
361+
}
362+
return os.WriteFile(fullPath, []byte(content), perm)
359363
}
360364

361-
func (h *testSessionFsHandler) AppendFile(path string, content string) error {
365+
func (h *testSessionFsHandler) AppendFile(path string, content string, mode *int) error {
362366
fullPath := providerPath(h.root, h.sessionID, path)
363367
if err := os.MkdirAll(filepath.Dir(fullPath), 0o755); err != nil {
364368
return err
365369
}
366-
f, err := os.OpenFile(fullPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0o666)
370+
perm := os.FileMode(0o666)
371+
if mode != nil {
372+
perm = os.FileMode(*mode)
373+
}
374+
f, err := os.OpenFile(fullPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, perm)
367375
if err != nil {
368376
return err
369377
}

go/session_fs_provider.go

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,11 @@ type SessionFsProvider interface {
2020
// if the file does not exist.
2121
ReadFile(path string) (string, error)
2222
// WriteFile writes content to a file, creating it and parent directories if needed.
23-
WriteFile(path string, content string) error
23+
// mode is an optional POSIX-style permission mode. Pass nil to use the OS default.
24+
WriteFile(path string, content string, mode *int) error
2425
// AppendFile appends content to a file, creating it and parent directories if needed.
25-
AppendFile(path string, content string) error
26+
// mode is an optional POSIX-style permission mode. Pass nil to use the OS default.
27+
AppendFile(path string, content string, mode *int) error
2628
// Exists checks whether the given path exists.
2729
Exists(path string) (bool, error)
2830
// Stat returns metadata about a file or directory.
@@ -72,14 +74,24 @@ func (a *sessionFsAdapter) ReadFile(request *rpc.SessionFSReadFileRequest) (*rpc
7274
}
7375

7476
func (a *sessionFsAdapter) WriteFile(request *rpc.SessionFSWriteFileRequest) (*rpc.SessionFSError, error) {
75-
if err := a.provider.WriteFile(request.Path, request.Content); err != nil {
77+
var mode *int
78+
if request.Mode != nil {
79+
m := int(*request.Mode)
80+
mode = &m
81+
}
82+
if err := a.provider.WriteFile(request.Path, request.Content, mode); err != nil {
7683
return toSessionFsError(err), nil
7784
}
7885
return nil, nil
7986
}
8087

8188
func (a *sessionFsAdapter) AppendFile(request *rpc.SessionFSAppendFileRequest) (*rpc.SessionFSError, error) {
82-
if err := a.provider.AppendFile(request.Path, request.Content); err != nil {
89+
var mode *int
90+
if request.Mode != nil {
91+
m := int(*request.Mode)
92+
mode = &m
93+
}
94+
if err := a.provider.AppendFile(request.Path, request.Content, mode); err != nil {
8395
return toSessionFsError(err), nil
8496
}
8597
return nil, nil

nodejs/src/sessionFsProvider.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,10 @@ export interface SessionFsProvider {
3030
readFile(path: string): Promise<string>;
3131

3232
/** Writes content to a file, creating parent directories if needed. */
33-
writeFile(path: string, content: string): Promise<void>;
33+
writeFile(path: string, content: string, mode?: number): Promise<void>;
3434

3535
/** Appends content to a file, creating parent directories if needed. */
36-
appendFile(path: string, content: string): Promise<void>;
36+
appendFile(path: string, content: string, mode?: number): Promise<void>;
3737

3838
/** Checks whether a path exists. */
3939
exists(path: string): Promise<boolean>;
@@ -72,17 +72,17 @@ export function createSessionFsAdapter(provider: SessionFsProvider): SessionFsHa
7272
return { content: "", error: toSessionFsError(err) };
7373
}
7474
},
75-
writeFile: async ({ path, content }) => {
75+
writeFile: async ({ path, content, mode }) => {
7676
try {
77-
await provider.writeFile(path, content);
77+
await provider.writeFile(path, content, mode);
7878
return undefined;
7979
} catch (err) {
8080
return toSessionFsError(err);
8181
}
8282
},
83-
appendFile: async ({ path, content }) => {
83+
appendFile: async ({ path, content, mode }) => {
8484
try {
85-
await provider.appendFile(path, content);
85+
await provider.appendFile(path, content, mode);
8686
return undefined;
8787
} catch (err) {
8888
return toSessionFsError(err);

python/copilot/session_fs_provider.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -60,11 +60,11 @@ async def read_file(self, path: str) -> str:
6060
"""Read the full content of a file. Raise if the file does not exist."""
6161

6262
@abc.abstractmethod
63-
async def write_file(self, path: str, content: str) -> None:
63+
async def write_file(self, path: str, content: str, mode: int | None = None) -> None:
6464
"""Write *content* to a file, creating parent directories if needed."""
6565

6666
@abc.abstractmethod
67-
async def append_file(self, path: str, content: str) -> None:
67+
async def append_file(self, path: str, content: str, mode: int | None = None) -> None:
6868
"""Append *content* to a file, creating parent directories if needed."""
6969

7070
@abc.abstractmethod
@@ -121,14 +121,14 @@ async def read_file(self, params: object) -> SessionFSReadFileResult:
121121

122122
async def write_file(self, params: object) -> SessionFSError | None:
123123
try:
124-
await self._p.write_file(params.path, params.content) # type: ignore[attr-defined]
124+
await self._p.write_file(params.path, params.content, getattr(params, "mode", None)) # type: ignore[attr-defined]
125125
return None
126126
except Exception as exc:
127127
return _to_session_fs_error(exc)
128128

129129
async def append_file(self, params: object) -> SessionFSError | None:
130130
try:
131-
await self._p.append_file(params.path, params.content) # type: ignore[attr-defined]
131+
await self._p.append_file(params.path, params.content, getattr(params, "mode", None)) # type: ignore[attr-defined]
132132
return None
133133
except Exception as exc:
134134
return _to_session_fs_error(exc)

python/e2e/test_session_fs.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -276,12 +276,12 @@ def _path(self, path: str) -> Path:
276276
async def read_file(self, path: str) -> str:
277277
return self._path(path).read_text(encoding="utf-8")
278278

279-
async def write_file(self, path: str, content: str) -> None:
279+
async def write_file(self, path: str, content: str, mode: int | None = None) -> None:
280280
p = self._path(path)
281281
p.parent.mkdir(parents=True, exist_ok=True)
282282
p.write_text(content, encoding="utf-8")
283283

284-
async def append_file(self, path: str, content: str) -> None:
284+
async def append_file(self, path: str, content: str, mode: int | None = None) -> None:
285285
p = self._path(path)
286286
p.parent.mkdir(parents=True, exist_ok=True)
287287
with p.open("a", encoding="utf-8") as handle:

0 commit comments

Comments
 (0)