Skip to content

Commit 96d53c6

Browse files
authored
refactor(core): move path resolve into fs service (#35202)
1 parent 911fb70 commit 96d53c6

4 files changed

Lines changed: 33 additions & 17 deletions

File tree

packages/core/src/fs-util.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ export namespace FSUtil {
3838
readonly ensureDir: (path: string) => Effect.Effect<void, Error>
3939
readonly writeWithDirs: (path: string, content: string | Uint8Array, mode?: number) => Effect.Effect<void, Error>
4040
readonly readDirectoryEntries: (path: string) => Effect.Effect<DirEntry[], Error>
41+
readonly resolve: (path: string) => Effect.Effect<string>
4142
readonly findUp: (target: string, start: string, stop?: string) => Effect.Effect<string[], Error>
4243
readonly up: (options: { targets: string[]; start: string; stop?: string }) => Effect.Effect<string[], Error>
4344
readonly globUp: (pattern: string, start: string, stop?: string) => Effect.Effect<string[], Error>
@@ -89,6 +90,14 @@ export namespace FSUtil {
8990
})
9091
})
9192

93+
const resolve = Effect.fn("FileSystem.resolve")(function* (path: string) {
94+
const resolved = pathResolve(windowsPath(path))
95+
return yield* fs.realPath(resolved).pipe(
96+
Effect.catchReason("PlatformError", "NotFound", () => Effect.succeed(resolved)),
97+
Effect.orDie,
98+
)
99+
})
100+
92101
const readJson = Effect.fn("FileSystem.readJson")(function* (path: string) {
93102
const text = yield* fs.readFileString(path)
94103
return yield* Effect.try({
@@ -187,6 +196,7 @@ export namespace FSUtil {
187196
isDir,
188197
isFile,
189198
readDirectoryEntries,
199+
resolve,
190200
readJson,
191201
writeJson,
192202
ensureDir,

packages/core/src/instruction-context.ts

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -38,22 +38,24 @@ const layer = Layer.effectDiscard(
3838
})
3939

4040
const observe = Effect.fn("InstructionContext.observe")(function* () {
41-
const start = FSUtil.resolve(location.directory)
42-
const stop = FSUtil.resolve(location.project.directory)
41+
const start = yield* fs.resolve(location.directory)
42+
const stop = yield* fs.resolve(location.project.directory)
4343
const fromProject = relative(stop, start)
4444
const insideProject =
4545
fromProject === "" || (fromProject !== ".." && !fromProject.startsWith(`..${sep}`) && !isAbsolute(fromProject))
4646
const discovered = new Set(
47-
(Flag.OPENCODE_DISABLE_PROJECT_CONFIG || !insideProject
48-
? []
49-
: yield* fs.up({
50-
targets: ["AGENTS.md"],
51-
start,
52-
stop,
53-
})
54-
).map(FSUtil.resolve),
47+
yield* Effect.forEach(
48+
Flag.OPENCODE_DISABLE_PROJECT_CONFIG || !insideProject
49+
? []
50+
: yield* fs.up({
51+
targets: ["AGENTS.md"],
52+
start,
53+
stop,
54+
}),
55+
fs.resolve,
56+
),
5557
)
56-
const paths = Array.dedupe([FSUtil.resolve(join(global.config, "AGENTS.md")), ...discovered])
58+
const paths = Array.dedupe([yield* fs.resolve(join(global.config, "AGENTS.md")), ...discovered])
5759
const files = yield* Effect.forEach(
5860
paths,
5961
(path) =>

packages/core/src/project/copy.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ const layer = Layer.effect(
139139
})
140140

141141
const canonical = Effect.fnUntraced(function* (input: AbsolutePath) {
142-
const resolved = AbsolutePath.make(FSUtil.resolve(input))
142+
const resolved = AbsolutePath.make(yield* fs.resolve(input))
143143
if (!(yield* fs.isDir(resolved))) return yield* new DirectoryUnavailableError({ directory: input })
144144
return resolved
145145
})

packages/core/src/tool/bash.ts

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -78,17 +78,21 @@ const isTimeout = (error: AppProcess.AppProcessError) =>
7878

7979
const shellTokens = (command: string) => command.match(/(?:[^\s"']+|"[^"]*"|'[^']*')+/g) ?? []
8080
const unquote = (value: string) => value.replace(/^(['"])(.*)\1$/, "$2")
81-
const externalCommandDirectories = (command: string, cwd: string) => {
81+
const externalCommandDirectories = Effect.fn("BashTool.externalCommandDirectories")(function* (
82+
fs: FSUtil.Interface,
83+
command: string,
84+
cwd: string,
85+
) {
8286
const directories = new Set<string>()
8387
for (const token of shellTokens(command)) {
8488
const value = unquote(token).replace(/[;,|&]+$/, "")
8589
if (!path.isAbsolute(value)) continue
86-
const resolved = FSUtil.resolve(value)
90+
const resolved = yield* fs.resolve(value)
8791
if (FSUtil.contains(cwd, resolved)) continue
88-
directories.add(FSUtil.resolve(path.dirname(resolved)))
92+
directories.add(yield* fs.resolve(path.dirname(resolved)))
8993
}
9094
return [...directories]
91-
}
95+
})
9296

9397
const layer = Layer.effectDiscard(
9498
Effect.gen(function* () {
@@ -131,7 +135,7 @@ const layer = Layer.effectDiscard(
131135
agent: context.agent,
132136
source,
133137
})
134-
const warnings = externalCommandDirectories(input.command, target.canonical).map(
138+
const warnings = (yield* externalCommandDirectories(fs, input.command, target.canonical)).map(
135139
(directory) =>
136140
`Command argument references external directory ${path.join(directory, "*").replaceAll("\\", "/")}. Bash runs with host-user filesystem, process, and network authority; this scan is advisory only.`,
137141
)

0 commit comments

Comments
 (0)