Remove AI config approval flow
This commit is contained in:
@@ -165,7 +165,7 @@ async function main() {
|
||||
{
|
||||
name: "AI 提供商管理",
|
||||
hash: "#/aiProviders",
|
||||
markers: ["AI 提供商管理", "平台 API", aiProvider.name, "密钥状态", "已配置", "AI 配置审查", pluginSeed.diff.diffSummary, server.id]
|
||||
markers: ["AI 提供商管理", "平台 API", aiProvider.name, "密钥状态", "已配置"]
|
||||
},
|
||||
{
|
||||
name: "系统维护",
|
||||
@@ -204,7 +204,7 @@ async function main() {
|
||||
|
||||
evidence.pluginInteractions = {
|
||||
pluginLifecycle: await verifyPluginLifecycleInteraction(chrome, authHeaders, plugin, server),
|
||||
aiDiffApproval: await verifyAIConfigDiffInteraction(chrome, authHeaders, pluginSeed.diff)
|
||||
aiDirectExecution: await verifyAIDirectExecution(chrome, authHeaders, server)
|
||||
};
|
||||
|
||||
evidence.walkthroughs = await verifyResponsiveThemeWalkthroughs(chrome, routeChecks, server);
|
||||
@@ -579,29 +579,29 @@ async function preparePluginOperations(headers, server, plugin) {
|
||||
requestId: `browser-acceptance-ai-config-${stamp}`,
|
||||
serverInstanceId: server.id,
|
||||
purpose: "config.suggest",
|
||||
prompt: "Keep existing settings and add a reviewed max players recommendation."
|
||||
prompt: "Keep existing settings and add a max players recommendation."
|
||||
},
|
||||
headers
|
||||
);
|
||||
if (aiInvocation.status !== "ok" || !aiInvocation.configRecommendation?.diffId) {
|
||||
throw new Error(`AI invocation did not persist a reviewable diff: ${JSON.stringify(aiInvocation)}`);
|
||||
if (aiInvocation.status !== "ok" || !aiInvocation.configRecommendation?.key || !aiInvocation.configExecution?.job?.id || aiInvocation.configExecution.job.capability !== "config.write") {
|
||||
throw new Error(`AI invocation did not dispatch a config.write job: ${JSON.stringify(aiInvocation)}`);
|
||||
}
|
||||
|
||||
const [lifecycles, diffs] = await Promise.all([
|
||||
getJson(`/plugin-lifecycles?pluginId=${encodeURIComponent(plugin.id)}&serverInstanceId=${encodeURIComponent(server.id)}`, headers),
|
||||
getJson(`/ai/config-diffs?serverInstanceId=${encodeURIComponent(server.id)}`, headers)
|
||||
]);
|
||||
const jobs = await getJson(`/jobs?serverInstanceId=${encodeURIComponent(server.id)}`, headers);
|
||||
const lifecycles = await getJson(`/plugin-lifecycles?pluginId=${encodeURIComponent(plugin.id)}&serverInstanceId=${encodeURIComponent(server.id)}`, headers);
|
||||
const installation = findRequired(lifecycles.items, (item) => item.id === lifecycle.installation.id && item.jobId === lifecycle.job.id, "durable plugin lifecycle installation");
|
||||
const diff = findRequired(diffs.items, (item) => item.id === aiInvocation.configRecommendation.diffId && item.state === "pending", "pending AI config diff");
|
||||
const configJob = findRequired(jobs.items, (item) => item.id === aiInvocation.configExecution.job.id && item.capability === "config.write", "AI config write job");
|
||||
|
||||
for (const [label, value] of Object.entries({ installation, diff, aiInvocation })) {
|
||||
for (const [label, value] of Object.entries({ installation, configJob, aiInvocation })) {
|
||||
assertNoForbiddenProjection(value, `plugin operations seed ${label}`);
|
||||
}
|
||||
return {
|
||||
diff,
|
||||
apiProof: {
|
||||
pluginLifecycle: pick(installation, ["id", "pluginId", "serverInstanceId", "currentVersion", "targetVersion", "desiredState", "currentState", "lastOperation", "compatibility", "dependencyState", "jobId"]),
|
||||
aiConfigDiff: pick(diff, ["id", "requestId", "serverInstanceId", "pluginId", "providerId", "model", "key", "configVersion", "diffSummary", "state", "expiresAt"])
|
||||
aiConfigExecution: {
|
||||
recommendation: pick(aiInvocation.configRecommendation, ["key", "diffSummary"]),
|
||||
job: pick(configJob, ["id", "serverInstanceId", "runEndpointId", "capability", "targetKey", "state", "resultRef"])
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -649,46 +649,35 @@ async function verifyPluginLifecycleInteraction(chrome, headers, plugin, server)
|
||||
};
|
||||
}
|
||||
|
||||
async function verifyAIConfigDiffInteraction(chrome, headers, seededDiff) {
|
||||
await chrome.navigate(`${webUrl}/#/aiProviders`);
|
||||
await chrome.waitForText(["AI 配置审查", seededDiff.serverInstanceId, seededDiff.diffSummary, "审查并批准"], "AI config diff review");
|
||||
async function verifyAIDirectExecution(chrome, headers, server) {
|
||||
await chrome.navigate(`${webUrl}/#/servers/${encodeURIComponent(server.id)}`);
|
||||
await chrome.waitForText([server.name, "AI 助手"], "server AI assistant");
|
||||
await chrome.evaluate(() => {
|
||||
const button = Array.from(document.querySelectorAll(".ai-diff-review-panel button")).find((item) => item.textContent?.includes("审查并批准"));
|
||||
if (!(button instanceof HTMLButtonElement)) throw new Error("AI diff review button not found");
|
||||
const tab = Array.from(document.querySelectorAll(".section-tab")).find((item) => item.textContent?.trim() === "AI 助手");
|
||||
if (!(tab instanceof HTMLButtonElement)) throw new Error("AI assistant tab not found");
|
||||
tab.click();
|
||||
});
|
||||
await chrome.waitForText(["AI 配置助手", "生成建议"], "AI direct execution panel");
|
||||
await chrome.evaluate(() => {
|
||||
const textarea = document.querySelector("textarea");
|
||||
if (!(textarea instanceof HTMLTextAreaElement)) throw new Error("AI prompt textarea not found");
|
||||
const setter = Object.getOwnPropertyDescriptor(HTMLTextAreaElement.prototype, "value")?.set;
|
||||
setter.call(textarea, "Keep existing settings and adjust max players.");
|
||||
textarea.dispatchEvent(new Event("input", { bubbles: true }));
|
||||
textarea.dispatchEvent(new Event("change", { bubbles: true }));
|
||||
});
|
||||
await chrome.evaluate(() => {
|
||||
const button = Array.from(document.querySelectorAll("button")).find((item) => item.textContent?.trim() === "生成建议");
|
||||
if (!(button instanceof HTMLButtonElement)) throw new Error("AI suggestion button not found");
|
||||
button.click();
|
||||
});
|
||||
await chrome.waitForText(["批准 AI 配置差异", seededDiff.id, "取消"], "AI diff approval confirmation");
|
||||
await chrome.evaluate(() => {
|
||||
const cancel = document.querySelector(".confirm-panel .confirm-actions button");
|
||||
if (!(cancel instanceof HTMLButtonElement)) throw new Error("AI diff approval cancel button not found");
|
||||
cancel.click();
|
||||
});
|
||||
const pendingResponse = await getJson(`/ai/config-diffs?serverInstanceId=${encodeURIComponent(seededDiff.serverInstanceId)}`, headers);
|
||||
const pending = findRequired(pendingResponse.items, (item) => item.id === seededDiff.id, "AI diff after approval cancel");
|
||||
assertEqual(pending.state, "pending", "cancel keeps AI diff pending");
|
||||
|
||||
await chrome.evaluate(() => {
|
||||
const button = Array.from(document.querySelectorAll(".ai-diff-review-panel button")).find((item) => item.textContent?.includes("审查并批准"));
|
||||
if (!(button instanceof HTMLButtonElement)) throw new Error("AI diff review button not found after cancel");
|
||||
button.click();
|
||||
});
|
||||
await chrome.waitForText(["批准 AI 配置差异", seededDiff.id], "AI diff approval confirmation reopen");
|
||||
await chrome.evaluate(() => {
|
||||
const confirm = document.querySelector(".confirm-panel .confirm-primary");
|
||||
if (!(confirm instanceof HTMLButtonElement)) throw new Error("AI diff approval submit button not found");
|
||||
confirm.click();
|
||||
});
|
||||
await chrome.waitForText(["已审批", "写入任务"], "AI diff durable approval");
|
||||
const approvedResponse = await getJson(`/ai/config-diffs?serverInstanceId=${encodeURIComponent(seededDiff.serverInstanceId)}`, headers);
|
||||
const approved = findRequired(approvedResponse.items, (item) => item.id === seededDiff.id, "approved AI config diff");
|
||||
assertEqual(approved.state, "approved", "browser AI diff approval persisted");
|
||||
if (!approved.jobId || !approved.approvedBy || !approved.approvedAt) {
|
||||
throw new Error(`approved AI diff missed durable approval linkage: ${JSON.stringify(approved)}`);
|
||||
}
|
||||
assertNoForbiddenProjection(approved, "approved AI diff response");
|
||||
await chrome.waitForText(["AI 配置写入已派发", "config.write", "AI 写入任务"], "AI direct config job");
|
||||
const jobs = await getJson(`/jobs?serverInstanceId=${encodeURIComponent(server.id)}`, headers);
|
||||
const configJob = findRequired(jobs.items, (item) => item.capability === "config.write", "browser AI config write job");
|
||||
const terminalSSEPath = `/api/v1/server-instances/${encodeURIComponent(server.id)}/logs/events?jobId=${encodeURIComponent(configJob.id)}`;
|
||||
return {
|
||||
cancelPreservedState: pending.state,
|
||||
persisted: pick(approved, ["id", "serverInstanceId", "state", "approvedBy", "approvedAt", "jobId", "configVersion", "currentConfigChecksum"]),
|
||||
persisted: pick(configJob, ["id", "serverInstanceId", "runEndpointId", "capability", "targetKey", "state", "resultRef"]),
|
||||
terminalSSEPath,
|
||||
forbiddenFragmentScan: "passed",
|
||||
textSample: (await chrome.visibleText()).slice(0, 1200)
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user