fix: keep workbench failures inside app shell
This commit is contained in:
@@ -31,6 +31,7 @@
|
||||
- `OneLiner 会话 / 运行详情 / 治理控制面 / integrations / live-recorder` 这些固定接口也已经切成 live-first,请求失败才降级,不再先被陈旧 capability 表拦住。
|
||||
- 任务恢复链会优先真实调用 `/v2/explore/jobs/{job_id}/retry`,只有接口真的不存在时才回退到手动恢复模板。
|
||||
- `找对标 / 跟踪账号` 里一批已经失效的 “当前平台待接入” 按钮禁用与入口分支已删除,当前 active 平台都直接走真实路由,失败时再给真实反馈。
|
||||
- 工作台前端已经清掉浏览器 `alert` 弹窗,缺对象、权限不足、刷新失败和加载失败都会回到站内反馈,不再把用户从当前流程里打断出去。
|
||||
|
||||
### NAS 联调与回归
|
||||
|
||||
@@ -38,7 +39,7 @@
|
||||
- Web: `http://192.168.31.188:19192/`
|
||||
- Collector: `http://192.168.31.188:19193/healthz`
|
||||
- 当前基线通过:
|
||||
- 前端测试 `60/60`
|
||||
- 前端测试 `61/61`
|
||||
- `bash scripts/check_repo_baseline.sh`
|
||||
- `bash scripts/smoke_fnos_storyforge_lan.sh`
|
||||
|
||||
|
||||
@@ -9201,7 +9201,8 @@ async function openPlatformAgentDetailAction(platform) {
|
||||
const normalizedPlatform = normalizePlatformValue(platform, getPreferredPlatform());
|
||||
const profile = safeArray(appState.platformAgents).find((item) => item.platform === normalizedPlatform) || null;
|
||||
if (!profile) {
|
||||
alert("没有找到这个平台 Agent。");
|
||||
rememberAction("平台 Agent 不存在", "当前没有找到这条平台 Agent,请先刷新当前页面。", "orange");
|
||||
renderAll();
|
||||
return;
|
||||
}
|
||||
const [memoriesPayload, skillsPayload] = await Promise.all([
|
||||
@@ -9379,7 +9380,8 @@ function openActionRegistryEditAction(actionKey) {
|
||||
const project = requireSelectedProject();
|
||||
const actionDef = safeArray(appState.onelinerActionRegistry).find((item) => item.action_key === actionKey) || null;
|
||||
if (!actionDef) {
|
||||
alert("没有找到这条动作定义。");
|
||||
rememberAction("动作定义不存在", "当前没有找到这条 OneLiner 动作定义,请先刷新当前页面。", "orange");
|
||||
renderAll();
|
||||
return;
|
||||
}
|
||||
openActionModal({
|
||||
@@ -9495,7 +9497,8 @@ function openCreateAssistantAction() {
|
||||
function openEditAssistantAction(assistantId = "") {
|
||||
const assistant = safeArray(appState.dashboard?.assistants).find((item) => item.id === assistantId) || getSelectedAssistant();
|
||||
if (!assistant) {
|
||||
alert("请先选择一个 Agent");
|
||||
rememberAction("请先选择 Agent", "当前还没有可编辑的 Agent,先在项目里选择或创建一个。", "orange");
|
||||
renderAll();
|
||||
return;
|
||||
}
|
||||
const modelOptions = getModelOptions();
|
||||
@@ -9718,12 +9721,14 @@ async function scanAdminOpsAction() {
|
||||
|
||||
function openAdminIncidentReviewAction(incidentId) {
|
||||
if (!isSuperAdmin()) {
|
||||
alert("只有平台管理者才能审计处理故障事件。");
|
||||
rememberAction("权限不足", "只有平台管理者才能审计处理故障事件。", "orange");
|
||||
renderAll();
|
||||
return;
|
||||
}
|
||||
const incident = safeArray(appState.adminOpsOverview?.incidents).find((item) => item.id === incidentId);
|
||||
if (!incident) {
|
||||
alert("没有找到这条故障事件。");
|
||||
rememberAction("故障事件不存在", "当前没有找到这条故障事件,请先重新扫描。", "orange");
|
||||
renderAll();
|
||||
return;
|
||||
}
|
||||
openActionModal({
|
||||
@@ -9776,12 +9781,14 @@ function openAdminIncidentReviewAction(incidentId) {
|
||||
|
||||
function openAdminRepairPlanAction(incidentId) {
|
||||
if (!isSuperAdmin()) {
|
||||
alert("只有平台管理者才能生成修复计划。");
|
||||
rememberAction("权限不足", "只有平台管理者才能生成修复计划。", "orange");
|
||||
renderAll();
|
||||
return;
|
||||
}
|
||||
const incident = safeArray(appState.adminOpsOverview?.incidents).find((item) => item.id === incidentId);
|
||||
if (!incident) {
|
||||
alert("没有找到这条故障事件。");
|
||||
rememberAction("故障事件不存在", "当前没有找到这条故障事件,请先重新扫描。", "orange");
|
||||
renderAll();
|
||||
return;
|
||||
}
|
||||
openActionModal({
|
||||
@@ -9810,12 +9817,14 @@ function openAdminRepairPlanAction(incidentId) {
|
||||
|
||||
function openAdminFixRunDetailAction(runId) {
|
||||
if (!isSuperAdmin()) {
|
||||
alert("只有平台管理者才能查看修复计划。");
|
||||
rememberAction("权限不足", "只有平台管理者才能查看修复计划。", "orange");
|
||||
renderAll();
|
||||
return;
|
||||
}
|
||||
const run = safeArray(appState.adminFixRuns.length ? appState.adminFixRuns : appState.adminOpsOverview?.recent_fix_runs).find((item) => item.id === runId);
|
||||
if (!run) {
|
||||
alert("没有找到这条修复计划。");
|
||||
rememberAction("修复计划不存在", "当前没有找到这条修复计划,请先刷新管理员配置台。", "orange");
|
||||
renderAll();
|
||||
return;
|
||||
}
|
||||
openActionModal({
|
||||
@@ -9857,12 +9866,14 @@ function openAdminFixRunDetailAction(runId) {
|
||||
|
||||
function openAdminFixRunAuditAction(runId) {
|
||||
if (!isSuperAdmin()) {
|
||||
alert("只有平台管理者才能审计修复计划。");
|
||||
rememberAction("权限不足", "只有平台管理者才能审计修复计划。", "orange");
|
||||
renderAll();
|
||||
return;
|
||||
}
|
||||
const run = safeArray(appState.adminFixRuns.length ? appState.adminFixRuns : appState.adminOpsOverview?.recent_fix_runs).find((item) => item.id === runId);
|
||||
if (!run) {
|
||||
alert("没有找到这条修复计划。");
|
||||
rememberAction("修复计划不存在", "当前没有找到这条修复计划,请先刷新管理员配置台。", "orange");
|
||||
renderAll();
|
||||
return;
|
||||
}
|
||||
openActionModal({
|
||||
@@ -10033,7 +10044,8 @@ function openRecoverJobAction(jobId) {
|
||||
const job = safeArray(appState.dashboard?.recent_jobs).find((item) => item.id === jobId)
|
||||
|| (appState.lastJobDetail?.job?.id === jobId ? appState.lastJobDetail.job : null);
|
||||
if (!job) {
|
||||
alert("没有找到这条任务。");
|
||||
rememberAction("任务不存在", "当前没有找到这条任务,请先刷新生产中心。", "orange");
|
||||
renderAll();
|
||||
return;
|
||||
}
|
||||
const recovery = getJobRecoverability(job);
|
||||
@@ -10350,7 +10362,8 @@ function openLiveRecorderCreateAction() {
|
||||
function openLiveRecorderSourceAction(sourceId) {
|
||||
const source = safeArray(appState.liveRecorderSources).find((item) => item.id === sourceId);
|
||||
if (!source) {
|
||||
alert("没有找到这条录制源。");
|
||||
rememberAction("录制源不存在", "当前没有找到这条录制源,请先刷新录制维护。", "orange");
|
||||
renderAll();
|
||||
return;
|
||||
}
|
||||
const currentProject = getSelectedProject() || safeArray(appState.dashboard?.projects).find((item) => item.id === source.project_id) || appState.dashboard?.projects?.[0] || null;
|
||||
@@ -10435,7 +10448,8 @@ function openLiveRecorderImportAction() {
|
||||
async function toggleLiveRecorderSourceAction(sourceId, nextEnabled) {
|
||||
const source = safeArray(appState.liveRecorderSources).find((item) => item.id === sourceId);
|
||||
if (!source) {
|
||||
alert("没有找到这条录制源。");
|
||||
rememberAction("录制源不存在", "当前没有找到这条录制源,请先刷新录制维护。", "orange");
|
||||
renderAll();
|
||||
return;
|
||||
}
|
||||
setBusy(true, nextEnabled ? "正在启用录制源..." : "正在停用录制源...");
|
||||
@@ -10456,7 +10470,8 @@ async function toggleLiveRecorderSourceAction(sourceId, nextEnabled) {
|
||||
async function deleteLiveRecorderSourceAction(sourceId) {
|
||||
const source = safeArray(appState.liveRecorderSources).find((item) => item.id === sourceId);
|
||||
if (!source) {
|
||||
alert("没有找到这条录制源。");
|
||||
rememberAction("录制源不存在", "当前没有找到这条录制源,请先刷新录制维护。", "orange");
|
||||
renderAll();
|
||||
return;
|
||||
}
|
||||
if (!window.confirm(`确认删除「${source.title || source.source_url || "录制源"}」吗?删除后需要重新导入。`)) {
|
||||
@@ -10660,7 +10675,6 @@ document.addEventListener("click", async (event) => {
|
||||
const reason = action.dataset.disabledReason || action.title || "当前动作暂不可用";
|
||||
rememberAction("动作已拦截", reason, "orange");
|
||||
renderAll();
|
||||
alert(reason);
|
||||
return;
|
||||
}
|
||||
if (name === "auth-refresh" || name === "refresh-data") {
|
||||
@@ -10678,7 +10692,7 @@ document.addEventListener("click", async (event) => {
|
||||
if (name === "auth-refresh" && message) {
|
||||
message.textContent = formatActionErrorMessage(error, "自动连接失败");
|
||||
} else {
|
||||
alert("刷新数据失败: " + error.message);
|
||||
presentActionFailure(error, "刷新数据失败");
|
||||
}
|
||||
} finally {
|
||||
setBusy(false, "");
|
||||
@@ -11064,7 +11078,8 @@ document.addEventListener("click", async (event) => {
|
||||
if (name === "open-review-edit") {
|
||||
const review = getReviewById(action.dataset.reviewId || "");
|
||||
if (!review) {
|
||||
alert("复盘记录不存在,请先刷新页面");
|
||||
rememberAction("复盘记录不存在", "当前没有找到这条复盘记录,请先刷新当前页面。", "orange");
|
||||
renderAll();
|
||||
return;
|
||||
}
|
||||
openReviewAction({ review });
|
||||
@@ -11196,7 +11211,7 @@ document.addEventListener("click", async (event) => {
|
||||
}
|
||||
} catch (error) {
|
||||
if (requestToken === appState.selectedAccountRequestToken) {
|
||||
alert("加载对标详情失败: " + error.message);
|
||||
presentActionFailure(error, "加载对标详情失败");
|
||||
}
|
||||
} finally {
|
||||
if (requestToken === appState.selectedAccountRequestToken) {
|
||||
|
||||
@@ -782,3 +782,10 @@ test("oneliner panel auto-polls active runs while the floating panel stays open"
|
||||
assert.match(APP, /setTimeout\(async \(\) => \{/);
|
||||
assert.match(APP, /await hydrateSelectedOneLinerRun\(\);/);
|
||||
});
|
||||
|
||||
test("workbench interaction flow avoids browser alerts and keeps failures inside the product shell", () => {
|
||||
const clicks = extractBetween(APP, "document.addEventListener(\"click\", async (event) => {", "document.addEventListener(\"submit\", async (event) => {");
|
||||
assert.doesNotMatch(APP, /alert\(/);
|
||||
assert.match(clicks, /name === "show-disabled-reason"[\s\S]*rememberAction\("动作已拦截"/);
|
||||
assert.match(clicks, /name === "auth-refresh" \|\| name === "refresh-data"[\s\S]*presentActionFailure\(error, "刷新数据失败"\)/);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user