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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

### 新增

- 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。
- Organization Phase 3(技能切片):宿主内 Skills。Dashboard `/organization/skills` 使用共享 `dashboard/src/org-ui` 的 `SkillsPage`。列表/详情走 `GET /api/org-module/skills` 与 `GET /api/org-module/skills/{slug}`,生成/发布仍是已有 `POST …/generate|publish`(`{FREEOS_HOME}/org-skills`,`skill_bridge`)。宿主 skill packages 只读列出,编辑仍在「个性化 → 技能包」。这不是第二套技能运行时。`freeos org export-standalone` 同时列出 Skills。未迁:Chat、边车市场/插件中心、其余 Open-12。
- Organization Phase 3(员工目录切片):宿主内 Employees。Dashboard `/organization/employees` 与 `/organization/employees/:id` 使用共享 `dashboard/src/org-ui` 的 `EmployeesPage` / `EmployeeDetailPage`。目录 list/detail/create/update 走 `/api/org-module/org/employees*`,与组织架构共用 `{FREEOS_HOME}/org/org_chart.sqlite`,不另开第二套员工库。lifecycle 同事仍是 `GET /api/org-module/employees`。`freeos org export-standalone` 同时列出 Announcements、Org chart、Employees。未迁:Chat、人才市场/备选/离职清算、绩效、汇报线、技能绑定、头像、其余 Open-12。
- Organization Phase 3(组织架构切片):宿主内 org chart。Dashboard `/organization/org` 使用共享 `dashboard/src/org-ui` 的 `OrgChartPage`;树与 CRUD 走 `/api/org-module/org`(`{FREEOS_HOME}/org/org_chart.sqlite`),不需要 Node sidecar。`freeos org export-standalone` 同时列出 Announcements 与 Org chart。未迁: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 @@ -187,6 +187,7 @@
"navEmployees": "Employees",
"navSkills": "Skills",
"navGovernance": "Governance",
"navKnowledge": "Knowledge",
"announcementsTitle": "Announcements",
"announcementsBody": "Publish notices and track who has read them. This page lives in the host — no sidecar or second port.",
"openAnnouncements": "Open announcements",
Expand All @@ -201,7 +202,10 @@
"openSkills": "Open skills",
"governanceTitle": "Governance",
"governanceBody": "Pending approvals and the host audit log. High-risk tools stay blocked until a human decides — no sidecar or second port.",
"openGovernance": "Open governance"
"openGovernance": "Open governance",
"knowledgeTitle": "Knowledge",
"knowledgeBody": "Lists the same FreeOS knowledge bases Chat retrieves from. Notes become host documents — no sidecar knowledge DB.",
"openKnowledge": "Open knowledge"
},
"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 @@ -187,6 +187,7 @@
"navEmployees": "员工目录",
"navSkills": "技能",
"navGovernance": "治理",
"navKnowledge": "知识",
"announcementsTitle": "通知公告",
"announcementsBody": "发布公告并跟踪已读。此页跑在宿主内,不需要边车或第二端口。",
"openAnnouncements": "打开通知公告",
Expand All @@ -201,7 +202,10 @@
"openSkills": "打开技能",
"governanceTitle": "治理",
"governanceBody": "待人工复核与宿主审计日志。高风险工具在有人拍板前保持拦截,不需要边车或第二端口。",
"openGovernance": "打开治理"
"openGovernance": "打开治理",
"knowledgeTitle": "知识",
"knowledgeBody": "列出 Chat 检索用的同一套 FreeOS 知识库。笔记写入宿主文档,没有边车知识库。",
"openKnowledge": "打开知识"
},
"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 @@ -54,6 +54,15 @@ describe("createOrgApiClient", () => {
host_packages: [{ id: "pkg-1", name: "Ops", skill_count: 2 }],
};
}
if (path === "/org-module/knowledge") {
return {
host_route: "/knowledge-bases",
store: "host_knowledge_bases",
capability: { feature_enabled: true, usable: true },
bases: [{ id: "kb-1", name: "Policies" }],
stats: { bases: 1, documents: 0, shared: 0, owned: 1 },
};
}
throw new Error(`unexpected ${path}`);
});
const client = createOrgApiClient({ fetchJson });
Expand All @@ -69,6 +78,7 @@ describe("createOrgApiClient", () => {
expect((await client.employees.list())[0]?.name).toBe("Ada");
expect((await client.governance.pauses()).pauses[0]?.pause_id).toBe("p1");
expect((await client.skills.list()).skills[0]?.slug).toBe("org-employees");
expect((await client.knowledge.list()).bases[0]?.name).toBe("Policies");
expect(fetchJson).toHaveBeenCalled();
});

Expand Down
131 changes: 130 additions & 1 deletion dashboard/src/org-ui/api/createClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -281,12 +281,97 @@ export interface OrgSkillsClient {
}): Promise<OrgSkillPublishResult>;
}

export interface OrgKnowledgeCapability {
feature_enabled: boolean;
usable: boolean;
prerequisites_ok: boolean;
selected_model: string;
backend: string;
}

export interface OrgKnowledgeBase {
id: string;
knowledge_base_id: string;
name: string;
description: string;
shared: boolean;
default_open: boolean;
icon_name: string;
document_count: number;
owner_user_id: number;
owned: boolean;
documents?: OrgKnowledgeDocument[];
}

