feat(admin): add Memory V2 personal controls and Skill Runtime page

Wire admin UI and API routes to restored Memind personal memory and skill
runtime modules, including candidate review and runtime status display.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
john
2026-07-13 14:00:20 +08:00
parent 4cbacb8529
commit 731d93843d
11 changed files with 872 additions and 5 deletions
+107
View File
@@ -98,6 +98,8 @@ export function createAdminApp(services) {
loadMindSpaceConfig,
updateMindSpaceConfig,
memoryV2ConfigService,
personalMemoryCandidateStore,
skillRuntimeConfigService,
wechatScheduleLlmConfigService,
adminSystemTestService,
systemTestAccountService,
@@ -366,6 +368,111 @@ export function createAdminApp(services) {
res.json(result);
});
adminApi.get('/memory-v2/status', requireAdmin, async (_req, res) => {
const portalBaseUrl = `http://127.0.0.1:${process.env.H5_PORT ?? 8081}`;
try {
const response = await fetch(`${portalBaseUrl}/api/runtime/status`, {
headers: { accept: 'application/json' },
signal: AbortSignal.timeout(2500),
});
if (!response.ok) {
return res.status(502).json({
ok: false,
message: `Memory V2 运行时状态返回 HTTP ${response.status}`,
});
}
const payload = await response.json();
return res.json({
ok: true,
checkedAt: Date.now(),
memory: payload?.memory ?? null,
});
} catch (err) {
return res.status(503).json({
ok: false,
checkedAt: Date.now(),
message: err instanceof Error ? err.message : 'Memory V2 运行时不可用',
});
}
});
adminApi.get('/memory-v2/candidates', requireAdmin, async (req, res) => {
if (!personalMemoryCandidateStore?.listCandidates) {
return res.status(503).json({ message: '候选记忆存储未启用' });
}
try {
const status = String(req.query.status ?? 'candidate');
const userId = String(req.query.userId ?? '').trim() || null;
const limit = Number(req.query.limit ?? 50);
const [items, counts] = await Promise.all([
personalMemoryCandidateStore.listCandidates({ status, userId, limit }),
personalMemoryCandidateStore.countByStatus(),
]);
return res.json({ items, counts });
} catch (err) {
const missingTable = err?.code === 'ER_NO_SUCH_TABLE';
return res.status(missingTable ? 503 : 400).json({
message: missingTable ? '候选记忆表尚未执行本地迁移' : (err instanceof Error ? err.message : '候选记忆读取失败'),
});
}
});
adminApi.post('/memory-v2/candidates/:id/accept', requireAdmin, async (req, res) => {
if (!personalMemoryCandidateStore?.reviewCandidate) {
return res.status(503).json({ message: '候选记忆存储未启用' });
}
const result = await personalMemoryCandidateStore.reviewCandidate(req.params.id, 'accepted', {
reviewedBy: req.currentUser.id,
});
if (!result.updated) return res.status(409).json({ message: '候选记忆已处理或不存在' });
return res.json(result);
});
adminApi.post('/memory-v2/candidates/:id/reject', requireAdmin, async (req, res) => {
if (!personalMemoryCandidateStore?.reviewCandidate) {
return res.status(503).json({ message: '候选记忆存储未启用' });
}
const result = await personalMemoryCandidateStore.reviewCandidate(req.params.id, 'rejected', {
reviewedBy: req.currentUser.id,
});
if (!result.updated) return res.status(409).json({ message: '候选记忆已处理或不存在' });
return res.json(result);
});
adminApi.get('/skill-runtime/config', requireAdmin, async (_req, res) => {
if (!skillRuntimeConfigService?.getAdminConfig) {
return res.status(503).json({ message: 'Skill Runtime 配置服务未启用' });
}
res.json(await skillRuntimeConfigService.getAdminConfig());
});
const updateSkillRuntimeConfig = async (req, res) => {
if (!skillRuntimeConfigService?.updateAdminConfig) {
return res.status(503).json({ message: 'Skill Runtime 配置服务未启用' });
}
const result = await skillRuntimeConfigService.updateAdminConfig(req.body?.config ?? req.body ?? {}, {
updatedBy: req.currentUser.id,
});
res.json(result);
};
adminApi.put('/skill-runtime/config', requireAdmin, updateSkillRuntimeConfig);
adminApi.patch('/skill-runtime/config', requireAdmin, updateSkillRuntimeConfig);
adminApi.get('/skill-runtime/catalog', requireAdmin, async (_req, res) => {
if (!skillRuntimeConfigService?.listCatalogSummary) {
return res.status(503).json({ message: 'Skill Runtime 配置服务未启用' });
}
res.json({ catalog: await skillRuntimeConfigService.listCatalogSummary() });
});
adminApi.get('/skill-runtime/runtime', requireAdmin, async (_req, res) => {
if (!skillRuntimeConfigService?.getPublicRuntimeConfig) {
return res.status(503).json({ message: 'Skill Runtime 配置服务未启用' });
}
res.json(await skillRuntimeConfigService.getPublicRuntimeConfig());
});
adminApi.get('/system-tests/accounts', requireAdmin, async (_req, res) => {
if (!systemTestAccountService) {
return res.status(503).json({ message: '系统测试账号服务未启用' });
+9
View File
@@ -29,6 +29,8 @@ export async function bootstrapAdminServices() {
updateMindSpaceConfig,
} = await importMemind('mindspace-config.mjs');
const { createMemoryV2AdminConfigService } = await importMemind('memory-v2-admin-config.mjs');
const { createPersonalMemoryCandidateStore } = await importMemind('memory-v2-personal-store.mjs');
const { createSkillRuntimeAdminConfigService } = await importMemind('skill-runtime-admin-config.mjs');
const { createAssetGatewayConfigService } = await importMemind('asset-gateway.mjs');
const { ensureAssetGatewaySchema } = await importMemind('db.mjs');
const { createWechatScheduleLlmConfigService } = await importMemind('wechat-schedule-llm-config.mjs');
@@ -93,6 +95,11 @@ export async function bootstrapAdminServices() {
const memoryV2ConfigService = createMemoryV2AdminConfigService(pool, {
env: process.env,
});
const personalMemoryCandidateStore = createPersonalMemoryCandidateStore(pool);
const skillRuntimeConfigService = createSkillRuntimeAdminConfigService(pool, {
env: process.env,
h5Root,
});
const wechatScheduleLlmConfigService = createWechatScheduleLlmConfigService(pool, {
env: process.env,
});
@@ -145,6 +152,8 @@ export async function bootstrapAdminServices() {
loadMindSpaceConfig,
updateMindSpaceConfig,
memoryV2ConfigService,
personalMemoryCandidateStore,
skillRuntimeConfigService,
wechatScheduleLlmConfigService,
adminSystemTestService,
systemTestAccountService,
+4
View File
@@ -105,6 +105,8 @@ ready
loadMindSpaceConfig,
updateMindSpaceConfig,
memoryV2ConfigService,
personalMemoryCandidateStore,
skillRuntimeConfigService,
wechatScheduleLlmConfigService,
adminSystemTestService,
systemTestAccountService,
@@ -128,6 +130,8 @@ ready
loadMindSpaceConfig,
updateMindSpaceConfig,
memoryV2ConfigService,
personalMemoryCandidateStore,
skillRuntimeConfigService,
wechatScheduleLlmConfigService,
adminSystemTestService,
systemTestAccountService,