Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@

### 新增

- Organization Phase 3(智能体切片):宿主内 Agent Studio / 智能体定制。Dashboard `/organization/agents` 使用共享 `dashboard/src/org-ui` 的 `AgentsPage`。列表走 `GET /api/org-module/agents`;编译 / 生命周期 / 注册复用已有 `POST /api/org-module/blueprints/compile`、`/employees/transition`、`/employees/spawn`(`{FREEOS_HOME}/tenants/<id>/employees/`)。**不是** FreeOS 个性化智能体编辑器,也不是 Chat,也不依赖未挂载的 sidecar `/api/agent-studio/*`。注册后的同事出现在 Experts。`freeos org export-standalone` 同时列出 Agents。未迁:Chat、资料上传、人才市场、薄 Workspace overview。
- Organization Phase 3(设置切片):宿主内 Organization Settings。Dashboard `/organization/settings` 使用共享 `dashboard/src/org-ui` 的 `SettingsPage`。本页只编辑 `org_os` 目录模块开关(复用 `GET/PUT /api/org-module/modules`)与组织本地偏好(`GET /api/org-module/settings` · `PUT /api/org-module/prefs`,`{FREEOS_HOME}/org-os/prefs.json`)。**不是** FreeOS 系统设置:大模型密钥、用户、时区仍链到 `/system-settings`。没有边车,也不复制 sidecar 的 AI/用户/数据库页。`freeos org export-standalone` 同时列出 Settings。未迁:Chat、Workspace、边车系统设置。
- Organization Phase 3(工作台总览切片,Open-12 收口):宿主内薄 Workspace / OpenDashboard。Dashboard `/organization/workspace` 使用共享 `dashboard/src/org-ui` 的 `WorkspacePage`。数据复用已有 `GET /api/org-module/overview`,链到已迁的公告 / 架构 / 员工 / 技能 / 智能体 / 任务 / 知识 / 反思 / 治理 / 设置。**不是**第二套控制面(assemble / pack / loop 仍在 `/organization`)。**Chat 永久不迁。** `freeos org export-standalone` 同时列出 Workspace。Phase 3 Open-12 宿主 UI 完成,下一步 Phase 4 导出。
- Organization Phase 3(智能体切片):宿主内 Agent Studio / 智能体定制。Dashboard `/organization/agents` 使用共享 `dashboard/src/org-ui` 的 `AgentsPage`。列表走 `GET /api/org-module/agents`;编译 / 生命周期 / 注册复用已有 `POST /api/org-module/blueprints/compile`、`/employees/transition`、`/employees/spawn`(`{FREEOS_HOME}/tenants/<id>/employees/`)。**不是** FreeOS 个性化智能体编辑器,也不是 Chat,也不依赖未挂载的 sidecar `/api/agent-studio/*`。注册后的同事出现在 Experts。`freeos org export-standalone` 同时列出 Agents。未迁:Chat、资料上传、人才市场。
- Organization Phase 3(设置切片):宿主内 Organization Settings。Dashboard `/organization/settings` 使用共享 `dashboard/src/org-ui` 的 `SettingsPage`。本页只编辑 `org_os` 目录模块开关(复用 `GET/PUT /api/org-module/modules`)与组织本地偏好(`GET /api/org-module/settings` · `PUT /api/org-module/prefs`,`{FREEOS_HOME}/org-os/prefs.json`)。**不是** FreeOS 系统设置:大模型密钥、用户、时区仍链到 `/system-settings`。没有边车,也不复制 sidecar 的 AI/用户/数据库页。`freeos org export-standalone` 同时列出 Settings。未迁:Chat、边车系统设置。
- Organization Phase 3(反思切片):宿主内 Reflections。Dashboard `/organization/reflections` 使用共享 `dashboard/src/org-ui` 的 `ReflectionsPage`。列表/创建/删除走 `/api/org-module/reflections*`,SQLite 在 `{FREEOS_HOME}/org/reflections.sqlite`。可选 `employee_id` / `task_id` 指向同一租户的目录员工与组织任务。这是组织复盘,不是 Chat,也不是第二套技能运行时。`freeos org export-standalone` 同时列出 Reflections。未迁:Chat、边车技能统计、边车部署铁律样例、其余 Open-12。
- Organization Phase 3(任务切片):宿主内 Tasks。Dashboard `/organization/tasks` 与 `/organization/tasks/:id` 使用共享 `dashboard/src/org-ui` 的 `TasksPage` / `TaskDetailPage`。CRUD、状态流转、子任务与人工评论走 `/api/org-module/tasks*`,SQLite 在 `{FREEOS_HOME}/org/tasks.sqlite`。这是组织待办,不是 Octop cron,也不是项目/Chat 对话。`freeos org export-standalone` 同时列出 Tasks。未迁:Chat、附件上传、边车任务库、其余 Open-12。
- Organization Phase 3(知识切片):宿主内 Knowledge。Dashboard `/organization/knowledge` 使用共享 `dashboard/src/org-ui` 的 `KnowledgePage`。列表/详情/笔记走 `/api/org-module/knowledge*`,包装已有 `KnowledgeService`(Octop `knowledge_bases` / `knowledge_documents`),与 Chat 检索同一套行。不克隆 openXYOS sidecar notes/files DB。上传、文件夹、嵌入模型仍在 `/knowledge-bases`。`freeos org export-standalone` 同时列出 Knowledge。未迁:Chat、边车知识库、其余 Open-12。
Expand Down
6 changes: 5 additions & 1 deletion dashboard/src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@
"packLanded": "Signed-in tenant {{tenant}}: employees {{employees}}, talent {{talent}}, skills {{skills}}, plugins {{plugins}}",
"packPreviewEmployees": "The preview now opens Employees so you can confirm the result.",
"navWorkbench": "Workbench",
"navWorkspace": "Workspace",
"navAnnouncements": "Announcements",
"navOrgChart": "Org chart",
"navEmployees": "Employees",
Expand Down Expand Up @@ -221,7 +222,10 @@
"openSettings": "Open settings",
"agentsTitle": "Agent Studio",
"agentsBody": "Compile organization digital colleagues from blueprints and manage lifecycle on the host. FreeOS Agents, Experts, and personalization stay where they are — no sidecar Agent Studio and no Chat page.",
"openAgents": "Open Agent Studio"
"openAgents": "Open Agent Studio",
"workspaceTitle": "Workspace",
"workspaceBody": "Organization landing overview: host status and links into already-migrated pages. Not a second control plane, and Chat stays on FreeOS.",
"openWorkspace": "Open workspace"
},
"systemSettings": {
"subtitle": "Workbench, remote desktop, and host administration in one place."
Expand Down
6 changes: 5 additions & 1 deletion dashboard/src/locales/zh.json
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@
"packLanded": "当前登录租户 {{tenant}}:员工 {{employees}},人才 {{talent}},技能 {{skills}},插件 {{plugins}}",
"packPreviewEmployees": "右侧预览已打开员工页,可直接核对。",
"navWorkbench": "工作台",
"navWorkspace": "总览",
"navAnnouncements": "通知公告",
"navOrgChart": "组织架构",
"navEmployees": "员工目录",
Expand Down Expand Up @@ -221,7 +222,10 @@
"openSettings": "打开设置",
"agentsTitle": "智能体定制",
"agentsBody": "在宿主内把蓝图编译成组织数字同事并管理生命周期。FreeOS 智能体、专家页和个性化保持原位——没有边车 Agent Studio,也不迁对话页。",
"openAgents": "打开智能体定制"
"openAgents": "打开智能体定制",
"workspaceTitle": "工作台总览",
"workspaceBody": "组织落地页:宿主状态摘要,并链到已迁入的子页。不是第二套控制面,对话仍在 FreeOS。",
"openWorkspace": "打开总览"
},
"systemSettings": {
"subtitle": "工作台、远程桌面与主机管理集中在这里。"
Expand Down
10 changes: 10 additions & 0 deletions dashboard/src/org-ui/api/createClient.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,15 @@ describe("createOrgApiClient", () => {
if (path === "/org-module/blueprints/compile") {
return { slug: "policy-analyst", workspace: "/tmp/policy-analyst" };
}
if (path === "/org-module/overview") {
return {
enabled: true,
runtime: "in_host",
freeos: { employees: 2, spawned_colleagues: 1, org_skills: 3 },
openxyos: { modules: 12, tenant_id: "acme" },
catalog: [{ key: "workspace", locked: true }],
};
}
throw new Error(`unexpected ${path}`);
});
const client = createOrgApiClient({ fetchJson });
Expand Down Expand Up @@ -148,6 +157,7 @@ describe("createOrgApiClient", () => {
})
).slug,
).toBe("policy-analyst");
expect((await client.workspace.overview()).freeos.employees).toBe(2);
expect(fetchJson).toHaveBeenCalled();
});

