feat: v2.62.0 - Astra Autonomous Loop (AAL) foundation & enhanced file analysis
This commit is contained in:
@@ -0,0 +1,88 @@
|
||||
/**
|
||||
* ============================================================
|
||||
* Astra Path Resolver (경로 해결기)
|
||||
*
|
||||
* Astra의 모든 데이터 파일(.astra 디렉토리)의 경로를 중앙에서 관리합니다.
|
||||
* 확장 프로그램의 설치 경로(extensionUri) 기반으로 .astra 디렉토리를 해결하여,
|
||||
* 사용자 프로젝트 루트가 아닌 ConnectAI 패키지 내부에 데이터를 저장합니다.
|
||||
*
|
||||
* 이 모듈은 AAL(Astra Autonomous Loop) 프로토콜의 기반이 됩니다.
|
||||
* ============================================================
|
||||
*/
|
||||
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
let _extensionRootPath: string | null = null;
|
||||
|
||||
/**
|
||||
* 확장 프로그램 활성화 시 1회 호출하여 extension root를 설정합니다.
|
||||
* extension.ts의 activate()에서 호출되어야 합니다.
|
||||
*/
|
||||
export function initAstraPathResolver(context: vscode.ExtensionContext): void {
|
||||
_extensionRootPath = context.extensionUri.fsPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* .astra 데이터 디렉토리의 절대 경로를 반환합니다.
|
||||
* 디렉토리가 없으면 자동 생성합니다.
|
||||
*
|
||||
* @returns ConnectAI/.astra/ 의 절대 경로
|
||||
*/
|
||||
export function getAstraDataDir(): string {
|
||||
const root = _extensionRootPath ?? _fallbackExtensionRoot();
|
||||
const astraDir = path.join(root, '.astra');
|
||||
if (!fs.existsSync(astraDir)) {
|
||||
fs.mkdirSync(astraDir, { recursive: true });
|
||||
}
|
||||
return astraDir;
|
||||
}
|
||||
|
||||
/**
|
||||
* .astra 내부의 특정 파일 경로를 반환합니다.
|
||||
*
|
||||
* @param filename - 파일 이름 (예: 'project_memory.json', 'tasks.json')
|
||||
* @returns 파일의 절대 경로
|
||||
*/
|
||||
export function getAstraFilePath(filename: string): string {
|
||||
return path.join(getAstraDataDir(), filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* .astra 내부의 프로젝트별 서브디렉토리 경로를 반환합니다.
|
||||
* 프로젝트별 메모리 분리가 필요한 경우 사용합니다.
|
||||
*
|
||||
* @param projectId - 프로젝트 식별자 (hash 또는 이름)
|
||||
* @returns 프로젝트별 .astra 서브디렉토리 경로
|
||||
*/
|
||||
export function getAstraProjectDir(projectId: string): string {
|
||||
const projDir = path.join(getAstraDataDir(), 'projects', projectId);
|
||||
if (!fs.existsSync(projDir)) {
|
||||
fs.mkdirSync(projDir, { recursive: true });
|
||||
}
|
||||
return projDir;
|
||||
}
|
||||
|
||||
/**
|
||||
* AAL(Autonomous Loop) 태스크 파일의 경로를 반환합니다.
|
||||
*/
|
||||
export function getAstraTaskFilePath(): string {
|
||||
return getAstraFilePath('tasks.json');
|
||||
}
|
||||
|
||||
/**
|
||||
* AAL 프로토콜 설정 파일 경로를 반환합니다.
|
||||
*/
|
||||
export function getAstraProtocolPath(): string {
|
||||
return getAstraFilePath('protocol.json');
|
||||
}
|
||||
|
||||
/**
|
||||
* extensionUri가 아직 설정되지 않은 경우의 fallback.
|
||||
* __dirname 기반으로 ConnectAI 루트를 추정합니다.
|
||||
*/
|
||||
function _fallbackExtensionRoot(): string {
|
||||
// esbuild로 번들된 out/extension.js → 상위 디렉토리가 ConnectAI 루트
|
||||
return path.resolve(__dirname, '..');
|
||||
}
|
||||
+18
-6
@@ -2,11 +2,17 @@ import { logInfo, logError } from '../utils';
|
||||
|
||||
/**
|
||||
* ActionQueueManager: Manages large-scale tasks by processing them
|
||||
* sequentially to prevent resource exhaustion and I/O bottlenecks.
|
||||
* with a concurrency limit to prevent resource exhaustion and I/O bottlenecks
|
||||
* while maintaining high throughput under maximum load.
|
||||
*/
|
||||
export class ActionQueueManager {
|
||||
private queue: (() => Promise<void>)[] = [];
|
||||
private isProcessing: boolean = false;
|
||||
private activeCount: number = 0;
|
||||
private readonly concurrencyLimit: number;
|
||||
|
||||
constructor(concurrencyLimit: number = 3) {
|
||||
this.concurrencyLimit = concurrencyLimit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a task to the queue.
|
||||
@@ -26,28 +32,34 @@ export class ActionQueueManager {
|
||||
}
|
||||
|
||||
private async processNext() {
|
||||
if (this.isProcessing || this.queue.length === 0) return;
|
||||
if (this.activeCount >= this.concurrencyLimit || this.queue.length === 0) return;
|
||||
|
||||
this.isProcessing = true;
|
||||
this.activeCount++;
|
||||
const task = this.queue.shift();
|
||||
|
||||
if (task) {
|
||||
try {
|
||||
// Add a micro-delay to allow system breathing room between heavy I/O
|
||||
await new Promise(r => setTimeout(r, 50));
|
||||
await new Promise(r => setTimeout(r, 10));
|
||||
await task();
|
||||
} catch (error) {
|
||||
logError('Task in queue failed:', error);
|
||||
} finally {
|
||||
this.isProcessing = false;
|
||||
this.activeCount--;
|
||||
this.processNext();
|
||||
}
|
||||
} else {
|
||||
this.activeCount--;
|
||||
}
|
||||
}
|
||||
|
||||
public getPendingCount(): number {
|
||||
return this.queue.length;
|
||||
}
|
||||
|
||||
public getActiveCount(): number {
|
||||
return this.activeCount;
|
||||
}
|
||||
}
|
||||
|
||||
export const actionQueue = new ActionQueueManager();
|
||||
|
||||
Reference in New Issue
Block a user