Преглед изворни кода

fix(opencode): make ACP resource text sourcing cross-platform (#33534)

Aiden Cline пре 1 месец
родитељ
комит
5152150bfe
2 измењених фајлова са 40 додато и 11 уклоњено
  1. 8 1
      packages/opencode/src/acp/content.ts
  2. 32 10
      packages/opencode/test/acp/content.test.ts

+ 8 - 1
packages/opencode/src/acp/content.ts

@@ -80,10 +80,17 @@ export function contentBlockToParts(block: ContentBlock): PromptPart[] {
           const parsed = new URL(block.resource.uri)
           const parsed = new URL(block.resource.uri)
           if (parsed.protocol === "file:") {
           if (parsed.protocol === "file:") {
             const line = parsed.hash.match(/^#L(\d+)/)?.[1]
             const line = parsed.hash.match(/^#L(\d+)/)?.[1]
+            let filepath: string
+            try {
+              filepath = fileURLToPath(parsed)
+            } catch {
+              filepath = decodeURIComponent(parsed.pathname)
+            }
+            if (path.sep === "\\") filepath = filepath.replace(/\\/g, "/")
             return [
             return [
               {
               {
                 type: "text",
                 type: "text",
-                text: `[${fileURLToPath(parsed)}${line ? `:${line}` : ""}]\n${block.resource.text}`,
+                text: `[${filepath}${line ? `:${line}` : ""}]\n${block.resource.text}`,
               },
               },
             ]
             ]
           }
           }

+ 32 - 10
packages/opencode/test/acp/content.test.ts

@@ -100,16 +100,21 @@ describe("acp content conversion", () => {
   })
   })
 
 
   test("resource with text becomes a sourced text part", () => {
   test("resource with text becomes a sourced text part", () => {
-    expect(
-      contentBlockToParts({
-        type: "resource",
-        resource: {
-          uri: "file:///tmp/context.txt#L12-L14",
-          mimeType: "text/plain",
-          text: "context",
-        },
-      }),
-    ).toEqual([{ type: "text", text: "[/tmp/context.txt:12]\ncontext" }])
+    const result = contentBlockToParts({
+      type: "resource",
+      resource: {
+        uri: "file:///tmp/context.txt#L12-L14",
+        mimeType: "text/plain",
+        text: "context",
+      },
+    })
+    expect(result).toHaveLength(1)
+    expect(result[0]?.type).toBe("text")
+    if (result[0]?.type === "text") {
+      expect(result[0].text.endsWith("\ncontext")).toBe(true)
+      expect(result[0].text.includes("context.txt")).toBe(true)
+      expect(result[0].text.includes("12")).toBe(true)
+    }
   })
   })
 
 
   test("resource with text uses URI fallback for non-file resources", () => {
   test("resource with text uses URI fallback for non-file resources", () => {
@@ -124,6 +129,23 @@ describe("acp content conversion", () => {
     ).toEqual([{ type: "text", text: "[mcp://server/context]\ncontext" }])
     ).toEqual([{ type: "text", text: "[mcp://server/context]\ncontext" }])
   })
   })
 
 
+  test("resource with text includes file path", () => {
+    const result = contentBlockToParts({
+      type: "resource",
+      resource: {
+        uri: "file:///tmp/context.txt",
+        mimeType: "text/plain",
+        text: "context",
+      },
+    })
+    expect(result).toHaveLength(1)
+    expect(result[0]?.type).toBe("text")
+    if (result[0]?.type === "text") {
+      expect(result[0].text.endsWith("\ncontext")).toBe(true)
+      expect(result[0].text.includes("context.txt")).toBe(true)
+    }
+  })
+
   test("resource with blob and mimeType becomes a data URL file part", () => {
   test("resource with blob and mimeType becomes a data URL file part", () => {
     expect(
     expect(
       contentBlockToParts({
       contentBlockToParts({