Expand Down
70 changes: 70 additions & 0 deletions dashboard/src/org-ui/api/createClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -633,6 +633,53 @@ export interface OrgAgentsClient {
spawn(slug: string): Promise<OrgSpawnedColleague>;
}

export interface OrgWorkspaceFreeos {
employees: number;
employee_states?: Record<string, number>;
agents?: number;
spawned_colleagues?: number;
org_skills?: number;
skill_packages?: number;
mcp?: number;
tasks?: number;
}

export interface OrgWorkspaceOpenxyos {
reachable?: boolean;
url?: string;
detail?: string;
modules?: number;
governance?: boolean;
tenant_id?: string;
approvals?: number;
}

export interface OrgWorkspaceColleague {
slug: string;
name: string;
lifecycle: string;
agent_id?: string;
spawned?: boolean;
}

export interface OrgWorkspaceOverview {
enabled: boolean;
runtime?: string;
sidecar_optional?: boolean;
last_sync?: string | null;
freeos: OrgWorkspaceFreeos;
openxyos: OrgWorkspaceOpenxyos;
colleagues?: OrgWorkspaceColleague[];
org_surfaces?: Record<string, number>;
catalog?: OrgCapability[];
module_toggles?: Record<string, boolean>;
notes?: string[];
}

export interface OrgWorkspaceClient {
overview(): Promise<OrgWorkspaceOverview>;
}

