@appkit/llamacpp-cli 1.3.2 → 1.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +51 -0
- package/dist/cli.js +16 -1
- package/dist/cli.js.map +1 -1
- package/dist/commands/config-global.d.ts +6 -0
- package/dist/commands/config-global.d.ts.map +1 -0
- package/dist/commands/config-global.js +38 -0
- package/dist/commands/config-global.js.map +1 -0
- package/dist/commands/create.d.ts.map +1 -1
- package/dist/commands/create.js +15 -6
- package/dist/commands/create.js.map +1 -1
- package/dist/commands/list.js +2 -2
- package/dist/commands/list.js.map +1 -1
- package/dist/commands/pull.d.ts.map +1 -1
- package/dist/commands/pull.js +4 -1
- package/dist/commands/pull.js.map +1 -1
- package/dist/lib/model-downloader.d.ts +9 -3
- package/dist/lib/model-downloader.d.ts.map +1 -1
- package/dist/lib/model-downloader.js +34 -7
- package/dist/lib/model-downloader.js.map +1 -1
- package/dist/lib/model-scanner.d.ts +8 -2
- package/dist/lib/model-scanner.d.ts.map +1 -1
- package/dist/lib/model-scanner.js +36 -8
- package/dist/lib/model-scanner.js.map +1 -1
- package/dist/lib/models-dir-setup.d.ts +6 -0
- package/dist/lib/models-dir-setup.d.ts.map +1 -0
- package/dist/lib/models-dir-setup.js +75 -0
- package/dist/lib/models-dir-setup.js.map +1 -0
- package/dist/lib/state-manager.d.ts +8 -0
- package/dist/lib/state-manager.d.ts.map +1 -1
- package/dist/lib/state-manager.js +15 -0
- package/dist/lib/state-manager.js.map +1 -1
- package/dist/utils/file-utils.d.ts +1 -1
- package/dist/utils/file-utils.js +2 -2
- package/dist/utils/file-utils.js.map +1 -1
- package/dist/utils/prompt-utils.d.ts +9 -0
- package/dist/utils/prompt-utils.d.ts.map +1 -0
- package/dist/utils/prompt-utils.js +79 -0
- package/dist/utils/prompt-utils.js.map +1 -0
- package/package.json +2 -2
- package/src/cli.ts +16 -1
- package/src/commands/config-global.ts +38 -0
- package/src/commands/create.ts +16 -6
- package/src/commands/list.ts +2 -2
- package/src/commands/pull.ts +5 -1
- package/src/lib/model-downloader.ts +40 -8
- package/src/lib/model-scanner.ts +41 -9
- package/src/lib/models-dir-setup.ts +46 -0
- package/src/lib/state-manager.ts +17 -0
- package/src/utils/file-utils.ts +2 -2
- package/src/utils/prompt-utils.ts +47 -0
package/src/lib/model-scanner.ts
CHANGED
|
@@ -5,18 +5,34 @@ import { getModelsDir } from '../utils/file-utils';
|
|
|
5
5
|
import { formatBytes } from '../utils/format-utils';
|
|
6
6
|
|
|
7
7
|
export class ModelScanner {
|
|
8
|
-
private modelsDir
|
|
8
|
+
private modelsDir?: string;
|
|
9
|
+
private getModelsDirFn?: () => Promise<string>;
|
|
9
10
|
|
|
10
|
-
constructor(modelsDir?: string) {
|
|
11
|
-
this.modelsDir = modelsDir
|
|
11
|
+
constructor(modelsDir?: string, getModelsDirFn?: () => Promise<string>) {
|
|
12
|
+
this.modelsDir = modelsDir;
|
|
13
|
+
this.getModelsDirFn = getModelsDirFn;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Get the models directory (either configured or default)
|
|
18
|
+
*/
|
|
19
|
+
private async getModelsDirectory(): Promise<string> {
|
|
20
|
+
if (this.modelsDir) {
|
|
21
|
+
return this.modelsDir;
|
|
22
|
+
}
|
|
23
|
+
if (this.getModelsDirFn) {
|
|
24
|
+
return await this.getModelsDirFn();
|
|
25
|
+
}
|
|
26
|
+
return getModelsDir();
|
|
12
27
|
}
|
|
13
28
|
|
|
14
29
|
/**
|
|
15
30
|
* Scan models directory for GGUF files
|
|
16
31
|
*/
|
|
17
32
|
async scanModels(): Promise<ModelInfo[]> {
|
|
33
|
+
const modelsDir = await this.getModelsDirectory();
|
|
18
34
|
try {
|
|
19
|
-
const files = await fs.readdir(
|
|
35
|
+
const files = await fs.readdir(modelsDir);
|
|
20
36
|
const ggufFiles = files.filter((f) => f.toLowerCase().endsWith('.gguf'));
|
|
21
37
|
|
|
22
38
|
const models: ModelInfo[] = [];
|
|
@@ -41,7 +57,8 @@ export class ModelScanner {
|
|
|
41
57
|
* Get information about a specific model file
|
|
42
58
|
*/
|
|
43
59
|
async getModelInfo(filename: string): Promise<ModelInfo | null> {
|
|
44
|
-
const
|
|
60
|
+
const modelsDir = await this.getModelsDirectory();
|
|
61
|
+
const modelPath = path.join(modelsDir, filename);
|
|
45
62
|
|
|
46
63
|
try {
|
|
47
64
|
const stats = await fs.stat(modelPath);
|
|
@@ -84,8 +101,10 @@ export class ModelScanner {
|
|
|
84
101
|
return filename;
|
|
85
102
|
}
|
|
86
103
|
|
|
104
|
+
const modelsDir = await this.getModelsDirectory();
|
|
105
|
+
|
|
87
106
|
// Try in models directory
|
|
88
|
-
const modelPath = path.join(
|
|
107
|
+
const modelPath = path.join(modelsDir, filename);
|
|
89
108
|
const modelInfo = await this.getModelInfo(filename);
|
|
90
109
|
|
|
91
110
|
if (modelInfo && modelInfo.exists) {
|
|
@@ -97,7 +116,7 @@ export class ModelScanner {
|
|
|
97
116
|
const withExtension = `${filename}.gguf`;
|
|
98
117
|
const modelInfoWithExt = await this.getModelInfo(withExtension);
|
|
99
118
|
if (modelInfoWithExt && modelInfoWithExt.exists) {
|
|
100
|
-
return path.join(
|
|
119
|
+
return path.join(modelsDir, withExtension);
|
|
101
120
|
}
|
|
102
121
|
}
|
|
103
122
|
|
|
@@ -121,5 +140,18 @@ export class ModelScanner {
|
|
|
121
140
|
}
|
|
122
141
|
}
|
|
123
142
|
|
|
124
|
-
//
|
|
125
|
-
|
|
143
|
+
// Create singleton that uses configured models directory
|
|
144
|
+
// Use lazy import to avoid circular dependency
|
|
145
|
+
let _modelScanner: ModelScanner | null = null;
|
|
146
|
+
|
|
147
|
+
export function getModelScanner(): ModelScanner {
|
|
148
|
+
if (!_modelScanner) {
|
|
149
|
+
// Import stateManager dynamically to avoid circular dependency
|
|
150
|
+
const { stateManager } = require('./state-manager');
|
|
151
|
+
_modelScanner = new ModelScanner(undefined, () => stateManager.getModelsDirectory());
|
|
152
|
+
}
|
|
153
|
+
return _modelScanner;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Export singleton instance for backward compatibility
|
|
157
|
+
export const modelScanner = getModelScanner();
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import { stateManager } from './state-manager';
|
|
4
|
+
import { expandHome } from '../utils/file-utils';
|
|
5
|
+
import { prompt } from '../utils/prompt-utils';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Ensure models directory exists, prompting user if needed
|
|
9
|
+
* Returns the final models directory path
|
|
10
|
+
*/
|
|
11
|
+
export async function ensureModelsDirectory(): Promise<string> {
|
|
12
|
+
const configuredPath = await stateManager.getModelsDirectory();
|
|
13
|
+
|
|
14
|
+
// If directory exists, we're good
|
|
15
|
+
if (fs.existsSync(configuredPath)) {
|
|
16
|
+
return configuredPath;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Directory doesn't exist - prompt user
|
|
20
|
+
console.log(chalk.yellow('⚠️ Models directory not found'));
|
|
21
|
+
console.log();
|
|
22
|
+
console.log(chalk.dim('The models directory is where GGUF model files are stored.'));
|
|
23
|
+
console.log(chalk.dim(`Configured path: ${configuredPath}`));
|
|
24
|
+
console.log();
|
|
25
|
+
|
|
26
|
+
const answer = await prompt(
|
|
27
|
+
'Enter models directory path (press Enter to use default)',
|
|
28
|
+
configuredPath
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
const finalPath = expandHome(answer);
|
|
32
|
+
|
|
33
|
+
// If user changed the path, update config
|
|
34
|
+
if (finalPath !== configuredPath) {
|
|
35
|
+
console.log(chalk.dim(`Updating configuration to: ${finalPath}`));
|
|
36
|
+
await stateManager.setModelsDirectory(finalPath);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Create the directory
|
|
40
|
+
console.log(chalk.dim(`Creating directory: ${finalPath}`));
|
|
41
|
+
fs.mkdirSync(finalPath, { recursive: true, mode: 0o755 });
|
|
42
|
+
console.log(chalk.green('✅ Models directory created'));
|
|
43
|
+
console.log();
|
|
44
|
+
|
|
45
|
+
return finalPath;
|
|
46
|
+
}
|
package/src/lib/state-manager.ts
CHANGED
|
@@ -183,6 +183,23 @@ export class StateManager {
|
|
|
183
183
|
const servers = await this.getAllServers();
|
|
184
184
|
return new Set(servers.map((s) => s.port));
|
|
185
185
|
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Get the configured models directory
|
|
189
|
+
*/
|
|
190
|
+
async getModelsDirectory(): Promise<string> {
|
|
191
|
+
const config = await this.loadGlobalConfig();
|
|
192
|
+
return config.modelsDirectory;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Set the models directory
|
|
197
|
+
*/
|
|
198
|
+
async setModelsDirectory(directory: string): Promise<void> {
|
|
199
|
+
const config = await this.loadGlobalConfig();
|
|
200
|
+
config.modelsDirectory = directory;
|
|
201
|
+
await this.saveGlobalConfig(config);
|
|
202
|
+
}
|
|
186
203
|
}
|
|
187
204
|
|
|
188
205
|
// Export singleton instance
|
package/src/utils/file-utils.ts
CHANGED
|
@@ -82,10 +82,10 @@ export function getGlobalConfigPath(): string {
|
|
|
82
82
|
}
|
|
83
83
|
|
|
84
84
|
/**
|
|
85
|
-
* Get the models directory (
|
|
85
|
+
* Get the default models directory (~/.llamacpp/models)
|
|
86
86
|
*/
|
|
87
87
|
export function getModelsDir(): string {
|
|
88
|
-
return path.join(
|
|
88
|
+
return path.join(getConfigDir(), 'models');
|
|
89
89
|
}
|
|
90
90
|
|
|
91
91
|
/**
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import * as readline from 'readline';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Prompt user for input
|
|
5
|
+
*/
|
|
6
|
+
export function prompt(question: string, defaultValue?: string): Promise<string> {
|
|
7
|
+
const rl = readline.createInterface({
|
|
8
|
+
input: process.stdin,
|
|
9
|
+
output: process.stdout,
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
return new Promise((resolve) => {
|
|
13
|
+
const promptText = defaultValue
|
|
14
|
+
? `${question} [${defaultValue}]: `
|
|
15
|
+
: `${question}: `;
|
|
16
|
+
|
|
17
|
+
rl.question(promptText, (answer) => {
|
|
18
|
+
rl.close();
|
|
19
|
+
resolve(answer.trim() || defaultValue || '');
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Prompt user for yes/no confirmation
|
|
26
|
+
*/
|
|
27
|
+
export function confirm(question: string, defaultYes = true): Promise<boolean> {
|
|
28
|
+
const rl = readline.createInterface({
|
|
29
|
+
input: process.stdin,
|
|
30
|
+
output: process.stdout,
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
const suffix = defaultYes ? '[Y/n]' : '[y/N]';
|
|
34
|
+
|
|
35
|
+
return new Promise((resolve) => {
|
|
36
|
+
rl.question(`${question} ${suffix}: `, (answer) => {
|
|
37
|
+
rl.close();
|
|
38
|
+
const input = answer.trim().toLowerCase();
|
|
39
|
+
|
|
40
|
+
if (input === '') {
|
|
41
|
+
resolve(defaultYes);
|
|
42
|
+
} else {
|
|
43
|
+
resolve(input === 'y' || input === 'yes');
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
}
|