#!/usr/bin/env node
// @ts-nocheck
/**
* 🧩 Alias storage (JSON file).
*/
const fs = require('fs/promises');
const { resolveAliasesPath } = require('../utils/paths');
const { atomicWriteTextFile } = require('../utils/fsAtomic');
const ToolError = require('../errors/ToolError');
class AliasService {
constructor(logger) {
this.logger = logger.child('aliases');
this.filePath = resolveAliasesPath();
this.aliases = new Map();
this.stats = {
loaded: 0,
saved: 0,
created: 0,
updated: 0,
errors: 0,
};
this.initPromise = this.load();
}
async initialize() {
await this.initPromise;
}
async load() {
try {
const raw = await fs.readFile(this.filePath, 'utf8');
const parsed = JSON.parse(raw);
for (const [name, alias] of Object.entries(parsed || {})) {
this.aliases.set(name, alias);
}
this.stats.loaded = this.aliases.size;
} catch (error) {
if (error.code !== 'ENOENT') {
this.stats.errors += 1;
this.logger.warn('Failed to load aliases file', { error: error.message });
}
}
}
async persist() {
const data = Object.fromEntries(this.aliases);
await atomicWriteTextFile(this.filePath, `${JSON.stringify(data, null, 2)}\n`, { mode: 0o600 });
this.stats.saved += 1;
}
async ensureReady() {
await this.initPromise;
}
validateAlias(alias) {
if (!alias || typeof alias !== 'object' || Array.isArray(alias)) {
throw ToolError.invalidParams({ field: 'alias', message: 'alias must be an object' });
}
if (!alias.tool || typeof alias.tool !== 'string' || alias.tool.trim().length === 0) {
throw ToolError.invalidParams({ field: 'alias.tool', message: 'alias.tool must be a non-empty string' });
}
if (alias.args !== undefined) {
if (typeof alias.args !== 'object' || alias.args === null || Array.isArray(alias.args)) {
throw ToolError.invalidParams({ field: 'alias.args', message: 'alias.args must be an object' });
}
}
}
async setAlias(name, alias) {
await this.ensureReady();
if (typeof name !== 'string' || name.trim().length === 0) {
throw ToolError.invalidParams({
field: 'name',
message: 'alias name must be a non-empty string',
hint: 'Use action=alias_list to see existing aliases.',
});
}
this.validateAlias(alias);
const trimmed = name.trim();
const existing = this.aliases.get(trimmed);
const payload = {
...alias,
updated_at: new Date().toISOString(),
created_at: existing?.created_at || new Date().toISOString(),
};
this.aliases.set(trimmed, payload);
await this.persist();
if (existing) {
this.stats.updated += 1;
} else {
this.stats.created += 1;
}
return { success: true, alias: { name: trimmed, ...payload } };
}
async getAlias(name) {
await this.ensureReady();
if (typeof name !== 'string' || name.trim().length === 0) {
throw ToolError.invalidParams({ field: 'name', message: 'alias name must be a non-empty string' });
}
const trimmed = name.trim();
const entry = this.aliases.get(trimmed);
if (!entry) {
throw ToolError.notFound({
code: 'ALIAS_NOT_FOUND',
message: `alias '${trimmed}' not found`,
hint: 'Use action=alias_list to see known aliases.',
});
}
return { success: true, alias: { name: trimmed, ...entry } };
}
async listAliases() {
await this.ensureReady();
const items = [];
for (const [name, alias] of this.aliases.entries()) {
items.push({
name,
tool: alias.tool,
description: alias.description,
created_at: alias.created_at,
updated_at: alias.updated_at,
});
}
return { success: true, aliases: items };
}
async deleteAlias(name) {
await this.ensureReady();
if (typeof name !== 'string' || name.trim().length === 0) {
throw ToolError.invalidParams({ field: 'name', message: 'alias name must be a non-empty string' });
}
const trimmed = name.trim();
if (!this.aliases.delete(trimmed)) {
throw ToolError.notFound({
code: 'ALIAS_NOT_FOUND',
message: `alias '${trimmed}' not found`,
hint: 'Use action=alias_list to see known aliases.',
});
}
await this.persist();
return { success: true, alias: trimmed };
}
async resolveAlias(name) {
await this.ensureReady();
if (!name || typeof name !== 'string') {
return null;
}
return this.aliases.get(name) || null;
}
getStats() {
return { ...this.stats, total: this.aliases.size };
}
async cleanup() {
this.aliases.clear();
}
}
module.exports = AliasService;