22
33from __future__ import annotations
44
5- import dataclasses , fnmatch , hashlib , hmac , os , re , secrets , shutil , signal , subprocess , sys , time
5+ import dataclasses , fnmatch , hashlib , hmac , os , re , secrets , shlex , shutil , signal , subprocess , sys , time
66from pathlib import Path
77from typing import Any , Callable , Dict , Iterable , List , Literal , Mapping , Optional , Sequence , Tuple , Union
88
@@ -18,8 +18,6 @@ def __init__(self, tool: str, request_id: Optional[str] = None) -> None:
1818 super ().__init__ (f"Confirmation required for { tool !r} ; pass confirm=True." )
1919 self .tool = tool
2020 self .request_id = request_id
21- self .tool = tool
22- self .request_id = request_id
2321
2422
2523_GLOB_SAFE = re .compile (r"^[A-Za-z0-9_./?!\*\-]+$" )
@@ -173,6 +171,7 @@ def _check_write_path(
173171 * ,
174172 policy : WritePolicy ,
175173 original : Optional [Path ] = None ,
174+ workspace_root : Optional [str ] = None ,
176175) -> None :
177176 if policy .follow_symlinks == "reject" :
178177 # Walk the *unresolved* path's components — symlinks at the leaf or
@@ -197,7 +196,7 @@ def _check_write_path(
197196 _check_realpath (
198197 real ,
199198 policy_deny = ReadPolicy ().deny_paths ,
200- workspace = policy .require_cwd_under ,
199+ workspace = workspace_root or policy .require_cwd_under ,
201200 )
202201def read_file (path : str , encoding : str = "utf-8" , offset : int = 0 , limit : Optional [int ] = None , * , policy : Optional [ReadPolicy ] = None , workspace_root : Optional [str ] = None ) -> str :
203202 if offset < 0 :
@@ -206,7 +205,8 @@ def read_file(path: str, encoding: str = "utf-8", offset: int = 0, limit: Option
206205 raise InvalidInputError ("limit exceeds 10 MiB cap" )
207206 p = _reject_nul (path , arg_name = "path" )
208207 real = _canonical (p )
209- _check_realpath (real , policy_deny = (policy or ReadPolicy ()).deny_paths , workspace = workspace_root )
208+ eff = policy or ReadPolicy ()
209+ _check_realpath (real , policy_deny = eff .deny_paths , workspace = workspace_root )
210210 try :
211211 if real .is_dir ():
212212 return f"Error: { real } is a directory; use list_directory"
@@ -424,10 +424,7 @@ def _atomic_write(
424424 mode = "wb" if isinstance (content , bytes ) else "w"
425425 enc = None if isinstance (content , bytes ) else encoding
426426 with open (tmp , mode , encoding = enc ) as fh :
427- if isinstance (content , str ):
428- fh .write (content )
429- else :
430- fh .write (content )
427+ fh .write (content )
431428 fh .flush ()
432429 os .fsync (fh .fileno ())
433430 os .replace (tmp , target )
@@ -452,7 +449,7 @@ def write_file(
452449 eff = policy or WritePolicy ()
453450 real = _canonical (p )
454451 leaf = Path (p )
455- _check_write_path (real , policy = eff , original = leaf )
452+ _check_write_path (real , policy = eff , original = leaf , workspace_root = workspace_root )
456453 if eff .require_confirm and mode != "fail" and not confirm :
457454 raise ConfirmationRequired (tool = "write_file" )
458455 if mode == "overwrite" and not confirm and eff .require_confirm :
@@ -501,7 +498,7 @@ def edit_file(
501498 raise InvalidInputError ("'new' exceeds content size cap" )
502499 real = _canonical (p )
503500 leaf = Path (p )
504- _check_write_path (real , policy = eff , original = leaf )
501+ _check_write_path (real , policy = eff , original = leaf , workspace_root = workspace_root )
505502 if eff .require_confirm and not confirm :
506503 raise ConfirmationRequired (tool = "edit_file" )
507504 text = real .read_text (encoding = "utf-8" )
@@ -536,7 +533,7 @@ def delete_file(
536533 eff = policy or WritePolicy ()
537534 real = _canonical (p )
538535 leaf = Path (p )
539- _check_write_path (real , policy = eff , original = leaf )
536+ _check_write_path (real , policy = eff , original = leaf , workspace_root = workspace_root )
540537 if eff .require_confirm and not confirm :
541538 raise ConfirmationRequired (tool = "delete_file" )
542539 if real .is_dir () and not recursive :
@@ -696,14 +693,12 @@ def create_computer_use_tools(
696693 shell_policy : ShellPolicy = None ,
697694):
698695 """Create all computer-use tools pre-configured for a workspace."""
699- import re , shlex
700696 workspace_root = workspace_root or os .environ .get ("COMPUTER_USE_WORKSPACE" ,
701- os . path . dirname ( os . path . dirname ( os . path . abspath ( __file__ )) ) if "/examples/tools" in os .getcwd () else os .getcwd ())
697+ str ( Path ( __file__ ). resolve (). parent . parent . parent ) if "/examples/tools" in os .getcwd () else os .getcwd ())
702698 write_policy = write_policy or WritePolicy (require_confirm = False , follow_symlinks = "reject" )
703699 shell_policy = shell_policy or ShellPolicy (cwd_extra = frozenset ({"/tmp" , workspace_root }))
704700 def _run_cmd (cmd : str ):
705701 """Run a shell command."""
706- import re , shlex
707702 argv = shlex .split (re .sub (r'^\s*cd\s+\S+\s*&&\s*' , '' , cmd .strip ()))
708703 return run_command (argv = argv , cwd = workspace_root , policy = shell_policy )
709704 def _read (path : str ):
0 commit comments