forked from bai/curriculum-project-hub
fix: allow arbitrary workspace file delivery
This commit is contained in:
@@ -15,6 +15,7 @@ export interface FileDeliveryToolOptions {
|
||||
readonly runId: string;
|
||||
readonly workspaceRoot?: string | undefined;
|
||||
readonly workspaceDir: string;
|
||||
readonly maxFileBytes?: number | undefined;
|
||||
readonly sendOptions?: SendMessageOptions | undefined;
|
||||
readonly approvalManager: ApprovalManager;
|
||||
readonly onDelivered?: (path: string) => void;
|
||||
@@ -29,7 +30,7 @@ export function createFileDeliveryMcpServer(options: FileDeliveryToolOptions): M
|
||||
tools.push(
|
||||
tool(
|
||||
"send_file",
|
||||
"Upload an existing file from the current project workspace to the current Feishu chat. The path must point to a concrete no-symlink file, for example build/student.pdf or README.md.",
|
||||
"Upload any existing regular file from the current project workspace to the current Feishu chat. The path must point to a concrete no-symlink file and must not be inside platform runtime directories.",
|
||||
{
|
||||
path: z.string().describe("Workspace-relative path, or an absolute path physically inside the current workspace."),
|
||||
name: z.string().optional().describe("Optional display filename. Defaults to the file's basename."),
|
||||
@@ -46,12 +47,18 @@ export function createFileDeliveryMcpServer(options: FileDeliveryToolOptions): M
|
||||
content: [{ type: "text", text: "File delivery is unavailable because workspace isolation is not configured." }],
|
||||
};
|
||||
}
|
||||
if (options.maxFileBytes === undefined) {
|
||||
throw new Error("Agent file delivery requires a configured maximum file size");
|
||||
}
|
||||
let file;
|
||||
try {
|
||||
file = await resolveDeliverableFile(args.path, workspaceRoot, options.workspaceDir);
|
||||
file = await resolveDeliverableFile(args.path, workspaceRoot, options.workspaceDir, options.maxFileBytes);
|
||||
} catch (error) {
|
||||
const boundaryViolation = error instanceof WorkspaceFileBoundaryError && error.reason === "boundary";
|
||||
const log = boundaryViolation ? options.rt.logger.warn.bind(options.rt.logger) : options.rt.logger.error.bind(options.rt.logger);
|
||||
const limitViolation = error instanceof WorkspaceFileBoundaryError && error.reason === "limit";
|
||||
const log = boundaryViolation || limitViolation
|
||||
? options.rt.logger.warn.bind(options.rt.logger)
|
||||
: options.rt.logger.error.bind(options.rt.logger);
|
||||
log(
|
||||
{
|
||||
runId: options.runId,
|
||||
@@ -61,12 +68,20 @@ export function createFileDeliveryMcpServer(options: FileDeliveryToolOptions): M
|
||||
},
|
||||
boundaryViolation
|
||||
? "Agent file delivery refused by workspace boundary"
|
||||
: "Agent file delivery failed during workspace file access",
|
||||
: limitViolation
|
||||
? "Agent file delivery refused by file size limit"
|
||||
: "Agent file delivery failed during workspace file access",
|
||||
);
|
||||
if (limitViolation) {
|
||||
return {
|
||||
isError: true,
|
||||
content: [{ type: "text", text: `File exceeds the configured delivery limit: ${args.path}` }],
|
||||
};
|
||||
}
|
||||
if (!boundaryViolation) throw error;
|
||||
return {
|
||||
isError: true,
|
||||
content: [{ type: "text", text: "File path is outside the current workspace or uses a symlink." }],
|
||||
content: [{ type: "text", text: "File path is outside the current workspace, belongs to platform runtime, or uses a symlink." }],
|
||||
};
|
||||
}
|
||||
if (file === null) {
|
||||
|
||||
Reference in New Issue
Block a user