export interface OrgKnowledgeDocument {
id: string;
document_id: string;
kb_id: string;
filename: string;
path: string;
content_type: string;
byte_size: number;
is_dir: boolean;
status: string;
chunk_count: number;
created_at: number;
kind: "note" | "file" | string;
}

export interface OrgKnowledgePreview {
id: string;
document_id: string;
kb_id: string;
filename: string;
content_type: string;
text: string;
kind: "note" | "file" | string;
}

export interface OrgKnowledgeStats {
bases: number;
documents: number;
shared: number;
owned: number;
}

export interface OrgKnowledgeList {
host_route: string;
store: string;
capability: OrgKnowledgeCapability;
bases: OrgKnowledgeBase[];
stats: OrgKnowledgeStats;
}

export interface OrgKnowledgeWrite {
name: string;
description?: string;
shared?: boolean;
}

export interface OrgKnowledgeNoteWrite {
title: string;
content?: string;
}

export interface OrgKnowledgeClient {
list(): Promise<OrgKnowledgeList>;
get(kbId: string): Promise<OrgKnowledgeBase>;
preview(kbId: string, docId: string): Promise<OrgKnowledgePreview>;
createBase(body: OrgKnowledgeWrite): Promise<OrgKnowledgeBase>;
createNote(
kbId: string,
body: OrgKnowledgeNoteWrite,
): Promise<OrgKnowledgeDocument>;
}

export interface OrgApiClient {
announcements: OrgAnnouncementsClient;
org: OrgChartClient;
employees: OrgEmployeesClient;
governance: OrgGovernanceClient;
skills: OrgSkillsClient;
knowledge: OrgKnowledgeClient;
}

async function unwrap<T>(payload: OrgEnvelope<T>): Promise<T> {
Expand All @@ -306,11 +391,13 @@ export function createOrgApiClient(opts: {
orgPrefix?: string;
governancePrefix?: string;
skillsPrefix?: string;
knowledgePrefix?: string;
}): OrgApiClient {
const prefix = opts.announcementsPrefix ?? "/org-module/announcements";
const orgPrefix = opts.orgPrefix ?? "/org-module/org";
const governancePrefix = opts.governancePrefix ?? "/org-module/governance";
const skillsPrefix = opts.skillsPrefix ?? "/org-module/skills";
const knowledgePrefix = opts.knowledgePrefix ?? "/org-module/knowledge";
const fetchJson = opts.fetchJson;

const announcements: OrgAnnouncementsClient = {
Expand Down Expand Up @@ -560,5 +647,47 @@ export function createOrgApiClient(opts: {
},
};

return { announcements, org, employees, governance, skills };
const knowledge: OrgKnowledgeClient = {
async list() {
return fetchJson<OrgKnowledgeList>(knowledgePrefix);
},
async get(kbId) {
return fetchJson<OrgKnowledgeBase>(
`${knowledgePrefix}/${encodeURIComponent(kbId)}`,
);
},
async preview(kbId, docId) {
return fetchJson<OrgKnowledgePreview>(
`${knowledgePrefix}/${encodeURIComponent(
kbId,
)}/documents/${encodeURIComponent(docId)}`,
);
},
async createBase(body) {
return fetchJson<OrgKnowledgeBase>(knowledgePrefix, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
name: body.name,
description: body.description || "",
shared: Boolean(body.shared),
}),
});
},
async createNote(kbId, body) {
return fetchJson<OrgKnowledgeDocument>(
`${knowledgePrefix}/${encodeURIComponent(kbId)}/notes`,
{
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
title: body.title,
content: body.content || "",
}),
},
);
},
};

return { announcements, org, employees, governance, skills, knowledge };
}
1 change: 1 addition & 0 deletions dashboard/src/org-ui/contract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export const SHARED_ORG_UI_MODULES = [
"employees",
"skills",
"governance",
"knowledge",
] as const;

export type SharedOrgUiModule = (typeof SHARED_ORG_UI_MODULES)[number];
11 changes: 11 additions & 0 deletions dashboard/src/org-ui/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,15 @@ export type {
OrgEmployeesClient,
OrgEnvelope,
OrgGovernanceClient,
OrgKnowledgeBase,
OrgKnowledgeCapability,
OrgKnowledgeClient,
OrgKnowledgeDocument,
OrgKnowledgeList,
OrgKnowledgeNoteWrite,
OrgKnowledgePreview,
OrgKnowledgeStats,
OrgKnowledgeWrite,
OrgSkill,
OrgSkillCatalogRow,
OrgSkillGenerateResult,
Expand All @@ -43,6 +52,8 @@ export { SkillsPage } from "./pages/skills/SkillsPage";
export type { SkillsPageProps } from "./pages/skills/SkillsPage";
export { GovernancePage } from "./pages/governance/GovernancePage";
export type { GovernancePageProps } from "./pages/governance/GovernancePage";
export { KnowledgePage } from "./pages/knowledge/KnowledgePage";
export type { KnowledgePageProps } from "./pages/knowledge/KnowledgePage";
export type {
IdentityBridge,
OrgFetcher,
Expand Down
Loading
Loading