feat(workspace): trust resolved project path
This commit is contained in:
@@ -7,6 +7,7 @@ command, default image, and prompt/auth behavior.
|
|||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import json
|
||||||
import os
|
import os
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
@@ -136,9 +137,11 @@ def agent_provision_plan(
|
|||||||
auth_token: str = "",
|
auth_token: str = "",
|
||||||
forward_host_credentials: bool = False,
|
forward_host_credentials: bool = False,
|
||||||
host_env: dict[str, str] | None = None,
|
host_env: dict[str, str] | None = None,
|
||||||
|
trusted_project_path: str = "",
|
||||||
) -> AgentProvisionPlan:
|
) -> AgentProvisionPlan:
|
||||||
runtime = runtime_for(template)
|
runtime = runtime_for(template)
|
||||||
resolved_guest_env = dict(guest_env or {})
|
resolved_guest_env = dict(guest_env or {})
|
||||||
|
trusted_path = trusted_project_path or guest_home
|
||||||
env_vars: dict[str, str] = {}
|
env_vars: dict[str, str] = {}
|
||||||
provisioned_env: dict[str, str] = {}
|
provisioned_env: dict[str, str] = {}
|
||||||
dirs: list[AgentProvisionDir] = []
|
dirs: list[AgentProvisionDir] = []
|
||||||
@@ -156,8 +159,9 @@ def agent_provision_plan(
|
|||||||
dirs.append(AgentProvisionDir(auth_dir))
|
dirs.append(AgentProvisionDir(auth_dir))
|
||||||
config_path = f"{auth_dir}/config.toml"
|
config_path = f"{auth_dir}/config.toml"
|
||||||
config_file = state_dir / "codex-config.toml"
|
config_file = state_dir / "codex-config.toml"
|
||||||
|
toml_path = trusted_path.replace("\\", "\\\\").replace('"', '\\"')
|
||||||
config_file.write_text(
|
config_file.write_text(
|
||||||
f'[projects."{guest_home}"]\n'
|
f'[projects."{toml_path}"]\n'
|
||||||
'trust_level = "trusted"\n'
|
'trust_level = "trusted"\n'
|
||||||
)
|
)
|
||||||
config_file.chmod(0o600)
|
config_file.chmod(0o600)
|
||||||
@@ -202,6 +206,19 @@ def agent_provision_plan(
|
|||||||
if template == PROVIDER_CLAUDE:
|
if template == PROVIDER_CLAUDE:
|
||||||
env_vars["CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC"] = "1"
|
env_vars["CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC"] = "1"
|
||||||
env_vars["DISABLE_ERROR_REPORTING"] = "1"
|
env_vars["DISABLE_ERROR_REPORTING"] = "1"
|
||||||
|
claude_config = state_dir / "claude.json"
|
||||||
|
claude_projects = {
|
||||||
|
guest_home: {"hasTrustDialogAccepted": True},
|
||||||
|
}
|
||||||
|
claude_projects[trusted_path] = {"hasTrustDialogAccepted": True}
|
||||||
|
claude_config.write_text(json.dumps({
|
||||||
|
"hasCompletedOnboarding": True,
|
||||||
|
"theme": "dark",
|
||||||
|
"bypassPermissionsModeAccepted": True,
|
||||||
|
"projects": claude_projects,
|
||||||
|
}, indent=2) + "\n")
|
||||||
|
claude_config.chmod(0o600)
|
||||||
|
files.append(AgentProvisionFile(claude_config, f"{guest_home}/.claude.json"))
|
||||||
egress_routes.append(EgressRoute(
|
egress_routes.append(EgressRoute(
|
||||||
host="api.anthropic.com",
|
host="api.anthropic.com",
|
||||||
auth_scheme="Bearer" if auth_token else "",
|
auth_scheme="Bearer" if auth_token else "",
|
||||||
|
|||||||
@@ -184,6 +184,7 @@ def resolve_plan(
|
|||||||
forward_host_credentials=provider.forward_host_credentials,
|
forward_host_credentials=provider.forward_host_credentials,
|
||||||
auth_token=provider.auth_token,
|
auth_token=provider.auth_token,
|
||||||
host_env=dict(os.environ),
|
host_env=dict(os.environ),
|
||||||
|
trusted_project_path=workspace_plan.workdir,
|
||||||
)
|
)
|
||||||
guest_env = dict(agent_provision.guest_env)
|
guest_env = dict(agent_provision.guest_env)
|
||||||
for key, val in agent_provision.env_vars.items():
|
for key, val in agent_provision.env_vars.items():
|
||||||
|
|||||||
@@ -138,6 +138,7 @@ def resolve_plan(
|
|||||||
forward_host_credentials=provider.forward_host_credentials,
|
forward_host_credentials=provider.forward_host_credentials,
|
||||||
auth_token=provider.auth_token,
|
auth_token=provider.auth_token,
|
||||||
host_env=dict(os.environ),
|
host_env=dict(os.environ),
|
||||||
|
trusted_project_path=workspace_plan.workdir,
|
||||||
)
|
)
|
||||||
merged_guest_env = dict(agent_provision.guest_env)
|
merged_guest_env = dict(agent_provision.guest_env)
|
||||||
for key, val in agent_provision.env_vars.items():
|
for key, val in agent_provision.env_vars.items():
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ class TestAgentProviderRuntime(unittest.TestCase):
|
|||||||
dockerfile="/tmp/Dockerfile.codex",
|
dockerfile="/tmp/Dockerfile.codex",
|
||||||
state_dir=Path(tmp),
|
state_dir=Path(tmp),
|
||||||
)
|
)
|
||||||
|
config = Path(tmp, "codex-config.toml").read_text()
|
||||||
self.assertEqual("codex", plan.template)
|
self.assertEqual("codex", plan.template)
|
||||||
self.assertEqual("codex", plan.command)
|
self.assertEqual("codex", plan.command)
|
||||||
self.assertEqual("read_prompt_file", plan.prompt_mode)
|
self.assertEqual("read_prompt_file", plan.prompt_mode)
|
||||||
@@ -45,6 +46,18 @@ class TestAgentProviderRuntime(unittest.TestCase):
|
|||||||
("/home/node/.codex/config.toml",),
|
("/home/node/.codex/config.toml",),
|
||||||
tuple(f.guest_path for f in plan.files),
|
tuple(f.guest_path for f in plan.files),
|
||||||
)
|
)
|
||||||
|
self.assertIn('[projects."/home/node"]', config)
|
||||||
|
|
||||||
|
def test_codex_trusts_requested_project_path(self):
|
||||||
|
with tempfile.TemporaryDirectory(prefix="bb-provider.") as tmp:
|
||||||
|
agent_provision_plan(
|
||||||
|
template="codex",
|
||||||
|
dockerfile="",
|
||||||
|
state_dir=Path(tmp),
|
||||||
|
trusted_project_path="/home/node/workspace",
|
||||||
|
)
|
||||||
|
config = Path(tmp, "codex-config.toml").read_text()
|
||||||
|
self.assertIn('[projects."/home/node/workspace"]', config)
|
||||||
|
|
||||||
def test_codex_forward_host_credentials_adds_auth_and_verify(self):
|
def test_codex_forward_host_credentials_adds_auth_and_verify(self):
|
||||||
with tempfile.TemporaryDirectory(prefix="bb-provider.") as tmp:
|
with tempfile.TemporaryDirectory(prefix="bb-provider.") as tmp:
|
||||||
@@ -79,6 +92,7 @@ class TestAgentProviderRuntime(unittest.TestCase):
|
|||||||
state_dir=Path(tmp),
|
state_dir=Path(tmp),
|
||||||
auth_token="BOT_BOTTLE_CLAUDE_OAUTH_TOKEN",
|
auth_token="BOT_BOTTLE_CLAUDE_OAUTH_TOKEN",
|
||||||
)
|
)
|
||||||
|
claude_config = json.loads(Path(tmp, "claude.json").read_text())
|
||||||
self.assertEqual(1, len(plan.egress_routes))
|
self.assertEqual(1, len(plan.egress_routes))
|
||||||
route = plan.egress_routes[0]
|
route = plan.egress_routes[0]
|
||||||
self.assertEqual("api.anthropic.com", route.host)
|
self.assertEqual("api.anthropic.com", route.host)
|
||||||
@@ -89,6 +103,20 @@ class TestAgentProviderRuntime(unittest.TestCase):
|
|||||||
self.assertEqual("1", plan.env_vars["CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC"])
|
self.assertEqual("1", plan.env_vars["CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC"])
|
||||||
self.assertEqual("1", plan.env_vars["DISABLE_ERROR_REPORTING"])
|
self.assertEqual("1", plan.env_vars["DISABLE_ERROR_REPORTING"])
|
||||||
self.assertEqual(frozenset({"CLAUDE_CODE_OAUTH_TOKEN"}), plan.hidden_env_names)
|
self.assertEqual(frozenset({"CLAUDE_CODE_OAUTH_TOKEN"}), plan.hidden_env_names)
|
||||||
|
self.assertIn("/home/node", claude_config["projects"])
|
||||||
|
self.assertIn("/home/node/.claude.json", {f.guest_path for f in plan.files})
|
||||||
|
|
||||||
|
def test_claude_trusts_requested_project_path(self):
|
||||||
|
with tempfile.TemporaryDirectory(prefix="bb-provider.") as tmp:
|
||||||
|
agent_provision_plan(
|
||||||
|
template="claude",
|
||||||
|
dockerfile="",
|
||||||
|
state_dir=Path(tmp),
|
||||||
|
trusted_project_path="/home/node/workspace",
|
||||||
|
)
|
||||||
|
config = json.loads(Path(tmp, "claude.json").read_text())
|
||||||
|
self.assertIn("/home/node", config["projects"])
|
||||||
|
self.assertIn("/home/node/workspace", config["projects"])
|
||||||
|
|
||||||
def test_codex_forward_host_credentials_populates_egress_routes(self):
|
def test_codex_forward_host_credentials_populates_egress_routes(self):
|
||||||
with tempfile.TemporaryDirectory(prefix="bb-provider.") as tmp:
|
with tempfile.TemporaryDirectory(prefix="bb-provider.") as tmp:
|
||||||
|
|||||||
Reference in New Issue
Block a user