Files
memind/cursor-channel-features.mjs
T
john 617ff0d1dd
Memind CI / Test, build, and release guards (push) Has been cancelled
Add per-feature Cursor channel toggles for admin and runtime.
Expose page/data/scheduled-task/chat-bridge switches in the智趣体验通道 config so Tang can roll out Cursor paths independently with DeepSeek fallback.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-31 09:40:52 +08:00

98 lines
3.6 KiB
JavaScript

export const CURSOR_CHANNEL_FEATURES = Object.freeze({
PAGE_GENERATE: 'pageGenerate',
PAGE_DATA: 'pageData',
EXCEL_ANALYSIS: 'excelAnalysis',
CHAT_BRIDGE: 'chatBridge',
SCHEDULED_TASKS: 'scheduledTasks',
});
export const CURSOR_TASK_KIND = Object.freeze({
PAGE_GENERATION: 'page_generation',
PAGE_DATA: 'page_data',
EXCEL_ANALYSIS: 'excel_analysis',
});
const TASK_KIND_TO_FEATURE = Object.freeze({
[CURSOR_TASK_KIND.PAGE_GENERATION]: CURSOR_CHANNEL_FEATURES.PAGE_GENERATE,
[CURSOR_TASK_KIND.PAGE_DATA]: CURSOR_CHANNEL_FEATURES.PAGE_DATA,
[CURSOR_TASK_KIND.EXCEL_ANALYSIS]: CURSOR_CHANNEL_FEATURES.EXCEL_ANALYSIS,
});
export function defaultCursorChannelFeatures() {
return {
[CURSOR_CHANNEL_FEATURES.PAGE_GENERATE]: { enabled: true },
[CURSOR_CHANNEL_FEATURES.PAGE_DATA]: { enabled: false },
[CURSOR_CHANNEL_FEATURES.EXCEL_ANALYSIS]: { enabled: false },
[CURSOR_CHANNEL_FEATURES.CHAT_BRIDGE]: { enabled: false },
[CURSOR_CHANNEL_FEATURES.SCHEDULED_TASKS]: { enabled: false },
};
}
function normalizeFeatureFlag(value, fallback = false) {
if (value == null) return fallback;
if (typeof value === 'boolean') return value;
if (typeof value === 'object' && value !== null && 'enabled' in value) {
return normalizeFeatureFlag(value.enabled, fallback);
}
const normalized = String(value).trim().toLowerCase();
if (['1', 'true', 'yes', 'on'].includes(normalized)) return true;
if (['0', 'false', 'no', 'off'].includes(normalized)) return false;
return fallback;
}
export function normalizeCursorChannelFeatures(rawFeatures, {
intentAllowlist = ['page.generate'],
} = {}) {
const defaults = defaultCursorChannelFeatures();
const source = rawFeatures && typeof rawFeatures === 'object' ? rawFeatures : {};
const legacyIntents = Array.isArray(intentAllowlist)
? intentAllowlist.map((item) => String(item ?? '').trim()).filter(Boolean)
: [];
const hasExplicitFeatures = Object.keys(source).length > 0;
return {
[CURSOR_CHANNEL_FEATURES.PAGE_GENERATE]: {
enabled: hasExplicitFeatures
? normalizeFeatureFlag(
source[CURSOR_CHANNEL_FEATURES.PAGE_GENERATE],
legacyIntents.includes('page.generate'),
)
: legacyIntents.includes('page.generate'),
},
[CURSOR_CHANNEL_FEATURES.PAGE_DATA]: {
enabled: normalizeFeatureFlag(source[CURSOR_CHANNEL_FEATURES.PAGE_DATA], false),
},
[CURSOR_CHANNEL_FEATURES.EXCEL_ANALYSIS]: {
enabled: normalizeFeatureFlag(source[CURSOR_CHANNEL_FEATURES.EXCEL_ANALYSIS], false),
},
[CURSOR_CHANNEL_FEATURES.CHAT_BRIDGE]: {
enabled: normalizeFeatureFlag(source[CURSOR_CHANNEL_FEATURES.CHAT_BRIDGE], false),
},
[CURSOR_CHANNEL_FEATURES.SCHEDULED_TASKS]: {
enabled: normalizeFeatureFlag(source[CURSOR_CHANNEL_FEATURES.SCHEDULED_TASKS], false),
},
};
}
export function intentAllowlistFromFeatures(features) {
const normalized = normalizeCursorChannelFeatures(features);
const intents = [];
if (normalized[CURSOR_CHANNEL_FEATURES.PAGE_GENERATE]?.enabled) {
intents.push('page.generate');
}
return intents.length > 0 ? intents : ['page.generate'];
}
export function isCursorFeatureEnabled(policy, featureKey) {
if (!policy?.enabled) return false;
const features = policy.features
?? normalizeCursorChannelFeatures(null, { intentAllowlist: policy.intentAllowlist });
return normalizeFeatureFlag(features?.[featureKey], false);
}
export function isCursorTaskKindEnabled(policy, taskKind) {
const featureKey = TASK_KIND_TO_FEATURE[String(taskKind ?? '').trim()];
if (!featureKey) return false;
return isCursorFeatureEnabled(policy, featureKey);
}