"use strict";
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.flowsDir = flowsDir;
exports.workflowDataPath = workflowDataPath;
exports.flowStatePath = flowStatePath;
exports.sanitizeFlowFilename = sanitizeFlowFilename;
exports.listFlowDefs = listFlowDefs;
exports.loadFlowDef = loadFlowDef;
exports.saveFlowDef = saveFlowDef;
exports.deleteFlowDef = deleteFlowDef;
exports.loadFlowState = loadFlowState;
exports.saveFlowState = saveFlowState;
exports.clearFlowState = clearFlowState;
exports.readWorkflowData = readWorkflowData;
exports.writeWorkflowData = writeWorkflowData;
exports.initWorkflowData = initWorkflowData;
exports.appendTaskProgress = appendTaskProgress;
exports.globalFlowsDir = globalFlowsDir;
exports.listGlobalFlowDefs = listGlobalFlowDefs;
exports.loadGlobalFlowDef = loadGlobalFlowDef;
exports.saveGlobalFlowDef = saveGlobalFlowDef;
exports.deleteGlobalFlowDef = deleteGlobalFlowDef;
const fs = __importStar(require("fs"));
const os = __importStar(require("os"));
const path = __importStar(require("path"));
const types_1 = require("./types");
/**
 * Path conventions and CRUD for the flow JSON files (schemaVersion 2).
 *
 *  - <project>/.ccweb/flows/<name>.json    flow definitions
 *  - <project>/.ccweb/workflow_data.json   unified data (constants + variables + task_progress)
 *  - <project>/.ccweb/flow_state.json      runtime status (runner-only)
 *
 *  + ~/.ccweb/users/<username>/flows/<name>.json   per-user global flow templates
 */
function ccwebDir(folderPath) {
    return path.join(folderPath, '.ccweb');
}
function flowsDir(folderPath) {
    return path.join(ccwebDir(folderPath), 'flows');
}
function workflowDataPath(folderPath) {
    return path.join(folderPath, types_1.WORKFLOW_DATA_PATH);
}
function flowStatePath(folderPath) {
    return path.join(ccwebDir(folderPath), 'flow_state.json');
}
/** Reject filenames that contain path separators / parent-dir tokens / NUL.
 *  Returned name has `.json` appended if missing. */
function sanitizeFlowFilename(name) {
    if (!name || typeof name !== 'string')
        return null;
    if (name.includes('/') || name.includes('\\') || name.includes('\0'))
        return null;
    if (name === '.' || name === '..')
        return null;
    return name.endsWith('.json') ? name : `${name}.json`;
}
function ensureDir(dir) {
    fs.mkdirSync(dir, { recursive: true });
}
function readJson(filePath) {
    try {
        const raw = fs.readFileSync(filePath, 'utf-8');
        return JSON.parse(raw);
    }
    catch {
        return null;
    }
}
function writeJsonAtomic(filePath, data) {
    ensureDir(path.dirname(filePath));
    const tmp = `${filePath}.tmp-${process.pid}-${Date.now()}`;
    fs.writeFileSync(tmp, JSON.stringify(data, null, 2));
    fs.renameSync(tmp, filePath);
}
// ── Flow definitions ───────────────────────────────────────────────────────
function listFlowDefs(folderPath) {
    const dir = flowsDir(folderPath);
    try {
        return fs
            .readdirSync(dir)
            .filter((f) => f.endsWith('.json') && !f.startsWith('.'))
            .sort();
    }
    catch {
        return [];
    }
}
function loadFlowDef(folderPath, filename) {
    const safe = sanitizeFlowFilename(filename);
    if (!safe)
        return null;
    return readJson(path.join(flowsDir(folderPath), safe));
}
function saveFlowDef(folderPath, filename, def) {
    const safe = sanitizeFlowFilename(filename);
    if (!safe)
        return false;
    writeJsonAtomic(path.join(flowsDir(folderPath), safe), def);
    return true;
}
function deleteFlowDef(folderPath, filename) {
    const safe = sanitizeFlowFilename(filename);
    if (!safe)
        return false;
    try {
        fs.unlinkSync(path.join(flowsDir(folderPath), safe));
        return true;
    }
    catch {
        return false;
    }
}
// ── Flow state ─────────────────────────────────────────────────────────────
function loadFlowState(folderPath) {
    return readJson(flowStatePath(folderPath));
}
function saveFlowState(folderPath, state) {
    writeJsonAtomic(flowStatePath(folderPath), state);
}
function clearFlowState(folderPath) {
    try {
        fs.unlinkSync(flowStatePath(folderPath));
    }
    catch {
        /* ignore */
    }
}
// ── workflow_data.json ─────────────────────────────────────────────────────
/** Read the unified data file, returning a fresh empty shape on miss/parse
 *  error. Callers should never see null — partial corruption recovers to the
 *  zero state rather than crashing the runner mid-flight. */
