fix(knowledge): 修复内容文件读取时的编码和错误提示

- 为知识块添加和更新命令的读取内容文件函数添加了可选的inline flag参数
- 更新readUtf8TextFile以支持根据inline flag生成更友好的ENOENT错误提示
- 确保内容读取时使用严格的UTF-8编码解码方式
- 如果读取文件失败,提供文件路径和权限相关的详细错误信息
- 校验知识块内容长度时保持一致的错误处理逻辑
This commit is contained in:
zeyu.fz
2026-08-11 17:48:21 +08:00
parent 92a978af3c
commit 0369bd36b0
3 changed files with 24 additions and 9 deletions
@@ -121,7 +121,9 @@ export default defineCommand({
field = parseFieldEntries(flags.field);
} else {
const content =
flags.contentFile !== undefined ? readUtf8TextFile(flags.contentFile) : flags.content;
flags.contentFile !== undefined
? readUtf8TextFile(flags.contentFile, "--content")
: flags.content;
if (typeof content === "string" && content.length > 6000) {
throw new BailianError("Chunk content must be at most 6000 characters", ExitCode.USAGE);
}
@@ -122,7 +122,9 @@ export default defineCommand({
// dry-run also reads the file and validates (rehearsal semantics); the read-back
// request is only made outside dry-run and when no new content is given
let content =
flags.contentFile !== undefined ? readUtf8TextFile(flags.contentFile) : flags.content;
flags.contentFile !== undefined
? readUtf8TextFile(flags.contentFile, "--content")
: flags.content;
if (content !== undefined && (content.length < 10 || content.length > 6000)) {
throw new BailianError("Chunk content must be 10-6000 characters", ExitCode.USAGE);
}
@@ -192,22 +192,33 @@ const DOCUMENT_EXTENSIONS = new Set([
]);
/**
* Read a UTF-8 plain-text file (--content-file for chunk add/update).
* Read a UTF-8 plain-text file.
* Support is defined by content, not extension: any UTF-8 text is valid.
* Strict decode failure → USAGE, with a hint pointing to the document upload
* flow when the extension is a document format; file I/O failure → GENERAL + errno.
*
* @param inlineAlternativeFlag When provided, an ENOENT on a value that looks
* like inline text (no path separator / no extension) appends a hint pointing
* the user to this flag instead. Pass the inline-text flag name (e.g.
* `"--content"`) only from commands that have a file-vs-inline choice.
*/
export function readUtf8TextFile(filePath: string): string {
export function readUtf8TextFile(filePath: string, inlineAlternativeFlag?: string): string {
let fileBuffer: Buffer;
try {
fileBuffer = readFileSync(filePath);
} catch (error) {
const errno = (error as { code?: string }).code ?? "unknown";
throw new BailianError(
`Cannot read file: ${filePath}`,
ExitCode.GENERAL,
`File system error (${errno}) — check the path and permissions.`,
);
let hint = `File system error (${errno}) — check the path and permissions.`;
if (
inlineAlternativeFlag &&
errno === "ENOENT" &&
!extname(filePath) &&
!filePath.includes("/") &&
!filePath.includes("\\")
) {
hint += ` If you meant to pass text content directly, use ${inlineAlternativeFlag} instead of --content-file.`;
}
throw new BailianError(`Cannot read file: ${filePath}`, ExitCode.GENERAL, hint);
}
try {
return new TextDecoder("utf-8", { fatal: true }).decode(fileBuffer);