UNPKG

codestate-core

Version:

Core domain models, use cases, and services for CodeState

1,450 lines (1,434 loc) 131 kB
"use strict"; var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // packages/core/index.ts var core_exports = {}; __export(core_exports, { AppError: () => AppError, ApplyStash: () => ApplyStash, CommitChanges: () => CommitChanges, ConfigError: () => ConfigError, ConfigurableLogger: () => CLILoggerFacade, CreateScript: () => CreateScript, CreateScripts: () => CreateScripts, CreateStash: () => CreateStash, DeleteScript: () => DeleteScript, DeleteScriptsByRootPath: () => DeleteScriptsByRootPath, DeleteSession: () => DeleteSession, DeleteStash: () => DeleteStash, EncryptionError: () => EncryptionError, ErrorCode: () => ErrorCode, ErrorRegistry: () => ErrorRegistry, ExportConfig: () => ExportConfig, ExportScripts: () => ExportScripts, GetAvailableIDEs: () => GetAvailableIDEs, GetConfig: () => GetConfig, GetCurrentCommit: () => GetCurrentCommit, GetDirtyData: () => GetDirtyData, GetGitStatus: () => GetGitStatus, GetIsDirty: () => GetIsDirty, GetScripts: () => GetScripts, GetScriptsByRootPath: () => GetScriptsByRootPath, GitError: () => GitError, GitService: () => GitFacade, IDEService: () => IDEFacade, ImportConfig: () => ImportConfig, ImportScripts: () => ImportScripts, ListSessions: () => ListSessions, ListStashes: () => ListStashes, OpenFiles: () => OpenFiles, OpenIDE: () => OpenIDE, ResetConfig: () => ResetConfig, ResumeSession: () => ResumeSession, SaveSession: () => SaveSession, ScriptError: () => ScriptError, StorageError: () => StorageError, Terminal: () => TerminalFacade, TerminalError: () => TerminalError, UpdateConfig: () => UpdateConfig, UpdateScript: () => UpdateScript, UpdateSession: () => UpdateSession, getExitCodeForErrorCode: () => getExitCodeForErrorCode, getUserMessageForErrorCode: () => getUserMessageForErrorCode, isFailure: () => isFailure, isSuccess: () => isSuccess }); module.exports = __toCommonJS(core_exports); // packages/core/domain/models/Result.ts function isSuccess(result) { return result.ok === true; } function isFailure(result) { return result.ok === false; } // packages/core/services/config/ConfigService.ts var ConfigService = class { constructor(repository, logger) { this.repository = repository; this.logger = logger; } async getConfig() { this.logger.debug("ConfigService.getConfig called"); const result = await this.repository.load(); if (isFailure(result)) { this.logger.error("Failed to get config", { error: result.error }); } else { this.logger.log("Config loaded", {}); } return result; } async setConfig(config) { this.logger.debug("ConfigService.setConfig called"); const result = await this.repository.save(config); if (isFailure(result)) { this.logger.error("Failed to save config", { error: result.error }); } else { this.logger.log("Config saved", {}); } return result; } async updateConfig(partial) { this.logger.debug("ConfigService.updateConfig called", { partial }); const current = await this.repository.load(); if (isFailure(current)) { this.logger.error("Failed to load config for update", { error: current.error }); return current; } const merged = { ...current.value, ...partial }; const saveResult = await this.repository.save(merged); if (isFailure(saveResult)) { this.logger.error("Failed to save updated config", { error: saveResult.error }); return { ok: false, error: saveResult.error }; } this.logger.log("Config updated", {}); return { ok: true, value: merged }; } }; // packages/core/domain/schemas/SchemaRegistry.ts var import_zod = require("zod"); var LogLevelSchema = import_zod.z.enum(["ERROR", "WARN", "LOG", "DEBUG"]); var LoggerConfigSchema = import_zod.z.object({ level: LogLevelSchema, sinks: import_zod.z.array(import_zod.z.enum(["console", "file"])), filePath: import_zod.z.string().optional() }); var FileStorageConfigSchema = import_zod.z.object({ encryptionEnabled: import_zod.z.boolean(), encryptionKey: import_zod.z.string().optional(), dataDir: import_zod.z.string() }); var FeatureFlagsSchema = import_zod.z.object({ experimentalTui: import_zod.z.boolean(), experimentalIde: import_zod.z.boolean(), advancedSearch: import_zod.z.boolean(), cloudSync: import_zod.z.boolean() }); var PluginEnvironmentSchema = import_zod.z.enum(["cli", "tui", "ide"]); var ErrorCodeSchema = import_zod.z.enum([ "UNKNOWN", "CONFIG_INVALID", "STORAGE_INVALID_PATH", "STORAGE_DECRYPTION_FAILED", "STORAGE_READ_FAILED", "STORAGE_WRITE_FAILED", "STORAGE_DELETE_FAILED", "ENCRYPTION_FAILED", "ENCRYPTION_INVALID_FORMAT", "SCRIPT_INVALID", "SCRIPT_DUPLICATE", "SCRIPT_NOT_FOUND", "SCRIPT_PATH_INVALID", "SCRIPT_MALICIOUS", "GIT_NOT_REPOSITORY", "GIT_COMMAND_FAILED", "GIT_STASH_NOT_FOUND", "GIT_STASH_CONFLICT", "TERMINAL_COMMAND_FAILED", "TERMINAL_TIMEOUT", "TERMINAL_COMMAND_NOT_FOUND" ]); var EncryptionConfigSchema = import_zod.z.object({ enabled: import_zod.z.boolean(), encryptionKey: import_zod.z.string().optional() }); var ConfigSchema = import_zod.z.object({ version: import_zod.z.string(), ide: import_zod.z.string(), encryption: EncryptionConfigSchema, storagePath: import_zod.z.string(), logger: LoggerConfigSchema, experimental: import_zod.z.record(import_zod.z.string(), import_zod.z.boolean()).optional(), extensions: import_zod.z.record(import_zod.z.string(), import_zod.z.unknown()).optional() }); var ScriptSchema = import_zod.z.object({ name: import_zod.z.string().min(1, "Script name is required"), rootPath: import_zod.z.string().min(1, "Root path is required"), script: import_zod.z.string().min(1, "Script command is required") }); var ScriptIndexEntrySchema = import_zod.z.object({ rootPath: import_zod.z.string().min(1, "Root path is required"), referenceFile: import_zod.z.string().min(1, "Reference file path is required") }); var ScriptIndexSchema = import_zod.z.object({ entries: import_zod.z.array(ScriptIndexEntrySchema) }); var ScriptCollectionSchema = import_zod.z.object({ scripts: import_zod.z.array(ScriptSchema) }); var GitFileStatusSchema = import_zod.z.enum(["modified", "added", "deleted", "untracked", "renamed", "copied", "updated"]); var GitFileSchema = import_zod.z.object({ path: import_zod.z.string(), status: GitFileStatusSchema, staged: import_zod.z.boolean() }); var GitStatusSchema = import_zod.z.object({ isDirty: import_zod.z.boolean(), dirtyFiles: import_zod.z.array(GitFileSchema), newFiles: import_zod.z.array(GitFileSchema), modifiedFiles: import_zod.z.array(GitFileSchema), deletedFiles: import_zod.z.array(GitFileSchema), untrackedFiles: import_zod.z.array(GitFileSchema) }); var GitStashSchema = import_zod.z.object({ id: import_zod.z.string(), name: import_zod.z.string(), message: import_zod.z.string(), timestamp: import_zod.z.number(), branch: import_zod.z.string() }); var GitStashResultSchema = import_zod.z.object({ success: import_zod.z.boolean(), stashId: import_zod.z.string().optional(), error: import_zod.z.string().optional() }); var GitStashApplyResultSchema = import_zod.z.object({ success: import_zod.z.boolean(), conflicts: import_zod.z.array(import_zod.z.string()).optional(), error: import_zod.z.string().optional() }); var TerminalCommandSchema = import_zod.z.object({ command: import_zod.z.string(), args: import_zod.z.array(import_zod.z.string()).optional(), cwd: import_zod.z.string().optional(), env: import_zod.z.record(import_zod.z.string(), import_zod.z.string()).optional(), timeout: import_zod.z.number().optional() }); var TerminalResultSchema = import_zod.z.object({ success: import_zod.z.boolean(), exitCode: import_zod.z.number(), stdout: import_zod.z.string(), stderr: import_zod.z.string(), duration: import_zod.z.number(), error: import_zod.z.string().optional() }); var TerminalOptionsSchema = import_zod.z.object({ cwd: import_zod.z.string().optional(), env: import_zod.z.record(import_zod.z.string(), import_zod.z.string()).optional(), timeout: import_zod.z.number().optional(), shell: import_zod.z.string().optional() }); var FileStateSchema = import_zod.z.object({ path: import_zod.z.string(), cursor: import_zod.z.object({ line: import_zod.z.number(), column: import_zod.z.number() }).optional(), scroll: import_zod.z.object({ top: import_zod.z.number(), left: import_zod.z.number() }).optional(), isActive: import_zod.z.boolean() }); var GitStateSchema = import_zod.z.object({ branch: import_zod.z.string(), commit: import_zod.z.string(), isDirty: import_zod.z.boolean(), stashId: import_zod.z.string().nullable().optional() }); var SessionSchema = import_zod.z.object({ id: import_zod.z.string(), name: import_zod.z.string(), projectRoot: import_zod.z.string(), createdAt: import_zod.z.union([import_zod.z.string(), import_zod.z.date()]).transform((val) => typeof val === "string" ? new Date(val) : val), updatedAt: import_zod.z.union([import_zod.z.string(), import_zod.z.date()]).transform((val) => typeof val === "string" ? new Date(val) : val), tags: import_zod.z.array(import_zod.z.string()), notes: import_zod.z.string().optional(), files: import_zod.z.array(FileStateSchema), git: GitStateSchema, extensions: import_zod.z.record(import_zod.z.string(), import_zod.z.unknown()).optional() }); var SessionIndexEntrySchema = import_zod.z.object({ id: import_zod.z.string(), name: import_zod.z.string(), projectRoot: import_zod.z.string(), createdAt: import_zod.z.union([import_zod.z.string(), import_zod.z.date()]), updatedAt: import_zod.z.union([import_zod.z.string(), import_zod.z.date()]), tags: import_zod.z.array(import_zod.z.string()), notes: import_zod.z.string().optional(), referenceFile: import_zod.z.string() }); var SessionIndexSchema = import_zod.z.object({ version: import_zod.z.string(), sessions: import_zod.z.array(SessionIndexEntrySchema) }); function validateFileStorageConfig(data) { return FileStorageConfigSchema.parse(data); } function validateConfig(data) { return ConfigSchema.parse(data); } function validateScript(data) { return ScriptSchema.parse(data); } function validateScriptIndex(data) { return ScriptIndexSchema.parse(data); } function validateScriptCollection(data) { return ScriptCollectionSchema.parse(data); } function validateSession(data) { return SessionSchema.parse(data); } function validateSessionIndex(data) { return SessionIndexSchema.parse(data); } // packages/core/domain/types/ErrorTypes.ts var ErrorCode = /* @__PURE__ */ ((ErrorCode2) => { ErrorCode2["UNKNOWN"] = "UNKNOWN"; ErrorCode2["CONFIG_INVALID"] = "CONFIG_INVALID"; ErrorCode2["STORAGE_INVALID_PATH"] = "STORAGE_INVALID_PATH"; ErrorCode2["STORAGE_DECRYPTION_FAILED"] = "STORAGE_DECRYPTION_FAILED"; ErrorCode2["STORAGE_READ_FAILED"] = "STORAGE_READ_FAILED"; ErrorCode2["STORAGE_WRITE_FAILED"] = "STORAGE_WRITE_FAILED"; ErrorCode2["STORAGE_DELETE_FAILED"] = "STORAGE_DELETE_FAILED"; ErrorCode2["ENCRYPTION_FAILED"] = "ENCRYPTION_FAILED"; ErrorCode2["ENCRYPTION_INVALID_FORMAT"] = "ENCRYPTION_INVALID_FORMAT"; ErrorCode2["SCRIPT_INVALID"] = "SCRIPT_INVALID"; ErrorCode2["SCRIPT_DUPLICATE"] = "SCRIPT_DUPLICATE"; ErrorCode2["SCRIPT_NOT_FOUND"] = "SCRIPT_NOT_FOUND"; ErrorCode2["SCRIPT_PATH_INVALID"] = "SCRIPT_PATH_INVALID"; ErrorCode2["SCRIPT_MALICIOUS"] = "SCRIPT_MALICIOUS"; ErrorCode2["GIT_NOT_REPOSITORY"] = "GIT_NOT_REPOSITORY"; ErrorCode2["GIT_COMMAND_FAILED"] = "GIT_COMMAND_FAILED"; ErrorCode2["GIT_STASH_NOT_FOUND"] = "GIT_STASH_NOT_FOUND"; ErrorCode2["GIT_STASH_CONFLICT"] = "GIT_STASH_CONFLICT"; ErrorCode2["TERMINAL_COMMAND_FAILED"] = "TERMINAL_COMMAND_FAILED"; ErrorCode2["TERMINAL_TIMEOUT"] = "TERMINAL_TIMEOUT"; ErrorCode2["TERMINAL_COMMAND_NOT_FOUND"] = "TERMINAL_COMMAND_NOT_FOUND"; return ErrorCode2; })(ErrorCode || {}); var AppError = class extends Error { constructor(message, code = "UNKNOWN" /* UNKNOWN */, meta) { super(message); this.name = "AppError"; this.code = code; this.meta = meta; } }; var ConfigError = class extends AppError { constructor(message, meta) { super(message, "CONFIG_INVALID" /* CONFIG_INVALID */, meta); this.name = "ConfigError"; } }; var StorageError = class extends AppError { constructor(message, code = "STORAGE_READ_FAILED" /* STORAGE_READ_FAILED */, meta) { super(message, code, meta); this.name = "StorageError"; } }; var EncryptionError = class extends AppError { constructor(message, code = "ENCRYPTION_FAILED" /* ENCRYPTION_FAILED */, meta) { super(message, code, meta); this.name = "EncryptionError"; } }; var ScriptError = class extends AppError { constructor(message, code = "SCRIPT_INVALID" /* SCRIPT_INVALID */, meta) { super(message, code, meta); this.name = "ScriptError"; } }; var GitError = class extends AppError { constructor(message, code = "GIT_COMMAND_FAILED" /* GIT_COMMAND_FAILED */, meta) { super(message, code, meta); this.name = "GitError"; } }; var TerminalError = class extends AppError { constructor(message, code = "TERMINAL_COMMAND_FAILED" /* TERMINAL_COMMAND_FAILED */, meta) { super(message, code, meta); this.name = "TerminalError"; } }; // packages/core/infrastructure/repositories/ConfigRepository.ts var fs = __toESM(require("fs/promises")); var path = __toESM(require("path")); var DEFAULT_CONFIG_PATH = path.join( process.env.HOME || process.env.USERPROFILE || ".", ".codestate", "config.json" ); var TEMP_SUFFIX = ".tmp"; var BACKUP_SUFFIX = ".bak"; function getDefaultConfig() { return { version: "1.0.0", ide: "vscode", encryption: { enabled: false }, storagePath: path.join( process.env.HOME || process.env.USERPROFILE || ".", ".codestate" ), logger: { level: "LOG", sinks: ["file"], filePath: path.join( process.env.HOME || process.env.USERPROFILE || ".", ".codestate", "logs", "codestate.log" ) }, experimental: {}, extensions: {} }; } var ConfigRepository = class { constructor(logger, encryption, configPath = DEFAULT_CONFIG_PATH) { this.logger = logger; this.encryption = encryption; this.configPath = configPath; } async load() { try { await this.ensureDir(); this.logger.debug("Attempting to load config", { path: this.configPath }); const raw = await fs.readFile(this.configPath, { encoding: "utf8" }); let data = raw; if (raw.startsWith("ENCRYPTED_v1")) { this.logger.log("Config file is encrypted. Attempting decryption.", { path: this.configPath }); const key = ""; const decrypted = await this.encryption.decrypt(raw, key); if (isFailure(decrypted)) { this.logger.error("Decryption failed", { error: decrypted.error }); return { ok: false, error: decrypted.error }; } data = decrypted.value; } let parsed; try { parsed = JSON.parse(data); } catch (parseErr) { this.logger.error( "Config file is corrupt (invalid JSON). Backing up and creating default.", { path: this.configPath } ); await this.backupCorruptConfig(); const defaults = getDefaultConfig(); await this.save(defaults); return { ok: true, value: defaults }; } let config; try { config = validateConfig(parsed); } catch (validationErr) { this.logger.error( "Config file is corrupt (schema validation failed). Backing up and creating default.", { path: this.configPath } ); await this.backupCorruptConfig(); const defaults = getDefaultConfig(); await this.save(defaults); return { ok: true, value: defaults }; } this.logger.log("Config loaded successfully", { path: this.configPath, encrypted: raw.startsWith("ENCRYPTED_v1") }); return { ok: true, value: config }; } catch (err) { if (err.code === "ENOENT") { this.logger.warn("Config file not found. Creating default config.", { path: this.configPath }); const defaults = getDefaultConfig(); await this.save(defaults); return { ok: true, value: defaults }; } this.logger.error("Failed to load config", { error: err.message, path: this.configPath }); return { ok: false, error: new ConfigError(err.message) }; } } async save(config) { try { await this.ensureDir(); this.logger.debug("Attempting to save config", { path: this.configPath }); const validated = validateConfig(config); let data = JSON.stringify(validated, null, 2); let encrypted = false; if (config.encryption?.enabled && config.encryption.encryptionKey) { this.logger.log("Encrypting config before save", { path: this.configPath }); const encResult = await this.encryption.encrypt( data, config.encryption.encryptionKey ); if (isFailure(encResult)) { this.logger.error("Encryption failed", { error: encResult.error }); return { ok: false, error: encResult.error }; } data = encResult.value; encrypted = true; } const tempPath = this.configPath + TEMP_SUFFIX; await fs.writeFile(tempPath, data, { encoding: "utf8", mode: 384 }); this.logger.debug("Temp config file written", { tempPath }); await fs.rename(this.configPath, this.configPath + BACKUP_SUFFIX).then(() => { this.logger.log("Config backup created", { backupPath: this.configPath + BACKUP_SUFFIX }); }).catch(() => { }); await fs.rename(tempPath, this.configPath); this.logger.log("Config saved successfully", { path: this.configPath, encrypted }); return { ok: true, value: void 0 }; } catch (err) { this.logger.error("Failed to save config", { error: err.message, path: this.configPath }); return { ok: false, error: new ConfigError(err.message) }; } } async ensureDir() { const dir = path.dirname(this.configPath); await fs.mkdir(dir, { recursive: true, mode: 448 }).then(() => { this.logger.debug("Ensured config directory exists", { dir }); }).catch(() => { }); } async backupCorruptConfig() { try { const backupPath = this.configPath + ".bak." + Date.now(); await fs.rename(this.configPath, backupPath); this.logger.warn("Backed up corrupt config file", { backupPath }); } catch (err) { this.logger.error("Failed to backup corrupt config file", { error: err.message }); } } }; // packages/core/infrastructure/services/FileLogger.ts var import_fs = require("fs"); var path2 = __toESM(require("path")); var LOG_LEVEL_PRIORITY = { "ERROR": 0, "WARN": 1, "LOG": 2, "DEBUG": 3 }; var FileLogger = class { constructor(config) { if (!config.filePath) throw new Error("FileLogger requires filePath in LoggerConfig"); this.level = config.level; this.filePath = config.filePath; this.ensureLogDirectory(); } plainLog(message, meta) { const entry = { level: "plain", timestamp: (/* @__PURE__ */ new Date()).toISOString(), message, ...meta ? { meta } : {} }; (0, import_fs.appendFileSync)(this.filePath, JSON.stringify(entry) + "\n", { encoding: "utf8" }); } ensureLogDirectory() { const logDir = path2.dirname(this.filePath); try { (0, import_fs.mkdirSync)(logDir, { recursive: true }); } catch (error) { } } shouldLog(messageLevel) { return LOG_LEVEL_PRIORITY[this.level] >= LOG_LEVEL_PRIORITY[messageLevel]; } write(level, message, meta) { const entry = { level, timestamp: (/* @__PURE__ */ new Date()).toISOString(), message, ...meta ? { meta } : {} }; (0, import_fs.appendFileSync)(this.filePath, JSON.stringify(entry) + "\n", { encoding: "utf8" }); } log(message, meta) { if (!this.shouldLog("LOG")) return; this.write("log", message, meta); } error(message, meta) { if (!this.shouldLog("ERROR")) return; this.write("error", message, meta); } warn(message, meta) { if (!this.shouldLog("WARN")) return; this.write("warn", message, meta); } debug(message, meta) { if (!this.shouldLog("DEBUG")) return; this.write("debug", message, meta); } }; // packages/core/infrastructure/services/BasicEncryption.ts var import_crypto = require("crypto"); var HEADER = "ENCRYPTED_v1"; var SALT_LENGTH = 16; var IV_LENGTH = 12; var KEY_LENGTH = 32; var PBKDF2_ITER = 1e5; var BasicEncryption = class { constructor(logger) { this.logger = logger; } async encrypt(data, key) { try { const salt = (0, import_crypto.randomBytes)(SALT_LENGTH); const iv = (0, import_crypto.randomBytes)(IV_LENGTH); const derivedKey = (0, import_crypto.pbkdf2Sync)( key, salt, PBKDF2_ITER, KEY_LENGTH, "sha512" ); const cipher = (0, import_crypto.createCipheriv)("aes-256-gcm", derivedKey, iv); const ciphertext = Buffer.concat([ cipher.update(data, "utf8"), cipher.final() ]); const authTag = cipher.getAuthTag(); this.logger.debug("Data encrypted", { algorithm: "AES-256-GCM", operation: "encrypt" }); return { ok: true, value: [ HEADER, iv.toString("base64"), salt.toString("base64"), ciphertext.toString("base64"), authTag.toString("base64") ].join(":") }; } catch (err) { this.logger.error("Encryption failed", { error: err instanceof Error ? err.message : err, operation: "encrypt" }); return { ok: false, error: new EncryptionError( "Encryption failed", "ENCRYPTION_FAILED" /* ENCRYPTION_FAILED */, { originalError: err instanceof Error ? err.message : err, operation: "encrypt" } ) }; } } async decrypt(data, key) { try { const parts = data.split(":"); if (parts[0] !== HEADER || parts.length !== 5) { this.logger.error("Invalid encrypted data format", { operation: "decrypt" }); return { ok: false, error: new EncryptionError( "Invalid encrypted data format", "ENCRYPTION_INVALID_FORMAT" /* ENCRYPTION_INVALID_FORMAT */, { operation: "decrypt" } ) }; } const [, ivB64, saltB64, ciphertextB64, authTagB64] = parts; const iv = Buffer.from(ivB64, "base64"); const salt = Buffer.from(saltB64, "base64"); const ciphertext = Buffer.from(ciphertextB64, "base64"); const authTag = Buffer.from(authTagB64, "base64"); const derivedKey = (0, import_crypto.pbkdf2Sync)( key, salt, PBKDF2_ITER, KEY_LENGTH, "sha512" ); const decipher = (0, import_crypto.createDecipheriv)("aes-256-gcm", derivedKey, iv); decipher.setAuthTag(authTag); const plaintext = Buffer.concat([ decipher.update(ciphertext), decipher.final() ]); this.logger.debug("Data decrypted", { algorithm: "AES-256-GCM", operation: "decrypt" }); return { ok: true, value: plaintext.toString("utf8") }; } catch (err) { this.logger.error("Decryption failed", { error: err instanceof Error ? err.message : err, operation: "decrypt" }); return { ok: false, error: new EncryptionError( "Decryption failed", "ENCRYPTION_FAILED" /* ENCRYPTION_FAILED */, { originalError: err instanceof Error ? err.message : err, operation: "decrypt" } ) }; } } }; // packages/core/services/config/ConfigFacade.ts var path3 = __toESM(require("path")); var ConfigFacade = class { constructor(configPath, logger, encryption) { const _logger = logger || new FileLogger({ level: "LOG", sinks: ["file"], filePath: path3.join(process.env.HOME || process.env.USERPROFILE || ".", ".codestate", "logs", "codestate.log") }); const _encryption = encryption || new BasicEncryption(_logger); const repository = new ConfigRepository(_logger, _encryption, configPath); this.service = new ConfigService(repository, _logger); } async getConfig(...args) { return this.service.getConfig(...args); } async setConfig(...args) { return this.service.setConfig(...args); } async updateConfig(...args) { return this.service.updateConfig(...args); } }; // packages/core/use-cases/config/GetConfig.ts var GetConfig = class { constructor(configService) { this.configService = configService || new ConfigFacade(); } async execute() { return this.configService.getConfig(); } }; // packages/core/use-cases/config/UpdateConfig.ts var UpdateConfig = class { constructor(configService) { this.configService = configService || new ConfigFacade(); } async execute(partial) { return this.configService.updateConfig(partial); } }; // packages/core/use-cases/config/ResetConfig.ts var path4 = __toESM(require("path")); function getDefaultConfig2() { return { version: "1.0.0", ide: "vscode", encryption: { enabled: false }, storagePath: path4.join(process.env.HOME || process.env.USERPROFILE || ".", ".codestate"), logger: { level: "LOG", sinks: ["file"], filePath: path4.join(process.env.HOME || process.env.USERPROFILE || ".", ".codestate", "logs", "codestate.log") }, experimental: {}, extensions: {} }; } var ResetConfig = class { constructor(configService) { this.configService = configService || new ConfigFacade(); } async execute() { const result = await this.configService.setConfig(getDefaultConfig2()); if (isFailure(result)) return { ok: false, error: result.error }; return { ok: true, value: getDefaultConfig2() }; } }; // packages/core/use-cases/config/ExportConfig.ts var ExportConfig = class { constructor(configService) { this.configService = configService || new ConfigFacade(); } async execute() { const result = await this.configService.getConfig(); if (isFailure(result)) return { ok: false, error: result.error }; return { ok: true, value: JSON.stringify(result.value, null, 2) }; } }; // packages/core/use-cases/config/ImportConfig.ts var ImportConfig = class { constructor(configService) { this.configService = configService || new ConfigFacade(); } async execute(json) { let parsed; try { parsed = validateConfig(JSON.parse(json)); } catch (err) { return { ok: false, error: err }; } const result = await this.configService.setConfig(parsed); if (isFailure(result)) return { ok: false, error: result.error }; return { ok: true, value: parsed }; } }; // packages/core/services/scripts/ScriptService.ts var ScriptService = class { constructor(repository, logger) { this.repository = repository; this.logger = logger; } async createScript(script) { this.logger.debug("ScriptService.createScript called", { script }); const result = await this.repository.createScript(script); if (isFailure(result)) { this.logger.error("Failed to create script", { error: result.error, script }); } else { this.logger.log("Script created successfully", { script }); } return result; } async createScripts(scripts) { this.logger.debug("ScriptService.createScripts called", { count: scripts.length }); const result = await this.repository.createScripts(scripts); if (isFailure(result)) { this.logger.error("Failed to create scripts", { error: result.error, count: scripts.length }); } else { this.logger.log("Scripts created successfully", { count: scripts.length }); } return result; } async getScriptsByRootPath(rootPath) { this.logger.debug("ScriptService.getScriptsByRootPath called", { rootPath }); const result = await this.repository.getScriptsByRootPath(rootPath); if (isFailure(result)) { this.logger.error("Failed to get scripts by root path", { error: result.error, rootPath }); } else { this.logger.log("Scripts retrieved by root path", { rootPath, count: result.value.length }); } return result; } async getAllScripts() { this.logger.debug("ScriptService.getAllScripts called"); const result = await this.repository.getAllScripts(); if (isFailure(result)) { this.logger.error("Failed to get all scripts", { error: result.error }); } else { this.logger.log("All scripts retrieved", { count: result.value.length }); } return result; } async updateScript(name, rootPath, scriptUpdate) { this.logger.debug("ScriptService.updateScript called", { name, rootPath, scriptUpdate }); const result = await this.repository.updateScript(name, rootPath, scriptUpdate); if (isFailure(result)) { this.logger.error("Failed to update script", { error: result.error, name, rootPath }); } else { this.logger.log("Script updated successfully", { name, rootPath }); } return result; } async updateScripts(updates) { this.logger.debug("ScriptService.updateScripts called", { count: updates.length }); const result = await this.repository.updateScripts(updates); if (isFailure(result)) { this.logger.error("Failed to update scripts", { error: result.error, count: updates.length }); } else { this.logger.log("Scripts updated successfully", { count: updates.length }); } return result; } async deleteScript(name, rootPath) { this.logger.debug("ScriptService.deleteScript called", { name, rootPath }); const result = await this.repository.deleteScript(name, rootPath); if (isFailure(result)) { this.logger.error("Failed to delete script", { error: result.error, name, rootPath }); } else { this.logger.log("Script deleted successfully", { name, rootPath }); } return result; } async deleteScripts(scripts) { this.logger.debug("ScriptService.deleteScripts called", { count: scripts.length }); const result = await this.repository.deleteScripts(scripts); if (isFailure(result)) { this.logger.error("Failed to delete scripts", { error: result.error, count: scripts.length }); } else { this.logger.log("Scripts deleted successfully", { count: scripts.length }); } return result; } async deleteScriptsByRootPath(rootPath) { this.logger.debug("ScriptService.deleteScriptsByRootPath called", { rootPath }); const result = await this.repository.deleteScriptsByRootPath(rootPath); if (isFailure(result)) { this.logger.error("Failed to delete scripts by root path", { error: result.error, rootPath }); } else { this.logger.log("Scripts deleted by root path successfully", { rootPath }); } return result; } async getScriptIndex() { this.logger.debug("ScriptService.getScriptIndex called"); const result = await this.repository.loadScriptIndex(); if (isFailure(result)) { this.logger.error("Failed to get script index", { error: result.error }); } else { this.logger.log("Script index retrieved", { entryCount: result.value.entries.length }); } return result; } async updateScriptIndex(index) { this.logger.debug("ScriptService.updateScriptIndex called"); const result = await this.repository.saveScriptIndex(index); if (isFailure(result)) { this.logger.error("Failed to update script index", { error: result.error }); } else { this.logger.log("Script index updated successfully"); } return result; } }; // packages/core/infrastructure/repositories/ScriptRepository.ts var fs2 = __toESM(require("fs/promises")); var path5 = __toESM(require("path")); var DEFAULT_SCRIPTS_DIR = path5.join( process.env.HOME || process.env.USERPROFILE || ".", ".codestate", "scripts" ); var INDEX_FILE = "index.json"; var TEMP_SUFFIX2 = ".tmp"; var BACKUP_SUFFIX2 = ".bak"; var ScriptRepository = class { constructor(logger, encryption, configService, scriptsDir = DEFAULT_SCRIPTS_DIR) { this.logger = logger; this.encryption = encryption; this.configService = configService; this.scriptsDir = scriptsDir; } async createScript(script) { try { await this.ensureScriptsDir(); const validatedScript = validateScript(script); if (!await this.pathExists(validatedScript.rootPath)) { this.logger.error("Root path does not exist", { rootPath: validatedScript.rootPath }); return { ok: false, error: new ScriptError( "Root path does not exist", "SCRIPT_PATH_INVALID" /* SCRIPT_PATH_INVALID */, { rootPath: validatedScript.rootPath } ) }; } const existingScripts = await this.getScriptsByRootPath( validatedScript.rootPath ); if (existingScripts.ok) { const duplicate = existingScripts.value.find( (s) => s.script === validatedScript.script ); if (duplicate) { this.logger.error("Duplicate script command found", { script: validatedScript.script, rootPath: validatedScript.rootPath }); return { ok: false, error: new ScriptError( "Script command already exists", "SCRIPT_DUPLICATE" /* SCRIPT_DUPLICATE */, { script: validatedScript.script, rootPath: validatedScript.rootPath } ) }; } } const collection = await this.getOrCreateScriptCollection( validatedScript.rootPath ); if (isFailure(collection)) { return { ok: false, error: collection.error }; } collection.value.scripts.push(validatedScript); const saveResult = await this.saveScriptCollection( validatedScript.rootPath, collection.value ); if (isFailure(saveResult)) { return { ok: false, error: saveResult.error }; } await this.updateIndexForRootPath(validatedScript.rootPath); this.logger.log("Script created successfully", { name: validatedScript.name, rootPath: validatedScript.rootPath }); return { ok: true, value: void 0 }; } catch (err) { this.logger.error("Failed to create script", { error: err.message, script }); return { ok: false, error: new ScriptError(err.message, "SCRIPT_INVALID" /* SCRIPT_INVALID */, { originalError: err.message }) }; } } async createScripts(scripts) { try { if (scripts.length === 0) { return { ok: true, value: void 0 }; } this.logger.debug("Creating multiple scripts", { count: scripts.length }); const scriptsByRootPath = /* @__PURE__ */ new Map(); for (const script of scripts) { const validatedScript = validateScript(script); if (!await this.pathExists(validatedScript.rootPath)) { this.logger.error("Root path does not exist", { rootPath: validatedScript.rootPath }); return { ok: false, error: new ScriptError( "Root path does not exist", "SCRIPT_PATH_INVALID" /* SCRIPT_PATH_INVALID */, { rootPath: validatedScript.rootPath } ) }; } if (!scriptsByRootPath.has(validatedScript.rootPath)) { scriptsByRootPath.set(validatedScript.rootPath, []); } scriptsByRootPath.get(validatedScript.rootPath).push(validatedScript); } for (const [rootPath, rootScripts] of scriptsByRootPath) { const existingScripts = await this.getScriptsByRootPath(rootPath); const existingCollection = existingScripts.ok ? existingScripts.value : []; const allScripts = [...existingCollection, ...rootScripts]; const scriptCommands = /* @__PURE__ */ new Set(); const duplicates = []; for (const script of allScripts) { if (scriptCommands.has(script.script)) { duplicates.push(script.script); } else { scriptCommands.add(script.script); } } if (duplicates.length > 0) { this.logger.error("Duplicate script commands found", { duplicates, rootPath }); return { ok: false, error: new ScriptError( "Duplicate script commands found", "SCRIPT_DUPLICATE" /* SCRIPT_DUPLICATE */, { duplicates, rootPath } ) }; } const collection = { scripts: allScripts }; const saveResult = await this.saveScriptCollection( rootPath, collection ); if (isFailure(saveResult)) { return { ok: false, error: saveResult.error }; } await this.updateIndexForRootPath(rootPath); } this.logger.log("Multiple scripts created successfully", { count: scripts.length }); return { ok: true, value: void 0 }; } catch (err) { this.logger.error("Failed to create multiple scripts", { error: err.message, count: scripts.length }); return { ok: false, error: new ScriptError(err.message, "SCRIPT_INVALID" /* SCRIPT_INVALID */, { originalError: err.message }) }; } } async getScriptsByRootPath(rootPath) { try { const collection = await this.loadScriptCollection(rootPath); if (isFailure(collection)) { return { ok: true, value: [] }; } return { ok: true, value: collection.value.scripts }; } catch (err) { this.logger.error("Failed to get scripts by root path", { error: err.message, rootPath }); return { ok: false, error: new ScriptError(err.message, "SCRIPT_INVALID" /* SCRIPT_INVALID */, { originalError: err.message }) }; } } async getAllScripts() { try { const index = await this.loadScriptIndex(); if (isFailure(index)) { return { ok: true, value: [] }; } const allScripts = []; for (const entry of index.value.entries) { const scripts = await this.getScriptsByRootPath(entry.rootPath); if (scripts.ok) { allScripts.push(...scripts.value); } } return { ok: true, value: allScripts }; } catch (err) { this.logger.error("Failed to get all scripts", { error: err.message }); return { ok: false, error: new ScriptError(err.message, "SCRIPT_INVALID" /* SCRIPT_INVALID */, { originalError: err.message }) }; } } async updateScript(name, rootPath, scriptUpdate) { try { const scripts = await this.getScriptsByRootPath(rootPath); if (isFailure(scripts)) { return { ok: false, error: scripts.error }; } const scriptIndex = scripts.value.findIndex((s) => s.name === name); if (scriptIndex === -1) { this.logger.error("Script not found", { name, rootPath }); return { ok: false, error: new ScriptError( "Script not found", "SCRIPT_NOT_FOUND" /* SCRIPT_NOT_FOUND */, { name, rootPath } ) }; } const updatedScript = { ...scripts.value[scriptIndex], ...scriptUpdate }; const validatedScript = validateScript(updatedScript); const duplicate = scripts.value.find( (s) => s.script === validatedScript.script && s.name !== name ); if (duplicate) { this.logger.error("Duplicate script command found", { script: validatedScript.script, rootPath }); return { ok: false, error: new ScriptError( "Script command already exists", "SCRIPT_DUPLICATE" /* SCRIPT_DUPLICATE */, { script: validatedScript.script, rootPath } ) }; } scripts.value[scriptIndex] = validatedScript; const collection = { scripts: scripts.value }; const saveResult = await this.saveScriptCollection(rootPath, collection); if (isFailure(saveResult)) { return { ok: false, error: saveResult.error }; } this.logger.log("Script updated successfully", { name, rootPath }); return { ok: true, value: void 0 }; } catch (err) { this.logger.error("Failed to update script", { error: err.message, name, rootPath }); return { ok: false, error: new ScriptError(err.message, "SCRIPT_INVALID" /* SCRIPT_INVALID */, { originalError: err.message }) }; } } async updateScripts(updates) { try { if (updates.length === 0) { return { ok: true, value: void 0 }; } this.logger.debug("Updating multiple scripts", { count: updates.length }); const updatesByRootPath = /* @__PURE__ */ new Map(); for (const update of updates) { if (!updatesByRootPath.has(update.rootPath)) { updatesByRootPath.set(update.rootPath, []); } updatesByRootPath.get(update.rootPath).push({ name: update.name, script: update.script }); } for (const [rootPath, rootUpdates] of updatesByRootPath) { const scripts = await this.getScriptsByRootPath(rootPath); if (isFailure(scripts)) { return { ok: false, error: scripts.error }; } const updatedScripts = [...scripts.value]; const updatedNames = /* @__PURE__ */ new Set(); for (const update of rootUpdates) { const scriptIndex = updatedScripts.findIndex( (s) => s.name === update.name ); if (scriptIndex === -1) { this.logger.error("Script not found for update", { name: update.name, rootPath }); return { ok: false, error: new ScriptError( "Script not found", "SCRIPT_NOT_FOUND" /* SCRIPT_NOT_FOUND */, { name: update.name, rootPath } ) }; } const updatedScript = { ...updatedScripts[scriptIndex], ...update.script }; const validatedScript = validateScript(updatedScript); updatedScripts[scriptIndex] = validatedScript; updatedNames.add(update.name); } const scriptCommands = /* @__PURE__ */ new Set(); const duplicates = []; for (const script of updatedScripts) { if (scriptCommands.has(script.script)) { if (!updatedNames.has(script.name)) { duplicates.push(script.script); } } else { scriptCommands.add(script.script); } } if (duplicates.length > 0) { this.logger.error("Duplicate script commands found after updates", { duplicates, rootPath }); return { ok: false, error: new ScriptError( "Duplicate script commands found", "SCRIPT_DUPLICATE" /* SCRIPT_DUPLICATE */, { duplicates, rootPath } ) }; } const collection = { scripts: updatedScripts }; const saveResult = await this.saveScriptCollection( rootPath, collection ); if (isFailure(saveResult)) { return { ok: false, error: saveResult.error }; } } this.logger.log("Multiple scripts updated successfully", { count: updates.length }); return { ok: true, value: void 0 }; } catch (err) { this.logger.error("Failed to update multiple scripts", { error: err.message, count: updates.length }); return { ok: false, error: new ScriptError(err.message, "SCRIPT_INVALID" /* SCRIPT_INVALID */, { originalError: err.message }) }; } } async deleteScript(name, rootPath) { try { const scripts = await this.getScriptsByRootPath(rootPath); if (isFailure(scripts)) { return { ok: false, error: scripts.error }; } const scriptIndex = scripts.value.findIndex((s) => s.name === name); if (scriptIndex === -1) { this.logger.error("Script not found", { name, rootPath }); return { ok: false, error: new ScriptError( "Script not found", "SCRIPT_NOT_FOUND" /* SCRIPT_NOT_FOUND */, { name, rootPath } ) }; } scripts.value.splice(scriptIndex, 1); const collection = { scripts: scripts.value }; const saveResult = await this.saveScriptCollection(rootPath, collection); if (isFailure(saveResult)) { return { ok: false, error: saveResult.error }; } this.logger.log("Script deleted successfully", { name, rootPath }); return { ok: true, value: void 0 }; } catch (err) { this.logger.error("Failed to delete script", { error: err.message, name, rootPath }); return { ok: false, error: new ScriptError(err.message, "SCRIPT_INVALID" /* SCRIPT_INVALID */, { originalError: err.message }) }; } } async deleteScripts(scripts) { try { if (scripts.length === 0) { return { ok: true, value: void 0 }; } this.logger.debug("Deleting multiple scripts", { count: scripts.length }); const deletionsByRootPath = /* @__PURE__ */ new Map(); for (const script of scripts) { if (!deletionsByRootPath.has(script.rootPath)) { deletionsByRootPath.set(script.rootPath, []); } deletionsByRootPath.get(script.rootPath).push(script.name); } for (const [rootPath, scriptNames] of deletionsByRootPath) { const existingScripts = await this.getScriptsByRootPath(rootPath); if (isFailure(existingScripts)) { return { ok: false, error: existingScripts.error }; } const remainingScripts = existingScripts.value.filter( (script) => !scriptNames.includes(script.name) ); const foundNames = existingScripts.value.filter((script) => scriptNames.includes(script.name)).map((s) => s.name); const missingNames = scriptNames.filter( (name) => !foundNames.includes(name) ); if (missingNames.length > 0) { this.logger.error("Some scripts not found for deletion", { missingNames, rootPath }); return { ok: false, error: new ScriptError( "Some scripts not found", "SCRIPT_NOT_FOUND" /* SCRIPT_NOT_FOUND */, { missingNames, rootPath } ) }; } const collection = { scripts: remainingScripts }; const saveResult = await this.saveScriptCollection( rootPath, collection ); if (isFailure(saveResult)) { return { ok: false, error: saveResult.error }; } } this.logger.log("Multiple scripts deleted successfully", { count: scripts.length }); return { ok: true, value: void 0 }; } catch (err) { this.logger.error("Failed to delete multiple scripts", { error: err.message, count: scripts.length }); return { ok: false, error: new ScriptError(err.message, "SCRIPT_INVALID" /* SCRIPT_INVALID */, { originalError: err.message }) }; } } async deleteScriptsByRootPath(rootPath) { try { const referenceFile = await this.getReferenceFilePath(rootPath); if (referenceFile) { await fs2.unlink(referenceFi