Add per-feature Cursor channel toggles for admin and runtime.
Memind CI / Test, build, and release guards (push) Has been cancelled
Memind CI / Test, build, and release guards (push) Has been cancelled
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>
This commit is contained in:
@@ -1,3 +1,12 @@
|
||||
import {
|
||||
CURSOR_CHANNEL_FEATURES,
|
||||
defaultCursorChannelFeatures,
|
||||
intentAllowlistFromFeatures,
|
||||
isCursorFeatureEnabled,
|
||||
isCursorTaskKindEnabled,
|
||||
normalizeCursorChannelFeatures,
|
||||
} from './cursor-channel-features.mjs';
|
||||
|
||||
const CONFIG_TABLE = 'h5_wechat_cursor_executor_config';
|
||||
const CONFIG_SCOPE = 'global';
|
||||
const POLICY_SOURCE_DEFAULT = 'default';
|
||||
@@ -6,17 +15,21 @@ const POLICY_SOURCE_ADMIN_DB = 'admin-db';
|
||||
const DEFAULT_INTENT_ALLOWLIST = Object.freeze(['page.generate']);
|
||||
const DEFAULT_CHANNEL_ALLOWLIST = Object.freeze(['h5', 'wechat_mp']);
|
||||
|
||||
export { CURSOR_CHANNEL_FEATURES, isCursorFeatureEnabled, isCursorTaskKindEnabled };
|
||||
|
||||
export const CURSOR_EXECUTOR_CHANNEL = Object.freeze({
|
||||
H5: 'h5',
|
||||
WECHAT_MP: 'wechat_mp',
|
||||
});
|
||||
|
||||
function defaultConfigShape() {
|
||||
const features = defaultCursorChannelFeatures();
|
||||
return {
|
||||
enabled: false,
|
||||
userAllowlist: [],
|
||||
channelAllowlist: [...DEFAULT_CHANNEL_ALLOWLIST],
|
||||
intentAllowlist: [...DEFAULT_INTENT_ALLOWLIST],
|
||||
intentAllowlist: intentAllowlistFromFeatures(features),
|
||||
features,
|
||||
fallbackToDeepseek: true,
|
||||
meta: {
|
||||
notes: '',
|
||||
@@ -61,6 +74,25 @@ function parseJsonLike(value, fallback) {
|
||||
return fallback;
|
||||
}
|
||||
|
||||
function mergeFeaturePatch(currentFeatures, patchFeatures) {
|
||||
const base = normalizeCursorChannelFeatures(currentFeatures, {
|
||||
intentAllowlist: DEFAULT_INTENT_ALLOWLIST,
|
||||
});
|
||||
if (!patchFeatures || typeof patchFeatures !== 'object') return base;
|
||||
const next = { ...base };
|
||||
for (const featureKey of Object.values(CURSOR_CHANNEL_FEATURES)) {
|
||||
if (!(featureKey in patchFeatures)) continue;
|
||||
const raw = patchFeatures[featureKey];
|
||||
next[featureKey] = {
|
||||
enabled: normalizeBoolean(
|
||||
typeof raw === 'object' && raw !== null ? raw.enabled : raw,
|
||||
base[featureKey]?.enabled ?? false,
|
||||
),
|
||||
};
|
||||
}
|
||||
return next;
|
||||
}
|
||||
|
||||
function mergePatch(currentConfig, patch = {}) {
|
||||
const next = cloneConfig(currentConfig);
|
||||
if ('enabled' in patch) next.enabled = normalizeBoolean(patch.enabled, false);
|
||||
@@ -69,9 +101,17 @@ function mergePatch(currentConfig, patch = {}) {
|
||||
const channels = normalizeStringList(patch.channelAllowlist);
|
||||
next.channelAllowlist = channels.length ? channels : [...DEFAULT_CHANNEL_ALLOWLIST];
|
||||
}
|
||||
if ('features' in patch) {
|
||||
next.features = mergeFeaturePatch(next.features, patch.features);
|
||||
}
|
||||
if ('intentAllowlist' in patch) {
|
||||
const intents = normalizeStringList(patch.intentAllowlist);
|
||||
next.intentAllowlist = intents.length ? intents : [...DEFAULT_INTENT_ALLOWLIST];
|
||||
next.features = normalizeCursorChannelFeatures(next.features, {
|
||||
intentAllowlist: next.intentAllowlist,
|
||||
});
|
||||
} else if ('features' in patch) {
|
||||
next.intentAllowlist = intentAllowlistFromFeatures(next.features);
|
||||
}
|
||||
if ('fallbackToDeepseek' in patch) {
|
||||
next.fallbackToDeepseek = normalizeBoolean(patch.fallbackToDeepseek, true);
|
||||
@@ -79,18 +119,27 @@ function mergePatch(currentConfig, patch = {}) {
|
||||
if (patch?.meta && typeof patch.meta.notes === 'string') {
|
||||
next.meta.notes = patch.meta.notes;
|
||||
}
|
||||
if (!next.features) {
|
||||
next.features = normalizeCursorChannelFeatures(null, {
|
||||
intentAllowlist: next.intentAllowlist,
|
||||
});
|
||||
}
|
||||
return next;
|
||||
}
|
||||
|
||||
function flattenPolicy(config, source) {
|
||||
const intentAllowlist = normalizeStringList(config.intentAllowlist);
|
||||
const channelAllowlist = normalizeStringList(config.channelAllowlist);
|
||||
const features = normalizeCursorChannelFeatures(config.features, {
|
||||
intentAllowlist: intentAllowlist.length ? intentAllowlist : [...DEFAULT_INTENT_ALLOWLIST],
|
||||
});
|
||||
return {
|
||||
source,
|
||||
enabled: Boolean(config.enabled),
|
||||
userAllowlist: normalizeStringList(config.userAllowlist),
|
||||
channelAllowlist: channelAllowlist.length ? channelAllowlist : [...DEFAULT_CHANNEL_ALLOWLIST],
|
||||
intentAllowlist: intentAllowlist.length ? intentAllowlist : [...DEFAULT_INTENT_ALLOWLIST],
|
||||
intentAllowlist: intentAllowlistFromFeatures(features),
|
||||
features,
|
||||
fallbackToDeepseek: config.fallbackToDeepseek !== false,
|
||||
};
|
||||
}
|
||||
@@ -123,11 +172,14 @@ export function isUserAllowedByWechatCursorPolicy(user, policy) {
|
||||
}
|
||||
|
||||
export function isIntentAllowedByWechatCursorPolicy(intentKind, policy) {
|
||||
const normalized = String(intentKind ?? '').trim();
|
||||
if (!normalized) return false;
|
||||
if (normalized === 'page.generate') {
|
||||
return isCursorFeatureEnabled(policy, CURSOR_CHANNEL_FEATURES.PAGE_GENERATE);
|
||||
}
|
||||
const allowlist = (policy?.intentAllowlist ?? DEFAULT_INTENT_ALLOWLIST)
|
||||
.map((item) => String(item ?? '').trim())
|
||||
.filter(Boolean);
|
||||
const normalized = String(intentKind ?? '').trim();
|
||||
if (!normalized) return false;
|
||||
return allowlist.includes(normalized);
|
||||
}
|
||||
|
||||
@@ -154,8 +206,9 @@ async function loadStoredState(pool) {
|
||||
const row = rows[0];
|
||||
if (!row) return null;
|
||||
const parsed = parseJsonLike(row.config_json, {});
|
||||
const merged = mergePatch(defaultConfigShape(), parsed);
|
||||
return {
|
||||
config: mergePatch(defaultConfigShape(), parsed),
|
||||
config: merged,
|
||||
updatedAt: Number(row.updated_at ?? 0) || null,
|
||||
updatedBy: row.updated_by ?? null,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user