"use strict";
/**
 * GatewayManifest — publish daemon endpoint + auth token to a well-known path
 * so the openlife-core CLI can discover the daemon from any cwd.
 *
 * v1.11.0 — Core ↔ Agent Bridge.
 *
 * Path resolution:
 *   - If OPENLIFE_STATE_DIR is set (project-scoped daemon), publish there.
 *   - Otherwise publish to `~/.openlife/gateway-manifest.json` (machine-scoped).
 *
 * AgentDiscovery checks cwd-local first, then home — same multi-root pattern
 * v1.8.3 introduced for catalogs.
 *
 * File mode: 0o600. The token IS the Basic Auth password for `/api/v1/*`.
 * Anyone who can read this file already has filesystem access to the home
 * directory — same trust boundary as `.env`.
 */
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.MANIFEST_AUTH_USER = exports.TOKEN_FILENAME = exports.MANIFEST_FILENAME = exports.MANIFEST_SCHEMA_VERSION = void 0;
exports.publish = publish;
exports.refresh = refresh;
exports.remove = remove;
exports.peek = peek;
exports._resolvedPath = _resolvedPath;
exports._resolvedTokenPath = _resolvedTokenPath;
const crypto = __importStar(require("crypto"));
const fs = __importStar(require("fs"));
const os = __importStar(require("os"));
const path = __importStar(require("path"));
const AtomicWriter_1 = require("./AtomicWriter");
exports.MANIFEST_SCHEMA_VERSION = 1;
exports.MANIFEST_FILENAME = 'gateway-manifest.json';
exports.TOKEN_FILENAME = 'gateway-token';
/** Fixed Basic Auth user — the token is the password. */
exports.MANIFEST_AUTH_USER = 'openlife-core';
function resolveDir() {
    const explicit = process.env.OPENLIFE_STATE_DIR;
    if (explicit && explicit.trim())
        return explicit;
    return path.join(os.homedir(), '.openlife');
}
function resolveManifestPath() {
    return path.join(resolveDir(), exports.MANIFEST_FILENAME);
}
function resolveTokenPath() {
    return path.join(resolveDir(), exports.TOKEN_FILENAME);
}
function genToken() {
    return crypto.randomBytes(32).toString('hex');
}
// Internal mutable state — populated on publish() so refresh()/remove() can
// operate without re-deriving paths or re-generating tokens.
let current = null;
/**
 * Publish a fresh manifest with a new random token. Caller must invoke
 * after the HTTP server is listening so `port` reflects the actually-bound
 * port. Returns the published record (token included — caller may need it).
 *
 * Subsequent refresh() calls update refreshedAt only; the token is stable
 * for the lifetime of the daemon process.
 */
function publish(opts) {
    const dir = resolveDir();
    fs.mkdirSync(dir, { recursive: true });
    const nowIso = new Date().toISOString();
    const token = genToken();
    const record = {
        schemaVersion: exports.MANIFEST_SCHEMA_VERSION,
        host: opts.host || '127.0.0.1',
        port: opts.port,
        token,
        pid: process.pid,
        profile: opts.profile || 'autonomous',
        startedAt: nowIso,
        refreshedAt: nowIso,
        telegram: opts.telegram || { enabled: false, allowedUserId: null },
        version: opts.version,
    };
    const manifestPath = resolveManifestPath();
    (0, AtomicWriter_1.writeJsonAtomic)(manifestPath, record);
    // Sidecar token file (mode 0o600) for callers that don't want to parse JSON
    try {
        fs.writeFileSync(resolveTokenPath(), token, { encoding: 'utf-8', mode: 0o600 });
    }
    catch (err) {
        console.error('[GATEWAY-MANIFEST] failed to write token sidecar:', err);
    }
    // Tighten manifest permissions (best-effort on Windows — chmod is a no-op
    // there but should not throw).
    try {
        fs.chmodSync(manifestPath, 0o600);
    }
    catch { /* ignore */ }
    current = record;
    return record;
}
/**
 * Update refreshedAt to now. No-op if publish() was never called.
 * Designed to be invoked from the WatchdogHeartbeat onTick callback so
 * staleness detection mirrors the 30s heartbeat cadence.
 */
function refresh() {
    if (!current)
        return null;
    const updated = { ...current, refreshedAt: new Date().toISOString() };
    try {
        (0, AtomicWriter_1.writeJsonAtomic)(resolveManifestPath(), updated);
        current = updated;
        return updated;
    }
    catch (err) {
        console.error('[GATEWAY-MANIFEST] refresh failed:', err);
        return current;
    }
}
/**
 * Delete the manifest + token sidecar. Idempotent. Call during graceful
 * shutdown so AgentDiscovery sees an explicit "no daemon" state instead
 * of a stale refreshedAt that ages into "dead".
 */
function remove() {
    current = null;
    for (const p of [resolveManifestPath(), resolveTokenPath()]) {
        try {
            if (fs.existsSync(p))
                fs.unlinkSync(p);
        }
        catch { /* swallow */ }
    }
}
/** Returns the in-memory record without touching disk. */
function peek() {
    return current ? { ...current } : null;
}
/** Exposed for tests. */
function _resolvedPath() {
    return resolveManifestPath();
}
/** Exposed for tests. */
function _resolvedTokenPath() {
    return resolveTokenPath();
}
