Sfoglia il codice sorgente

refactor(core): move path resolve into fs service (#35202)

James Long 2 mesi fa
parent
commit
96d53c6716

+ 10 - 0
packages/core/src/fs-util.ts

@@ -38,6 +38,7 @@ export namespace FSUtil {
     readonly ensureDir: (path: string) => Effect.Effect<void, Error>
     readonly writeWithDirs: (path: string, content: string | Uint8Array, mode?: number) => Effect.Effect<void, Error>
     readonly readDirectoryEntries: (path: string) => Effect.Effect<DirEntry[], Error>
+    readonly resolve: (path: string) => Effect.Effect<string>
     readonly findUp: (target: string, start: string, stop?: string) => Effect.Effect<string[], Error>
     readonly up: (options: { targets: string[]; start: string; stop?: string }) => Effect.Effect<string[], Error>
     readonly globUp: (pattern: string, start: string, stop?: string) => Effect.Effect<string[], Error>
@@ -89,6 +90,14 @@ export namespace FSUtil {
         })
       })
 
+      const resolve = Effect.fn("FileSystem.resolve")(function* (path: string) {
+        const resolved = pathResolve(windowsPath(path))
+        return yield* fs.realPath(resolved).pipe(
+          Effect.catchReason("PlatformError", "NotFound", () => Effect.succeed(resolved)),
+          Effect.orDie,
+        )
+      })
+
       const readJson = Effect.fn("FileSystem.readJson")(function* (path: string) {
         const text = yield* fs.readFileString(path)
         return yield* Effect.try({
@@ -187,6 +196,7 @@ export namespace FSUtil {
         isDir,
         isFile,
         readDirectoryEntries,
+        resolve,
         readJson,
         writeJson,
         ensureDir,

+ 13 - 11
packages/core/src/instruction-context.ts

@@ -38,22 +38,24 @@ const layer = Layer.effectDiscard(
       })
 
     const observe = Effect.fn("InstructionContext.observe")(function* () {
-      const start = FSUtil.resolve(location.directory)
-      const stop = FSUtil.resolve(location.project.directory)
+      const start = yield* fs.resolve(location.directory)
+      const stop = yield* fs.resolve(location.project.directory)
       const fromProject = relative(stop, start)
       const insideProject =
         fromProject === "" || (fromProject !== ".." && !fromProject.startsWith(`..${sep}`) && !isAbsolute(fromProject))
       const discovered = new Set(
-        (Flag.OPENCODE_DISABLE_PROJECT_CONFIG || !insideProject
-          ? []
-          : yield* fs.up({
-              targets: ["AGENTS.md"],
-              start,
-              stop,
-            })
-        ).map(FSUtil.resolve),
+        yield* Effect.forEach(
+          Flag.OPENCODE_DISABLE_PROJECT_CONFIG || !insideProject
+            ? []
+            : yield* fs.up({
+                targets: ["AGENTS.md"],
+                start,
+                stop,
+              }),
+          fs.resolve,
+        ),
       )
-      const paths = Array.dedupe([FSUtil.resolve(join(global.config, "AGENTS.md")), ...discovered])
+      const paths = Array.dedupe([yield* fs.resolve(join(global.config, "AGENTS.md")), ...discovered])
       const files = yield* Effect.forEach(
         paths,
         (path) =>

+ 1 - 1
packages/core/src/project/copy.ts

@@ -139,7 +139,7 @@ const layer = Layer.effect(
     })
 
     const canonical = Effect.fnUntraced(function* (input: AbsolutePath) {
-      const resolved = AbsolutePath.make(FSUtil.resolve(input))
+      const resolved = AbsolutePath.make(yield* fs.resolve(input))
       if (!(yield* fs.isDir(resolved))) return yield* new DirectoryUnavailableError({ directory: input })
       return resolved
     })

+ 9 - 5
packages/core/src/tool/bash.ts

@@ -78,17 +78,21 @@ const isTimeout = (error: AppProcess.AppProcessError) =>
 
 const shellTokens = (command: string) => command.match(/(?:[^\s"']+|"[^"]*"|'[^']*')+/g) ?? []
 const unquote = (value: string) => value.replace(/^(['"])(.*)\1$/, "$2")
-const externalCommandDirectories = (command: string, cwd: string) => {
+const externalCommandDirectories = Effect.fn("BashTool.externalCommandDirectories")(function* (
+  fs: FSUtil.Interface,
+  command: string,
+  cwd: string,
+) {
   const directories = new Set<string>()
   for (const token of shellTokens(command)) {
     const value = unquote(token).replace(/[;,|&]+$/, "")
     if (!path.isAbsolute(value)) continue
-    const resolved = FSUtil.resolve(value)
+    const resolved = yield* fs.resolve(value)
     if (FSUtil.contains(cwd, resolved)) continue
-    directories.add(FSUtil.resolve(path.dirname(resolved)))
+    directories.add(yield* fs.resolve(path.dirname(resolved)))
   }
   return [...directories]
-}
+})
 
 const layer = Layer.effectDiscard(
   Effect.gen(function* () {
@@ -131,7 +135,7 @@ const layer = Layer.effectDiscard(
                   agent: context.agent,
                   source,
                 })
-              const warnings = externalCommandDirectories(input.command, target.canonical).map(
+              const warnings = (yield* externalCommandDirectories(fs, input.command, target.canonical)).map(
                 (directory) =>
                   `Command argument references external directory ${path.join(directory, "*").replaceAll("\\", "/")}. Bash runs with host-user filesystem, process, and network authority; this scan is advisory only.`,
               )