feat: surface active governance overrides
This commit is contained in:
@@ -683,6 +683,14 @@ def register_oneliner_routes(app: Any, legacy: Any) -> None:
|
||||
def _resolve_project(account: dict[str, Any], project_id: str | None) -> dict[str, Any]:
|
||||
return legacy.resolve_target_project(account["id"], project_id or None, username=account["username"])
|
||||
|
||||
def _resolve_project_for_read(account: dict[str, Any], project_id: str | None) -> dict[str, Any] | None:
|
||||
if project_id:
|
||||
return legacy.resolve_target_project(account["id"], project_id, username=account["username"])
|
||||
return legacy.db.fetch_one(
|
||||
"SELECT * FROM projects WHERE user_id = ? ORDER BY created_at ASC LIMIT 1",
|
||||
(account["id"],),
|
||||
)
|
||||
|
||||
def _resolve_assistant(account: dict[str, Any], assistant_id: str | None, project_id: str = "") -> dict[str, Any] | None:
|
||||
return legacy.resolve_target_assistant(account["id"], assistant_id or None, project_id)
|
||||
|
||||
@@ -4522,10 +4530,10 @@ def register_oneliner_routes(app: Any, legacy: Any) -> None:
|
||||
platform: str | None = Query(default=None),
|
||||
account: dict[str, Any] = Depends(legacy.require_approved),
|
||||
) -> dict[str, Any]:
|
||||
project = _resolve_project(account, project_id or None)
|
||||
project = _resolve_project_for_read(account, project_id or None)
|
||||
return _effective_policy_payload(
|
||||
subject_account=account,
|
||||
subject_project_id=project["id"],
|
||||
subject_project_id=(project or {}).get("id", ""),
|
||||
platform=platform or "",
|
||||
)
|
||||
|
||||
@@ -4534,22 +4542,23 @@ def register_oneliner_routes(app: Any, legacy: Any) -> None:
|
||||
project_id: str | None = Query(default=None),
|
||||
account: dict[str, Any] = Depends(legacy.require_approved),
|
||||
) -> dict[str, Any]:
|
||||
project = _resolve_project(account, project_id or None)
|
||||
project = _resolve_project_for_read(account, project_id or None)
|
||||
resolved_project_id = (project or {}).get("id", "")
|
||||
scope_row = _policy_scope_row(
|
||||
scope_kind="user_global",
|
||||
subject_user_id=account["id"],
|
||||
subject_project_id=project["id"],
|
||||
subject_project_id=resolved_project_id,
|
||||
)
|
||||
payload = _bundle_with_versions(
|
||||
scope_row,
|
||||
fallback_kind="user_global",
|
||||
fallback_user_id=account["id"],
|
||||
fallback_project_id=project["id"],
|
||||
fallback_project_id=resolved_project_id,
|
||||
active_version_only=True,
|
||||
)
|
||||
payload["effective_policy"] = _effective_policy_payload(
|
||||
subject_account=account,
|
||||
subject_project_id=project["id"],
|
||||
subject_project_id=resolved_project_id,
|
||||
platform="",
|
||||
)["effective_policy"]
|
||||
return payload
|
||||
@@ -4600,11 +4609,11 @@ def register_oneliner_routes(app: Any, legacy: Any) -> None:
|
||||
project_id: str | None = Query(default=None),
|
||||
account: dict[str, Any] = Depends(legacy.require_approved),
|
||||
) -> dict[str, Any]:
|
||||
project = _resolve_project(account, project_id or None)
|
||||
project = _resolve_project_for_read(account, project_id or None)
|
||||
scope_row = _policy_scope_row(
|
||||
scope_kind="user_global",
|
||||
subject_user_id=account["id"],
|
||||
subject_project_id=project["id"],
|
||||
subject_project_id=(project or {}).get("id", ""),
|
||||
)
|
||||
items = _list_policy_versions(scope_row)
|
||||
return {"items": items, "count": len(items)}
|
||||
@@ -4616,13 +4625,14 @@ def register_oneliner_routes(app: Any, legacy: Any) -> None:
|
||||
limit: int = Query(default=20, ge=1, le=100),
|
||||
account: dict[str, Any] = Depends(legacy.require_approved),
|
||||
) -> dict[str, Any]:
|
||||
project = _resolve_project(account, project_id or None)
|
||||
project = _resolve_project_for_read(account, project_id or None)
|
||||
resolved_project_id = (project or {}).get("id", "")
|
||||
normalized_platform = _normalize_policy_platform(platform)
|
||||
where_clauses = [
|
||||
"WHERE scope.subject_user_id = ?",
|
||||
"AND (scope.subject_project_id = ? OR scope.subject_project_id = '')",
|
||||
]
|
||||
params: list[Any] = [account["id"], project["id"]]
|
||||
params: list[Any] = [account["id"], resolved_project_id]
|
||||
if normalized_platform:
|
||||
where_clauses.append("AND (scope.platform = '' OR scope.platform = ?)")
|
||||
params.append(normalized_platform)
|
||||
@@ -4670,12 +4680,13 @@ def register_oneliner_routes(app: Any, legacy: Any) -> None:
|
||||
project_id: str | None = Query(default=None),
|
||||
account: dict[str, Any] = Depends(legacy.require_approved),
|
||||
) -> dict[str, Any]:
|
||||
project = _resolve_project(account, project_id or None)
|
||||
project = _resolve_project_for_read(account, project_id or None)
|
||||
resolved_project_id = (project or {}).get("id", "")
|
||||
normalized_platform = _normalize_policy_platform(platform)
|
||||
scope_row = _policy_scope_row(
|
||||
scope_kind="user_platform",
|
||||
subject_user_id=account["id"],
|
||||
subject_project_id=project["id"],
|
||||
subject_project_id=resolved_project_id,
|
||||
platform=normalized_platform,
|
||||
)
|
||||
payload = _bundle_with_versions(
|
||||
@@ -4683,12 +4694,12 @@ def register_oneliner_routes(app: Any, legacy: Any) -> None:
|
||||
fallback_kind="user_platform",
|
||||
fallback_platform=normalized_platform,
|
||||
fallback_user_id=account["id"],
|
||||
fallback_project_id=project["id"],
|
||||
fallback_project_id=resolved_project_id,
|
||||
active_version_only=True,
|
||||
)
|
||||
payload["effective_policy"] = _effective_policy_payload(
|
||||
subject_account=account,
|
||||
subject_project_id=project["id"],
|
||||
subject_project_id=resolved_project_id,
|
||||
platform=normalized_platform,
|
||||
)["effective_policy"]
|
||||
return payload
|
||||
@@ -4743,12 +4754,12 @@ def register_oneliner_routes(app: Any, legacy: Any) -> None:
|
||||
project_id: str | None = Query(default=None),
|
||||
account: dict[str, Any] = Depends(legacy.require_approved),
|
||||
) -> dict[str, Any]:
|
||||
project = _resolve_project(account, project_id or None)
|
||||
project = _resolve_project_for_read(account, project_id or None)
|
||||
normalized_platform = _normalize_policy_platform(platform)
|
||||
scope_row = _policy_scope_row(
|
||||
scope_kind="user_platform",
|
||||
subject_user_id=account["id"],
|
||||
subject_project_id=project["id"],
|
||||
subject_project_id=(project or {}).get("id", ""),
|
||||
platform=normalized_platform,
|
||||
)
|
||||
items = _list_policy_versions(scope_row)
|
||||
|
||||
@@ -130,6 +130,56 @@ class MainAgentGovernanceTests(unittest.TestCase):
|
||||
"member_headers": {"Authorization": f"Bearer {member_token}"},
|
||||
}
|
||||
|
||||
def _seed_approved_member_without_project(self) -> dict[str, Any]:
|
||||
now = self.db_module.utc_now()
|
||||
admin_id = "acct_admin"
|
||||
member_id = "acct_member_noproject"
|
||||
model_id = "model_default"
|
||||
admin_token = "token_admin"
|
||||
member_token = "token_member_noproject"
|
||||
|
||||
self.core.db.execute(
|
||||
"""
|
||||
INSERT INTO accounts (
|
||||
id, username, password_hash, password_salt, display_name, role, approval_status,
|
||||
approved_by, approved_at, preferred_analysis_model_id, created_at, updated_at
|
||||
) VALUES (?, ?, 'hash', 'salt', ?, ?, 'approved', ?, ?, ?, ?, ?)
|
||||
""",
|
||||
(admin_id, "admin", "Admin", "super_admin", admin_id, now, model_id, now, now),
|
||||
)
|
||||
self.core.db.execute(
|
||||
"""
|
||||
INSERT INTO accounts (
|
||||
id, username, password_hash, password_salt, display_name, role, approval_status,
|
||||
approved_by, approved_at, preferred_analysis_model_id, created_at, updated_at
|
||||
) VALUES (?, ?, 'hash', 'salt', ?, ?, 'approved', ?, ?, ?, ?, ?)
|
||||
""",
|
||||
(member_id, "member_noproject", "Member No Project", "operator", admin_id, now, model_id, now, now),
|
||||
)
|
||||
self.core.db.execute(
|
||||
"""
|
||||
INSERT INTO model_profiles (
|
||||
id, owner_account_id, name, provider, base_url, api_key, model_name,
|
||||
is_system, is_default, created_at, updated_at
|
||||
) VALUES (?, NULL, 'Default Model', 'openai_compat', 'http://127.0.0.1:8317/v1', '', 'GLM-5', 1, 1, ?, ?)
|
||||
""",
|
||||
(model_id, now, now),
|
||||
)
|
||||
self.core.db.execute(
|
||||
"INSERT INTO auth_tokens (token, account_id, created_at) VALUES (?, ?, ?)",
|
||||
(admin_token, admin_id, now),
|
||||
)
|
||||
self.core.db.execute(
|
||||
"INSERT INTO auth_tokens (token, account_id, created_at) VALUES (?, ?, ?)",
|
||||
(member_token, member_id, now),
|
||||
)
|
||||
return {
|
||||
"admin_id": admin_id,
|
||||
"member_id": member_id,
|
||||
"admin_headers": {"Authorization": f"Bearer {admin_token}"},
|
||||
"member_headers": {"Authorization": f"Bearer {member_token}"},
|
||||
}
|
||||
|
||||
def test_effective_policy_merges_system_user_global_and_platform_layers(self) -> None:
|
||||
system_response = self.client.put(
|
||||
"/v2/admin/oneliner/governance/system/main-agent",
|
||||
@@ -378,6 +428,33 @@ class MainAgentGovernanceTests(unittest.TestCase):
|
||||
self.assertEqual(system_read.status_code, 200, system_read.text)
|
||||
self.assertEqual(system_read.json()["current_version"]["title"], "System baseline")
|
||||
|
||||
def test_governance_read_endpoints_do_not_create_default_project_when_project_is_missing(self) -> None:
|
||||
self._clear_tables()
|
||||
ctx = self._seed_approved_member_without_project()
|
||||
before_count = self.core.db.fetch_one("SELECT COUNT(*) AS count FROM projects WHERE user_id = ?", (ctx["member_id"],))
|
||||
self.assertEqual(int((before_count or {}).get("count") or 0), 0)
|
||||
|
||||
effective_response = self.client.get(
|
||||
"/v2/oneliner/governance/effective",
|
||||
headers=ctx["member_headers"],
|
||||
params={"platform": "douyin"},
|
||||
)
|
||||
self.assertEqual(effective_response.status_code, 200, effective_response.text)
|
||||
effective_payload = effective_response.json()
|
||||
self.assertEqual(effective_payload["project_id"], "")
|
||||
self.assertEqual(effective_payload["layers"], [])
|
||||
|
||||
global_response = self.client.get(
|
||||
"/v2/oneliner/governance/user/global",
|
||||
headers=ctx["member_headers"],
|
||||
)
|
||||
self.assertEqual(global_response.status_code, 200, global_response.text)
|
||||
self.assertEqual(global_response.json()["scope"]["subject_project_id"], "")
|
||||
self.assertIsNone(global_response.json()["current_version"])
|
||||
|
||||
after_count = self.core.db.fetch_one("SELECT COUNT(*) AS count FROM projects WHERE user_id = ?", (ctx["member_id"],))
|
||||
self.assertEqual(int((after_count or {}).get("count") or 0), 0)
|
||||
|
||||
def test_admin_governance_directory_lists_accounts_and_projects(self) -> None:
|
||||
response = self.client.get(
|
||||
"/v2/admin/oneliner/governance/directory",
|
||||
|
||||
@@ -965,6 +965,7 @@ function renderOneLinerMessagesHtml() {
|
||||
const result = message.result || {};
|
||||
const plan = message.plan || {};
|
||||
const executionCard = result.execution_card || {};
|
||||
const activeAdminOverrideNotice = executionCard.active_admin_override_notice || null;
|
||||
const actions = safeArray(plan.suggested_actions);
|
||||
const secondaryActions = safeArray(executionCard.secondary_actions);
|
||||
return `
|
||||
@@ -995,6 +996,16 @@ function renderOneLinerMessagesHtml() {
|
||||
${executionCard.readiness_label ? `<span class="tag ${executionCard.readiness_score >= 75 ? "green" : executionCard.readiness_score >= 50 ? "blue" : "orange"}">${escapeHtml(executionCard.readiness_label)} ${escapeHtml(formatNumber(executionCard.readiness_score || 0))}</span>` : ""}
|
||||
${executionCard.primary_action?.key ? `<span class="tag clickable-tag" data-action="${escapeHtml(executionCard.primary_action.key)}">${escapeHtml(executionCard.primary_action.label || "执行下一步")}</span>` : ""}
|
||||
</div>
|
||||
${activeAdminOverrideNotice?.title ? `
|
||||
<div class="task-item compact" style="margin-top:10px; border-color:rgba(245, 158, 11, 0.28); background:linear-gradient(180deg, rgba(255, 250, 240, 0.98) 0%, rgba(255, 255, 255, 0.98) 100%);">
|
||||
<h4>管理员覆盖生效中</h4>
|
||||
<p>${escapeHtml(activeAdminOverrideNotice.summary || "当前这轮执行会优先遵循管理员覆盖,再叠加你的个人策略。")}</p>
|
||||
<div class="task-meta">
|
||||
<span class="tag orange">${escapeHtml(activeAdminOverrideNotice.title || "管理员覆盖")}</span>
|
||||
${activeAdminOverrideNotice.platform_label ? `<span class="tag">${escapeHtml(activeAdminOverrideNotice.platform_label)}</span>` : ""}
|
||||
</div>
|
||||
</div>
|
||||
` : ""}
|
||||
${safeArray(executionCard.evidence).length ? `
|
||||
<div class="list" style="margin-top:10px;">
|
||||
${safeArray(executionCard.evidence).slice(0, 2).map((item) => `
|
||||
@@ -1053,6 +1064,7 @@ function renderOneLinerUi() {
|
||||
const input = document.querySelector('[data-role="oneliner-input"]');
|
||||
const profile = appState.onelinerProfile;
|
||||
const effective = appState.onelinerGovernanceEffective;
|
||||
const activeAdminOverrideNotice = effective?.active_admin_override_notice || null;
|
||||
const highlights = summarizePolicyHighlights(effective?.effective_policy || {}, effective?.platform || "");
|
||||
const layers = safeArray(effective?.layers);
|
||||
if (fab) {
|
||||
@@ -1072,6 +1084,16 @@ function renderOneLinerUi() {
|
||||
${highlights.map((item) => `<span class="tag green">${escapeHtml(item)}</span>`).join("")}
|
||||
<span class="tag clickable-tag" data-action="open-user-global-policy">我的策略</span>
|
||||
</div>
|
||||
${activeAdminOverrideNotice?.title ? `
|
||||
<div class="task-item compact" style="margin-top:10px; border-color:rgba(245, 158, 11, 0.28); background:linear-gradient(180deg, rgba(255, 250, 240, 0.98) 0%, rgba(255, 255, 255, 0.98) 100%);">
|
||||
<h4>管理员覆盖生效中</h4>
|
||||
<p>${escapeHtml(activeAdminOverrideNotice.summary || "当前主 Agent 会优先遵循管理员覆盖层。")}</p>
|
||||
<div class="task-meta">
|
||||
<span class="tag orange">${escapeHtml(activeAdminOverrideNotice.title || "管理员覆盖")}</span>
|
||||
<span class="tag clickable-tag" data-action="open-user-global-policy">查看我的策略</span>
|
||||
</div>
|
||||
</div>
|
||||
` : ""}
|
||||
`;
|
||||
}
|
||||
if (sessions) sessions.innerHTML = renderOneLinerSessionTabs();
|
||||
@@ -3333,6 +3355,7 @@ function getAdminOverrideTargetSummary(target = appState.adminOverrideTarget) {
|
||||
function renderGovernanceSummaryCard({ title, subtitle, effective, primaryAction = "", primaryLabel = "编辑策略", secondaryAction = "", secondaryLabel = "", secondaryPlatform = "", actions = null }) {
|
||||
const layers = safeArray(effective?.layers);
|
||||
const highlights = summarizePolicyHighlights(effective?.effective_policy || {}, effective?.platform || secondaryPlatform || "");
|
||||
const activeAdminOverrideNotice = effective?.active_admin_override_notice || null;
|
||||
const resolvedActions = safeArray(actions?.length ? actions : [
|
||||
primaryAction ? { action: primaryAction, label: primaryLabel } : null,
|
||||
secondaryAction ? { action: secondaryAction, label: secondaryLabel, platform: secondaryPlatform } : null
|
||||
@@ -3345,6 +3368,16 @@ function renderGovernanceSummaryCard({ title, subtitle, effective, primaryAction
|
||||
${layers.map((layer) => `<span class="tag ${layer.scope_kind === "admin_override" ? "orange" : "blue"}">${escapeHtml(policyScopeTagLabel(layer.scope_kind, layer.scope?.platform || effective?.platform || ""))}</span>`).join("") || `<span class="tag">尚未发布</span>`}
|
||||
${highlights.map((item) => `<span class="tag green">${escapeHtml(item)}</span>`).join("")}
|
||||
</div>
|
||||
${activeAdminOverrideNotice?.title ? `
|
||||
<div class="task-item compact" style="margin-top:10px; border-color:rgba(245, 158, 11, 0.28); background:linear-gradient(180deg, rgba(255, 250, 240, 0.98) 0%, rgba(255, 255, 255, 0.98) 100%);">
|
||||
<h4>管理员覆盖生效中</h4>
|
||||
<p>${escapeHtml(activeAdminOverrideNotice.summary || activeAdminOverrideNotice.title || "当前这层管理员覆盖会优先于你的个人策略生效。")}</p>
|
||||
<div class="task-meta">
|
||||
<span class="tag orange">${escapeHtml(activeAdminOverrideNotice.title || "管理员覆盖")}</span>
|
||||
${activeAdminOverrideNotice.platform_label ? `<span class="tag">${escapeHtml(activeAdminOverrideNotice.platform_label)}</span>` : ""}
|
||||
</div>
|
||||
</div>
|
||||
` : ""}
|
||||
${resolvedActions.length ? `
|
||||
<div class="task-meta" style="margin-top:10px;">
|
||||
${resolvedActions.map((item) => `
|
||||
@@ -4900,6 +4933,7 @@ function renderPlaybookScreen() {
|
||||
const currentModel = getCurrentModelProfile();
|
||||
const currentAssistant = getSelectedAssistant();
|
||||
const localCatalog = appState.localModelCatalog || {};
|
||||
const activeAdminOverrideNotice = appState.onelinerGovernanceEffective?.active_admin_override_notice || null;
|
||||
const gatewayModels = safeArray(localCatalog.models).map((item) => item.id).filter(Boolean);
|
||||
const tabs = [
|
||||
{ value: "workspace", label: "当前 Agent 工作台" },
|
||||
@@ -4951,6 +4985,16 @@ function renderPlaybookScreen() {
|
||||
<span class="tag clickable-tag" data-action="open-oneliner-profile">编辑配置</span>
|
||||
</div>
|
||||
</div>
|
||||
${activeAdminOverrideNotice?.title ? `
|
||||
<div class="task-item compact" style="margin-top:12px; border-color:rgba(245, 158, 11, 0.28); background:linear-gradient(180deg, rgba(255, 250, 240, 0.98) 0%, rgba(255, 255, 255, 0.98) 100%);">
|
||||
<h4>管理员覆盖生效中</h4>
|
||||
<p>${escapeHtml(activeAdminOverrideNotice.summary || "当前 OneLiner 和平台 Agent 都会先遵循管理员覆盖层。")}</p>
|
||||
<div class="task-meta">
|
||||
<span class="tag orange">${escapeHtml(activeAdminOverrideNotice.title || "管理员覆盖")}</span>
|
||||
<span class="tag clickable-tag" data-action="open-user-global-policy">看我的策略</span>
|
||||
</div>
|
||||
</div>
|
||||
` : ""}
|
||||
${renderGovernanceSummaryCard({
|
||||
title: "我的策略与历史",
|
||||
subtitle: appState.userGlobalPolicy?.current_version?.summary || "你和主 Agent 的策略对话,会先沉淀成用户全局策略,再按需要下放到单平台。",
|
||||
@@ -5320,6 +5364,7 @@ function renderStrategyScreen() {
|
||||
const activeTab = getActiveDetailTab("strategyDetailTab", tabs);
|
||||
const project = getSelectedProject();
|
||||
const platform = appState.onelinerGovernanceEffective?.platform || appState.onelinerProfile?.default_platform || getPreferredPlatform();
|
||||
const activeAdminOverrideNotice = appState.onelinerGovernanceEffective?.active_admin_override_notice || null;
|
||||
return screenShell(
|
||||
"我的策略",
|
||||
"把你和主 Agent 的对话沉淀成可查看、可回滚、可追溯的个人策略层。",
|
||||
@@ -5328,6 +5373,16 @@ function renderStrategyScreen() {
|
||||
<div class="hero-card">
|
||||
<h3>当前策略工作区</h3>
|
||||
<p>${escapeHtml(project?.name || "当前项目")} · ${escapeHtml(platformLabel(platform))}。这里展示系统默认、你的个性化策略和管理员覆盖是如何叠加生效的。</p>
|
||||
${activeAdminOverrideNotice?.title ? `
|
||||
<div class="task-item compact" style="margin-top:14px; border-color:rgba(245, 158, 11, 0.28); background:linear-gradient(180deg, rgba(255, 250, 240, 0.98) 0%, rgba(255, 255, 255, 0.98) 100%);">
|
||||
<h4>管理员覆盖生效中</h4>
|
||||
<p>${escapeHtml(activeAdminOverrideNotice.summary || "当前项目下的部分策略被管理员覆盖层托底。")}</p>
|
||||
<div class="task-meta">
|
||||
<span class="tag orange">${escapeHtml(activeAdminOverrideNotice.title || "管理员覆盖")}</span>
|
||||
${activeAdminOverrideNotice.platform_label ? `<span class="tag">${escapeHtml(activeAdminOverrideNotice.platform_label)}</span>` : ""}
|
||||
</div>
|
||||
</div>
|
||||
` : ""}
|
||||
</div>
|
||||
<div class="panel pad" style="margin-top:18px;">
|
||||
<div class="panel-head">
|
||||
|
||||
@@ -47,6 +47,7 @@ test("agent screen excludes quota and registry panels and uses page tabs", () =>
|
||||
assert.match(source, /renderGovernanceSummaryCard\(/);
|
||||
assert.match(source, /open-user-global-policy/);
|
||||
assert.match(source, /open-user-platform-policy/);
|
||||
assert.match(source, /active_admin_override_notice/);
|
||||
});
|
||||
|
||||
test("discovery, production, and admin screens use page tabs for heavy content", () => {
|
||||
@@ -125,9 +126,11 @@ test("agent control surfaces load governance endpoints for user and admin summar
|
||||
|
||||
test("oneliner meta and action handlers expose governance entry points", () => {
|
||||
const meta = extractBetween(APP, "function renderOneLinerUi()", "function openOneLinerPanel()");
|
||||
const messages = extractBetween(APP, "function renderOneLinerMessagesHtml()", "function renderOneLinerUi()");
|
||||
const actions = extractBetween(APP, "document.addEventListener(\"click\", async (event) => {", "document.addEventListener(\"submit\", async (event) => {");
|
||||
assert.match(meta, /open-user-global-policy/);
|
||||
assert.match(meta, /policyScopeTagLabel/);
|
||||
assert.match(messages, /active_admin_override_notice/);
|
||||
assert.match(actions, /name === "open-user-global-policy"/);
|
||||
assert.match(actions, /name === "open-system-main-policy"/);
|
||||
});
|
||||
@@ -181,10 +184,13 @@ test("governance UI exposes admin override target picker and history rollback en
|
||||
|
||||
test("user governance UI exposes personal history and rollback entrypoints", () => {
|
||||
const playbook = extractBetween(APP, "function renderPlaybookScreen()", "function renderProductionScreen()");
|
||||
const strategy = extractBetween(APP, "function renderStrategyScreen()", "function renderCreditsScreen()");
|
||||
const actions = extractBetween(APP, "document.addEventListener(\"click\", async (event) => {", "document.addEventListener(\"submit\", async (event) => {");
|
||||
|
||||
assert.match(playbook, /open-user-global-policy-history/);
|
||||
assert.match(playbook, /open-user-platform-policy-history/);
|
||||
assert.match(strategy, /active_admin_override_notice/);
|
||||
assert.match(strategy, /管理员覆盖生效中/);
|
||||
|
||||
assert.match(actions, /name === "open-user-global-policy-history"/);
|
||||
assert.match(actions, /name === "open-user-platform-policy-history"/);
|
||||
|
||||
Reference in New Issue
Block a user