run: inline files for attached servers (#33317)
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import type { PermissionV1 } from "@opencode-ai/core/v1/permission"
|
||||
import { FSUtil } from "@opencode-ai/core/fs-util"
|
||||
// CLI entry point for `opencode run`.
|
||||
//
|
||||
// Handles three modes:
|
||||
@@ -15,6 +16,7 @@ import type { PermissionV1 } from "@opencode-ai/core/v1/permission"
|
||||
import type { Argv } from "yargs"
|
||||
import path from "path"
|
||||
import { pathToFileURL } from "url"
|
||||
import { open } from "node:fs/promises"
|
||||
import { Effect } from "effect"
|
||||
import { UI } from "../ui"
|
||||
import { effectCmd } from "../effect-cmd"
|
||||
@@ -54,6 +56,8 @@ type FilePart = {
|
||||
mime: string
|
||||
}
|
||||
|
||||
const ATTACH_FILE_MAX_BYTES = 10 * 1024 * 1024
|
||||
|
||||
type Inline = {
|
||||
icon: string
|
||||
title: string
|
||||
@@ -337,11 +341,48 @@ export const RunCommand = effectCmd({
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
const mime = (await Filesystem.isDir(resolvedPath)) ? "application/x-directory" : "text/plain"
|
||||
const stat = Filesystem.stat(resolvedPath)
|
||||
const isDirectory = stat?.isDirectory() ?? false
|
||||
if (args.attach && isDirectory) {
|
||||
UI.error(`Cannot attach local directory without a shared filesystem: ${filePath}`)
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
const content = await (async () => {
|
||||
if (!args.attach) return
|
||||
const handle = await open(resolvedPath, "r")
|
||||
try {
|
||||
const opened = await handle.stat()
|
||||
if (!opened.isFile() || Number(opened.size) > ATTACH_FILE_MAX_BYTES) {
|
||||
UI.error(`Cannot attach local file larger than 10 MiB or a special file: ${filePath}`)
|
||||
process.exit(1)
|
||||
}
|
||||
if (opened.size === 0) return Buffer.alloc(0)
|
||||
const buffer = Buffer.alloc(Number(opened.size))
|
||||
let offset = 0
|
||||
while (offset < buffer.length) {
|
||||
const read = await handle.read(buffer, offset, buffer.length - offset, offset)
|
||||
if (read.bytesRead === 0) break
|
||||
offset += read.bytesRead
|
||||
}
|
||||
return buffer.subarray(0, offset)
|
||||
} finally {
|
||||
await handle.close()
|
||||
}
|
||||
})()
|
||||
const detected = FSUtil.mimeType(resolvedPath)
|
||||
const text = content?.toString("utf8")
|
||||
const mime = !args.attach
|
||||
? isDirectory
|
||||
? "application/x-directory"
|
||||
: "text/plain"
|
||||
: content && text !== undefined && Buffer.from(text, "utf8").equals(content)
|
||||
? "text/plain"
|
||||
: detected
|
||||
|
||||
files.push({
|
||||
type: "file",
|
||||
url: pathToFileURL(resolvedPath).href,
|
||||
url: content ? `data:${mime};base64,${content.toString("base64")}` : pathToFileURL(resolvedPath).href,
|
||||
filename: path.basename(resolvedPath),
|
||||
mime,
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user