mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-10 00:33:31 +00:00
fix(tools): honor tools.fs.workspaceOnly=false for host write/edit (#28822)
Merged via squash.
Prepared head SHA: 83d432961d
Co-authored-by: lailoo <20536249+lailoo@users.noreply.github.com>
Co-authored-by: velvet-shark <126378+velvet-shark@users.noreply.github.com>
Reviewed-by: @velvet-shark
This commit is contained in:
@@ -667,16 +667,16 @@ export function createSandboxedEditTool(params: SandboxToolParams) {
|
||||
return wrapToolParamNormalization(base, CLAUDE_PARAM_GROUPS.edit);
|
||||
}
|
||||
|
||||
export function createHostWorkspaceWriteTool(root: string) {
|
||||
export function createHostWorkspaceWriteTool(root: string, options?: { workspaceOnly?: boolean }) {
|
||||
const base = createWriteTool(root, {
|
||||
operations: createHostWriteOperations(root),
|
||||
operations: createHostWriteOperations(root, options),
|
||||
}) as unknown as AnyAgentTool;
|
||||
return wrapToolParamNormalization(base, CLAUDE_PARAM_GROUPS.write);
|
||||
}
|
||||
|
||||
export function createHostWorkspaceEditTool(root: string) {
|
||||
export function createHostWorkspaceEditTool(root: string, options?: { workspaceOnly?: boolean }) {
|
||||
const base = createEditTool(root, {
|
||||
operations: createHostEditOperations(root),
|
||||
operations: createHostEditOperations(root, options),
|
||||
}) as unknown as AnyAgentTool;
|
||||
return wrapToolParamNormalization(base, CLAUDE_PARAM_GROUPS.edit);
|
||||
}
|
||||
@@ -757,7 +757,26 @@ function createSandboxEditOperations(params: SandboxToolParams) {
|
||||
} as const;
|
||||
}
|
||||
|
||||
function createHostWriteOperations(root: string) {
|
||||
function createHostWriteOperations(root: string, options?: { workspaceOnly?: boolean }) {
|
||||
const workspaceOnly = options?.workspaceOnly !== false;
|
||||
|
||||
if (!workspaceOnly) {
|
||||
// When workspaceOnly is false, allow writes anywhere on the host
|
||||
return {
|
||||
mkdir: async (dir: string) => {
|
||||
const resolved = path.resolve(dir);
|
||||
await fs.mkdir(resolved, { recursive: true });
|
||||
},
|
||||
writeFile: async (absolutePath: string, content: string) => {
|
||||
const resolved = path.resolve(absolutePath);
|
||||
const dir = path.dirname(resolved);
|
||||
await fs.mkdir(dir, { recursive: true });
|
||||
await fs.writeFile(resolved, content, "utf-8");
|
||||
},
|
||||
} as const;
|
||||
}
|
||||
|
||||
// When workspaceOnly is true (default), enforce workspace boundary
|
||||
return {
|
||||
mkdir: async (dir: string) => {
|
||||
const relative = toRelativePathInRoot(root, dir, { allowRoot: true });
|
||||
@@ -777,7 +796,30 @@ function createHostWriteOperations(root: string) {
|
||||
} as const;
|
||||
}
|
||||
|
||||
function createHostEditOperations(root: string) {
|
||||
function createHostEditOperations(root: string, options?: { workspaceOnly?: boolean }) {
|
||||
const workspaceOnly = options?.workspaceOnly !== false;
|
||||
|
||||
if (!workspaceOnly) {
|
||||
// When workspaceOnly is false, allow edits anywhere on the host
|
||||
return {
|
||||
readFile: async (absolutePath: string) => {
|
||||
const resolved = path.resolve(absolutePath);
|
||||
return await fs.readFile(resolved);
|
||||
},
|
||||
writeFile: async (absolutePath: string, content: string) => {
|
||||
const resolved = path.resolve(absolutePath);
|
||||
const dir = path.dirname(resolved);
|
||||
await fs.mkdir(dir, { recursive: true });
|
||||
await fs.writeFile(resolved, content, "utf-8");
|
||||
},
|
||||
access: async (absolutePath: string) => {
|
||||
const resolved = path.resolve(absolutePath);
|
||||
await fs.access(resolved);
|
||||
},
|
||||
} as const;
|
||||
}
|
||||
|
||||
// When workspaceOnly is true (default), enforce workspace boundary
|
||||
return {
|
||||
readFile: async (absolutePath: string) => {
|
||||
const relative = toRelativePathInRoot(root, absolutePath);
|
||||
|
||||
Reference in New Issue
Block a user