mirror of
https://github.com/AstrBotDevs/AstrBot.git
synced 2026-10-16 06:58:51 +08:00
fix: use supported local permission defaults on Windows
This commit is contained in:
@@ -97,24 +97,17 @@ class AstrBotConfig(dict):
|
||||
and "computer_use_local_permissions" in default_provider_settings
|
||||
and "computer_use_local_permissions" not in provider_settings
|
||||
):
|
||||
# Preserve the legacy Local meaning while replacing its use of the
|
||||
# shared admin switch with explicit per-role permissions.
|
||||
member_execution = not provider_settings.get(
|
||||
"computer_use_require_admin",
|
||||
True,
|
||||
# Preserve legacy POSIX access; Windows uses its supported defaults.
|
||||
permissions = copy.deepcopy(
|
||||
default_provider_settings["computer_use_local_permissions"]
|
||||
)
|
||||
provider_settings["computer_use_local_permissions"] = {
|
||||
"member": {
|
||||
"allow_execution": member_execution,
|
||||
"allow_network": False,
|
||||
"filesystem_scope": "workspace",
|
||||
},
|
||||
"admin": {
|
||||
"allow_execution": True,
|
||||
"allow_network": True,
|
||||
"filesystem_scope": "host",
|
||||
},
|
||||
}
|
||||
permissions["member"]["allow_execution"] = permissions["member"][
|
||||
"filesystem_scope"
|
||||
] != "none" and not provider_settings.get(
|
||||
"computer_use_require_admin", True
|
||||
)
|
||||
permissions["admin"]["filesystem_scope"] = "host"
|
||||
provider_settings["computer_use_local_permissions"] = permissions
|
||||
config_migrated = True
|
||||
# 检查配置完整性,并插入
|
||||
has_new = self.check_config_integrity(default_config, conf, schema=schema)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
"""如需修改配置,请在 `data/cmd_config.json` 中修改或者在管理面板中可视化修改。"""
|
||||
|
||||
import os
|
||||
import platform
|
||||
|
||||
from astrbot import __version__
|
||||
from astrbot.core.computer.booters.cua_defaults import CUA_DEFAULT_CONFIG
|
||||
@@ -8,6 +9,32 @@ from astrbot.core.utils.astrbot_path import get_astrbot_data_path
|
||||
|
||||
from .agent_runner import get_agent_runner_config_default
|
||||
|
||||
|
||||
def get_local_permission_defaults(system: str | None = None) -> dict:
|
||||
"""Return fresh Local permission defaults for the operating system.
|
||||
|
||||
Args:
|
||||
system: Operating system name, or None to use the current system.
|
||||
|
||||
Returns:
|
||||
Per-role policies. Windows disables member access and gives admins
|
||||
unrestricted access because workspace isolation is unavailable.
|
||||
"""
|
||||
windows = (system or platform.system()).lower() == "windows"
|
||||
return {
|
||||
"member": {
|
||||
"allow_execution": False,
|
||||
"allow_network": False,
|
||||
"filesystem_scope": "none" if windows else "workspace",
|
||||
},
|
||||
"admin": {
|
||||
"allow_execution": True,
|
||||
"allow_network": True,
|
||||
"filesystem_scope": "host" if windows else "workspace",
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
VERSION = __version__
|
||||
|
||||
DB_PATH = os.path.join(get_astrbot_data_path(), "data_v4.db")
|
||||
@@ -152,18 +179,7 @@ DEFAULT_CONFIG = {
|
||||
"add_cron_tools": True,
|
||||
},
|
||||
"computer_use_runtime": "none",
|
||||
"computer_use_local_permissions": {
|
||||
"member": {
|
||||
"allow_execution": False,
|
||||
"allow_network": False,
|
||||
"filesystem_scope": "workspace",
|
||||
},
|
||||
"admin": {
|
||||
"allow_execution": True,
|
||||
"allow_network": True,
|
||||
"filesystem_scope": "workspace",
|
||||
},
|
||||
},
|
||||
"computer_use_local_permissions": get_local_permission_defaults(),
|
||||
"computer_use_require_admin": True,
|
||||
"sandbox": {
|
||||
"booter": "shipyard_neo",
|
||||
|
||||
@@ -5,6 +5,7 @@ from typing import Literal
|
||||
from astrbot.core.agent.run_context import ContextWrapper
|
||||
from astrbot.core.astr_agent_context import AstrAgentContext
|
||||
from astrbot.core.computer.process_sandbox import create_process_sandbox
|
||||
from astrbot.core.config.default import get_local_permission_defaults
|
||||
from astrbot.core.db import BaseDatabase
|
||||
from astrbot.core.utils.astrbot_path import get_astrbot_workspaces_path
|
||||
from astrbot.core.workspace import (
|
||||
@@ -93,29 +94,28 @@ def get_local_permission_policy(
|
||||
)
|
||||
provider_settings = cfg.get("provider_settings", {})
|
||||
role = "admin" if context.context.event.role == "admin" else "member"
|
||||
default_execution = role == "admin"
|
||||
default_network = role == "admin"
|
||||
default_filesystem = "workspace"
|
||||
defaults = get_local_permission_defaults()[role]
|
||||
|
||||
permissions = provider_settings.get("computer_use_local_permissions")
|
||||
role_policy = permissions.get(role) if isinstance(permissions, dict) else None
|
||||
if not isinstance(role_policy, dict):
|
||||
role_policy = {}
|
||||
if role == "member" and not isinstance(permissions, dict):
|
||||
default_execution = not provider_settings.get(
|
||||
defaults["allow_execution"] = not provider_settings.get(
|
||||
"computer_use_require_admin",
|
||||
True,
|
||||
)
|
||||
|
||||
filesystem_scope = role_policy.get("filesystem_scope", default_filesystem)
|
||||
filesystem_scope = role_policy.get("filesystem_scope", defaults["filesystem_scope"])
|
||||
if filesystem_scope not in {"none", "workspace", "host"}:
|
||||
filesystem_scope = default_filesystem
|
||||
filesystem_scope = defaults["filesystem_scope"]
|
||||
allow_execution = (
|
||||
filesystem_scope != "none"
|
||||
and role_policy.get("allow_execution", default_execution) is True
|
||||
and role_policy.get("allow_execution", defaults["allow_execution"]) is True
|
||||
)
|
||||
allow_network = (
|
||||
allow_execution and role_policy.get("allow_network", default_network) is True
|
||||
allow_execution
|
||||
and role_policy.get("allow_network", defaults["allow_network"]) is True
|
||||
)
|
||||
return LocalPermissionPolicy(
|
||||
allow_execution=allow_execution,
|
||||
|
||||
@@ -19,6 +19,7 @@ from astrbot.core.config.default import (
|
||||
CONFIG_METADATA_3_SYSTEM,
|
||||
DEFAULT_CONFIG,
|
||||
DEFAULT_VALUE_MAP,
|
||||
get_local_permission_defaults,
|
||||
)
|
||||
from astrbot.core.config.i18n_utils import ConfigMetadataI18n
|
||||
from astrbot.core.core_lifecycle import AstrBotCoreLifecycle
|
||||
@@ -323,6 +324,7 @@ def validate_config(
|
||||
}
|
||||
validate(data, meta_all)
|
||||
provider_settings = data.get("provider_settings", {})
|
||||
defaults = get_local_permission_defaults(runtime.get("os") if runtime else None)
|
||||
permissions = (
|
||||
provider_settings.get("computer_use_local_permissions", {})
|
||||
if isinstance(provider_settings, dict)
|
||||
@@ -346,7 +348,9 @@ def validate_config(
|
||||
errors.append(
|
||||
f"Local permission {role}.{key} must be a boolean."
|
||||
)
|
||||
scope = policy.get("filesystem_scope", "workspace")
|
||||
scope = policy.get(
|
||||
"filesystem_scope", defaults[role]["filesystem_scope"]
|
||||
)
|
||||
if scope not in ("none", "workspace", "host"):
|
||||
errors.append(
|
||||
f"Invalid local filesystem scope for {role}: {scope}."
|
||||
@@ -354,7 +358,10 @@ def validate_config(
|
||||
if scope == "none":
|
||||
policy["allow_execution"] = False
|
||||
policy["allow_network"] = False
|
||||
elif policy.get("allow_execution", role == "admin") is False:
|
||||
elif (
|
||||
policy.get("allow_execution", defaults[role]["allow_execution"])
|
||||
is False
|
||||
):
|
||||
policy["allow_network"] = False
|
||||
|
||||
if (
|
||||
@@ -367,9 +374,6 @@ def validate_config(
|
||||
old_settings = (current_config or {}).get("provider_settings", {})
|
||||
old_permissions = old_settings.get("computer_use_local_permissions", {})
|
||||
was_local = old_settings.get("computer_use_runtime") == "local"
|
||||
defaults = DEFAULT_CONFIG["provider_settings"][
|
||||
"computer_use_local_permissions"
|
||||
]
|
||||
for role in ("member", "admin"):
|
||||
# Keep unchanged legacy policies, but check both roles when
|
||||
# activating Local access or creating a profile.
|
||||
|
||||
@@ -257,7 +257,8 @@ import KnowledgeBaseSelector from './KnowledgeBaseSelector.vue'
|
||||
import PluginSetSelector from './PluginSetSelector.vue'
|
||||
import T2ITemplateEditor from './T2ITemplateEditor.vue'
|
||||
import DashboardTotpManager from './DashboardTotpManager.vue'
|
||||
import LocalPermissionMatrix from './LocalPermissionMatrix.vue'
|
||||
import LocalPermissionMatrix, { windowsPermissionDefaults } from './LocalPermissionMatrix.vue'
|
||||
import { statsApi } from '@/api/v1'
|
||||
import { computed, ref } from 'vue'
|
||||
import { useI18n, useModuleI18n } from '@/i18n/composables'
|
||||
import { usePluginI18n } from '@/utils/pluginI18n'
|
||||
@@ -306,7 +307,9 @@ const { t } = useI18n()
|
||||
const { getRaw } = useModuleI18n('features/config-metadata')
|
||||
const { configText } = usePluginI18n()
|
||||
|
||||
function emitUpdate(val) {
|
||||
async function emitUpdate(val) {
|
||||
const enablingLocal = props.configKey === 'provider_settings.computer_use_runtime'
|
||||
&& (props.modelValue === 'none' || props.modelValue == null) && val === 'local'
|
||||
if (
|
||||
props.itemMeta?._special === 'agent_runner_type'
|
||||
&& props.configRoot?.agent_runner
|
||||
@@ -317,6 +320,22 @@ function emitUpdate(val) {
|
||||
)
|
||||
}
|
||||
emit('update:modelValue', val)
|
||||
if (enablingLocal && props.configRoot?.provider_settings) {
|
||||
const settings = props.configRoot.provider_settings
|
||||
try {
|
||||
const response = await statsApi.version()
|
||||
if (response.data?.data?.runtime?.os === 'windows' && settings.computer_use_runtime === 'local') {
|
||||
const permissions = { ...settings.computer_use_local_permissions }
|
||||
for (const [role, defaults] of Object.entries(windowsPermissionDefaults)) {
|
||||
const policy = permissions[role]
|
||||
if (!policy || policy.filesystem_scope === 'workspace') permissions[role] = { ...defaults }
|
||||
}
|
||||
settings.computer_use_local_permissions = permissions
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('Failed to initialize local permissions:', error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const listSelectItems = computed(() =>
|
||||
|
||||
@@ -133,6 +133,13 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export const windowsPermissionDefaults = {
|
||||
member: { filesystem_scope: 'none', allow_execution: false, allow_network: false },
|
||||
admin: { filesystem_scope: 'host', allow_execution: true, allow_network: true }
|
||||
}
|
||||
</script>
|
||||
|
||||
<script setup>
|
||||
import { computed, onMounted, ref } from 'vue'
|
||||
import { Cpu, FolderOpen, LockKeyhole, Monitor, Shield, ShieldAlert, ShieldOff } from '@lucide/vue'
|
||||
@@ -153,7 +160,7 @@ const runtimeLoading = ref(true)
|
||||
const unsupported = computed(() => runtime.value?.sandbox?.status === 'unsupported')
|
||||
const sandboxMissing = computed(() => runtime.value?.sandbox?.status === 'missing')
|
||||
const roles = ['member', 'admin']
|
||||
const defaults = {
|
||||
const defaults = computed(() => runtime.value?.os === 'windows' ? windowsPermissionDefaults : {
|
||||
member: {
|
||||
allow_execution: false,
|
||||
allow_network: false,
|
||||
@@ -164,7 +171,7 @@ const defaults = {
|
||||
allow_network: true,
|
||||
filesystem_scope: 'workspace'
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
onMounted(async () => {
|
||||
try {
|
||||
@@ -179,11 +186,11 @@ onMounted(async () => {
|
||||
|
||||
function policy(role) {
|
||||
const resolved = {
|
||||
...defaults[role],
|
||||
...defaults.value[role],
|
||||
...(props.modelValue?.[role] || {})
|
||||
}
|
||||
if (!['none', 'workspace', 'host'].includes(resolved.filesystem_scope)) {
|
||||
resolved.filesystem_scope = defaults[role].filesystem_scope
|
||||
resolved.filesystem_scope = defaults.value[role].filesystem_scope
|
||||
}
|
||||
resolved.allow_execution = resolved.filesystem_scope !== 'none' && resolved.allow_execution === true
|
||||
resolved.allow_network = resolved.allow_execution && resolved.allow_network === true
|
||||
@@ -215,9 +222,9 @@ function updatePermission(role, changes) {
|
||||
}
|
||||
|
||||
const accessPolicies = {
|
||||
none: { filesystem_scope: 'none', allow_execution: false, allow_network: false },
|
||||
none: windowsPermissionDefaults.member,
|
||||
files: { filesystem_scope: 'host', allow_execution: false, allow_network: false },
|
||||
full: { filesystem_scope: 'host', allow_execution: true, allow_network: true }
|
||||
full: windowsPermissionDefaults.admin
|
||||
}
|
||||
const accessIcons = { none: LockKeyhole, files: FolderOpen, full: ShieldAlert }
|
||||
const permissionLocks = computed(() => Object.fromEntries(roles.map(role => {
|
||||
|
||||
@@ -140,7 +140,7 @@ def test_local_permission_platform_matrix(
|
||||
("local", "local", "member", {}, False),
|
||||
("local", "local", "admin", {"filesystem_scope": "host"}, False),
|
||||
("local", "local", "member", {"allow_execution": True}, True),
|
||||
("local", "local", "member", None, True),
|
||||
("local", "local", "member", None, False),
|
||||
("none", "local", "member", {}, True),
|
||||
("local", "none", "member", {}, False),
|
||||
("local", "sandbox", "member", {}, False),
|
||||
|
||||
@@ -393,7 +393,11 @@ async def test_local_member_stays_restricted_when_admin_requirement_is_disabled(
|
||||
outside_file.write_text("host secret\n", encoding="utf-8")
|
||||
|
||||
result = await fs_tools.FileReadTool().call(
|
||||
_make_context(role="member", require_admin=False),
|
||||
_make_context(
|
||||
role="member",
|
||||
require_admin=False,
|
||||
local_permissions={"member": {"filesystem_scope": "workspace"}},
|
||||
),
|
||||
path=str(outside_file),
|
||||
)
|
||||
|
||||
@@ -462,7 +466,10 @@ async def test_restricted_local_member_cannot_write_plugin_provided_skill(
|
||||
plugin_skill.write_text("# Demo Skill\n", encoding="utf-8")
|
||||
|
||||
result = await fs_tools.FileWriteTool().call(
|
||||
_make_context(role="member"),
|
||||
_make_context(
|
||||
role="member",
|
||||
local_permissions={"member": {"filesystem_scope": "workspace"}},
|
||||
),
|
||||
path=str(plugin_skill),
|
||||
content="# Changed\n",
|
||||
)
|
||||
@@ -548,13 +555,17 @@ async def test_restricted_local_member_rejects_workspace_hardlink_alias(
|
||||
outside_file.write_text("outside-secret\n", encoding="utf-8")
|
||||
hardlink_path = workspace / "linked.txt"
|
||||
_make_hardlink_or_skip(outside_file, hardlink_path)
|
||||
context = _make_context(
|
||||
role="member",
|
||||
local_permissions={"member": {"filesystem_scope": "workspace"}},
|
||||
)
|
||||
|
||||
read_result = await fs_tools.FileReadTool().call(
|
||||
_make_context(role="member"),
|
||||
context,
|
||||
path="linked.txt",
|
||||
)
|
||||
write_result = await fs_tools.FileWriteTool().call(
|
||||
_make_context(role="member"),
|
||||
context,
|
||||
path="linked.txt",
|
||||
content="changed\n",
|
||||
)
|
||||
@@ -595,7 +606,10 @@ async def test_windows_restricted_file_access_fails_closed(
|
||||
monkeypatch.setattr(local_file_security, "os", SimpleNamespace(name="nt"))
|
||||
|
||||
result = await tool_type().call(
|
||||
_make_context(role=role),
|
||||
_make_context(
|
||||
role=role,
|
||||
local_permissions={role: {"filesystem_scope": "workspace"}},
|
||||
),
|
||||
**arguments,
|
||||
)
|
||||
|
||||
@@ -757,7 +771,10 @@ async def test_restricted_local_grep_requests_read_only_os_sandbox(
|
||||
monkeypatch.setattr(fs_tools, "get_booter", _fake_get_booter)
|
||||
|
||||
result = await fs_tools.GrepTool().call(
|
||||
_make_context(role="member"),
|
||||
_make_context(
|
||||
role="member",
|
||||
local_permissions={"member": {"filesystem_scope": "workspace"}},
|
||||
),
|
||||
pattern="needle",
|
||||
path="target.txt",
|
||||
)
|
||||
|
||||
@@ -6,6 +6,7 @@ import pytest
|
||||
|
||||
from astrbot.core.agent.run_context import ContextWrapper
|
||||
from astrbot.core.computer import process_sandbox
|
||||
from astrbot.core.config import default as config_defaults
|
||||
from astrbot.core.tools.computer_tools import fs, python, shell, util
|
||||
from astrbot.core.tools.computer_tools.shipyard_neo.browser import BrowserExecTool
|
||||
from astrbot.core.tools.computer_tools.shipyard_neo.neo_skills import (
|
||||
@@ -108,16 +109,29 @@ def test_local_permission_policy_treats_unknown_roles_as_members():
|
||||
|
||||
assert resolved.allow_execution is False
|
||||
assert resolved.allow_network is False
|
||||
assert resolved.filesystem_scope == "workspace"
|
||||
assert (
|
||||
resolved.filesystem_scope
|
||||
== config_defaults.get_local_permission_defaults()["member"]["filesystem_scope"]
|
||||
)
|
||||
|
||||
|
||||
def test_local_permission_policy_defaults_admin_to_workspace_access():
|
||||
resolved = get_local_permission_policy(_make_local_run_context("admin", {}))
|
||||
@pytest.mark.parametrize("system", ["Windows", "Linux", "Darwin"])
|
||||
@pytest.mark.parametrize("role", ["member", "admin", "unexpected"])
|
||||
@pytest.mark.parametrize("permissions", [None, {}])
|
||||
def test_local_permission_policy_platform_defaults(
|
||||
monkeypatch, system, role, permissions
|
||||
):
|
||||
monkeypatch.setattr(
|
||||
config_defaults, "platform", SimpleNamespace(system=lambda: system)
|
||||
)
|
||||
resolved = get_local_permission_policy(_make_local_run_context(role, permissions))
|
||||
|
||||
assert resolved.allow_execution is True
|
||||
assert resolved.allow_network is True
|
||||
assert resolved.filesystem_scope == "workspace"
|
||||
assert resolved.requires_sandbox is True
|
||||
assert resolved.allow_execution == (role == "admin")
|
||||
assert resolved.allow_network == (role == "admin")
|
||||
assert resolved.filesystem_scope == (
|
||||
("host" if role == "admin" else "none") if system == "Windows" else "workspace"
|
||||
)
|
||||
assert resolved.requires_sandbox == (system != "Windows" or role != "admin")
|
||||
|
||||
|
||||
@pytest.mark.parametrize("role", ["member", "admin", "unexpected"])
|
||||
|
||||
+45
-15
@@ -4,11 +4,12 @@ import asyncio
|
||||
import json
|
||||
import os
|
||||
import threading
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from astrbot.core.config.astrbot_config import AstrBotConfig, RateLimitStrategy
|
||||
from astrbot.core.config.default import DEFAULT_VALUE_MAP
|
||||
from astrbot.core.config.default import DEFAULT_VALUE_MAP, get_local_permission_defaults
|
||||
from astrbot.core.config.i18n_utils import ConfigMetadataI18n
|
||||
from astrbot.core.utils.auth_password import (
|
||||
DEFAULT_DASHBOARD_PASSWORD,
|
||||
@@ -92,27 +93,18 @@ class TestAstrBotConfigLoad:
|
||||
assert config.provider_settings["enable"] is False
|
||||
|
||||
@pytest.mark.parametrize("require_admin", [True, False])
|
||||
@pytest.mark.parametrize("system", ["Windows", "Linux", "Darwin"])
|
||||
def test_migrates_legacy_local_computer_permissions(
|
||||
self,
|
||||
temp_config_path,
|
||||
require_admin,
|
||||
system,
|
||||
):
|
||||
"""Legacy admin switches should become explicit Local role policies."""
|
||||
default_config = {
|
||||
"provider_settings": {
|
||||
"computer_use_require_admin": True,
|
||||
"computer_use_local_permissions": {
|
||||
"member": {
|
||||
"allow_execution": False,
|
||||
"allow_network": False,
|
||||
"filesystem_scope": "workspace",
|
||||
},
|
||||
"admin": {
|
||||
"allow_execution": True,
|
||||
"allow_network": True,
|
||||
"filesystem_scope": "workspace",
|
||||
},
|
||||
},
|
||||
"computer_use_local_permissions": get_local_permission_defaults(system),
|
||||
}
|
||||
}
|
||||
with open(temp_config_path, "w", encoding="utf-8-sig") as file:
|
||||
@@ -132,15 +124,53 @@ class TestAstrBotConfigLoad:
|
||||
|
||||
permissions = config["provider_settings"]["computer_use_local_permissions"]
|
||||
assert permissions["member"] == {
|
||||
"allow_execution": not require_admin,
|
||||
"allow_execution": system != "Windows" and not require_admin,
|
||||
"allow_network": False,
|
||||
"filesystem_scope": "workspace",
|
||||
"filesystem_scope": "none" if system == "Windows" else "workspace",
|
||||
}
|
||||
assert permissions["admin"] == {
|
||||
"allow_execution": True,
|
||||
"allow_network": True,
|
||||
"filesystem_scope": "host",
|
||||
}
|
||||
assert (
|
||||
json.loads(Path(temp_config_path).read_text(encoding="utf-8-sig"))[
|
||||
"provider_settings"
|
||||
]["computer_use_local_permissions"]
|
||||
== permissions
|
||||
)
|
||||
|
||||
@pytest.mark.parametrize("system", ["Windows", "Linux", "Darwin"])
|
||||
@pytest.mark.parametrize("scope", [None, "none", "workspace", "host"])
|
||||
def test_local_defaults_preserve_existing_policies(
|
||||
self, temp_config_path, system, scope
|
||||
):
|
||||
defaults = get_local_permission_defaults(system)
|
||||
existing = {"member": {"filesystem_scope": scope}} if scope else {}
|
||||
if scope:
|
||||
Path(temp_config_path).write_text(
|
||||
json.dumps(
|
||||
{"provider_settings": {"computer_use_local_permissions": existing}}
|
||||
)
|
||||
)
|
||||
config = AstrBotConfig(
|
||||
temp_config_path,
|
||||
default_config={
|
||||
"provider_settings": {"computer_use_local_permissions": defaults}
|
||||
},
|
||||
)
|
||||
expected = {
|
||||
role: {**policy, **existing.get(role, {})}
|
||||
for role, policy in defaults.items()
|
||||
}
|
||||
assert config["provider_settings"]["computer_use_local_permissions"] == expected
|
||||
assert (
|
||||
json.loads(Path(temp_config_path).read_text(encoding="utf-8-sig"))[
|
||||
"provider_settings"
|
||||
]["computer_use_local_permissions"]
|
||||
== expected
|
||||
)
|
||||
assert defaults == get_local_permission_defaults(system)
|
||||
|
||||
def test_first_deploy_flag(self, temp_config_path, minimal_default_config):
|
||||
"""Test first_deploy flag is set for new config."""
|
||||
|
||||
@@ -47,20 +47,31 @@ async def test_profile_mutations_await_config_manager() -> None:
|
||||
|
||||
with pytest.raises(ValueError, match="Local permission member:") as exc:
|
||||
await service.create_profile(
|
||||
"Invalid", {"provider_settings": {"computer_use_runtime": "local"}}
|
||||
"Invalid",
|
||||
{
|
||||
"provider_settings": {
|
||||
"computer_use_runtime": "local",
|
||||
"computer_use_local_permissions": {
|
||||
role: {"filesystem_scope": "workspace"}
|
||||
for role in ("member", "admin")
|
||||
},
|
||||
}
|
||||
},
|
||||
)
|
||||
assert "Local permission admin:" in str(exc.value)
|
||||
config_manager.create_conf.assert_not_awaited()
|
||||
lifecycle.reload_pipeline_scheduler.assert_not_awaited()
|
||||
|
||||
result = await service.create_profile("Profile", {"timezone": "UTC"})
|
||||
result = await service.create_profile(
|
||||
"Profile", {"provider_settings": {"computer_use_runtime": "local"}}
|
||||
)
|
||||
await service.rename_profile("profile-id", "Renamed")
|
||||
await service.delete_profile("profile-id")
|
||||
|
||||
assert result == {"conf_id": "profile-id"}
|
||||
config_manager.create_conf.assert_awaited_once_with(
|
||||
name="Profile",
|
||||
config={"timezone": "UTC"},
|
||||
config={"provider_settings": {"computer_use_runtime": "local"}},
|
||||
)
|
||||
lifecycle.reload_pipeline_scheduler.assert_awaited_once_with("profile-id")
|
||||
config_manager.update_conf_info.assert_awaited_once_with(
|
||||
|
||||
Reference in New Issue
Block a user