fix: properly encode file URLs with special characters (#12424)

This commit is contained in:
Khang Ha (Kelvin)
2026-02-07 05:16:56 +07:00
committed by GitHub
parent e9a3cfc083
commit fde0b39b7c
12 changed files with 114 additions and 22 deletions

View File

@@ -29,6 +29,7 @@ import {
} from "@agentclientprotocol/sdk"
import { Log } from "../util/log"
import { pathToFileURL } from "bun"
import { ACPSessionManager } from "./session"
import type { ACPConfig } from "./types"
import { Provider } from "../provider/provider"
@@ -986,7 +987,7 @@ export namespace ACP {
type: "image",
mimeType: effectiveMime,
data: base64Data,
uri: `file://${filename}`,
uri: pathToFileURL(filename).href,
},
},
})
@@ -996,13 +997,14 @@ export namespace ACP {
} else {
// Non-image: text types get decoded, binary types stay as blob
const isText = effectiveMime.startsWith("text/") || effectiveMime === "application/json"
const fileUri = pathToFileURL(filename).href
const resource = isText
? {
uri: `file://${filename}`,
uri: fileUri,
mimeType: effectiveMime,
text: Buffer.from(base64Data, "base64").toString("utf-8"),
}
: { uri: `file://${filename}`, mimeType: effectiveMime, blob: base64Data }
: { uri: fileUri, mimeType: effectiveMime, blob: base64Data }
await this.connection
.sessionUpdate({
@@ -1544,7 +1546,7 @@ export namespace ACP {
const name = path.split("/").pop() || path
return {
type: "file",
url: `file://${path}`,
url: pathToFileURL(path).href,
filename: name,
mime: "text/plain",
}

View File

@@ -1,5 +1,6 @@
import type { Argv } from "yargs"
import path from "path"
import { pathToFileURL } from "bun"
import { UI } from "../ui"
import { cmd } from "./cmd"
import { Flag } from "../../flag/flag"
@@ -314,7 +315,7 @@ export const RunCommand = cmd({
files.push({
type: "file",
url: `file://${resolvedPath}`,
url: pathToFileURL(resolvedPath).href,
filename: path.basename(resolvedPath),
mime,
})

View File

@@ -1,4 +1,5 @@
import { TextAttributes } from "@opentui/core"
import { fileURLToPath } from "bun"
import { useTheme } from "../context/theme"
import { useDialog } from "@tui/ui/dialog"
import { useSync } from "@tui/context/sync"
@@ -19,7 +20,7 @@ export function DialogStatus() {
const list = sync.data.config.plugin ?? []
const result = list.map((value) => {
if (value.startsWith("file://")) {
const path = value.substring("file://".length)
const path = fileURLToPath(value)
const parts = path.split("/")
const filename = parts.pop() || path
if (!filename.includes(".")) return { name: filename }

View File

@@ -1,4 +1,5 @@
import type { BoxRenderable, TextareaRenderable, KeyEvent, ScrollBoxRenderable } from "@opentui/core"
import { pathToFileURL } from "bun"
import fuzzysort from "fuzzysort"
import { firstBy } from "remeda"
import { createMemo, createResource, createEffect, onMount, onCleanup, Index, Show, createSignal } from "solid-js"
@@ -246,17 +247,17 @@ export function Autocomplete(props: {
const width = props.anchor().width - 4
options.push(
...sortedFiles.map((item): AutocompleteOption => {
let url = `file://${process.cwd()}/${item}`
const fullPath = `${process.cwd()}/${item}`
const urlObj = pathToFileURL(fullPath)
let filename = item
if (lineRange && !item.endsWith("/")) {
filename = `${item}#${lineRange.startLine}${lineRange.endLine ? `-${lineRange.endLine}` : ""}`
const urlObj = new URL(url)
urlObj.searchParams.set("start", String(lineRange.startLine))
if (lineRange.endLine !== undefined) {
urlObj.searchParams.set("end", String(lineRange.endLine))
}
url = urlObj.toString()
}
const url = urlObj.href
const isDir = item.endsWith("/")
return {

View File

@@ -3,7 +3,7 @@ import { Bus } from "@/bus"
import { Log } from "../util/log"
import { LSPClient } from "./client"
import path from "path"
import { pathToFileURL } from "url"
import { pathToFileURL, fileURLToPath } from "url"
import { LSPServer } from "./server"
import z from "zod"
import { Config } from "../config/config"
@@ -369,7 +369,7 @@ export namespace LSP {
}
export async function documentSymbol(uri: string) {
const file = new URL(uri).pathname
const file = fileURLToPath(uri)
return run(file, (client) =>
client.connection
.sendRequest("textDocument/documentSymbol", {

View File

@@ -32,7 +32,7 @@ import { Flag } from "../flag/flag"
import { ulid } from "ulid"
import { spawn } from "child_process"
import { Command } from "../command"
import { $, fileURLToPath } from "bun"
import { $, fileURLToPath, pathToFileURL } from "bun"
import { ConfigMarkdown } from "../config/markdown"
import { SessionSummary } from "./summary"
import { NamedError } from "@opencode-ai/util/error"
@@ -210,7 +210,7 @@ export namespace SessionPrompt {
if (stats.isDirectory()) {
parts.push({
type: "file",
url: `file://${filepath}`,
url: pathToFileURL(filepath).href,
filename: name,
mime: "application/x-directory",
})
@@ -219,7 +219,7 @@ export namespace SessionPrompt {
parts.push({
type: "file",
url: `file://${filepath}`,
url: pathToFileURL(filepath).href,
filename: name,
mime: "text/plain",
})