Per docs/AI-ASSISTANT-CONCEPT.md §12, P0 is deliberately generation-free: prove platform gating and the policy round trip before any model exists behind it. No provider is called anywhere in this change. - lib/platform-capabilities.ts: supportsLocalLlm/localLlmNeedsCorsSetup, mirroring the same-named module in vncmail-native so the capability contract (§3, §11) reads identically on both clients. Web+Electron only here — mobile is a separate codebase. - lib/ai/types.ts: AiPolicy/AiEntitlement schema, locked in now per decision #4 (entitlement from day one — cheap now, a live-tenant migration later). - app/api/ai/policy/route.ts: GET, unauthenticated (users read this, like /api/admin/policy). Composes the real FeatureGates.aiAssistantEnabled toggle with a hardcoded unlicensed entitlement — there's no seats/billing backend yet (P2), so nothing here can honestly claim otherwise. - components/settings/ai-assistant-settings.tsx: fetches that policy, shows a real (not fake) locked/unlicensed state. No model config UI yet — there is nothing real to configure until P1/P2/P5 land. - New admin FeatureGates.aiAssistantEnabled (default false, like pluginsEnabled): the tab is entirely hidden until an admin opts in, so no existing install suddenly sees a tab that does nothing. Verified: typecheck clean, lint clean, translations test passes (48/48), full production build succeeds with /api/ai/policy compiled in.
34 lines
1.3 KiB
TypeScript
34 lines
1.3 KiB
TypeScript
import { NextResponse } from 'next/server';
|
|
import { configManager } from '@/lib/admin/config-manager';
|
|
import { logger } from '@/lib/logger';
|
|
import { DEFAULT_AI_ENTITLEMENT, type AiPolicy } from '@/lib/ai/types';
|
|
|
|
/**
|
|
* GET /api/ai/policy - AI Assistant policy (NOT admin-protected - users read this)
|
|
*
|
|
* P0 stub (docs/AI-ASSISTANT-CONCEPT.md §12): proves the client<->server
|
|
* policy-fetch plumbing end-to-end with no provider ever called. `enabled`
|
|
* mirrors the admin FeatureGates toggle; entitlement is hardcoded unlicensed
|
|
* until P2 wires a real seats/billing backend (§9) - there is no provider
|
|
* class to grant yet regardless of what an entitlement record might say.
|
|
*/
|
|
export async function GET() {
|
|
try {
|
|
await configManager.ensureLoaded();
|
|
const policy = configManager.getPolicy();
|
|
|
|
const aiPolicy: AiPolicy = {
|
|
enabled: policy.features.aiAssistantEnabled,
|
|
entitlement: { ...DEFAULT_AI_ENTITLEMENT },
|
|
publicConsentVersion: null,
|
|
};
|
|
|
|
return NextResponse.json(aiPolicy, {
|
|
headers: { 'Cache-Control': 'no-store' },
|
|
});
|
|
} catch (error) {
|
|
logger.error('AI policy read error', { error: error instanceof Error ? error.message : 'Unknown error' });
|
|
return NextResponse.json({ error: 'Internal server error' }, { status: 500 });
|
|
}
|
|
}
|