refactor(execution): 移除执行环境 TTL 自动回退机制
- 后端:移除 host_execution_direct_until、_refresh_execution_mode_by_ttl 及 HOST_EXECUTION_DIRECT_TTL_SECONDS 配置,切换执行环境后一直生效 - 前端:移除到期定时同步、回退检测、自动回退 toast 及权限自动降级逻辑 - 文档:AGENTS.md 与 host_sandbox_and_permission_model.md 同步更新
This commit is contained in:
parent
5969d04c99
commit
9618148ed3
@ -330,8 +330,8 @@ AI 执行以下流程时,每一步都要向用户说明在做什么:
|
|||||||
### 10.2 执行环境
|
### 10.2 执行环境
|
||||||
|
|
||||||
- `sandbox`(默认):使用 OS 沙箱执行(macOS: `sandbox-exec`,Linux: `bwrap + seccomp`,Windows: `WSL2`)
|
- `sandbox`(默认):使用 OS 沙箱执行(macOS: `sandbox-exec`,Linux: `bwrap + seccomp`,Windows: `WSL2`)
|
||||||
- `direct`:宿主机直接执行(高风险,仅建议短时使用)
|
- `direct`:宿主机直接执行(高风险)
|
||||||
- 宿主机下支持会话级 TTL 自动回退 `direct -> sandbox`
|
- 切换后一直生效,无自动回退机制(2026-07 已移除原 TTL 自动回退)
|
||||||
|
|
||||||
### 10.3 路径授权语义
|
### 10.3 路径授权语义
|
||||||
|
|
||||||
|
|||||||
@ -62,7 +62,6 @@ def _load_dotenv():
|
|||||||
_LEGACY_MAP = {
|
_LEGACY_MAP = {
|
||||||
"terminal.sandbox_mode": "TERMINAL_SANDBOX_MODE",
|
"terminal.sandbox_mode": "TERMINAL_SANDBOX_MODE",
|
||||||
"terminal.execution_mode_default": "HOST_EXECUTION_MODE_DEFAULT",
|
"terminal.execution_mode_default": "HOST_EXECUTION_MODE_DEFAULT",
|
||||||
"terminal.direct_ttl_seconds": "HOST_EXECUTION_DIRECT_TTL_SECONDS",
|
|
||||||
"terminal.macos_writable_paths": "HOST_SANDBOX_MACOS_WRITABLE_PATHS",
|
"terminal.macos_writable_paths": "HOST_SANDBOX_MACOS_WRITABLE_PATHS",
|
||||||
"terminal.max_active_containers": "MAX_ACTIVE_USER_CONTAINERS",
|
"terminal.max_active_containers": "MAX_ACTIVE_USER_CONTAINERS",
|
||||||
"terminal.project_max_storage_mb": "PROJECT_MAX_STORAGE_MB",
|
"terminal.project_max_storage_mb": "PROJECT_MAX_STORAGE_MB",
|
||||||
|
|||||||
@ -54,7 +54,6 @@ LINUX_SAFETY = os.environ.get("LINUX_SAFETY", "0") not in {"0", "false", "False"
|
|||||||
TOOLBOX_TERMINAL_IDLE_SECONDS = int(os.environ.get("TOOLBOX_TERMINAL_IDLE_SECONDS", "900"))
|
TOOLBOX_TERMINAL_IDLE_SECONDS = int(os.environ.get("TOOLBOX_TERMINAL_IDLE_SECONDS", "900"))
|
||||||
MAX_ACTIVE_USER_CONTAINERS = int(os.environ.get("MAX_ACTIVE_USER_CONTAINERS", "8"))
|
MAX_ACTIVE_USER_CONTAINERS = int(os.environ.get("MAX_ACTIVE_USER_CONTAINERS", "8"))
|
||||||
HOST_EXECUTION_MODE_DEFAULT = os.environ.get("HOST_EXECUTION_MODE_DEFAULT", "sandbox").strip().lower()
|
HOST_EXECUTION_MODE_DEFAULT = os.environ.get("HOST_EXECUTION_MODE_DEFAULT", "sandbox").strip().lower()
|
||||||
HOST_EXECUTION_DIRECT_TTL_SECONDS = int(os.environ.get("HOST_EXECUTION_DIRECT_TTL_SECONDS", "600"))
|
|
||||||
HOST_SANDBOX_MACOS_WRITABLE_PATHS = _parse_paths(
|
HOST_SANDBOX_MACOS_WRITABLE_PATHS = _parse_paths(
|
||||||
os.environ.get("HOST_SANDBOX_MACOS_WRITABLE_PATHS", "")
|
os.environ.get("HOST_SANDBOX_MACOS_WRITABLE_PATHS", "")
|
||||||
)
|
)
|
||||||
@ -88,7 +87,6 @@ __all__ = [
|
|||||||
"TOOLBOX_TERMINAL_IDLE_SECONDS",
|
"TOOLBOX_TERMINAL_IDLE_SECONDS",
|
||||||
"MAX_ACTIVE_USER_CONTAINERS",
|
"MAX_ACTIVE_USER_CONTAINERS",
|
||||||
"HOST_EXECUTION_MODE_DEFAULT",
|
"HOST_EXECUTION_MODE_DEFAULT",
|
||||||
"HOST_EXECUTION_DIRECT_TTL_SECONDS",
|
|
||||||
"HOST_SANDBOX_MACOS_WRITABLE_PATHS",
|
"HOST_SANDBOX_MACOS_WRITABLE_PATHS",
|
||||||
"HOST_SANDBOX_NETWORK_PERMISSION",
|
"HOST_SANDBOX_NETWORK_PERMISSION",
|
||||||
]
|
]
|
||||||
|
|||||||
@ -18,7 +18,6 @@ try:
|
|||||||
CUSTOM_TOOLS_ENABLED,
|
CUSTOM_TOOLS_ENABLED,
|
||||||
MCP_TOOLS_ENABLED,
|
MCP_TOOLS_ENABLED,
|
||||||
HOST_EXECUTION_MODE_DEFAULT,
|
HOST_EXECUTION_MODE_DEFAULT,
|
||||||
HOST_EXECUTION_DIRECT_TTL_SECONDS,
|
|
||||||
HOST_SANDBOX_NETWORK_PERMISSION,
|
HOST_SANDBOX_NETWORK_PERMISSION,
|
||||||
)
|
)
|
||||||
except ImportError:
|
except ImportError:
|
||||||
@ -38,7 +37,6 @@ except ImportError:
|
|||||||
CUSTOM_TOOLS_ENABLED,
|
CUSTOM_TOOLS_ENABLED,
|
||||||
MCP_TOOLS_ENABLED,
|
MCP_TOOLS_ENABLED,
|
||||||
HOST_EXECUTION_MODE_DEFAULT,
|
HOST_EXECUTION_MODE_DEFAULT,
|
||||||
HOST_EXECUTION_DIRECT_TTL_SECONDS,
|
|
||||||
HOST_SANDBOX_NETWORK_PERMISSION,
|
HOST_SANDBOX_NETWORK_PERMISSION,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -209,7 +207,6 @@ class MainTerminal(MainTerminalCommandMixin, MainTerminalContextMixin, MainTermi
|
|||||||
self.default_permission_mode: str = "unrestricted"
|
self.default_permission_mode: str = "unrestricted"
|
||||||
self.current_permission_mode: str = "unrestricted"
|
self.current_permission_mode: str = "unrestricted"
|
||||||
self.host_execution_mode: str = "sandbox"
|
self.host_execution_mode: str = "sandbox"
|
||||||
self.host_execution_direct_until: Optional[float] = None
|
|
||||||
self.host_network_permission: str = "restricted"
|
self.host_network_permission: str = "restricted"
|
||||||
self.pending_permission_mode: Optional[str] = None
|
self.pending_permission_mode: Optional[str] = None
|
||||||
self.pending_execution_mode: Optional[str] = None
|
self.pending_execution_mode: Optional[str] = None
|
||||||
@ -251,7 +248,6 @@ class MainTerminal(MainTerminalCommandMixin, MainTerminalContextMixin, MainTermi
|
|||||||
def _init_host_execution_mode(self):
|
def _init_host_execution_mode(self):
|
||||||
default_mode = str(HOST_EXECUTION_MODE_DEFAULT or "sandbox").strip().lower()
|
default_mode = str(HOST_EXECUTION_MODE_DEFAULT or "sandbox").strip().lower()
|
||||||
self.host_execution_mode = "direct" if default_mode == "direct" else "sandbox"
|
self.host_execution_mode = "direct" if default_mode == "direct" else "sandbox"
|
||||||
self.host_execution_direct_until = None
|
|
||||||
self._apply_execution_mode_to_runtime()
|
self._apply_execution_mode_to_runtime()
|
||||||
|
|
||||||
def _apply_execution_mode_to_runtime(self):
|
def _apply_execution_mode_to_runtime(self):
|
||||||
@ -380,29 +376,14 @@ class MainTerminal(MainTerminalCommandMixin, MainTerminalContextMixin, MainTermi
|
|||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def _refresh_execution_mode_by_ttl(self):
|
|
||||||
if self.host_execution_mode != "direct":
|
|
||||||
return
|
|
||||||
until = self.host_execution_direct_until
|
|
||||||
if not until:
|
|
||||||
return
|
|
||||||
if time.time() >= float(until):
|
|
||||||
self.host_execution_mode = "sandbox"
|
|
||||||
self.host_execution_direct_until = None
|
|
||||||
self._apply_execution_mode_to_runtime()
|
|
||||||
|
|
||||||
def get_execution_mode(self) -> str:
|
def get_execution_mode(self) -> str:
|
||||||
self._refresh_execution_mode_by_ttl()
|
|
||||||
return "direct" if self.host_execution_mode == "direct" else "sandbox"
|
return "direct" if self.host_execution_mode == "direct" else "sandbox"
|
||||||
|
|
||||||
def get_execution_mode_state(self) -> Dict:
|
def get_execution_mode_state(self) -> Dict:
|
||||||
mode = self.get_execution_mode()
|
mode = self.get_execution_mode()
|
||||||
ttl_seconds = int(HOST_EXECUTION_DIRECT_TTL_SECONDS)
|
|
||||||
return {
|
return {
|
||||||
"mode": mode,
|
"mode": mode,
|
||||||
"default_mode": "direct" if str(HOST_EXECUTION_MODE_DEFAULT).lower() == "direct" else "sandbox",
|
"default_mode": "direct" if str(HOST_EXECUTION_MODE_DEFAULT).lower() == "direct" else "sandbox",
|
||||||
"ttl_seconds": ttl_seconds,
|
|
||||||
"direct_until": self.host_execution_direct_until,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def set_execution_mode(self, mode: str) -> Dict:
|
def set_execution_mode(self, mode: str) -> Dict:
|
||||||
@ -410,11 +391,6 @@ class MainTerminal(MainTerminalCommandMixin, MainTerminalContextMixin, MainTermi
|
|||||||
if normalized not in {"sandbox", "direct"}:
|
if normalized not in {"sandbox", "direct"}:
|
||||||
raise ValueError("无效执行环境,仅支持 sandbox / direct")
|
raise ValueError("无效执行环境,仅支持 sandbox / direct")
|
||||||
self.host_execution_mode = normalized
|
self.host_execution_mode = normalized
|
||||||
ttl_seconds = int(HOST_EXECUTION_DIRECT_TTL_SECONDS)
|
|
||||||
if normalized == "direct":
|
|
||||||
self.host_execution_direct_until = None if ttl_seconds in (0, -1) else (time.time() + max(1, ttl_seconds))
|
|
||||||
else:
|
|
||||||
self.host_execution_direct_until = None
|
|
||||||
self._apply_execution_mode_to_runtime()
|
self._apply_execution_mode_to_runtime()
|
||||||
return self.get_execution_mode_state()
|
return self.get_execution_mode_state()
|
||||||
|
|
||||||
|
|||||||
@ -778,8 +778,6 @@ class MainTerminalToolsExecutionMixin:
|
|||||||
"""处理工具调用(添加参数预检查和改进错误处理)"""
|
"""处理工具调用(添加参数预检查和改进错误处理)"""
|
||||||
logger.info("[handle_tool_call] 工具调用开始: tool_name=%s, arguments=%s", tool_name, arguments)
|
logger.info("[handle_tool_call] 工具调用开始: tool_name=%s, arguments=%s", tool_name, arguments)
|
||||||
try:
|
try:
|
||||||
if hasattr(self, "_refresh_execution_mode_by_ttl"):
|
|
||||||
self._refresh_execution_mode_by_ttl()
|
|
||||||
if hasattr(self, "_apply_execution_mode_to_runtime"):
|
if hasattr(self, "_apply_execution_mode_to_runtime"):
|
||||||
self._apply_execution_mode_to_runtime()
|
self._apply_execution_mode_to_runtime()
|
||||||
except Exception:
|
except Exception:
|
||||||
|
|||||||
@ -123,7 +123,7 @@
|
|||||||
|
|
||||||
- 命令直接在宿主机执行
|
- 命令直接在宿主机执行
|
||||||
- 仅建议临时用于必须操作
|
- 仅建议临时用于必须操作
|
||||||
- 支持 TTL 自动回退(会话级)到 `sandbox`
|
- 切换后一直生效,无自动回退机制(2026-07 已移除原 TTL 自动回退)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@ -127,8 +127,6 @@ export const resourceMethods = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
applyStatusSnapshot(status) {
|
applyStatusSnapshot(status) {
|
||||||
const prevExecutionMode = this.currentExecutionMode;
|
|
||||||
const prevDirectUntil = this.executionModeDirectUntil;
|
|
||||||
this.resourceApplyStatusSnapshot(status);
|
this.resourceApplyStatusSnapshot(status);
|
||||||
// 模型/思考模式/推理强度的权威来源规则:
|
// 模型/思考模式/推理强度的权威来源规则:
|
||||||
// - 非空对话:以对话 meta 为准(enterConversation bootstrap 恢复),
|
// - 非空对话:以对话 meta 为准(enterConversation bootstrap 恢复),
|
||||||
@ -168,54 +166,8 @@ export const resourceMethods = {
|
|||||||
if (typeof status.execution_mode.mode === 'string') {
|
if (typeof status.execution_mode.mode === 'string') {
|
||||||
this.currentExecutionMode = status.execution_mode.mode;
|
this.currentExecutionMode = status.execution_mode.mode;
|
||||||
}
|
}
|
||||||
if (typeof status.execution_mode.ttl_seconds === 'number') {
|
|
||||||
this.executionModeTtlSeconds = status.execution_mode.ttl_seconds;
|
|
||||||
}
|
|
||||||
this.executionModeDirectUntil = status.execution_mode.direct_until ?? null;
|
|
||||||
this.executionModeEnabled =
|
this.executionModeEnabled =
|
||||||
typeof status.execution_mode_enabled === 'boolean' ? status.execution_mode_enabled : true;
|
typeof status.execution_mode_enabled === 'boolean' ? status.execution_mode_enabled : true;
|
||||||
if (typeof this.scheduleExecutionModeExpirySync === 'function') {
|
|
||||||
this.scheduleExecutionModeExpirySync(this.executionModeDirectUntil);
|
|
||||||
}
|
|
||||||
const currentMode = this.currentExecutionMode;
|
|
||||||
const parseDirectUntilMs = (value: any): number => {
|
|
||||||
if (!value) return 0;
|
|
||||||
if (typeof value === 'number') {
|
|
||||||
return value > 1e12 ? value : value * 1000;
|
|
||||||
}
|
|
||||||
if (typeof value === 'string') {
|
|
||||||
const parsed = Date.parse(value);
|
|
||||||
return Number.isNaN(parsed) ? 0 : parsed;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
};
|
|
||||||
const prevDirectUntilMs = parseDirectUntilMs(prevDirectUntil);
|
|
||||||
const fallbackLikelyByExpiry = !!prevDirectUntilMs && Date.now() >= prevDirectUntilMs - 1200;
|
|
||||||
const directExpiredToSandbox =
|
|
||||||
prevExecutionMode === 'direct' && currentMode === 'sandbox' && fallbackLikelyByExpiry;
|
|
||||||
if (directExpiredToSandbox) {
|
|
||||||
if (this.currentPermissionMode === 'unrestricted') {
|
|
||||||
if (typeof pendingModes.permission_mode === 'string' && pendingModes.permission_mode) {
|
|
||||||
this.currentPermissionMode = pendingModes.permission_mode;
|
|
||||||
} else {
|
|
||||||
this.currentPermissionMode = 'approval';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (typeof this.fetchPermissionMode === 'function') {
|
|
||||||
Promise.resolve(this.fetchPermissionMode()).catch(() => {});
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
if (this.executionModeAutoFallbackToastId) {
|
|
||||||
this.uiDismissToast(this.executionModeAutoFallbackToastId);
|
|
||||||
}
|
|
||||||
} catch (_err) {}
|
|
||||||
this.executionModeAutoFallbackToastId = this.uiPushToast({
|
|
||||||
title: '执行环境已自动回退',
|
|
||||||
message: '完全访问权限已到期,已自动切回受限权限并切换为沙箱执行环境。',
|
|
||||||
type: 'warning',
|
|
||||||
duration: null
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (typeof status.network_permission === 'string') {
|
if (typeof status.network_permission === 'string') {
|
||||||
this.currentNetworkPermission = status.network_permission;
|
this.currentNetworkPermission = status.network_permission;
|
||||||
|
|||||||
@ -129,10 +129,6 @@ export const permissionMethods = {
|
|||||||
this.currentExecutionMode = state.mode;
|
this.currentExecutionMode = state.mode;
|
||||||
}
|
}
|
||||||
this.pendingExecutionMode = '';
|
this.pendingExecutionMode = '';
|
||||||
this.executionModeDirectUntil = state.direct_until ?? null;
|
|
||||||
this.executionModeTtlSeconds =
|
|
||||||
typeof state.ttl_seconds === 'number' ? state.ttl_seconds : this.executionModeTtlSeconds;
|
|
||||||
this.scheduleExecutionModeExpirySync(this.executionModeDirectUntil);
|
|
||||||
this.uiPushToast({
|
this.uiPushToast({
|
||||||
title: '执行环境已更新',
|
title: '执行环境已更新',
|
||||||
message: payload?.message || '已立即生效',
|
message: payload?.message || '已立即生效',
|
||||||
@ -234,8 +230,6 @@ export const permissionMethods = {
|
|||||||
},
|
},
|
||||||
async fetchExecutionMode() {
|
async fetchExecutionMode() {
|
||||||
try {
|
try {
|
||||||
const prevExecutionMode = this.currentExecutionMode;
|
|
||||||
const prevDirectUntil = this.executionModeDirectUntil;
|
|
||||||
const query = this.currentConversationId
|
const query = this.currentConversationId
|
||||||
? `?conversation_id=${encodeURIComponent(this.currentConversationId)}`
|
? `?conversation_id=${encodeURIComponent(this.currentConversationId)}`
|
||||||
: '';
|
: '';
|
||||||
@ -250,79 +244,10 @@ export const permissionMethods = {
|
|||||||
this.currentExecutionMode = state.mode;
|
this.currentExecutionMode = state.mode;
|
||||||
}
|
}
|
||||||
this.pendingExecutionMode = typeof payload.pending_mode === 'string' ? payload.pending_mode : '';
|
this.pendingExecutionMode = typeof payload.pending_mode === 'string' ? payload.pending_mode : '';
|
||||||
this.executionModeDirectUntil = state.direct_until ?? null;
|
|
||||||
if (typeof state.ttl_seconds === 'number') {
|
|
||||||
this.executionModeTtlSeconds = state.ttl_seconds;
|
|
||||||
}
|
|
||||||
this.scheduleExecutionModeExpirySync(this.executionModeDirectUntil);
|
|
||||||
const parseDirectUntilMs = (value) => {
|
|
||||||
if (!value) return 0;
|
|
||||||
if (typeof value === 'number') {
|
|
||||||
return value > 1e12 ? value : value * 1000;
|
|
||||||
}
|
|
||||||
if (typeof value === 'string') {
|
|
||||||
const parsed = Date.parse(value);
|
|
||||||
return Number.isNaN(parsed) ? 0 : parsed;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
};
|
|
||||||
const prevDirectUntilMs = parseDirectUntilMs(prevDirectUntil);
|
|
||||||
const fallbackLikelyByExpiry = !!prevDirectUntilMs && Date.now() >= prevDirectUntilMs - 1200;
|
|
||||||
const directExpiredToSandbox =
|
|
||||||
prevExecutionMode === 'direct' &&
|
|
||||||
this.currentExecutionMode === 'sandbox' &&
|
|
||||||
fallbackLikelyByExpiry;
|
|
||||||
if (directExpiredToSandbox) {
|
|
||||||
if (this.currentPermissionMode === 'unrestricted') {
|
|
||||||
this.currentPermissionMode = 'approval';
|
|
||||||
}
|
|
||||||
Promise.resolve(this.fetchPermissionMode()).catch(() => {});
|
|
||||||
try {
|
|
||||||
if (this.executionModeAutoFallbackToastId) {
|
|
||||||
this.uiDismissToast(this.executionModeAutoFallbackToastId);
|
|
||||||
}
|
|
||||||
} catch (_err) {}
|
|
||||||
this.executionModeAutoFallbackToastId = this.uiPushToast({
|
|
||||||
title: '执行环境已自动回退',
|
|
||||||
message: '完全访问权限已到期,已自动切回受限权限并切换为沙箱执行环境。',
|
|
||||||
type: 'warning',
|
|
||||||
duration: null
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} catch (_error) {
|
} catch (_error) {
|
||||||
// ignore
|
// ignore
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
clearExecutionModeExpirySyncTimer() {
|
|
||||||
if (this.executionModeExpirySyncTimer) {
|
|
||||||
clearTimeout(this.executionModeExpirySyncTimer);
|
|
||||||
this.executionModeExpirySyncTimer = null;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
scheduleExecutionModeExpirySync(directUntil) {
|
|
||||||
this.clearExecutionModeExpirySyncTimer();
|
|
||||||
if (!this.executionModeEnabled || this.currentExecutionMode !== 'direct' || !directUntil) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let untilMs = 0;
|
|
||||||
if (typeof directUntil === 'number') {
|
|
||||||
untilMs = directUntil > 1e12 ? directUntil : directUntil * 1000;
|
|
||||||
} else if (typeof directUntil === 'string') {
|
|
||||||
const parsed = Date.parse(directUntil);
|
|
||||||
if (!Number.isNaN(parsed)) {
|
|
||||||
untilMs = parsed;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!untilMs) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const delayMs = Math.max(250, untilMs - Date.now() + 250);
|
|
||||||
this.executionModeExpirySyncTimer = window.setTimeout(async () => {
|
|
||||||
this.executionModeExpirySyncTimer = null;
|
|
||||||
await this.fetchExecutionMode();
|
|
||||||
await this.fetchPermissionMode();
|
|
||||||
}, delayMs);
|
|
||||||
},
|
|
||||||
async openPathAuthorizationDialog() {
|
async openPathAuthorizationDialog() {
|
||||||
if (!this.executionModeEnabled) return;
|
if (!this.executionModeEnabled) return;
|
||||||
this.pathAuthorizationDialogOpen = true;
|
this.pathAuthorizationDialogOpen = true;
|
||||||
|
|||||||
@ -129,10 +129,6 @@ export function dataState() {
|
|||||||
executionModeEnabled: false,
|
executionModeEnabled: false,
|
||||||
currentExecutionMode: 'sandbox',
|
currentExecutionMode: 'sandbox',
|
||||||
pendingExecutionMode: '',
|
pendingExecutionMode: '',
|
||||||
executionModeDirectUntil: null,
|
|
||||||
executionModeAutoFallbackToastId: null,
|
|
||||||
executionModeTtlSeconds: 600,
|
|
||||||
executionModeExpirySyncTimer: null,
|
|
||||||
networkPermissionEnabled: false,
|
networkPermissionEnabled: false,
|
||||||
currentNetworkPermission: 'restricted',
|
currentNetworkPermission: 'restricted',
|
||||||
pendingNetworkPermission: '',
|
pendingNetworkPermission: '',
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user