@nomyx/assistant
Version:
A powerful assistant library and cli for your AI projects. works with Vertex AI (Claude and Gemini)
222 lines (221 loc) • 8.56 kB
JavaScript
;
// LOCKED: TRUE
// All content in this file is locked and cannot be edited.
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 (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.CodeEditor = void 0;
const fs_1 = require("fs");
const path_1 = __importDefault(require("path"));
const worker_threads_1 = require("worker_threads");
const acorn = __importStar(require("acorn"));
const astring = __importStar(require("astring"));
const lru_cache_1 = require("lru-cache");
const diff_1 = require("diff");
class CodeEditor {
constructor(options = { maxCacheSize: 100, cacheMaxAge: 1000 * 60 * 60, workerCount: 4 }) {
this.fileCache = new lru_cache_1.LRUCache({
max: options.maxCacheSize || 100
});
this.workerPool = this.createWorkerPool(options.workerCount || 4);
}
createWorkerPool(size) {
const pool = [];
for (let i = 0; i < size; i++) {
const workerPath = process.env.NODE_ENV === 'production'
? path_1.default.resolve(__dirname, 'worker.js')
: path_1.default.resolve(__dirname, '..', '..', 'dist', 'code-editor', 'worker.js');
const worker = new worker_threads_1.Worker(workerPath);
pool.push(worker);
}
return pool;
}
getWorker() {
return this.workerPool[Math.floor(Math.random() * this.workerPool.length)];
}
async runWorkerTask(task, data) {
return new Promise((resolve, reject) => {
const worker = this.getWorker();
worker.postMessage({ task, data });
worker.once('message', resolve);
worker.once('error', reject);
});
}
async open(filePath) {
try {
const content = await fs_1.promises.readFile(filePath, 'utf-8');
const ast = acorn.parse(content, { ecmaVersion: 2020, sourceType: 'module' });
this.fileCache.set(filePath, { content, ast });
}
catch (error) {
throw new Error(`Failed to open file ${filePath}: ${error.message}`);
}
}
async read(filePath) {
if (!this.fileCache.has(filePath)) {
await this.open(filePath);
}
const fileHit = this.fileCache.get(filePath);
return fileHit ? fileHit.content : null;
}
async write(filePath, content) {
try {
await fs_1.promises.writeFile(filePath, content, 'utf-8');
const ast = acorn.parse(content, { ecmaVersion: 2020, sourceType: 'module' });
this.fileCache.set(filePath, { content, ast });
}
catch (error) {
throw new Error(`Failed to write to file ${filePath}: ${error.message}`);
}
}
async search(pattern, options = { flags: 'g' }) {
try {
const results = [];
for (const [filePath, { content }] of this.fileCache.entries()) {
const regex = new RegExp(pattern, options.flags || 'g');
const matches = content.match(regex);
if (matches) {
results.push({ filePath, matches });
}
}
return results;
}
catch (error) {
throw new Error(`Search failed: ${error.message}`);
}
}
async replace(pattern, replacement, options = { flags: 'g' }) {
try {
const results = [];
for (const [filePath, { content }] of this.fileCache.entries()) {
const regex = new RegExp(pattern, options.flags || 'g');
const newContent = content.replace(regex, replacement);
if (newContent !== content) {
await this.write(filePath, newContent);
results.push({ filePath, content: newContent, changesApplied: true });
}
}
return results;
}
catch (error) {
throw new Error(`Replace operation failed: ${error.message}`);
}
}
async transform(transformation, filePath) {
try {
const value = this.fileCache.get(filePath);
if (!value) {
throw new Error(`File ${filePath} not found in cache`);
}
const { ast } = value;
const transformedAst = transformation(ast);
const transformedCode = astring.generate(transformedAst);
await this.write(filePath, transformedCode);
}
catch (error) {
throw new Error(`Transformation failed for file ${filePath}: ${error.message}`);
}
}
async diff(filePath) {
try {
const originalContent = await fs_1.promises.readFile(filePath, 'utf-8');
const fileHit = this.fileCache.get(filePath);
const currentContent = fileHit ? fileHit.content : null;
if (!currentContent) {
throw new Error(`File ${filePath} not found in cache`);
}
return this.generateDiff(originalContent, currentContent, filePath);
}
catch (error) {
throw new Error(`Failed to generate diff for file ${filePath}: ${error.message}`);
}
}
async listFiles(directory) {
try {
return await fs_1.promises.readdir(directory);
}
catch (error) {
throw new Error(`Failed to list files in directory ${directory}: ${error.message}`);
}
}
generateDiff(oldStr, newStr, filePath) {
return (0, diff_1.createPatch)(filePath, oldStr, newStr);
}
async format(input) {
try {
const ast = acorn.parse(input, { ecmaVersion: 2020, sourceType: 'module' });
return this.generateFormattedCode(ast);
}
catch (error) {
throw new Error(`Formatting failed: ${error.message}`);
}
}
generateFormattedCode(ast) {
return astring.generate(ast, {
indent: ' ',
lineEnd: '\n',
generator: {
...astring.baseGenerator,
},
});
}
async applyChanges(filePath, changes) {
try {
const content = await this.read(filePath);
if (content === null) {
throw new Error(`File ${filePath} not found in cache`);
}
const newContent = this.applyChangesToContent(content, changes);
await this.write(filePath, newContent);
return { success: true, message: `Changes applied to ${filePath}` };
}
catch (error) {
throw new Error(`Failed to apply changes to file ${filePath}: ${error.message}`);
}
}
applyChangesToContent(content, changes) {
let newContent = content;
if (typeof changes === 'string') {
newContent = changes;
}
else if (Array.isArray(changes)) {
for (const change of changes) {
if (change.type === 'replace' && typeof change.start === 'number' && typeof change.end === 'number') {
newContent = newContent.slice(0, change.start) + change.text + newContent.slice(change.end);
}
}
}
return newContent;
}
async close() {
this.fileCache.clear();
for (const worker of this.workerPool) {
worker.terminate();
}
}
}
exports.CodeEditor = CodeEditor;