diff --git a/CHANGELOG.md b/CHANGELOG.md
index a8e0b3df..d58b1399 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,6 +8,7 @@
### 修复
+- 组织页嵌入本机 openXYOS 不再因为边车「已连接」却空白:`CORS_ORIGIN` 即使漏了 `http://127.0.0.1:3780` / `localhost` / `[::1]`,服务端也会合并监听自源,模块脚本和登录不再 500。FreeOS 启动会先停掉 live 目录里旧的 Node,再用当前 `start-sidecar.ps1` 拉起;嵌套的 `openxyos\dist` 会在启动前提升到 live 根。若 livez 正常但自源请求失败,状态栏和预览区会明确提示,而不是一片白。
- Windows 安装成功后删除 `$INSTDIR\openxyos-runtime` 目录和 `openxyos-runtime.zip`(以及 `%LOCALAPPDATA%\FreeOS` 下同名暂存)。预配失败时保留这些文件以便排查。最终工作目录 `$INSTDIR\openxyos` 与 `%LOCALAPPDATA%\FreeOS\openxyos` 不受影响。
- ima 知识库挂接对齐官方 Agent Interface(https://ima.qq.com/agent-interface,落地页现发 ima-skill 1.1.10):用 Client ID / API Key 连接后,按 `search_knowledge_base` + `get_knowledge_base` 选知识库,按 `get_knowledge_list` / `search_knowledge` 自选文档;文件夹 ID 用接口返回的 `media_id`(不自造 `folder_` 前缀),选择会写入挂接。
- 模型页注册的本机 Ollama / GGUF 现在会出现在对话区模型选择器里,并走本机 `127.0.0.1:11434` 调用。注册会补齐 Ollama 供应商默认值、打开服务开关、热加载对话列表;Ollama 未启动时给出可操作错误,而不是静默失败。
diff --git a/dashboard/src/api/modules/orgModule.ts b/dashboard/src/api/modules/orgModule.ts
index 795c8e85..f2c2f86d 100644
--- a/dashboard/src/api/modules/orgModule.ts
+++ b/dashboard/src/api/modules/orgModule.ts
@@ -52,6 +52,7 @@ export interface OrgControlPlane {
export interface OrgOverview {
enabled: boolean;
sidecar_reachable: boolean;
+ sidecar_embed_ok?: boolean;
sidecar_url: string;
start_available: boolean;
install_ready: boolean;
diff --git a/dashboard/src/locales/en.json b/dashboard/src/locales/en.json
index c2532a3f..dab0a7e8 100644
--- a/dashboard/src/locales/en.json
+++ b/dashboard/src/locales/en.json
@@ -20,6 +20,7 @@
"on": "Enabled",
"off": "Disabled",
"sidecarUp": "Local openXYOS connected",
+ "sidecarEmbedFailed": "Preview failed to load",
"sidecarDown": "Opening local openXYOS",
"sidecarOpening": "Opening local openXYOS",
"lastSync": "Last sync",
@@ -103,6 +104,7 @@
"disabled": "Organization module disabled",
"previewTitle": "openXYOS preview",
"previewOffline": "Opening local openXYOS (http://127.0.0.1:3780)…",
+ "previewBlank": "Local openXYOS is healthy, but the preview stayed blank: requests from its own origin (http://127.0.0.1:3780) were blocked by CORS, so scripts and styles never loaded. Fully quit and reopen FreeOS so the sidecar restarts with the current allow-list.",
"previewLoading": "Opening local openXYOS…",
"fullscreen": "Fullscreen",
"exitFullscreen": "Exit fullscreen",
diff --git a/dashboard/src/locales/zh.json b/dashboard/src/locales/zh.json
index 6aec4802..71577adc 100644
--- a/dashboard/src/locales/zh.json
+++ b/dashboard/src/locales/zh.json
@@ -20,6 +20,7 @@
"on": "已启用",
"off": "已关闭",
"sidecarUp": "本机 openXYOS 已连接",
+ "sidecarEmbedFailed": "预览页未能加载",
"sidecarDown": "正在打开本机 openXYOS",
"sidecarOpening": "正在打开本机 openXYOS",
"lastSync": "最近同步",
@@ -103,6 +104,7 @@
"disabled": "组织模块已关闭",
"previewTitle": "openXYOS 预览",
"previewOffline": "正在打开本机 openXYOS(http://127.0.0.1:3780)…",
+ "previewBlank": "本机 openXYOS 健康检查已通过,但预览页是空白的:页面自己的地址(http://127.0.0.1:3780)被跨域拦截,脚本和样式没有加载。请完全退出并重新打开 FreeOS,用当前白名单重启本机服务。",
"previewLoading": "正在打开本机 openXYOS…",
"fullscreen": "全屏",
"exitFullscreen": "退出全屏",
diff --git a/dashboard/src/pages/Organization/Organization.module.less b/dashboard/src/pages/Organization/Organization.module.less
index bae9a0b0..658dac7b 100644
--- a/dashboard/src/pages/Organization/Organization.module.less
+++ b/dashboard/src/pages/Organization/Organization.module.less
@@ -105,6 +105,21 @@
pointer-events: none;
}
+.previewBlank {
+ position: absolute;
+ inset: 0;
+ z-index: 1;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ padding: 32px 40px;
+ max-width: none;
+ text-align: center;
+ line-height: 1.6;
+ color: var(--fn-text-primary);
+ background: rgba(255, 255, 255, 0.94);
+}
+
.drawerBody {
display: flex;
flex-direction: column;
diff --git a/dashboard/src/pages/Organization/index.test.tsx b/dashboard/src/pages/Organization/index.test.tsx
index 39b9eec2..5c0900b5 100644
--- a/dashboard/src/pages/Organization/index.test.tsx
+++ b/dashboard/src/pages/Organization/index.test.tsx
@@ -1,11 +1,12 @@
import { fireEvent, render, screen, waitFor } from "@testing-library/react";
import { describe, expect, it, vi } from "vitest";
import OrganizationPage from "./index";
-import type { OrgOverview } from "../../api/modules/orgModule";
+import { orgModuleApi, type OrgOverview } from "../../api/modules/orgModule";
const overview: OrgOverview = {
enabled: true,
sidecar_reachable: true,
+ sidecar_embed_ok: true,
sidecar_url: "http://127.0.0.1:3780",
start_available: false,
install_ready: true,
@@ -92,5 +93,20 @@ describe("OrganizationPage", () => {
expect(
screen.getAllByText("organization.downloadSource").length,
).toBeGreaterThan(0);
+ expect(screen.queryByTestId("org-preview-blank")).toBeNull();
+ });
+
+ it("surfaces a blank-preview error when livez is up but embed origin fails", async () => {
+ vi.mocked(orgModuleApi.overview).mockResolvedValue({
+ ...overview,
+ sidecar_embed_ok: false,
+ });
+ render(