export interface OrgApiClient {
announcements: OrgAnnouncementsClient;
org: OrgChartClient;
Expand All @@ -644,6 +691,7 @@ export interface OrgApiClient {
reflections: OrgReflectionsClient;
settings: OrgSettingsClient;
agents: OrgAgentsClient;
workspace: OrgWorkspaceClient;
}

async function unwrap<T>(payload: OrgEnvelope<T>): Promise<T> {
Expand All @@ -668,6 +716,7 @@ export function createOrgApiClient(opts: {
reflectionsPrefix?: string;
settingsPrefix?: string;
agentsPrefix?: string;
workspacePrefix?: string;
}): OrgApiClient {
const prefix = opts.announcementsPrefix ?? "/org-module/announcements";
const orgPrefix = opts.orgPrefix ?? "/org-module/org";
Expand All @@ -678,6 +727,7 @@ export function createOrgApiClient(opts: {
const reflectionsPrefix = opts.reflectionsPrefix ?? "/org-module/reflections";
const settingsPrefix = opts.settingsPrefix ?? "/org-module";
const agentsPrefix = opts.agentsPrefix ?? "/org-module";
const workspacePrefix = opts.workspacePrefix ?? "/org-module";
const fetchJson = opts.fetchJson;

const announcements: OrgAnnouncementsClient = {
Expand Down Expand Up @@ -1206,6 +1256,25 @@ export function createOrgApiClient(opts: {
},
};

const workspace: OrgWorkspaceClient = {
async overview() {
const raw = await fetchJson<
OrgWorkspaceOverview &
OrgEnvelope<OrgWorkspaceOverview> & { error?: string }
>(`${workspacePrefix}/overview`);
if (raw && typeof raw === "object" && raw.freeos) {
return raw;
}
if (raw && typeof raw === "object" && raw.success === false) {
throw new Error(raw.error || "request failed");
}
if (raw?.data?.freeos) {
return raw.data;
}
throw new Error(raw?.error || "request failed");
},
};

return {
announcements,
org,
Expand All @@ -1217,5 +1286,6 @@ export function createOrgApiClient(opts: {
reflections,
settings,
agents,
workspace,
};
}
1 change: 1 addition & 0 deletions dashboard/src/org-ui/contract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export const SHARED_ORG_UI_MODULES = [
"reflections",
"settings",
"agents",
"workspace",
] as const;

export type SharedOrgUiModule = (typeof SHARED_ORG_UI_MODULES)[number];
10 changes: 10 additions & 0 deletions dashboard/src/org-ui/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,11 @@ export type {
OrgColleagueStats,
OrgCompiledColleague,
OrgSpawnedColleague,
OrgWorkspaceClient,
OrgWorkspaceColleague,
OrgWorkspaceFreeos,
OrgWorkspaceOpenxyos,
OrgWorkspaceOverview,
OrgSkill,
OrgSkillCatalogRow,
OrgSkillGenerateResult,
Expand Down Expand Up @@ -94,6 +99,11 @@ export { SettingsPage } from "./pages/settings/SettingsPage";
export type { SettingsPageProps } from "./pages/settings/SettingsPage";
export { AgentsPage } from "./pages/agents/AgentsPage";
export type { AgentsPageProps } from "./pages/agents/AgentsPage";
export { WorkspacePage } from "./pages/workspace/WorkspacePage";
export type {
OrgWorkspaceLinks,
WorkspacePageProps,
} from "./pages/workspace/WorkspacePage";
export type {
IdentityBridge,
OrgFetcher,
Expand Down
167 changes: 167 additions & 0 deletions dashboard/src/org-ui/pages/workspace/WorkspacePage.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
.page {
max-width: 960px;
margin: 0 auto;
padding: 8px 4px 32px;
display: flex;
flex-direction: column;
gap: 16px;
}

.header {
display: flex;
flex-wrap: wrap;
align-items: flex-start;
justify-content: space-between;
gap: 12px;
}

.titleRow {
display: flex;
align-items: center;
gap: 10px;
}

.title {
margin: 0;
font-size: 20px;
font-weight: 650;
}

.subtitle {
margin: 4px 0 0;
color: var(--fn-text-secondary, #667085);
font-size: 13px;
max-width: 640px;
line-height: 1.5;
}

.hint {
margin: 0;
color: var(--fn-text-secondary, #667085);
font-size: 13px;
line-height: 1.5;
}

.callout {
border: 1px solid var(--fn-border, #e5e7eb);
border-radius: 12px;
padding: 14px 16px;
background: var(--fn-bg, #f9fafb);
display: flex;
flex-direction: column;
gap: 8px;
}

.calloutTitle {
margin: 0;
font-size: 13px;
font-weight: 650;
}

.links {
display: flex;
flex-wrap: wrap;
gap: 8px;
}

.chips {
display: flex;
flex-wrap: wrap;
gap: 8px;
}

.metrics {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 10px;
}

@media (min-width: 720px) {
.metrics {
grid-template-columns: repeat(4, minmax(0, 1fr));
}
}

.metric {
border: 1px solid var(--fn-border, #e5e7eb);
border-radius: 12px;
padding: 14px 16px;
background: var(--fn-bg-container, #fff);
display: flex;
flex-direction: column;
gap: 4px;
}

.metricValue {
font-size: 22px;
font-weight: 650;
line-height: 1.2;
}

.metricLabel {
font-size: 12px;
color: var(--fn-text-secondary, #667085);
}

.card {
border: 1px solid var(--fn-border, #e5e7eb);
border-radius: 12px;
padding: 16px;
background: var(--fn-bg-container, #fff);
display: flex;
flex-direction: column;
gap: 12px;
}

.cardTitle {
margin: 0;
font-size: 15px;
font-weight: 650;
}

.grid {
display: grid;
grid-template-columns: 1fr;
gap: 10px;
}

@media (min-width: 720px) {
.grid {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
}

.module {
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 8px;
padding: 12px 14px;
border: 1px solid var(--fn-border, #e5e7eb);
border-radius: 10px;
text-align: left;
}

.moduleName {
font-size: 13px;
font-weight: 600;
}

.moduleDesc {
margin: 0;
font-size: 12px;
color: var(--fn-text-secondary, #667085);
line-height: 1.4;
}

.error {
margin: 0;
color: #b42318;
font-size: 13px;
}

.empty {
text-align: center;
padding: 48px 12px;
color: var(--fn-text-secondary, #667085);
}
Loading
Loading