"use strict";
// Service Lifecycle — state machine for agentic services.
//
// Services transition through: draft → active → paused → disabled → archived → error → needs_attention
// Each transition is validated, timestamped, and produces an event for the event store.
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.SERVICE_TEMPLATES = void 0;
exports.canTransition = canTransition;
exports.getServiceTemplate = getServiceTemplate;
exports.getServiceLifecycle = getServiceLifecycle;
exports.initServiceLifecycle = initServiceLifecycle;
exports.transitionService = transitionService;
exports.probeServiceHealth = probeServiceHealth;
const fs = __importStar(require("fs/promises"));
const path = __importStar(require("path"));
// ─── Transition rules ───────────────────────────────────────────────
const VALID_TRANSITIONS = {
    draft: ['active', 'archived'],
    active: ['paused', 'disabled', 'error', 'needs_attention', 'archived'],
    paused: ['active', 'disabled', 'archived'],
    disabled: ['active', 'archived'],
    archived: ['active'], // allow re-activation
    error: ['active', 'disabled', 'needs_attention', 'archived'],
    needs_attention: ['active', 'paused', 'disabled', 'archived'],
};
function canTransition(from, to) {
    return VALID_TRANSITIONS[from]?.includes(to) ?? false;
}
// ─── Templates ──────────────────────────────────────────────────────
exports.SERVICE_TEMPLATES = [
    {
        template_id: 'bullet_journal',
        name: 'Bullet Journal',
        description: 'Task tracking with daily/weekly reviews and reminders.',
        default_commands: ['add_task', 'close_task', 'add_note', 'show_today', 'daily_review', 'weekly_review'],
        default_schedule: '0 9 * * *',
        default_state_schema: { tasks: 'Array of tasks', notes: 'Array of notes', reviews: 'Array of review summaries' },
    },
    {
        template_id: 'habit_tracker',
        name: 'Habit Tracker',
        description: 'Track daily habits with streaks and accountability.',
        default_commands: ['log_habit', 'show_streaks', 'show_today', 'weekly_review'],
        default_schedule: '0 21 * * *',
        default_state_schema: { habits: 'Array of habit definitions', logs: 'Daily habit check-ins', streaks: 'Current streak counts' },
    },
    {
        template_id: 'project_standup',
        name: 'Project Standup',
        description: 'Daily standup summaries with blockers and progress.',
        default_commands: ['log_progress', 'add_blocker', 'resolve_blocker', 'show_status', 'daily_standup'],
        default_schedule: '0 9 * * 1-5',
        default_state_schema: { progress: 'Progress entries', blockers: 'Active blockers', standups: 'Standup summaries' },
    },
    {
        template_id: 'site_monitor',
        name: 'Site Monitor',
        description: 'Monitor a website for changes or conditions.',
        default_commands: ['show_status', 'add_note', 'record_observation', 'pause_reminders'],
        default_schedule: '0 9 * * *',
        default_state_schema: { observations: 'Timestamped findings', notes: 'User notes' },
    },
    {
        template_id: 'finance_checkin',
        name: 'Finance Check-in',
        description: 'Weekly financial review and budget tracking.',
        default_commands: ['log_expense', 'show_budget', 'weekly_review'],
        default_schedule: '0 18 * * 0',
        default_state_schema: { expenses: 'Logged expenses', budgets: 'Budget categories', reviews: 'Weekly summaries' },
    },
    {
        template_id: 'reading_tracker',
        name: 'Reading Tracker',
        description: 'Track books, articles, and reading progress.',
        default_commands: ['add_book', 'log_progress', 'finish_book', 'show_reading_list', 'show_finished'],
        default_schedule: '0 21 * * 0',
        default_state_schema: { reading_list: 'Books in progress', finished: 'Completed books', progress_logs: 'Reading sessions' },
    },
    {
        template_id: 'learning_tracker',
        name: 'Learning Tracker',
        description: 'Track courses, skills, and learning goals.',
        default_commands: ['add_goal', 'log_session', 'complete_goal', 'show_goals', 'weekly_review'],
        default_schedule: '0 20 * * 0',
        default_state_schema: { goals: 'Learning goals', sessions: 'Study sessions', completed: 'Completed goals' },
    },
];
function getServiceTemplate(templateId) {
    return exports.SERVICE_TEMPLATES.find((t) => t.template_id === templateId);
}
// ─── Lifecycle persistence ──────────────────────────────────────────
function lifecyclePath(projectDir, serviceId) {
    return path.join(projectDir, '.harness', 'services', serviceId, 'lifecycle.json');
}
async function getServiceLifecycle(projectDir, serviceId) {
    try {
        const raw = await fs.readFile(lifecyclePath(projectDir, serviceId), 'utf-8');
        return JSON.parse(raw);
    }
    catch {
        return null;
    }
}
async function initServiceLifecycle(projectDir, serviceId, initialStatus = 'draft') {
    const now = new Date().toISOString();
    const state = {
        service_id: serviceId,
        status: initialStatus,
        created_at: now,
        updated_at: now,
    };
    const fp = lifecyclePath(projectDir, serviceId);
    await fs.mkdir(path.dirname(fp), { recursive: true });
    await fs.writeFile(fp, JSON.stringify(state, null, 2), 'utf-8');
    return state;
}
async function transitionService(projectDir, serviceId, targetStatus, errorMessage) {
    let current = await getServiceLifecycle(projectDir, serviceId);
    if (!current) {
        current = await initServiceLifecycle(projectDir, serviceId, 'draft');
    }
    if (current.status === targetStatus) {
        return { success: true, from: current.status, to: targetStatus };
    }
    if (!canTransition(current.status, targetStatus)) {
        return {
            success: false,
            from: current.status,
            to: targetStatus,
            error: `Cannot transition from ${current.status} to ${targetStatus}`,
        };
    }
    const updated = {
        ...current,
        previous_status: current.status,
        status: targetStatus,
        error_message: targetStatus === 'error' ? errorMessage : undefined,
        updated_at: new Date().toISOString(),
    };
    await fs.writeFile(lifecyclePath(projectDir, serviceId), JSON.stringify(updated, null, 2), 'utf-8');
    return { success: true, from: current.status, to: targetStatus };
}
/** Probe service health — checks that service/state files exist and are parseable. */
async function probeServiceHealth(projectDir, serviceId) {
    const issues = [];
    const serviceFile = path.join(projectDir, '.harness', 'services', serviceId, 'service.json');
    const stateFile = path.join(projectDir, '.harness', 'services', serviceId, 'state.json');
    try {
        const raw = await fs.readFile(serviceFile, 'utf-8');
        JSON.parse(raw);
    }
    catch {
        issues.push('service.json missing or corrupt');
    }
    try {
        const raw = await fs.readFile(stateFile, 'utf-8');
        JSON.parse(raw);
    }
    catch {
        issues.push('state.json missing or corrupt');
    }
    const lifecycle = await getServiceLifecycle(projectDir, serviceId);
    if (lifecycle?.status === 'error')
        issues.push(`lifecycle in error state: ${lifecycle.error_message ?? 'unknown'}`);
    if (issues.length === 0 && lifecycle) {
        const updated = { ...lifecycle, health_check_at: new Date().toISOString(), updated_at: new Date().toISOString() };
        await fs.writeFile(lifecyclePath(projectDir, serviceId), JSON.stringify(updated, null, 2), 'utf-8');
    }
    return { healthy: issues.length === 0, issues };
}
//# sourceMappingURL=serviceLifecycle.js.map