Skip to content
This repository was archived by the owner on Jul 4, 2025. It is now read-only.

Commit 4e3caf8

Browse files
committed
feat: add cortex data folder
1 parent 6097606 commit 4e3caf8

File tree

18 files changed

+183
-39
lines changed

18 files changed

+183
-39
lines changed

cortex-js/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
"class-validator": "^0.14.1",
4242
"cli-progress": "^3.12.0",
4343
"decompress": "^4.2.1",
44+
"js-yaml": "^4.1.0",
4445
"nest-commander": "^3.13.0",
4546
"readline": "^1.3.0",
4647
"reflect-metadata": "^0.2.0",
@@ -49,7 +50,6 @@
4950
"sqlite3": "^5.1.7",
5051
"typeorm": "^0.3.20",
5152
"ulid": "^2.3.0",
52-
"yaml": "^2.4.2",
5353
"uuid": "^9.0.1"
5454
},
5555
"devDependencies": {
@@ -61,6 +61,7 @@
6161
"@types/decompress": "^4.2.7",
6262
"@types/express": "^4.17.17",
6363
"@types/jest": "^29.5.2",
64+
"@types/js-yaml": "^4.0.9",
6465
"@types/node": "^20.12.9",
6566
"@types/supertest": "^6.0.0",
6667
"@types/uuid": "^9.0.8",

cortex-js/src/app.module.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { CortexModule } from './usecases/cortex/cortex.module';
1111
import { ConfigModule } from '@nestjs/config';
1212
import { env } from 'node:process';
1313
import { SeedService } from './usecases/seed/seed.service';
14+
import { FileManagerModule } from './file-manager/file-manager.module';
1415

1516
@Module({
1617
imports: [
@@ -29,6 +30,7 @@ import { SeedService } from './usecases/seed/seed.service';
2930
AssistantsModule,
3031
CortexModule,
3132
ExtensionModule,
33+
FileManagerModule,
3234
],
3335
providers: [SeedService],
3436
})

cortex-js/src/command.module.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import { ModelUpdateCommand } from './infrastructure/commanders/models/model-upd
2323
import { AssistantsModule } from './usecases/assistants/assistants.module';
2424
import { CliUsecasesModule } from './infrastructure/commanders/usecases/cli.usecases.module';
2525
import { MessagesModule } from './usecases/messages/messages.module';
26+
import { FileManagerModule } from './file-manager/file-manager.module';
2627

2728
@Module({
2829
imports: [
@@ -39,6 +40,7 @@ import { MessagesModule } from './usecases/messages/messages.module';
3940
CliUsecasesModule,
4041
AssistantsModule,
4142
MessagesModule,
43+
FileManagerModule,
4244
],
4345
providers: [
4446
CortexCommand,
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export interface Config {
2+
dataFolderPath: string;
3+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { Module } from '@nestjs/common';
2+
import { FileManagerService } from './file-manager.service';
3+
4+
@Module({
5+
providers: [FileManagerService],
6+
exports: [FileManagerService],
7+
})
8+
export class FileManagerModule {}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { Test, TestingModule } from '@nestjs/testing';
2+
import { FileManagerService } from './file-manager.service';
3+
4+
describe('FileManagerService', () => {
5+
let service: FileManagerService;
6+
7+
beforeEach(async () => {
8+
const module: TestingModule = await Test.createTestingModule({
9+
providers: [FileManagerService],
10+
}).compile();
11+
12+
service = module.get<FileManagerService>(FileManagerService);
13+
});
14+
15+
it('should be defined', () => {
16+
expect(service).toBeDefined();
17+
});
18+
});
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import { Config } from '@/domain/config/config.interface';
2+
import { Injectable } from '@nestjs/common';
3+
import os from 'os';
4+
import { join } from 'node:path';
5+
import { existsSync, promises } from 'node:fs';
6+
import yaml from 'js-yaml';
7+
8+
@Injectable()
9+
export class FileManagerService {
10+
private configFile = '.cortexrc';
11+
private cortexDirectoryName = 'cortex';
12+
private modelFolderName = 'models';
13+
private cortexCppFolderName = 'cortex-cpp';
14+
15+
async getConfig(): Promise<Config> {
16+
const homeDir = os.homedir();
17+
const configPath = join(homeDir, this.configFile);
18+
19+
if (!existsSync(configPath)) {
20+
const config = this.defaultConfig();
21+
await this.createFolderIfNotExist(config.dataFolderPath);
22+
await this.writeConfigFile(config);
23+
return config;
24+
}
25+
26+
try {
27+
const content = await promises.readFile(configPath, 'utf8');
28+
const config = yaml.load(content) as Config;
29+
return config;
30+
} catch (error) {
31+
console.warn('Error reading config file. Using default config.');
32+
console.warn(error);
33+
const config = this.defaultConfig();
34+
await this.createFolderIfNotExist(config.dataFolderPath);
35+
await this.writeConfigFile(config);
36+
return config;
37+
}
38+
}
39+
40+
private async writeConfigFile(config: Config): Promise<void> {
41+
const homeDir = os.homedir();
42+
const configPath = join(homeDir, this.configFile);
43+
44+
// write config to file as yaml
45+
const configString = yaml.dump(config);
46+
await promises.writeFile(configPath, configString, 'utf8');
47+
}
48+
49+
private async createFolderIfNotExist(dataFolderPath: string): Promise<void> {
50+
if (!existsSync(dataFolderPath)) {
51+
await promises.mkdir(dataFolderPath, { recursive: true });
52+
}
53+
54+
const modelFolderPath = join(dataFolderPath, this.modelFolderName);
55+
const cortexCppFolderPath = join(dataFolderPath, this.cortexCppFolderName);
56+
if (!existsSync(modelFolderPath)) {
57+
await promises.mkdir(modelFolderPath, { recursive: true });
58+
}
59+
if (!existsSync(cortexCppFolderPath)) {
60+
await promises.mkdir(cortexCppFolderPath, { recursive: true });
61+
}
62+
}
63+
64+
private defaultConfig(): Config {
65+
// default will store at home directory
66+
const homeDir = os.homedir();
67+
const dataFolderPath = join(homeDir, this.cortexDirectoryName);
68+
69+
return {
70+
dataFolderPath,
71+
};
72+
}
73+
74+
async getDataFolderPath(): Promise<string> {
75+
const config = await this.getConfig();
76+
return config.dataFolderPath;
77+
}
78+
}

cortex-js/src/infrastructure/commanders/usecases/cli.usecases.module.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { CortexModule } from '@/usecases/cortex/cortex.module';
99
import { ThreadsModule } from '@/usecases/threads/threads.module';
1010
import { AssistantsModule } from '@/usecases/assistants/assistants.module';
1111
import { MessagesModule } from '@/usecases/messages/messages.module';
12+
import { FileManagerModule } from '@/file-manager/file-manager.module';
1213

1314
@Module({
1415
imports: [
@@ -19,6 +20,7 @@ import { MessagesModule } from '@/usecases/messages/messages.module';
1920
ThreadsModule,
2021
AssistantsModule,
2122
MessagesModule,
23+
FileManagerModule,
2224
],
2325
providers: [InitCliUsecases, ModelsCliUsecases, ChatCliUsecases],
2426
exports: [InitCliUsecases, ModelsCliUsecases, ChatCliUsecases],

cortex-js/src/infrastructure/commanders/usecases/init.cli.usecases.ts

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,24 @@
11
import { createWriteStream, existsSync, rmSync } from 'fs';
2-
import { resolve, delimiter, join } from 'path';
2+
import { delimiter, join } from 'path';
33
import { HttpService } from '@nestjs/axios';
44
import { Presets, SingleBar } from 'cli-progress';
55
import decompress from 'decompress';
66
import { exit } from 'node:process';
77
import { InitOptions } from '../types/init-options.interface';
88
import { Injectable } from '@nestjs/common';
99
import { firstValueFrom } from 'rxjs';
10+
import { FileManagerService } from '@/file-manager/file-manager.service';
1011

1112
@Injectable()
1213
export class InitCliUsecases {
1314
CORTEX_RELEASES_URL = 'https://api.github.com/repos/janhq/cortex/releases';
1415
CUDA_DOWNLOAD_URL =
1516
'https://catalog.jan.ai/dist/cuda-dependencies/<version>/<platform>/cuda.tar.gz';
1617

17-
constructor(private readonly httpService: HttpService) {}
18+
constructor(
19+
private readonly httpService: HttpService,
20+
private readonly fileManagerService: FileManagerService,
21+
) {}
1822

1923
installEngine = async (
2024
engineFileName: string,
@@ -53,7 +57,8 @@ export class InitCliUsecases {
5357
}
5458

5559
console.log(`Downloading engine file ${engineFileName}`);
56-
const engineDir = resolve(this.rootDir(), 'cortex-cpp');
60+
const dataFolderPath = await this.fileManagerService.getDataFolderPath();
61+
const engineDir = join(dataFolderPath, 'cortex-cpp');
5762
if (existsSync(engineDir)) rmSync(engineDir, { recursive: true });
5863

5964
const download = await firstValueFrom(
@@ -66,7 +71,7 @@ export class InitCliUsecases {
6671
process.exit(1);
6772
}
6873

69-
const destination = resolve(this.rootDir(), toDownloadAsset.name);
74+
const destination = join(dataFolderPath, toDownloadAsset.name);
7075

7176
await new Promise((resolve, reject) => {
7277
const writer = createWriteStream(destination);
@@ -95,10 +100,7 @@ export class InitCliUsecases {
95100
});
96101

97102
try {
98-
await decompress(
99-
resolve(this.rootDir(), destination),
100-
resolve(this.rootDir()),
101-
);
103+
await decompress(destination, join(dataFolderPath));
102104
} catch (e) {
103105
console.error('Error decompressing file', e);
104106
exit(1);
@@ -124,8 +126,6 @@ export class InitCliUsecases {
124126
return `${engineName}.tar.gz`;
125127
};
126128

127-
rootDir = () => resolve(__dirname, `../../../../`);
128-
129129
cudaVersion = async () => {
130130
let filesCuda12: string[];
131131
let filesCuda11: string[];
@@ -178,11 +178,12 @@ export class InitCliUsecases {
178178
installCudaToolkitDependency = async (options: InitOptions) => {
179179
const platform = process.platform === 'win32' ? 'windows' : 'linux';
180180

181+
const dataFolderPath = await this.fileManagerService.getDataFolderPath();
181182
const url = this.CUDA_DOWNLOAD_URL.replace(
182183
'<version>',
183184
options.cudaVersion === '11' ? '11.7' : '12.0',
184185
).replace('<platform>', platform);
185-
const destination = resolve(this.rootDir(), 'cuda-toolkit.tar.gz');
186+
const destination = join(dataFolderPath, 'cuda-toolkit.tar.gz');
186187

187188
const download = await firstValueFrom(
188189
this.httpService.get(url, {
@@ -222,10 +223,7 @@ export class InitCliUsecases {
222223
});
223224

224225
try {
225-
await decompress(
226-
resolve(this.rootDir(), destination),
227-
resolve(this.rootDir(), 'cortex-cpp'),
228-
);
226+
await decompress(destination, join(dataFolderPath, 'cortex-cpp'));
229227
} catch (e) {
230228
console.log(e);
231229
exit(1);

cortex-js/src/infrastructure/database/database.module.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@ import { sqliteDatabaseProviders } from './sqlite-database.providers';
44
import { modelProviders } from './providers/model.providers';
55
import { assistantProviders } from './providers/assistant.providers';
66
import { messageProviders } from './providers/message.providers';
7+
import { FileManagerModule } from '@/file-manager/file-manager.module';
78

89
@Module({
10+
imports: [FileManagerModule],
911
providers: [
1012
...sqliteDatabaseProviders,
1113
...threadProviders,

0 commit comments

Comments
 (0)