patchpal 0.25.0__tar.gz → 0.25.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {patchpal-0.25.0/patchpal.egg-info → patchpal-0.25.1}/PKG-INFO +1 -1
- {patchpal-0.25.0 → patchpal-0.25.1}/patchpal/__init__.py +1 -1
- {patchpal-0.25.0 → patchpal-0.25.1}/patchpal/cli/sandbox.py +85 -13
- {patchpal-0.25.0 → patchpal-0.25.1/patchpal.egg-info}/PKG-INFO +1 -1
- {patchpal-0.25.0 → patchpal-0.25.1}/LICENSE +0 -0
- {patchpal-0.25.0 → patchpal-0.25.1}/MANIFEST.in +0 -0
- {patchpal-0.25.0 → patchpal-0.25.1}/README.md +0 -0
- {patchpal-0.25.0 → patchpal-0.25.1}/patchpal/agent/__init__.py +0 -0
- {patchpal-0.25.0 → patchpal-0.25.1}/patchpal/agent/bedrock_profile_utils.py +0 -0
- {patchpal-0.25.0 → patchpal-0.25.1}/patchpal/agent/function_calling.py +0 -0
- {patchpal-0.25.0 → patchpal-0.25.1}/patchpal/agent/react.py +0 -0
- {patchpal-0.25.0 → patchpal-0.25.1}/patchpal/cli/__init__.py +0 -0
- {patchpal-0.25.0 → patchpal-0.25.1}/patchpal/cli/autopilot.py +0 -0
- {patchpal-0.25.0 → patchpal-0.25.1}/patchpal/cli/interactive.py +0 -0
- {patchpal-0.25.0 → patchpal-0.25.1}/patchpal/cli/mcp.py +0 -0
- {patchpal-0.25.0 → patchpal-0.25.1}/patchpal/cli/streaming.py +0 -0
- {patchpal-0.25.0 → patchpal-0.25.1}/patchpal/config.py +0 -0
- {patchpal-0.25.0 → patchpal-0.25.1}/patchpal/context.py +0 -0
- {patchpal-0.25.0 → patchpal-0.25.1}/patchpal/permissions.py +0 -0
- {patchpal-0.25.0 → patchpal-0.25.1}/patchpal/prompts/react_prompt.md +0 -0
- {patchpal-0.25.0 → patchpal-0.25.1}/patchpal/prompts/system_prompt.md +0 -0
- {patchpal-0.25.0 → patchpal-0.25.1}/patchpal/skills.py +0 -0
- {patchpal-0.25.0 → patchpal-0.25.1}/patchpal/tools/__init__.py +0 -0
- {patchpal-0.25.0 → patchpal-0.25.1}/patchpal/tools/audit.py +0 -0
- {patchpal-0.25.0 → patchpal-0.25.1}/patchpal/tools/browser_tools.py +0 -0
- {patchpal-0.25.0 → patchpal-0.25.1}/patchpal/tools/code_analysis.py +0 -0
- {patchpal-0.25.0 → patchpal-0.25.1}/patchpal/tools/common.py +0 -0
- {patchpal-0.25.0 → patchpal-0.25.1}/patchpal/tools/definitions.py +0 -0
- {patchpal-0.25.0 → patchpal-0.25.1}/patchpal/tools/file_reading.py +0 -0
- {patchpal-0.25.0 → patchpal-0.25.1}/patchpal/tools/file_writing.py +0 -0
- {patchpal-0.25.0 → patchpal-0.25.1}/patchpal/tools/find_tool.py +0 -0
- {patchpal-0.25.0 → patchpal-0.25.1}/patchpal/tools/grep_tool.py +0 -0
- {patchpal-0.25.0 → patchpal-0.25.1}/patchpal/tools/image_handler.py +0 -0
- {patchpal-0.25.0 → patchpal-0.25.1}/patchpal/tools/mcp.py +0 -0
- {patchpal-0.25.0 → patchpal-0.25.1}/patchpal/tools/repo_map.py +0 -0
- {patchpal-0.25.0 → patchpal-0.25.1}/patchpal/tools/shell_tools.py +0 -0
- {patchpal-0.25.0 → patchpal-0.25.1}/patchpal/tools/todo_tools.py +0 -0
- {patchpal-0.25.0 → patchpal-0.25.1}/patchpal/tools/tool_schema.py +0 -0
- {patchpal-0.25.0 → patchpal-0.25.1}/patchpal/tools/user_interaction.py +0 -0
- {patchpal-0.25.0 → patchpal-0.25.1}/patchpal/tools/web_tools.py +0 -0
- {patchpal-0.25.0 → patchpal-0.25.1}/patchpal.egg-info/SOURCES.txt +0 -0
- {patchpal-0.25.0 → patchpal-0.25.1}/patchpal.egg-info/dependency_links.txt +0 -0
- {patchpal-0.25.0 → patchpal-0.25.1}/patchpal.egg-info/entry_points.txt +0 -0
- {patchpal-0.25.0 → patchpal-0.25.1}/patchpal.egg-info/requires.txt +0 -0
- {patchpal-0.25.0 → patchpal-0.25.1}/patchpal.egg-info/top_level.txt +0 -0
- {patchpal-0.25.0 → patchpal-0.25.1}/pyproject.toml +0 -0
- {patchpal-0.25.0 → patchpal-0.25.1}/setup.cfg +0 -0
- {patchpal-0.25.0 → patchpal-0.25.1}/tests/test_agent.py +0 -0
- {patchpal-0.25.0 → patchpal-0.25.1}/tests/test_cli.py +0 -0
- {patchpal-0.25.0 → patchpal-0.25.1}/tests/test_config_dynamic.py +0 -0
- {patchpal-0.25.0 → patchpal-0.25.1}/tests/test_context.py +0 -0
- {patchpal-0.25.0 → patchpal-0.25.1}/tests/test_custom_tools.py +0 -0
- {patchpal-0.25.0 → patchpal-0.25.1}/tests/test_enabled_tools.py +0 -0
- {patchpal-0.25.0 → patchpal-0.25.1}/tests/test_find_tool.py +0 -0
- {patchpal-0.25.0 → patchpal-0.25.1}/tests/test_guardrails.py +0 -0
- {patchpal-0.25.0 → patchpal-0.25.1}/tests/test_image_blocking.py +0 -0
- {patchpal-0.25.0 → patchpal-0.25.1}/tests/test_maximum_security.py +0 -0
- {patchpal-0.25.0 → patchpal-0.25.1}/tests/test_mcp_config.py +0 -0
- {patchpal-0.25.0 → patchpal-0.25.1}/tests/test_memory.py +0 -0
- {patchpal-0.25.0 → patchpal-0.25.1}/tests/test_memory_locations.py +0 -0
- {patchpal-0.25.0 → patchpal-0.25.1}/tests/test_operational_safety.py +0 -0
- {patchpal-0.25.0 → patchpal-0.25.1}/tests/test_optional_tools.py +0 -0
- {patchpal-0.25.0 → patchpal-0.25.1}/tests/test_permissions.py +0 -0
- {patchpal-0.25.0 → patchpal-0.25.1}/tests/test_react.py +0 -0
- {patchpal-0.25.0 → patchpal-0.25.1}/tests/test_reasoning_content.py +0 -0
- {patchpal-0.25.0 → patchpal-0.25.1}/tests/test_repo_map.py +0 -0
- {patchpal-0.25.0 → patchpal-0.25.1}/tests/test_simplified_prompt.py +0 -0
- {patchpal-0.25.0 → patchpal-0.25.1}/tests/test_skills.py +0 -0
- {patchpal-0.25.0 → patchpal-0.25.1}/tests/test_streaming.py +0 -0
- {patchpal-0.25.0 → patchpal-0.25.1}/tests/test_tools.py +0 -0
|
@@ -106,6 +106,13 @@ def is_interactive_command(patchpal_args):
|
|
|
106
106
|
return False
|
|
107
107
|
|
|
108
108
|
# Check for autopilot
|
|
109
|
+
# NOTE: Only patchpal_args[0] is checked (not a scan of the whole list) by
|
|
110
|
+
# design. The "--" convention guarantees everything after it is passed to
|
|
111
|
+
# patchpal, and the autopilot subcommand, when present, is always the first
|
|
112
|
+
# of those args (e.g. `... -- autopilot --prompt-file task.md`). A later
|
|
113
|
+
# occurrence of the word "autopilot" would be a value/argument, not the
|
|
114
|
+
# subcommand, so it must not flip interactivity. Do not "fix" this into a
|
|
115
|
+
# full-list scan.
|
|
109
116
|
if patchpal_args[0] in ["autopilot", "patchpal-autopilot"]:
|
|
110
117
|
return False
|
|
111
118
|
|
|
@@ -124,6 +131,9 @@ def detect_llm_endpoints_from_env():
|
|
|
124
131
|
standard_endpoints = {
|
|
125
132
|
"OPENAI_API_KEY": "https://api.openai.com",
|
|
126
133
|
"ANTHROPIC_API_KEY": "https://api.anthropic.com",
|
|
134
|
+
# Google Gemini: LiteLLM commonly uses GEMINI_API_KEY; GOOGLE_API_KEY is
|
|
135
|
+
# also supported. Both map to the same generative language endpoint.
|
|
136
|
+
"GEMINI_API_KEY": "https://generativelanguage.googleapis.com",
|
|
127
137
|
"GOOGLE_API_KEY": "https://generativelanguage.googleapis.com",
|
|
128
138
|
"GROQ_API_KEY": "https://api.groq.com",
|
|
129
139
|
"COHERE_API_KEY": "https://api.cohere.ai",
|
|
@@ -574,6 +584,14 @@ def build_container_args(sandbox_args, patchpal_args, env_file_keys=None):
|
|
|
574
584
|
if sandbox_args.cpus:
|
|
575
585
|
container_args.extend(["--cpus", str(sandbox_args.cpus)])
|
|
576
586
|
|
|
587
|
+
# Forward user-specified volume mounts (-v/--volume) and environment
|
|
588
|
+
# variables (-e/--env). These are passed through verbatim to the container
|
|
589
|
+
# runtime, mirroring docker/podman's own -v and -e semantics.
|
|
590
|
+
for volume in getattr(sandbox_args, "volume", []) or []:
|
|
591
|
+
container_args.extend(["-v", volume])
|
|
592
|
+
for env_var in getattr(sandbox_args, "env", []) or []:
|
|
593
|
+
container_args.extend(["-e", env_var])
|
|
594
|
+
|
|
577
595
|
# Pass through environment variables.
|
|
578
596
|
# WITHOUT --env-file: pass through matching vars from the full host environment.
|
|
579
597
|
# WITH --env-file: isolate from the host, but still forward the vars that were
|
|
@@ -788,14 +806,48 @@ def build_container_args(sandbox_args, patchpal_args, env_file_keys=None):
|
|
|
788
806
|
|
|
789
807
|
# Add main patchpal command
|
|
790
808
|
if sandbox_args.restrict_network:
|
|
791
|
-
#
|
|
792
|
-
#
|
|
809
|
+
# Pass through all patchpal args via "$@" (identical to normal mode) so that
|
|
810
|
+
# the autopilot subcommand and flags like --prompt-file / --max-iterations are
|
|
811
|
+
# preserved. The model is included in "$@" when --model was given on the command
|
|
812
|
+
# line. If no --model was provided, fall back to the MODEL/PATCHPAL_MODEL env var
|
|
813
|
+
# (launch-agent-sandbox pattern).
|
|
814
|
+
#
|
|
815
|
+
# SECURITY: Drop CAP_NET_ADMIN / CAP_NET_RAW before handing control to the
|
|
816
|
+
# agent. The container runs as root (UID 0) and keeps the capability that
|
|
817
|
+
# was used to build the iptables firewall. Without dropping it, an agent
|
|
818
|
+
# that can execute shell commands could simply run `iptables -F OUTPUT`
|
|
819
|
+
# (or `iptables -P OUTPUT ACCEPT`) and dismantle the very network
|
|
820
|
+
# restrictions that --restrict-network is supposed to enforce.
|
|
821
|
+
#
|
|
822
|
+
# We drop the capabilities from BOTH the inheritable and bounding sets via
|
|
823
|
+
# setpriv. For a root process this also clears them from the effective and
|
|
824
|
+
# permitted sets, and removing them from the bounding set means they cannot
|
|
825
|
+
# be re-raised afterwards. The firewall rules remain in force, but the
|
|
826
|
+
# agent process can no longer modify netfilter. (setpriv ships with
|
|
827
|
+
# util-linux and is present in the sandbox image; if it is somehow missing
|
|
828
|
+
# we fall back to a plain exec and warn loudly.)
|
|
829
|
+
if model_arg:
|
|
830
|
+
# --model is already present in the pass-through args
|
|
831
|
+
model_display = model_arg
|
|
832
|
+
run_cmd = f'{patchpal_cmd} "$@"'
|
|
833
|
+
else:
|
|
834
|
+
# No --model on command line; supply it from the env var fallback
|
|
835
|
+
model_display = "${MODEL:-$PATCHPAL_MODEL}"
|
|
836
|
+
run_cmd = f'{patchpal_cmd} --model "${{MODEL:-$PATCHPAL_MODEL}}" "$@"'
|
|
793
837
|
shell_cmd_parts.append(f"""
|
|
794
838
|
echo ""
|
|
795
839
|
echo "=== Starting PatchPal ==="
|
|
796
|
-
echo "Model:
|
|
840
|
+
echo "Model: {model_display}"
|
|
797
841
|
echo ""
|
|
798
|
-
|
|
842
|
+
if command -v setpriv >/dev/null 2>&1; then
|
|
843
|
+
echo "Dropping CAP_NET_ADMIN/CAP_NET_RAW before starting agent (firewall now tamper-resistant)"
|
|
844
|
+
exec setpriv --inh-caps -net_admin,-net_raw --bounding-set -net_admin,-net_raw \\
|
|
845
|
+
{run_cmd}
|
|
846
|
+
else
|
|
847
|
+
echo "⚠️ WARNING: setpriv not found - cannot drop network capabilities."
|
|
848
|
+
echo "⚠️ The agent runs as root and could tamper with the iptables firewall."
|
|
849
|
+
exec {run_cmd}
|
|
850
|
+
fi
|
|
799
851
|
""")
|
|
800
852
|
else:
|
|
801
853
|
# Normal mode: use $@ to pass all arguments
|
|
@@ -813,13 +865,9 @@ exec {patchpal_cmd} "$@"
|
|
|
813
865
|
# Add set -e at the beginning to exit on any error
|
|
814
866
|
shell_cmd = "set -e\n" + "\n".join(shell_cmd_parts)
|
|
815
867
|
|
|
816
|
-
# Pass
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
container_args.extend(["bash", "-c", shell_cmd])
|
|
820
|
-
else:
|
|
821
|
-
# Without restrictions: pass args after -- separator
|
|
822
|
-
container_args.extend(["bash", "-c", shell_cmd, "--"] + patchpal_cmd_args)
|
|
868
|
+
# Pass patchpal args after the "--" separator so they populate "$@" in the
|
|
869
|
+
# bash script. This applies to both restricted and unrestricted modes.
|
|
870
|
+
container_args.extend(["bash", "-c", shell_cmd, "--"] + patchpal_cmd_args)
|
|
823
871
|
|
|
824
872
|
return container_args, runtime
|
|
825
873
|
|
|
@@ -853,14 +901,20 @@ SCRIPT OPTIONS:
|
|
|
853
901
|
--host-network Use host network (for local Ollama/vLLM servers on localhost)
|
|
854
902
|
--memory LIMIT Memory limit (e.g., 2g, 4g) - optional, no limit by default
|
|
855
903
|
--cpus NUM CPU limit (e.g., 2, 4) - optional, no limit by default
|
|
856
|
-
--env-file FILE Load environment variables from .env file
|
|
904
|
+
--env-file FILE Load environment variables from .env file (single file)
|
|
905
|
+
-v, --volume HOST:CONTAINER[:ro]
|
|
906
|
+
Mount an additional volume (can be specified multiple times)
|
|
907
|
+
Example: -v /path/to/data:/data:ro
|
|
908
|
+
-e, --env KEY=VALUE Pass an additional environment variable into the container
|
|
909
|
+
(can be specified multiple times)
|
|
910
|
+
Example: -e MY_VAR=value
|
|
857
911
|
--restrict-network Enable iptables firewall for network isolation
|
|
858
912
|
Automatically detects LLM endpoints from environment variables:
|
|
859
913
|
* OpenAI (OPENAI_API_KEY)
|
|
860
914
|
* Anthropic (ANTHROPIC_API_KEY)
|
|
861
915
|
* AWS Bedrock (AWS_REGION + AWS_ACCESS_KEY_ID)
|
|
862
916
|
* Azure OpenAI (AZURE_OPENAI_KEY + AZURE_OPENAI_RESOURCE)
|
|
863
|
-
* Google AI (GOOGLE_API_KEY)
|
|
917
|
+
* Google AI (GEMINI_API_KEY or GOOGLE_API_KEY)
|
|
864
918
|
* Groq, Cohere, Together, Replicate, etc.
|
|
865
919
|
Custom endpoints also detected from *_BASE_URL env vars
|
|
866
920
|
--allow-url URL Allow network access to specific URL (can be specified multiple times)
|
|
@@ -1126,6 +1180,24 @@ def main():
|
|
|
1126
1180
|
action="store_true",
|
|
1127
1181
|
help="Drop into bash shell instead of running patchpal (for debugging)",
|
|
1128
1182
|
)
|
|
1183
|
+
parser.add_argument(
|
|
1184
|
+
"-v",
|
|
1185
|
+
"--volume",
|
|
1186
|
+
action="append",
|
|
1187
|
+
default=[],
|
|
1188
|
+
metavar="HOST:CONTAINER[:ro]",
|
|
1189
|
+
help="Mount an additional volume into the container (can be specified multiple times). "
|
|
1190
|
+
"Example: -v /path/to/data:/data:ro",
|
|
1191
|
+
)
|
|
1192
|
+
parser.add_argument(
|
|
1193
|
+
"-e",
|
|
1194
|
+
"--env",
|
|
1195
|
+
action="append",
|
|
1196
|
+
default=[],
|
|
1197
|
+
metavar="KEY=VALUE",
|
|
1198
|
+
help="Pass an additional environment variable into the container "
|
|
1199
|
+
"(can be specified multiple times). Example: -e MY_VAR=value",
|
|
1200
|
+
)
|
|
1129
1201
|
|
|
1130
1202
|
try:
|
|
1131
1203
|
sandbox_args = parser.parse_args(sandbox_argv)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|