Files
memind_adm/server/skills/flag-resolver.mjs
T
john 2e6b0c1818 feat: skill 系统 MVP — SkillRegistry + FeatureFlag + awesome-design-md
- 新增 server/skills/ 模块:
  - schema.mjs:adm_skills / adm_skill_flags / adm_skill_invocations 三表建表 + seed
  - index.mjs:SkillService(listSkills / getSkill / invokeSkill / setSkillEnabled / setSkillFlag)
  - flag-resolver.mjs:优先级链 user > plan > caller_type > global
  - goosed-client.mjs:HTTPS + 自签名证书 + round-robin 双节点支持
  - executors/goosed.mjs:一次性 goosed session,SSE 收集结果
  - design-catalog.mjs:54 个 awesome-design-md DESIGN.md 本地 bundle
  - routes.mjs:/api/skills/* (public) + /admin-api/skills/* (admin)
- 设计 DESIGN.md 无需 auth 即可访问(GET /api/skills/designs)
- Admin 可一键 PATCH /admin-api/skills/:slug/enabled 开关任意 skill
- GOOSED_URLS 环境变量支持本地单节点/103双节点兼容

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-26 17:27:11 +08:00

67 lines
2.1 KiB
JavaScript

/**
* SkillFlagResolver — decides whether a skill invocation is permitted.
*
* Priority chain (highest → lowest):
* 1. skill_flags WHERE scope_type='user' AND scope_id=userId
* 2. skill_flags WHERE scope_type='plan' AND scope_id=planName
* 3. skill_flags WHERE scope_type='caller_type' AND scope_id=callerType
* 4. skills.enabled (global on/off)
*
* Returns { allowed: boolean, reason?: string }
*/
export async function resolveSkillFlag(pool, skillId, { userId, planName, callerType } = {}) {
// Fetch skill base enabled + any flag overrides in one query
const [rows] = await pool.query(
`SELECT scope_type, scope_id, enabled
FROM adm_skill_flags
WHERE skill_id = ?
ORDER BY FIELD(scope_type, 'user', 'plan', 'caller_type', 'global')`,
[skillId],
);
// Build override map: "user:alice" → true/false
const overrides = new Map(rows.map((r) => [`${r.scope_type}:${r.scope_id ?? ''}`, r.enabled === 1]));
// Walk priority chain
if (userId !== undefined) {
const key = `user:${userId}`;
if (overrides.has(key)) {
const enabled = overrides.get(key);
return enabled
? { allowed: true }
: { allowed: false, reason: 'skill_disabled_for_user' };
}
}
if (planName) {
const key = `plan:${planName}`;
if (overrides.has(key)) {
const enabled = overrides.get(key);
return enabled
? { allowed: true }
: { allowed: false, reason: 'skill_disabled_for_plan' };
}
}
if (callerType) {
const key = `caller_type:${callerType}`;
if (overrides.has(key)) {
const enabled = overrides.get(key);
return enabled
? { allowed: true }
: { allowed: false, reason: 'skill_disabled_for_caller_type' };
}
}
// Fall through to global flag (already fetched with the skill row)
const globalKey = 'global:';
if (overrides.has(globalKey)) {
const enabled = overrides.get(globalKey);
return enabled ? { allowed: true } : { allowed: false, reason: 'skill_disabled_globally' };
}
// No override rows; rely on skills.enabled (fetched by caller)
return { allowed: true };
}