Spaces:
Running
Running
File size: 5,580 Bytes
9d0fd45 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 | """Runtime discovery and prompt guidance for the baked document Node toolchain."""
from __future__ import annotations
import json
import os
import shutil
import subprocess
from dataclasses import dataclass
from pathlib import Path
from plugins.tools._sandbox import is_e2b_available
_DOCUMENT_PACKAGES = ("docx", "pptxgenjs")
_BWRAP_VISIBLE_ROOTS = (
Path("/usr"),
Path("/bin"),
Path("/lib"),
Path("/lib64"),
Path("/etc"),
)
@dataclass(frozen=True)
class DocumentNodeToolchain:
"""Versions proven available to the active model-authored command backend."""
node_version: str
docx_version: str
pptxgenjs_version: str
def _is_within(path: Path, roots: tuple[Path, ...]) -> bool:
resolved = path.resolve()
for root in roots:
try:
resolved.relative_to(root)
return True
except ValueError:
continue
return False
def _package_version(package: str, roots: tuple[Path, ...]) -> tuple[str, Path] | None:
for root in roots:
manifest = root / package / "package.json"
if not manifest.is_file():
continue
try:
data = json.loads(manifest.read_text(encoding="utf-8"))
except (OSError, json.JSONDecodeError, TypeError):
continue
version = str(data.get("version") or "").strip()
if version:
return version, manifest
return None
def discover_document_node_toolchain(
*,
sandbox_mode: str,
) -> DocumentNodeToolchain | None:
"""Return the document Node stack visible to model-authored commands.
``container`` executes in the current image. ``bwrap`` (and ``auto``
without E2B) can see only paths mounted into the jail, so both the Node
binary and module roots must live under its read-only system mounts.
Explicit/auto E2B executes off-host and must never inherit a capability
merely because the serving process itself has it installed.
"""
mode = (sandbox_mode or "auto").strip().lower()
if mode not in {"container", "native", "bwrap"} and is_e2b_available():
return None
node_path = os.environ.get("NODE_PATH", "").strip()
path_env = os.environ.get("PATH", "").strip()
if not node_path or not path_env:
return None
roots = tuple(
Path(raw.strip()).expanduser()
for raw in node_path.split(os.pathsep)
if raw.strip()
)
node = shutil.which("node", path=path_env)
if not roots or not node:
return None
packages = {
package: _package_version(package, roots)
for package in _DOCUMENT_PACKAGES
}
if any(value is None for value in packages.values()):
return None
if mode not in {"container", "native"}:
visible_paths = [
Path(node),
*(value[1] for value in packages.values() if value is not None),
]
if not all(_is_within(path, _BWRAP_VISIBLE_ROOTS) for path in visible_paths):
return None
try:
result = subprocess.run(
[node, "--version"],
capture_output=True,
text=True,
timeout=2,
check=False,
env={"PATH": path_env, "NODE_PATH": node_path},
)
except (OSError, subprocess.SubprocessError):
return None
if result.returncode != 0:
return None
node_version = result.stdout.strip().removeprefix("v")
if not node_version:
return None
docx = packages["docx"]
pptxgenjs = packages["pptxgenjs"]
assert docx is not None and pptxgenjs is not None
return DocumentNodeToolchain(
node_version=node_version,
docx_version=docx[0],
pptxgenjs_version=pptxgenjs[0],
)
def render_document_node_toolchain_note(
*,
sandbox_mode: str,
tool_names: list[str] | tuple[str, ...],
audience: str = "agent",
) -> str:
"""Render a truthful prompt note for agents that can execute ``bash``."""
names = {str(name) for name in tool_names}
if "bash" not in names:
return ""
capability = discover_document_node_toolchain(sandbox_mode=sandbox_mode)
if capability is None:
return ""
if "create_file" in names:
fallback = (
"Use this fallback order: (1) `create_file`; (2) Python libraries "
"such as `python-docx` or `python-pptx` only after `create_file` "
"explicitly reports the required operation unsupported; (3) these "
"Node packages only when Python still cannot cover the operation, "
"or when the user requires accurate preservation of an existing "
"template that the Python path would not preserve."
)
else:
fallback = (
"`create_file` is not available in this tool set. Use Python "
"libraries first; use these Node packages only when Python cannot "
"cover the required operation, or when the user requires accurate "
"preservation of an existing template that Python would not "
"preserve."
)
scope = "sub-agent runtime" if audience == "coordinator" else "runtime-discovered"
return (
f"\n\nDOCUMENT TOOLCHAIN ({scope}): `bash` has Node.js "
f"{capability.node_version}, `docx@{capability.docx_version}`, and "
f"`pptxgenjs@{capability.pptxgenjs_version}`. `NODE_PATH` is already "
"configured: load them as `require('docx')` and "
"`require('pptxgenjs')`; do not hard-code `/usr/lib/node_modules`. "
f"{fallback}"
)
|