@appkit/llamacpp-cli 1.0.0
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/.versionrc.json +16 -0
- package/CHANGELOG.md +10 -0
- package/README.md +474 -0
- package/bin/llamacpp +26 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +196 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/delete.d.ts +2 -0
- package/dist/commands/delete.d.ts.map +1 -0
- package/dist/commands/delete.js +104 -0
- package/dist/commands/delete.js.map +1 -0
- package/dist/commands/list.d.ts +2 -0
- package/dist/commands/list.d.ts.map +1 -0
- package/dist/commands/list.js +37 -0
- package/dist/commands/list.js.map +1 -0
- package/dist/commands/logs.d.ts +8 -0
- package/dist/commands/logs.d.ts.map +1 -0
- package/dist/commands/logs.js +57 -0
- package/dist/commands/logs.js.map +1 -0
- package/dist/commands/ps.d.ts +2 -0
- package/dist/commands/ps.d.ts.map +1 -0
- package/dist/commands/ps.js +72 -0
- package/dist/commands/ps.js.map +1 -0
- package/dist/commands/pull.d.ts +6 -0
- package/dist/commands/pull.d.ts.map +1 -0
- package/dist/commands/pull.js +36 -0
- package/dist/commands/pull.js.map +1 -0
- package/dist/commands/rm.d.ts +2 -0
- package/dist/commands/rm.d.ts.map +1 -0
- package/dist/commands/rm.js +134 -0
- package/dist/commands/rm.js.map +1 -0
- package/dist/commands/run.d.ts +2 -0
- package/dist/commands/run.d.ts.map +1 -0
- package/dist/commands/run.js +198 -0
- package/dist/commands/run.js.map +1 -0
- package/dist/commands/search.d.ts +7 -0
- package/dist/commands/search.d.ts.map +1 -0
- package/dist/commands/search.js +93 -0
- package/dist/commands/search.js.map +1 -0
- package/dist/commands/show.d.ts +6 -0
- package/dist/commands/show.d.ts.map +1 -0
- package/dist/commands/show.js +196 -0
- package/dist/commands/show.js.map +1 -0
- package/dist/commands/start.d.ts +9 -0
- package/dist/commands/start.d.ts.map +1 -0
- package/dist/commands/start.js +150 -0
- package/dist/commands/start.js.map +1 -0
- package/dist/commands/stop.d.ts +2 -0
- package/dist/commands/stop.d.ts.map +1 -0
- package/dist/commands/stop.js +39 -0
- package/dist/commands/stop.js.map +1 -0
- package/dist/lib/config-generator.d.ts +30 -0
- package/dist/lib/config-generator.d.ts.map +1 -0
- package/dist/lib/config-generator.js +125 -0
- package/dist/lib/config-generator.js.map +1 -0
- package/dist/lib/launchctl-manager.d.ts +55 -0
- package/dist/lib/launchctl-manager.d.ts.map +1 -0
- package/dist/lib/launchctl-manager.js +227 -0
- package/dist/lib/launchctl-manager.js.map +1 -0
- package/dist/lib/model-downloader.d.ts +44 -0
- package/dist/lib/model-downloader.d.ts.map +1 -0
- package/dist/lib/model-downloader.js +248 -0
- package/dist/lib/model-downloader.js.map +1 -0
- package/dist/lib/model-scanner.d.ts +31 -0
- package/dist/lib/model-scanner.d.ts.map +1 -0
- package/dist/lib/model-scanner.js +145 -0
- package/dist/lib/model-scanner.js.map +1 -0
- package/dist/lib/model-search.d.ts +29 -0
- package/dist/lib/model-search.d.ts.map +1 -0
- package/dist/lib/model-search.js +131 -0
- package/dist/lib/model-search.js.map +1 -0
- package/dist/lib/port-manager.d.ts +26 -0
- package/dist/lib/port-manager.d.ts.map +1 -0
- package/dist/lib/port-manager.js +75 -0
- package/dist/lib/port-manager.js.map +1 -0
- package/dist/lib/state-manager.d.ts +59 -0
- package/dist/lib/state-manager.d.ts.map +1 -0
- package/dist/lib/state-manager.js +178 -0
- package/dist/lib/state-manager.js.map +1 -0
- package/dist/lib/status-checker.d.ts +28 -0
- package/dist/lib/status-checker.d.ts.map +1 -0
- package/dist/lib/status-checker.js +99 -0
- package/dist/lib/status-checker.js.map +1 -0
- package/dist/types/global-config.d.ts +16 -0
- package/dist/types/global-config.d.ts.map +1 -0
- package/dist/types/global-config.js +18 -0
- package/dist/types/global-config.js.map +1 -0
- package/dist/types/model-info.d.ts +9 -0
- package/dist/types/model-info.d.ts.map +1 -0
- package/dist/types/model-info.js +3 -0
- package/dist/types/model-info.js.map +1 -0
- package/dist/types/server-config.d.ts +27 -0
- package/dist/types/server-config.d.ts.map +1 -0
- package/dist/types/server-config.js +15 -0
- package/dist/types/server-config.js.map +1 -0
- package/dist/utils/file-utils.d.ts +49 -0
- package/dist/utils/file-utils.d.ts.map +1 -0
- package/dist/utils/file-utils.js +144 -0
- package/dist/utils/file-utils.js.map +1 -0
- package/dist/utils/format-utils.d.ts +29 -0
- package/dist/utils/format-utils.d.ts.map +1 -0
- package/dist/utils/format-utils.js +82 -0
- package/dist/utils/format-utils.js.map +1 -0
- package/dist/utils/process-utils.d.ts +27 -0
- package/dist/utils/process-utils.d.ts.map +1 -0
- package/dist/utils/process-utils.js +66 -0
- package/dist/utils/process-utils.js.map +1 -0
- package/package.json +56 -0
- package/src/cli.ts +195 -0
- package/src/commands/delete.ts +74 -0
- package/src/commands/list.ts +37 -0
- package/src/commands/logs.ts +61 -0
- package/src/commands/ps.ts +79 -0
- package/src/commands/pull.ts +40 -0
- package/src/commands/rm.ts +114 -0
- package/src/commands/run.ts +209 -0
- package/src/commands/search.ts +107 -0
- package/src/commands/show.ts +207 -0
- package/src/commands/start.ts +140 -0
- package/src/commands/stop.ts +39 -0
- package/src/lib/config-generator.ts +119 -0
- package/src/lib/launchctl-manager.ts +209 -0
- package/src/lib/model-downloader.ts +259 -0
- package/src/lib/model-scanner.ts +125 -0
- package/src/lib/model-search.ts +114 -0
- package/src/lib/port-manager.ts +80 -0
- package/src/lib/state-manager.ts +177 -0
- package/src/lib/status-checker.ts +113 -0
- package/src/types/global-config.ts +26 -0
- package/src/types/model-info.ts +8 -0
- package/src/types/server-config.ts +42 -0
- package/src/utils/file-utils.ts +106 -0
- package/src/utils/format-utils.ts +80 -0
- package/src/utils/process-utils.ts +60 -0
- package/tsconfig.json +20 -0
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.execAsync = void 0;
|
|
4
|
+
exports.execCommand = execCommand;
|
|
5
|
+
exports.execCommandFull = execCommandFull;
|
|
6
|
+
exports.commandExists = commandExists;
|
|
7
|
+
exports.isProcessRunning = isProcessRunning;
|
|
8
|
+
exports.isPortInUse = isPortInUse;
|
|
9
|
+
const child_process_1 = require("child_process");
|
|
10
|
+
const util_1 = require("util");
|
|
11
|
+
exports.execAsync = (0, util_1.promisify)(child_process_1.exec);
|
|
12
|
+
/**
|
|
13
|
+
* Execute a command and return stdout
|
|
14
|
+
* Throws on non-zero exit code
|
|
15
|
+
*/
|
|
16
|
+
async function execCommand(command) {
|
|
17
|
+
const { stdout } = await (0, exports.execAsync)(command);
|
|
18
|
+
return stdout.trim();
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Execute a command and return both stdout and stderr
|
|
22
|
+
*/
|
|
23
|
+
async function execCommandFull(command) {
|
|
24
|
+
const { stdout, stderr } = await (0, exports.execAsync)(command);
|
|
25
|
+
return {
|
|
26
|
+
stdout: stdout.trim(),
|
|
27
|
+
stderr: stderr.trim(),
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Check if a command exists in PATH
|
|
32
|
+
*/
|
|
33
|
+
async function commandExists(command) {
|
|
34
|
+
try {
|
|
35
|
+
await (0, exports.execAsync)(`which ${command}`);
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Check if a process is running by PID
|
|
44
|
+
*/
|
|
45
|
+
async function isProcessRunning(pid) {
|
|
46
|
+
try {
|
|
47
|
+
await (0, exports.execAsync)(`ps -p ${pid}`);
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Check if a port is in use
|
|
56
|
+
*/
|
|
57
|
+
async function isPortInUse(port) {
|
|
58
|
+
try {
|
|
59
|
+
await (0, exports.execAsync)(`lsof -iTCP:${port} -sTCP:LISTEN -t`);
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=process-utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"process-utils.js","sourceRoot":"","sources":["../../src/utils/process-utils.ts"],"names":[],"mappings":";;;AASA,kCAGC;AAKD,0CAMC;AAKD,sCAOC;AAKD,4CAOC;AAKD,kCAOC;AA3DD,iDAAqC;AACrC,+BAAiC;AAEpB,QAAA,SAAS,GAAG,IAAA,gBAAS,EAAC,oBAAI,CAAC,CAAC;AAEzC;;;GAGG;AACI,KAAK,UAAU,WAAW,CAAC,OAAe;IAC/C,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,IAAA,iBAAS,EAAC,OAAO,CAAC,CAAC;IAC5C,OAAO,MAAM,CAAC,IAAI,EAAE,CAAC;AACvB,CAAC;AAED;;GAEG;AACI,KAAK,UAAU,eAAe,CAAC,OAAe;IACnD,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,IAAA,iBAAS,EAAC,OAAO,CAAC,CAAC;IACpD,OAAO;QACL,MAAM,EAAE,MAAM,CAAC,IAAI,EAAE;QACrB,MAAM,EAAE,MAAM,CAAC,IAAI,EAAE;KACtB,CAAC;AACJ,CAAC;AAED;;GAEG;AACI,KAAK,UAAU,aAAa,CAAC,OAAe;IACjD,IAAI,CAAC;QACH,MAAM,IAAA,iBAAS,EAAC,SAAS,OAAO,EAAE,CAAC,CAAC;QACpC,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED;;GAEG;AACI,KAAK,UAAU,gBAAgB,CAAC,GAAW;IAChD,IAAI,CAAC;QACH,MAAM,IAAA,iBAAS,EAAC,SAAS,GAAG,EAAE,CAAC,CAAC;QAChC,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED;;GAEG;AACI,KAAK,UAAU,WAAW,CAAC,IAAY;IAC5C,IAAI,CAAC;QACH,MAAM,IAAA,iBAAS,EAAC,cAAc,IAAI,kBAAkB,CAAC,CAAC;QACtD,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@appkit/llamacpp-cli",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "CLI tool to manage local llama.cpp servers on macOS",
|
|
5
|
+
"main": "dist/cli.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"llamacpp": "./bin/llamacpp"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"dev": "tsx src/cli.ts",
|
|
11
|
+
"build": "tsc",
|
|
12
|
+
"start": "node dist/cli.js",
|
|
13
|
+
"clean": "rm -rf dist",
|
|
14
|
+
"prepublishOnly": "npm run build",
|
|
15
|
+
"release": "commit-and-tag-version",
|
|
16
|
+
"release:minor": "commit-and-tag-version --release-as minor",
|
|
17
|
+
"release:major": "commit-and-tag-version --release-as major",
|
|
18
|
+
"release:patch": "commit-and-tag-version --release-as patch",
|
|
19
|
+
"release:first": "commit-and-tag-version --first-release",
|
|
20
|
+
"postrelease": "git push --follow-tags origin main && npm publish --access public"
|
|
21
|
+
},
|
|
22
|
+
"keywords": [
|
|
23
|
+
"llama",
|
|
24
|
+
"llama.cpp",
|
|
25
|
+
"llm",
|
|
26
|
+
"cli",
|
|
27
|
+
"server-management",
|
|
28
|
+
"gguf",
|
|
29
|
+
"macos",
|
|
30
|
+
"launchctl"
|
|
31
|
+
],
|
|
32
|
+
"author": "Appkit Studio",
|
|
33
|
+
"license": "MIT",
|
|
34
|
+
"repository": {
|
|
35
|
+
"type": "git",
|
|
36
|
+
"url": "git+https://github.com/appkitstudio/llamacpp-cli.git"
|
|
37
|
+
},
|
|
38
|
+
"homepage": "https://github.com/appkitstudio/llamacpp-cli#readme",
|
|
39
|
+
"bugs": {
|
|
40
|
+
"url": "https://github.com/appkitstudio/llamacpp-cli/issues"
|
|
41
|
+
},
|
|
42
|
+
"dependencies": {
|
|
43
|
+
"chalk": "^5.3.0",
|
|
44
|
+
"cli-table3": "^0.6.5",
|
|
45
|
+
"commander": "^13.0.0"
|
|
46
|
+
},
|
|
47
|
+
"devDependencies": {
|
|
48
|
+
"@types/node": "^20.12.7",
|
|
49
|
+
"commit-and-tag-version": "^12.6.1",
|
|
50
|
+
"tsx": "^4.7.2",
|
|
51
|
+
"typescript": "^5.4.5"
|
|
52
|
+
},
|
|
53
|
+
"engines": {
|
|
54
|
+
"node": ">=18.0.0"
|
|
55
|
+
}
|
|
56
|
+
}
|
package/src/cli.ts
ADDED
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { Command } from 'commander';
|
|
4
|
+
import chalk from 'chalk';
|
|
5
|
+
import { listCommand } from './commands/list';
|
|
6
|
+
import { psCommand } from './commands/ps';
|
|
7
|
+
import { startCommand } from './commands/start';
|
|
8
|
+
import { runCommand } from './commands/run';
|
|
9
|
+
import { stopCommand } from './commands/stop';
|
|
10
|
+
import { deleteCommand } from './commands/delete';
|
|
11
|
+
import { pullCommand } from './commands/pull';
|
|
12
|
+
import { rmCommand } from './commands/rm';
|
|
13
|
+
import { logsCommand } from './commands/logs';
|
|
14
|
+
import { searchCommand } from './commands/search';
|
|
15
|
+
import { showCommand } from './commands/show';
|
|
16
|
+
|
|
17
|
+
const program = new Command();
|
|
18
|
+
|
|
19
|
+
program
|
|
20
|
+
.name('llamacpp')
|
|
21
|
+
.description('CLI tool to manage local llama.cpp servers on macOS')
|
|
22
|
+
.version('1.0.0');
|
|
23
|
+
|
|
24
|
+
// List models
|
|
25
|
+
program
|
|
26
|
+
.command('ls')
|
|
27
|
+
.description('List available GGUF models in ~/models')
|
|
28
|
+
.action(async () => {
|
|
29
|
+
try {
|
|
30
|
+
await listCommand();
|
|
31
|
+
} catch (error) {
|
|
32
|
+
console.error(chalk.red('❌ Error:'), (error as Error).message);
|
|
33
|
+
process.exit(1);
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
// List running servers
|
|
38
|
+
program
|
|
39
|
+
.command('ps')
|
|
40
|
+
.description('List all servers with status')
|
|
41
|
+
.action(async () => {
|
|
42
|
+
try {
|
|
43
|
+
await psCommand();
|
|
44
|
+
} catch (error) {
|
|
45
|
+
console.error(chalk.red('❌ Error:'), (error as Error).message);
|
|
46
|
+
process.exit(1);
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
// Search for models
|
|
51
|
+
program
|
|
52
|
+
.command('search')
|
|
53
|
+
.description('Search Hugging Face for GGUF models')
|
|
54
|
+
.argument('<query>', 'Search query (e.g., "llama 3b" or "qwen")')
|
|
55
|
+
.option('-l, --limit <number>', 'Max results to show (default: 20)', parseInt)
|
|
56
|
+
.option('--files [number]', 'Show available files for result number (e.g., --files 1)', (val) => {
|
|
57
|
+
return val ? parseInt(val) : true;
|
|
58
|
+
})
|
|
59
|
+
.action(async (query: string, options) => {
|
|
60
|
+
try {
|
|
61
|
+
await searchCommand(query, options);
|
|
62
|
+
} catch (error) {
|
|
63
|
+
console.error(chalk.red('❌ Error:'), (error as Error).message);
|
|
64
|
+
process.exit(1);
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
// Show model details
|
|
69
|
+
program
|
|
70
|
+
.command('show')
|
|
71
|
+
.description('Show details about a model or file')
|
|
72
|
+
.argument('<identifier>', 'HuggingFace repo/file (e.g., owner/repo or owner/repo/file.gguf)')
|
|
73
|
+
.option('-f, --file <filename>', 'Specific GGUF file to show details for')
|
|
74
|
+
.action(async (identifier: string, options) => {
|
|
75
|
+
try {
|
|
76
|
+
await showCommand(identifier, options);
|
|
77
|
+
} catch (error) {
|
|
78
|
+
console.error(chalk.red('❌ Error:'), (error as Error).message);
|
|
79
|
+
process.exit(1);
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
// Download a model
|
|
84
|
+
program
|
|
85
|
+
.command('pull')
|
|
86
|
+
.description('Download a GGUF model from Hugging Face')
|
|
87
|
+
.argument('<identifier>', 'HuggingFace repo/file (e.g., owner/repo/file.gguf or owner/repo)')
|
|
88
|
+
.option('-f, --file <filename>', 'Specific GGUF file (alternative to path in identifier)')
|
|
89
|
+
.action(async (identifier: string, options) => {
|
|
90
|
+
try {
|
|
91
|
+
await pullCommand(identifier, options);
|
|
92
|
+
} catch (error) {
|
|
93
|
+
console.error(chalk.red('❌ Error:'), (error as Error).message);
|
|
94
|
+
process.exit(1);
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
// Delete a model
|
|
99
|
+
program
|
|
100
|
+
.command('rm')
|
|
101
|
+
.description('Delete a model file (and any associated servers)')
|
|
102
|
+
.argument('<model>', 'Model filename or partial name')
|
|
103
|
+
.action(async (model: string) => {
|
|
104
|
+
try {
|
|
105
|
+
await rmCommand(model);
|
|
106
|
+
} catch (error) {
|
|
107
|
+
console.error(chalk.red('❌ Error:'), (error as Error).message);
|
|
108
|
+
process.exit(1);
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
// Server management commands
|
|
113
|
+
const server = program
|
|
114
|
+
.command('server')
|
|
115
|
+
.description('Manage llama-server instances');
|
|
116
|
+
|
|
117
|
+
// Start a server
|
|
118
|
+
server
|
|
119
|
+
.command('start')
|
|
120
|
+
.description('Start a llama-server instance')
|
|
121
|
+
.argument('<model>', 'Model filename or path')
|
|
122
|
+
.option('-p, --port <number>', 'Port number (default: auto-assign)', parseInt)
|
|
123
|
+
.option('-t, --threads <number>', 'Thread count (default: auto)', parseInt)
|
|
124
|
+
.option('-c, --ctx-size <number>', 'Context size (default: auto)', parseInt)
|
|
125
|
+
.option('-g, --gpu-layers <number>', 'GPU layers (default: 60)', parseInt)
|
|
126
|
+
.action(async (model: string, options) => {
|
|
127
|
+
try {
|
|
128
|
+
await startCommand(model, options);
|
|
129
|
+
} catch (error) {
|
|
130
|
+
console.error(chalk.red('❌ Error:'), (error as Error).message);
|
|
131
|
+
process.exit(1);
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
// Run interactive chat with a model
|
|
136
|
+
server
|
|
137
|
+
.command('run')
|
|
138
|
+
.description('Run an interactive chat session with a model')
|
|
139
|
+
.argument('<model>', 'Model identifier: port (9000), server ID (llama-3-2-3b), partial name, or model filename')
|
|
140
|
+
.action(async (model: string) => {
|
|
141
|
+
try {
|
|
142
|
+
await runCommand(model);
|
|
143
|
+
} catch (error) {
|
|
144
|
+
console.error(chalk.red('❌ Error:'), (error as Error).message);
|
|
145
|
+
process.exit(1);
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
// Stop a server
|
|
150
|
+
server
|
|
151
|
+
.command('stop')
|
|
152
|
+
.description('Stop a running server')
|
|
153
|
+
.argument('<identifier>', 'Server identifier: port (9000), server ID (llama-3-2-3b), or partial model name')
|
|
154
|
+
.action(async (identifier: string) => {
|
|
155
|
+
try {
|
|
156
|
+
await stopCommand(identifier);
|
|
157
|
+
} catch (error) {
|
|
158
|
+
console.error(chalk.red('❌ Error:'), (error as Error).message);
|
|
159
|
+
process.exit(1);
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
// Delete a server
|
|
164
|
+
server
|
|
165
|
+
.command('rm')
|
|
166
|
+
.description('Remove a server configuration and launchctl service (preserves model file)')
|
|
167
|
+
.argument('<identifier>', 'Server identifier: port (9000), server ID (llama-3-2-3b), or partial model name')
|
|
168
|
+
.action(async (identifier: string) => {
|
|
169
|
+
try {
|
|
170
|
+
await deleteCommand(identifier);
|
|
171
|
+
} catch (error) {
|
|
172
|
+
console.error(chalk.red('❌ Error:'), (error as Error).message);
|
|
173
|
+
process.exit(1);
|
|
174
|
+
}
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
// View logs
|
|
178
|
+
server
|
|
179
|
+
.command('logs')
|
|
180
|
+
.description('View server logs')
|
|
181
|
+
.argument('<identifier>', 'Server identifier: port (9000), server ID (llama-3-2-3b), or partial model name')
|
|
182
|
+
.option('-f, --follow', 'Follow log output in real-time')
|
|
183
|
+
.option('-n, --lines <number>', 'Number of lines to show (default: 50)', parseInt)
|
|
184
|
+
.option('--errors', 'Show stderr instead of stdout')
|
|
185
|
+
.action(async (identifier: string, options) => {
|
|
186
|
+
try {
|
|
187
|
+
await logsCommand(identifier, options);
|
|
188
|
+
} catch (error) {
|
|
189
|
+
console.error(chalk.red('❌ Error:'), (error as Error).message);
|
|
190
|
+
process.exit(1);
|
|
191
|
+
}
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
// Parse arguments
|
|
195
|
+
program.parse();
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import * as readline from 'readline';
|
|
3
|
+
import { stateManager } from '../lib/state-manager';
|
|
4
|
+
import { launchctlManager } from '../lib/launchctl-manager';
|
|
5
|
+
|
|
6
|
+
export async function deleteCommand(identifier: string): Promise<void> {
|
|
7
|
+
// Find server
|
|
8
|
+
const server = await stateManager.findServer(identifier);
|
|
9
|
+
if (!server) {
|
|
10
|
+
throw new Error(`Server not found: ${identifier}\n\nUse: llamacpp ps`);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// Confirm deletion
|
|
14
|
+
console.log(chalk.yellow(`⚠️ Delete server configuration for ${server.modelName}?`));
|
|
15
|
+
console.log(chalk.dim(' This will remove the launchd service but keep the model file.'));
|
|
16
|
+
console.log();
|
|
17
|
+
|
|
18
|
+
const confirmed = await confirmDeletion();
|
|
19
|
+
if (!confirmed) {
|
|
20
|
+
console.log(chalk.dim('Cancelled'));
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
console.log();
|
|
25
|
+
console.log(chalk.blue(`🗑️ Deleting server ${server.modelName}...`));
|
|
26
|
+
|
|
27
|
+
// Stop server if running
|
|
28
|
+
if (server.status === 'running') {
|
|
29
|
+
console.log(chalk.dim('Stopping server...'));
|
|
30
|
+
try {
|
|
31
|
+
await launchctlManager.stopService(server.label);
|
|
32
|
+
await launchctlManager.waitForServiceStop(server.label, 5000);
|
|
33
|
+
} catch (error) {
|
|
34
|
+
console.log(chalk.yellow('⚠️ Failed to stop server gracefully'));
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Unload service
|
|
39
|
+
console.log(chalk.dim('Unloading launchctl service...'));
|
|
40
|
+
await launchctlManager.unloadService(server.plistPath);
|
|
41
|
+
|
|
42
|
+
// Delete plist
|
|
43
|
+
console.log(chalk.dim('Deleting plist file...'));
|
|
44
|
+
await launchctlManager.deletePlist(server.plistPath);
|
|
45
|
+
|
|
46
|
+
// Delete server config
|
|
47
|
+
console.log(chalk.dim('Deleting server configuration...'));
|
|
48
|
+
await stateManager.deleteServerConfig(server.id);
|
|
49
|
+
|
|
50
|
+
// Success
|
|
51
|
+
console.log();
|
|
52
|
+
console.log(chalk.green('✅ Server deleted'));
|
|
53
|
+
console.log(chalk.dim(` Plist removed: ${server.plistPath}`));
|
|
54
|
+
console.log(chalk.dim(` Config removed`));
|
|
55
|
+
console.log();
|
|
56
|
+
console.log(chalk.dim(` Model file preserved at: ${server.modelPath}`));
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Prompt user for confirmation
|
|
61
|
+
*/
|
|
62
|
+
function confirmDeletion(): Promise<boolean> {
|
|
63
|
+
return new Promise((resolve) => {
|
|
64
|
+
const rl = readline.createInterface({
|
|
65
|
+
input: process.stdin,
|
|
66
|
+
output: process.stdout,
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
rl.question(chalk.yellow(" Type 'yes' to confirm: "), (answer) => {
|
|
70
|
+
rl.close();
|
|
71
|
+
resolve(answer.toLowerCase() === 'yes');
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import Table from 'cli-table3';
|
|
3
|
+
import { modelScanner } from '../lib/model-scanner';
|
|
4
|
+
import { formatBytes, formatDateShort } from '../utils/format-utils';
|
|
5
|
+
import { getModelsDir } from '../utils/file-utils';
|
|
6
|
+
|
|
7
|
+
export async function listCommand(): Promise<void> {
|
|
8
|
+
const modelsDir = getModelsDir();
|
|
9
|
+
console.log(chalk.blue(`📦 Available models in ${modelsDir}\n`));
|
|
10
|
+
|
|
11
|
+
const models = await modelScanner.scanModels();
|
|
12
|
+
|
|
13
|
+
if (models.length === 0) {
|
|
14
|
+
console.log(chalk.yellow('No GGUF models found.'));
|
|
15
|
+
console.log(chalk.dim(`\nDownload models with: llamacpp pull <repo> --file <filename>`));
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const table = new Table({
|
|
20
|
+
head: ['MODEL', 'SIZE', 'MODIFIED'],
|
|
21
|
+
colWidths: [50, 12, 15],
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
for (const model of models) {
|
|
25
|
+
table.push([
|
|
26
|
+
model.filename,
|
|
27
|
+
model.sizeFormatted,
|
|
28
|
+
formatDateShort(model.modified),
|
|
29
|
+
]);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
console.log(table.toString());
|
|
33
|
+
|
|
34
|
+
const totalSize = models.reduce((sum, m) => sum + m.size, 0);
|
|
35
|
+
console.log(chalk.dim(`\nTotal: ${models.length} models (${formatBytes(totalSize)})`));
|
|
36
|
+
console.log(chalk.dim(`\nStart a server: llamacpp start <model-filename>`));
|
|
37
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { spawn } from 'child_process';
|
|
3
|
+
import { stateManager } from '../lib/state-manager';
|
|
4
|
+
import { fileExists } from '../utils/file-utils';
|
|
5
|
+
import { execCommand } from '../utils/process-utils';
|
|
6
|
+
|
|
7
|
+
interface LogsOptions {
|
|
8
|
+
follow?: boolean;
|
|
9
|
+
lines?: number;
|
|
10
|
+
errors?: boolean;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export async function logsCommand(identifier: string, options: LogsOptions): Promise<void> {
|
|
14
|
+
// Find server
|
|
15
|
+
const server = await stateManager.findServer(identifier);
|
|
16
|
+
if (!server) {
|
|
17
|
+
throw new Error(`Server not found: ${identifier}\n\nUse: llamacpp ps`);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Determine log file
|
|
21
|
+
const logPath = options.errors ? server.stderrPath : server.stdoutPath;
|
|
22
|
+
const logType = options.errors ? 'errors' : 'logs';
|
|
23
|
+
|
|
24
|
+
// Check if log file exists
|
|
25
|
+
if (!(await fileExists(logPath))) {
|
|
26
|
+
console.log(chalk.yellow(`⚠️ No ${logType} found for ${server.modelName}`));
|
|
27
|
+
console.log(chalk.dim(` Log file does not exist: ${logPath}`));
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
console.log(chalk.blue(`📋 ${options.errors ? 'Errors' : 'Logs'} for ${server.modelName}`));
|
|
32
|
+
console.log(chalk.dim(` ${logPath}\n`));
|
|
33
|
+
|
|
34
|
+
if (options.follow) {
|
|
35
|
+
// Follow logs in real-time
|
|
36
|
+
const tail = spawn('tail', ['-f', logPath], {
|
|
37
|
+
stdio: 'inherit',
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
// Handle Ctrl+C gracefully
|
|
41
|
+
process.on('SIGINT', () => {
|
|
42
|
+
tail.kill();
|
|
43
|
+
console.log();
|
|
44
|
+
process.exit(0);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
// Wait for tail to exit
|
|
48
|
+
tail.on('exit', () => {
|
|
49
|
+
process.exit(0);
|
|
50
|
+
});
|
|
51
|
+
} else {
|
|
52
|
+
// Show last N lines
|
|
53
|
+
const lines = options.lines || 50;
|
|
54
|
+
try {
|
|
55
|
+
const output = await execCommand(`tail -n ${lines} "${logPath}"`);
|
|
56
|
+
console.log(output);
|
|
57
|
+
} catch (error) {
|
|
58
|
+
throw new Error(`Failed to read logs: ${(error as Error).message}`);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import Table from 'cli-table3';
|
|
3
|
+
import { stateManager } from '../lib/state-manager';
|
|
4
|
+
import { statusChecker } from '../lib/status-checker';
|
|
5
|
+
import { formatUptime } from '../utils/format-utils';
|
|
6
|
+
|
|
7
|
+
export async function psCommand(): Promise<void> {
|
|
8
|
+
const servers = await stateManager.getAllServers();
|
|
9
|
+
|
|
10
|
+
if (servers.length === 0) {
|
|
11
|
+
console.log(chalk.yellow('No servers configured.'));
|
|
12
|
+
console.log(chalk.dim('\nStart a server: llamacpp server start <model-filename>'));
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Update all server statuses
|
|
17
|
+
console.log(chalk.dim('Checking server statuses...\n'));
|
|
18
|
+
const updated = await statusChecker.updateAllServerStatuses();
|
|
19
|
+
|
|
20
|
+
const table = new Table({
|
|
21
|
+
head: ['SERVER ID', 'MODEL', 'PORT', 'STATUS', 'PID', 'UPTIME'],
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
let runningCount = 0;
|
|
25
|
+
let stoppedCount = 0;
|
|
26
|
+
let crashedCount = 0;
|
|
27
|
+
|
|
28
|
+
for (const server of updated) {
|
|
29
|
+
let statusText: string;
|
|
30
|
+
let statusColor: (text: string) => string;
|
|
31
|
+
|
|
32
|
+
switch (server.status) {
|
|
33
|
+
case 'running':
|
|
34
|
+
statusText = '✅ RUNNING';
|
|
35
|
+
statusColor = chalk.green;
|
|
36
|
+
runningCount++;
|
|
37
|
+
break;
|
|
38
|
+
case 'crashed':
|
|
39
|
+
statusText = '❌ CRASHED';
|
|
40
|
+
statusColor = chalk.red;
|
|
41
|
+
crashedCount++;
|
|
42
|
+
break;
|
|
43
|
+
default:
|
|
44
|
+
statusText = '⚠️ STOPPED';
|
|
45
|
+
statusColor = chalk.yellow;
|
|
46
|
+
stoppedCount++;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const uptime =
|
|
50
|
+
server.status === 'running' && server.lastStarted
|
|
51
|
+
? formatUptime(server.lastStarted)
|
|
52
|
+
: '-';
|
|
53
|
+
|
|
54
|
+
table.push([
|
|
55
|
+
server.id,
|
|
56
|
+
server.modelName,
|
|
57
|
+
server.port.toString(),
|
|
58
|
+
statusColor(statusText),
|
|
59
|
+
server.pid?.toString() || '-',
|
|
60
|
+
uptime,
|
|
61
|
+
]);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
console.log(table.toString());
|
|
65
|
+
|
|
66
|
+
const summary = [
|
|
67
|
+
chalk.green(`${runningCount} running`),
|
|
68
|
+
chalk.yellow(`${stoppedCount} stopped`),
|
|
69
|
+
];
|
|
70
|
+
if (crashedCount > 0) {
|
|
71
|
+
summary.push(chalk.red(`${crashedCount} crashed`));
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
console.log(chalk.dim(`\nTotal: ${servers.length} servers (${summary.join(', ')})`));
|
|
75
|
+
|
|
76
|
+
if (crashedCount > 0) {
|
|
77
|
+
console.log(chalk.red('\n⚠️ Some servers have crashed. Check logs with: llamacpp server logs <id> --errors'));
|
|
78
|
+
}
|
|
79
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { modelDownloader } from '../lib/model-downloader';
|
|
3
|
+
|
|
4
|
+
interface PullOptions {
|
|
5
|
+
file?: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export async function pullCommand(identifier: string, options: PullOptions): Promise<void> {
|
|
9
|
+
// Parse repository identifier
|
|
10
|
+
const parsed = modelDownloader.parseHFIdentifier(identifier);
|
|
11
|
+
|
|
12
|
+
// Determine filename - from --file flag or from identifier path
|
|
13
|
+
let filename = options.file || parsed.file;
|
|
14
|
+
|
|
15
|
+
if (!filename) {
|
|
16
|
+
throw new Error(
|
|
17
|
+
'Please specify a file to download:\n\n' +
|
|
18
|
+
'Option 1: llamacpp pull owner/repo/filename.gguf\n' +
|
|
19
|
+
'Option 2: llamacpp pull owner/repo --file filename.gguf'
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Ensure filename ends with .gguf
|
|
24
|
+
if (!filename.toLowerCase().endsWith('.gguf')) {
|
|
25
|
+
filename += '.gguf';
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Download the model
|
|
29
|
+
try {
|
|
30
|
+
const modelPath = await modelDownloader.downloadModel(parsed.repo, filename);
|
|
31
|
+
|
|
32
|
+
console.log();
|
|
33
|
+
console.log(chalk.dim(`Start server: llamacpp start ${filename}`));
|
|
34
|
+
} catch (error) {
|
|
35
|
+
if ((error as Error).message.includes('interrupted')) {
|
|
36
|
+
console.log(chalk.dim('\nDownload was interrupted. Run the same command again to retry.'));
|
|
37
|
+
}
|
|
38
|
+
throw error;
|
|
39
|
+
}
|
|
40
|
+
}
|