"use strict";
// cloud-connector.ts — Secure Cloud Bridge for Cell OSS
// Handles: auth, push/pull sync, community packs, privacy validation
// Additive layer — reads from existing DB & files, writes config only.
// Zero core modification. Cloud is optional — everything works without it.
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.loadConfig = loadConfig;
exports.saveConfig = saveConfig;
exports.deleteConfig = deleteConfig;
exports.getDeviceId = getDeviceId;
exports.cloudLogin = cloudLogin;
exports.cloudVerify = cloudVerify;
exports.buildIntelligenceBundle = buildIntelligenceBundle;
exports.compressBundle = compressBundle;
exports.cloudPush = cloudPush;
exports.cloudPull = cloudPull;
exports.cloudStatus = cloudStatus;
exports.getLocalPacks = getLocalPacks;
exports.getPackById = getPackById;
exports.cloudHealthCheck = cloudHealthCheck;
const crypto = __importStar(require("crypto"));
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
const os = __importStar(require("os"));
const database_1 = require("../../06-memory/database/database");
// ─── Config Management ──────────────────────────────────────────────────────
const CONFIG_PATH = path.join(os.homedir(), '.fivo', 'cell-cloud.json');
const COMMUNITY_PACKS_DIR = path.join(process.cwd(), '.cell', 'community', 'packs');
function ensureDir(p) {
    if (!fs.existsSync(p))
        fs.mkdirSync(p, { recursive: true });
}
function loadConfig() {
    try {
        if (!fs.existsSync(CONFIG_PATH))
            return null;
        const raw = fs.readFileSync(CONFIG_PATH, 'utf-8');
        return JSON.parse(raw);
    }
    catch {
        return null;
    }
}
function saveConfig(config) {
    ensureDir(path.dirname(CONFIG_PATH));
    fs.writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2), 'utf-8');
    try {
        fs.chmodSync(CONFIG_PATH, 0o600);
    }
    catch { }
}
function deleteConfig() {
    if (fs.existsSync(CONFIG_PATH))
        fs.unlinkSync(CONFIG_PATH);
}
function getDeviceId() {
    try {
        return crypto.createHash('sha256')
            .update(os.hostname() + os.userInfo().username)
            .digest('hex').substring(0, 12);
    }
    catch {
        return crypto.randomBytes(6).toString('hex');
    }
}
// ─── HTTP Client ────────────────────────────────────────────────────────────
function apiUrl(config, endpoint) {
    const base = (config.cloudUrl || 'https://cell.fivo.live').replace(/\/$/, '');
    return `${base}${endpoint}`;
}
async function cloudPost(config, endpoint, body) {
    const url = apiUrl(config, endpoint);
    const data = JSON.stringify(body);
    const response = await fetch(url, {
        method: 'POST',
        headers: {
            'Authorization': `Bearer ${config.apiKey}`,
            'Content-Type': 'application/json',
            'X-Device-Id': config.deviceId,
        },
        body: data,
        signal: AbortSignal.timeout(15000),
    });
    if (!response.ok) {
        const text = await response.text();
        throw new Error(`Cloud API error ${response.status}: ${text.substring(0, 100)}`);
    }
    return response.json();
}
async function cloudGet(config, endpoint) {
    const url = apiUrl(config, endpoint);
    const response = await fetch(url, {
        method: 'GET',
        headers: {
            'Authorization': `Bearer ${config.apiKey}`,
            'X-Device-Id': config.deviceId,
        },
        signal: AbortSignal.timeout(15000),
    });
    if (!response.ok) {
        const text = await response.text();
        throw new Error(`Cloud API error ${response.status}: ${text.substring(0, 100)}`);
    }
    return response.json();
}
// ─── Auth ───────────────────────────────────────────────────────────────────
async function cloudLogin(email, password, cloudUrl) {
    const localDeviceId = getDeviceId();
    const base = (cloudUrl || 'https://cell.fivo.live').replace(/\/$/, '');
    const url = `${base}/auth/login`;
    const response = await fetch(url, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ email, password }),
        signal: AbortSignal.timeout(15000),
    });
    if (!response.ok) {
        const text = await response.text();
        throw new Error(`Login failed: ${response.status} — ${text.substring(0, 100)}`);
    }
    const result = await response.json();
    const apiKey = result.apiKey || result.api_key;
    if (!apiKey)
        throw new Error('No API key in response');
    const serverDeviceId = (result.deviceId || result.device_id || localDeviceId);
    const config = {
        apiKey,
        deviceId: serverDeviceId,
        email,
        lastSync: '',
        syncEnabled: true,
        cloudUrl: base,
    };
    saveConfig(config);
    return config;
}
async function cloudVerify(config) {
    try {
        const result = await cloudPost(config, '/auth/verify', { deviceId: config.deviceId });
        return result.valid === true || result.status === 'active';
    }
    catch {
        return false;
    }
}
// ─── Privacy Validation ─────────────────────────────────────────────────────
const FORBIDDEN_PATTERNS = [
    /['"]password['"]\s*[:=]/i,
    /['"](?:sk-|fv_)?[A-Za-z0-9+/]{30,}['"]/,
    /-----BEGIN (?:RSA |EC )?PRIVATE KEY-----/,
    /mongodb\+srv:\/\/[^/\s]+/,
    /postgres:\/\/[^:@]+:[^@]+@/,
    /DATABASE_URL\s*=\s*['"][^'"]+['"]/i,
    /API[_-]?KEY\s*=\s*['"][A-Za-z0-9]{20,}['"]/i,
];
function validatePrivacy(data) {
    const issues = [];
    for (const pattern of FORBIDDEN_PATTERNS) {
        if (pattern.test(data)) {
            issues.push(`Forbidden pattern detected: ${pattern.source.substring(0, 40)}`);
        }
    }
    return { valid: issues.length === 0, issues };
}
function privacyCheckFailed(warnings) {
    return warnings.length > 0 && warnings.some(w => w.startsWith('Forbidden'));
}
// ─── Stack Detection ────────────────────────────────────────────────────────
function detectStack() {
    try {
        const pkgPath = path.join(process.cwd(), 'package.json');
        if (fs.existsSync(pkgPath)) {
            const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
            const deps = { ...pkg.devDependencies, ...pkg.dependencies };
            const keys = Object.keys(deps).map(k => k.toLowerCase());
            if (keys.includes('next') || keys.includes('next.js'))
                return { language: 'typescript', framework: 'nextjs' };
            if (keys.includes('react'))
                return { language: 'typescript', framework: 'react' };
            if (keys.includes('vue'))
                return { language: 'typescript', framework: 'vue' };
            if (keys.includes('express'))
                return { language: 'typescript', framework: 'express' };
            if (keys.includes('svelte'))
                return { language: 'typescript', framework: 'svelte' };
            if (keys.includes('fastify'))
                return { language: 'typescript', framework: 'fastify' };
            if (keys.includes('nestjs'))
                return { language: 'typescript', framework: 'nestjs' };
            if (keys.includes('typescript'))
                return { language: 'typescript', framework: 'node' };
            return { language: 'javascript', framework: 'node' };
        }
        const tsConfig = path.join(process.cwd(), 'tsconfig.json');
        if (fs.existsSync(tsConfig))
            return { language: 'typescript', framework: 'node' };
    }
    catch { }
    return { language: 'unknown', framework: 'unknown' };
}
// ─── Intelligence Builder ───────────────────────────────────────────────────
function buildIntelligenceBundle(config) {
    const db = (0, database_1.getDb)();
    const warnings = [];
    // Profile — anonymize: only category counts, no raw text
    let profile = {};
    try {
        const rows = db.prepare('SELECT section_key FROM developer_profile ORDER BY updated_at DESC').all();
        const keyCounts = {};
        for (const r of rows) {
            const k = (r.section_key || r.key || 'unknown');
            keyCounts[k] = (keyCounts[k] || 0) + 1;
        }
        profile = { categories: Object.entries(keyCounts).map(([k, v]) => ({ key: k, count: v })) };
    }
    catch { }
    // Vibe signals — numbers only, no raw text, high confidence only
    let vibe = {};
    try {
        const vibePath = path.join(process.cwd(), '.cell', 'vibe', 'signals.json');
        if (fs.existsSync(vibePath)) {
            const raw = JSON.parse(fs.readFileSync(vibePath, 'utf-8'));
            if (raw.signals && Array.isArray(raw.signals)) {
                for (const s of raw.signals) {
                    if (s.vibe >= 60)
                        vibe[s.id] = s.vibe;
                }
            }
        }
    }
    catch { }
    // Memory summary — strip details, only type+categories sent
    let memorySummary = [];
    try {
        const events = db.prepare(`SELECT type, topic, importance FROM memory_events
       WHERE importance >= 7 AND project IS NOT NULL
       ORDER BY importance DESC, created_at DESC LIMIT 20`).all();
        memorySummary = events.map((e) => ({
            type: e.type,
            topic: (e.topic || '').substring(0, 40),
            summary: '',
            importance: e.importance || 7,
        }));
    }
    catch { }
    // Project summary — anonymize name, strip decision details
    let projectSummary = { name: 'project', status: 'active', fileCount: 0, recentDecisions: [] };
    try {
        const projects = db.prepare('SELECT name, file_count, status FROM projects LIMIT 1').all();
        if (projects.length > 0) {
            const p = projects[0];
            const decisions = db.prepare('SELECT technology FROM decisions ORDER BY created_at DESC LIMIT 10').all();
            projectSummary = {
                name: 'project',
                status: p.status || 'active',
                fileCount: p.file_count || 0,
                recentDecisions: decisions.map((d) => d.technology || 'unknown'),
            };
        }
    }
    catch { }
    // Anonymous signals — enriched with full pattern data from community-prior
    let anonymousSignals = [];
    try {
        const vibePath = path.join(process.cwd(), '.cell', 'vibe', 'signals.json');
        if (fs.existsSync(vibePath)) {
            const raw = JSON.parse(fs.readFileSync(vibePath, 'utf-8'));
            if (raw.signals && Array.isArray(raw.signals)) {
                anonymousSignals = raw.signals
                    .filter((s) => s.vibe >= 40 && s.count >= 2)
                    .map((s) => ({ category: s.group || 'unknown', signal: s.id, confidence: s.vibe }));
            }
        }
    }
    catch { }
    // Privacy check on serialized data
    const bundleStr = JSON.stringify({ profile, vibe, memorySummary, projectSummary, anonymousSignals });
    const privacyCheck = validatePrivacy(bundleStr);
    if (!privacyCheck.valid) {
        warnings.push(...privacyCheck.issues);
    }
    const bundle = {
        version: 1,
        deviceId: config.deviceId,
        compressedAt: new Date().toISOString(),
        profile,
        vibe,
        memorySummary,
        projectSummary,
        anonymousSignals,
    };
    return { bundle, warnings };
}
function compressBundle(bundle) {
    const json = JSON.stringify(bundle);
    const compressed = Buffer.from(json).toString('base64');
    const checksum = crypto.createHash('sha256').update(json).digest('hex').substring(0, 16);
    return { compressed, checksum };
}
// ─── Sync Operations ────────────────────────────────────────────────────────
async function cloudPush(config) {
    const { bundle, warnings } = buildIntelligenceBundle(config);
    if (privacyCheckFailed(warnings)) {
        return { pushed: false, warnings };
    }
    const { language, framework } = detectStack();
    // Full pattern format matching cloud server expectations
    const patterns = bundle.anonymousSignals.map(s => ({
        patternId: s.signal,
        category: s.category,
        confidence: s.confidence,
        description: `signal from ${bundle.deviceId}`,
        from: s.category,
        to: s.signal,
        signals: { positive: s.confidence, negative: 100 - s.confidence },
    }));
    await cloudPost(config, '/sync/push', {
        deviceId: config.deviceId,
        teamId: config.teamId || null,
        patterns,
        signalCount: patterns.length,
        language,
        framework,
        compressedAt: bundle.compressedAt,
    });
    config.lastSync = new Date().toISOString();
    saveConfig(config);
    return { pushed: true, warnings };
}
async function cloudPull(config) {
    const result = await cloudGet(config, `/sync/pull?deviceId=${encodeURIComponent(config.deviceId)}`);
    const communityPrior = (result.communityPrior || result.packs || []);
    const snapshots = (result.snapshots || []);
    // Map community prior patterns
    const priorPacks = communityPrior.map((p) => ({
        id: p.patternId || p.id || crypto.randomUUID(),
        title: p.title || p.patternId || p.name || 'Community Pattern',
        category: p.category || p.language || 'general',
        description: p.description || p.label || '',
        authorScore: p.authorScore || p.author_score || 50,
        trustScore: p.trustScore || p.trust_score || p.frequency || 50,
        downloadCount: p.downloadCount || p.download_count || 0,
        signals: p.signals || { [p.patternId || 'unknown']: p.frequency || 50 },
        rules: p.rules || [],
        version: p.version || 1,
    }));
    // Also map snapshot data as packs
    const snapshotPacks = snapshots.map((s) => ({
        id: `${s.deviceId || 'snap'}-${s.snapshotId || crypto.randomUUID()}`,
        title: `Snapshot ${s.snapshotId || ''}`,
        category: s.language || 'general',
        description: `${s.patternCount || 0} patterns from ${s.createdAt || 'unknown'}`,
        authorScore: 30,
        trustScore: 20,
        downloadCount: 0,
        signals: {},
        rules: [],
        version: 1,
    }));
    const packs = [...priorPacks, ...snapshotPacks];
    ensureDir(COMMUNITY_PACKS_DIR);
    for (const pack of packs) {
        const packFile = path.join(COMMUNITY_PACKS_DIR, `${pack.id}.json`);
        fs.writeFileSync(packFile, JSON.stringify(pack, null, 2), 'utf-8');
    }
    config.lastSync = new Date().toISOString();
    saveConfig(config);
    return { packs, count: packs.length };
}
async function cloudStatus(config) {
    const isValid = await cloudVerify(config);
    return {
        connected: isValid,
        email: config.email,
        deviceId: config.deviceId,
        lastSync: config.lastSync || 'never',
        pendingUpload: 0,
        pendingDownload: 0,
        serverHealthy: isValid,
    };
}
// ─── Community Pack Management ──────────────────────────────────────────────
function getLocalPacks() {
    if (!fs.existsSync(COMMUNITY_PACKS_DIR))
        return [];
    return fs.readdirSync(COMMUNITY_PACKS_DIR)
        .filter(f => f.endsWith('.json'))
        .map(f => {
        try {
            return JSON.parse(fs.readFileSync(path.join(COMMUNITY_PACKS_DIR, f), 'utf-8'));
        }
        catch {
            return null;
        }
    })
        .filter(Boolean);
}
function getPackById(id) {
    const packFile = path.join(COMMUNITY_PACKS_DIR, `${id}.json`);
    if (!fs.existsSync(packFile))
        return null;
    return JSON.parse(fs.readFileSync(packFile, 'utf-8'));
}
// ─── Health ─────────────────────────────────────────────────────────────────
async function cloudHealthCheck(config) {
    try {
        const result = await cloudGet(config, '/health');
        return result.status === 'ok' || result.healthy === true;
    }
    catch {
        return false;
    }
}
//# sourceMappingURL=cloud-connector.js.map