function readWorkflowData(folderPath) {
    const data = readJson(workflowDataPath(folderPath));
    if (!data || typeof data !== 'object') {
        return { constants: {}, variables: {}, task_progress: [] };
    }
    return {
        constants: data.constants && typeof data.constants === 'object' ? data.constants : {},
        variables: data.variables && typeof data.variables === 'object' ? data.variables : {},
        task_progress: Array.isArray(data.task_progress) ? data.task_progress : [],
    };
}
function writeWorkflowData(folderPath, data) {
    writeJsonAtomic(workflowDataPath(folderPath), data);
}
/** Initialize workflow_data at flow start:
 *  - constants ← FlowDef.constants (overwrites; constants are immutable per run)
 *  - variables ← FlowDef.variables[].initialValue where present (others left
 *    undefined / missing); preserves any pre-existing variable keys not
 *    declared in this flow (defensive — shouldn't happen in practice but
 *    avoids data loss if a partial def reload races with persistence)
 *  - task_progress ← [] (reset for new run)
 */
function initWorkflowData(folderPath, def) {
    const existing = readWorkflowData(folderPath);
    const constants = {};
    for (const c of def.constants ?? []) {
        constants[c.name] = c.value;
    }
    const variables = { ...existing.variables };
    for (const v of def.variables ?? []) {
        if (v.initialValue !== undefined) {
            variables[v.name] = v.initialValue;
        }
    }
    const next = { constants, variables, task_progress: [] };
    writeWorkflowData(folderPath, next);
    return next;
}
/** Append a task progress entry, return its index. Used by runner for LLM
 *  nodes — the LLM signals completion by flipping `task_progress[index].finish`
 *  to true via Edit/Write. */
function appendTaskProgress(folderPath, entry) {
    const data = readWorkflowData(folderPath);
    data.task_progress.push({
        ...entry,
        startedAt: entry.startedAt ?? Date.now(),
    });
    writeWorkflowData(folderPath, data);
    return data.task_progress.length - 1;
}
// ── Per-user global flow definitions ───────────────────────────────────────
// Stored at ~/.ccweb/users/<username>/flows/<name>.json. The flow definition
// itself is identical to a project-level def; only the storage location differs.
// At run time a global flow is bound to a project — relative-path concepts
// no longer exist in v2, so the only project-scoped resource is the project's
// own workflow_data.json which the running flow reads/writes.
/** Allow only safe username chars to namespace a directory. */
function isSafeUsername(username) {
    if (!username || typeof username !== 'string')
        return false;
    if (username.length === 0 || username.length > 64)
        return false;
    return /^[A-Za-z0-9_.@-]+$/.test(username) && !username.startsWith('.') && !username.startsWith('-');
}
function globalFlowsDir(username) {
    if (!isSafeUsername(username))
        return null;
    return path.join(os.homedir(), '.ccweb', 'users', username, 'flows');
}
function listGlobalFlowDefs(username) {
    const dir = globalFlowsDir(username);
    if (!dir)
        return [];
    try {
        return fs
            .readdirSync(dir)
            .filter((f) => f.endsWith('.json') && !f.startsWith('.'))
            .sort();
    }
    catch {
        return [];
    }
}
function loadGlobalFlowDef(username, filename) {
    const dir = globalFlowsDir(username);
    if (!dir)
        return null;
    const safe = sanitizeFlowFilename(filename);
    if (!safe)
        return null;
    return readJson(path.join(dir, safe));
}
function saveGlobalFlowDef(username, filename, def) {
    const dir = globalFlowsDir(username);
    if (!dir)
        return false;
    const safe = sanitizeFlowFilename(filename);
    if (!safe)
        return false;
    writeJsonAtomic(path.join(dir, safe), def);
    return true;
}
function deleteGlobalFlowDef(username, filename) {
    const dir = globalFlowsDir(username);
    if (!dir)
        return false;
    const safe = sanitizeFlowFilename(filename);
    if (!safe)
        return false;
    try {
        fs.unlinkSync(path.join(dir, safe));
        return true;
    }
    catch {
        return false;
    }
}
//# sourceMappingURL=store.js.map