rackmind-cli 0.1.2
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/README.md +179 -0
- package/bin/rackmind.js +6 -0
- package/dist/ai/client.d.ts +71 -0
- package/dist/ai/client.d.ts.map +1 -0
- package/dist/ai/client.js +198 -0
- package/dist/ai/client.js.map +1 -0
- package/dist/ai/system-prompt.d.ts +20 -0
- package/dist/ai/system-prompt.d.ts.map +1 -0
- package/dist/ai/system-prompt.js +169 -0
- package/dist/ai/system-prompt.js.map +1 -0
- package/dist/ai/tool-executor.d.ts +27 -0
- package/dist/ai/tool-executor.d.ts.map +1 -0
- package/dist/ai/tool-executor.js +184 -0
- package/dist/ai/tool-executor.js.map +1 -0
- package/dist/ai/tools.d.ts +7 -0
- package/dist/ai/tools.d.ts.map +1 -0
- package/dist/ai/tools.js +203 -0
- package/dist/ai/tools.js.map +1 -0
- package/dist/commands/config.d.ts +21 -0
- package/dist/commands/config.d.ts.map +1 -0
- package/dist/commands/config.js +169 -0
- package/dist/commands/config.js.map +1 -0
- package/dist/commands/connect.d.ts +7 -0
- package/dist/commands/connect.d.ts.map +1 -0
- package/dist/commands/connect.js +117 -0
- package/dist/commands/connect.js.map +1 -0
- package/dist/commands/containers.d.ts +5 -0
- package/dist/commands/containers.d.ts.map +1 -0
- package/dist/commands/containers.js +83 -0
- package/dist/commands/containers.js.map +1 -0
- package/dist/commands/exec.d.ts +5 -0
- package/dist/commands/exec.d.ts.map +1 -0
- package/dist/commands/exec.js +133 -0
- package/dist/commands/exec.js.map +1 -0
- package/dist/commands/lifecycle.d.ts +7 -0
- package/dist/commands/lifecycle.d.ts.map +1 -0
- package/dist/commands/lifecycle.js +213 -0
- package/dist/commands/lifecycle.js.map +1 -0
- package/dist/commands/logs.d.ts +5 -0
- package/dist/commands/logs.d.ts.map +1 -0
- package/dist/commands/logs.js +117 -0
- package/dist/commands/logs.js.map +1 -0
- package/dist/commands/report.d.ts +6 -0
- package/dist/commands/report.d.ts.map +1 -0
- package/dist/commands/report.js +203 -0
- package/dist/commands/report.js.map +1 -0
- package/dist/commands/servers.d.ts +17 -0
- package/dist/commands/servers.d.ts.map +1 -0
- package/dist/commands/servers.js +116 -0
- package/dist/commands/servers.js.map +1 -0
- package/dist/commands/status.d.ts +5 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +174 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/vms.d.ts +5 -0
- package/dist/commands/vms.d.ts.map +1 -0
- package/dist/commands/vms.js +83 -0
- package/dist/commands/vms.js.map +1 -0
- package/dist/config/crypto.d.ts +20 -0
- package/dist/config/crypto.d.ts.map +1 -0
- package/dist/config/crypto.js +78 -0
- package/dist/config/crypto.js.map +1 -0
- package/dist/config/index.d.ts +21 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +158 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/types.d.ts +263 -0
- package/dist/config/types.d.ts.map +1 -0
- package/dist/config/types.js +83 -0
- package/dist/config/types.js.map +1 -0
- package/dist/globals.d.ts +22 -0
- package/dist/globals.d.ts.map +1 -0
- package/dist/globals.js +43 -0
- package/dist/globals.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +399 -0
- package/dist/index.js.map +1 -0
- package/dist/interactive/App.d.ts +3 -0
- package/dist/interactive/App.d.ts.map +1 -0
- package/dist/interactive/App.js +103 -0
- package/dist/interactive/App.js.map +1 -0
- package/dist/interactive/components/Header.d.ts +8 -0
- package/dist/interactive/components/Header.d.ts.map +1 -0
- package/dist/interactive/components/Header.js +20 -0
- package/dist/interactive/components/Header.js.map +1 -0
- package/dist/interactive/components/InputBar.d.ts +9 -0
- package/dist/interactive/components/InputBar.d.ts.map +1 -0
- package/dist/interactive/components/InputBar.js +89 -0
- package/dist/interactive/components/InputBar.js.map +1 -0
- package/dist/interactive/components/MessageList.d.ts +9 -0
- package/dist/interactive/components/MessageList.d.ts.map +1 -0
- package/dist/interactive/components/MessageList.js +28 -0
- package/dist/interactive/components/MessageList.js.map +1 -0
- package/dist/interactive/components/Spinner.d.ts +7 -0
- package/dist/interactive/components/Spinner.d.ts.map +1 -0
- package/dist/interactive/components/Spinner.js +19 -0
- package/dist/interactive/components/Spinner.js.map +1 -0
- package/dist/interactive/components/StatusBar.d.ts +10 -0
- package/dist/interactive/components/StatusBar.d.ts.map +1 -0
- package/dist/interactive/components/StatusBar.js +7 -0
- package/dist/interactive/components/StatusBar.js.map +1 -0
- package/dist/interactive/components/StreamingMessage.d.ts +7 -0
- package/dist/interactive/components/StreamingMessage.d.ts.map +1 -0
- package/dist/interactive/components/StreamingMessage.js +9 -0
- package/dist/interactive/components/StreamingMessage.js.map +1 -0
- package/dist/interactive/components/ToolIndicator.d.ts +7 -0
- package/dist/interactive/components/ToolIndicator.d.ts.map +1 -0
- package/dist/interactive/components/ToolIndicator.js +10 -0
- package/dist/interactive/components/ToolIndicator.js.map +1 -0
- package/dist/interactive/components/ToolOutput.d.ts +8 -0
- package/dist/interactive/components/ToolOutput.d.ts.map +1 -0
- package/dist/interactive/components/ToolOutput.js +9 -0
- package/dist/interactive/components/ToolOutput.js.map +1 -0
- package/dist/interactive/components/WelcomeScreen.d.ts +9 -0
- package/dist/interactive/components/WelcomeScreen.d.ts.map +1 -0
- package/dist/interactive/components/WelcomeScreen.js +8 -0
- package/dist/interactive/components/WelcomeScreen.js.map +1 -0
- package/dist/interactive/launch.d.ts +6 -0
- package/dist/interactive/launch.d.ts.map +1 -0
- package/dist/interactive/launch.js +30 -0
- package/dist/interactive/launch.js.map +1 -0
- package/dist/interactive/slashCommands.d.ts +13 -0
- package/dist/interactive/slashCommands.d.ts.map +1 -0
- package/dist/interactive/slashCommands.js +102 -0
- package/dist/interactive/slashCommands.js.map +1 -0
- package/dist/interactive/useRackMind.d.ts +30 -0
- package/dist/interactive/useRackMind.d.ts.map +1 -0
- package/dist/interactive/useRackMind.js +241 -0
- package/dist/interactive/useRackMind.js.map +1 -0
- package/dist/oneshot/run.d.ts +2 -0
- package/dist/oneshot/run.d.ts.map +1 -0
- package/dist/oneshot/run.js +195 -0
- package/dist/oneshot/run.js.map +1 -0
- package/dist/server/client.d.ts +89 -0
- package/dist/server/client.d.ts.map +1 -0
- package/dist/server/client.js +239 -0
- package/dist/server/client.js.map +1 -0
- package/dist/server/proxmox.d.ts +160 -0
- package/dist/server/proxmox.d.ts.map +1 -0
- package/dist/server/proxmox.js +219 -0
- package/dist/server/proxmox.js.map +1 -0
- package/dist/server/ssh.d.ts +80 -0
- package/dist/server/ssh.d.ts.map +1 -0
- package/dist/server/ssh.js +262 -0
- package/dist/server/ssh.js.map +1 -0
- package/dist/utils/ascii.d.ts +3 -0
- package/dist/utils/ascii.d.ts.map +1 -0
- package/dist/utils/ascii.js +16 -0
- package/dist/utils/ascii.js.map +1 -0
- package/dist/utils/format.d.ts +7 -0
- package/dist/utils/format.d.ts.map +1 -0
- package/dist/utils/format.js +20 -0
- package/dist/utils/format.js.map +1 -0
- package/dist/utils/history.d.ts +23 -0
- package/dist/utils/history.d.ts.map +1 -0
- package/dist/utils/history.js +107 -0
- package/dist/utils/history.js.map +1 -0
- package/dist/utils/logger.d.ts +9 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +51 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/markdown.d.ts +12 -0
- package/dist/utils/markdown.d.ts.map +1 -0
- package/dist/utils/markdown.js +76 -0
- package/dist/utils/markdown.js.map +1 -0
- package/dist/utils/prompt.d.ts +25 -0
- package/dist/utils/prompt.d.ts.map +1 -0
- package/dist/utils/prompt.js +133 -0
- package/dist/utils/prompt.js.map +1 -0
- package/dist/utils/retry.d.ts +37 -0
- package/dist/utils/retry.d.ts.map +1 -0
- package/dist/utils/retry.js +122 -0
- package/dist/utils/retry.js.map +1 -0
- package/dist/utils/shutdown.d.ts +22 -0
- package/dist/utils/shutdown.d.ts.map +1 -0
- package/dist/utils/shutdown.js +94 -0
- package/dist/utils/shutdown.js.map +1 -0
- package/dist/utils/table.d.ts +38 -0
- package/dist/utils/table.d.ts.map +1 -0
- package/dist/utils/table.js +150 -0
- package/dist/utils/table.js.map +1 -0
- package/package.json +71 -0
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// rackmind status — show active profile, connection info, and live status
|
|
3
|
+
// ---------------------------------------------------------------------------
|
|
4
|
+
// Tests Proxmox API and SSH connectivity in real-time, then displays
|
|
5
|
+
// a summary with connection status, node info, and preferences.
|
|
6
|
+
// ---------------------------------------------------------------------------
|
|
7
|
+
import chalk from 'chalk';
|
|
8
|
+
import Table from 'cli-table3';
|
|
9
|
+
import { readConfig, configExists, getConfigPath, listServerProfiles, getAnthropicApiKey, getActiveProfile, } from '../config/index.js';
|
|
10
|
+
import { ServerClient } from '../server/client.js';
|
|
11
|
+
import { formatKeyValue, formatDim, formatWarning, formatSuccess } from '../utils/format.js';
|
|
12
|
+
import { logger } from '../utils/logger.js';
|
|
13
|
+
import { isJsonMode } from '../globals.js';
|
|
14
|
+
/**
|
|
15
|
+
* Show current configuration status and test live connections.
|
|
16
|
+
*/
|
|
17
|
+
export async function statusCommand() {
|
|
18
|
+
if (!configExists()) {
|
|
19
|
+
if (isJsonMode()) {
|
|
20
|
+
process.stdout.write(JSON.stringify({ error: 'No config found' }, null, 2) + '\n');
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
process.stderr.write(formatWarning('No config found. Run `rackmind connect <alias>` to set up a server.\n'));
|
|
24
|
+
process.stderr.write(formatDim(` Config path: ${getConfigPath()}\n`));
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
const config = readConfig();
|
|
28
|
+
const profiles = listServerProfiles();
|
|
29
|
+
const hasApiKey = Boolean(getAnthropicApiKey());
|
|
30
|
+
// JSON output mode — emit structured data and return
|
|
31
|
+
if (isJsonMode()) {
|
|
32
|
+
const statusJson = {
|
|
33
|
+
configPath: getConfigPath(),
|
|
34
|
+
version: config.version,
|
|
35
|
+
apiKeyConfigured: hasApiKey,
|
|
36
|
+
activeServer: config.activeServer,
|
|
37
|
+
servers: profiles.map((p) => ({
|
|
38
|
+
alias: p.alias,
|
|
39
|
+
platform: p.platform,
|
|
40
|
+
host: p.host,
|
|
41
|
+
port: p.port,
|
|
42
|
+
active: p.alias === config.activeServer,
|
|
43
|
+
})),
|
|
44
|
+
preferences: {
|
|
45
|
+
model: config.preferences.model,
|
|
46
|
+
theme: config.preferences.theme,
|
|
47
|
+
confirmDestructive: config.preferences.confirmDestructive,
|
|
48
|
+
timestampMessages: config.preferences.timestampMessages,
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
process.stdout.write(JSON.stringify(statusJson, null, 2) + '\n');
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
// -- Header --------------------------------------------------------------
|
|
55
|
+
process.stderr.write(chalk.cyan.bold('\nRackMind Status\n'));
|
|
56
|
+
process.stderr.write(chalk.dim('─'.repeat(50) + '\n\n'));
|
|
57
|
+
// -- Config info ---------------------------------------------------------
|
|
58
|
+
process.stderr.write(formatKeyValue('Config', getConfigPath()) + '\n');
|
|
59
|
+
process.stderr.write(formatKeyValue('Version', config.version) + '\n');
|
|
60
|
+
process.stderr.write(formatKeyValue('Anthropic API Key', hasApiKey ? chalk.green('configured') : chalk.yellow('not set')) + '\n');
|
|
61
|
+
process.stderr.write('\n');
|
|
62
|
+
// -- Active server -------------------------------------------------------
|
|
63
|
+
if (config.activeServer) {
|
|
64
|
+
const active = config.servers[config.activeServer];
|
|
65
|
+
if (active) {
|
|
66
|
+
process.stderr.write(chalk.bold('Active Server\n'));
|
|
67
|
+
process.stderr.write(formatKeyValue(' Alias', chalk.green(active.alias)) + '\n');
|
|
68
|
+
process.stderr.write(formatKeyValue(' Platform', active.platform) + '\n');
|
|
69
|
+
process.stderr.write(formatKeyValue(' Host', `${active.host}:${active.port}`) + '\n');
|
|
70
|
+
if (active.apiUser) {
|
|
71
|
+
process.stderr.write(formatKeyValue(' API User', active.apiUser) + '\n');
|
|
72
|
+
}
|
|
73
|
+
if (active.sshHost) {
|
|
74
|
+
process.stderr.write(formatKeyValue(' SSH', `${active.sshUsername}@${active.sshHost}:${active.sshPort}`) + '\n');
|
|
75
|
+
}
|
|
76
|
+
process.stderr.write('\n');
|
|
77
|
+
// -- Live connection test ----------------------------------------
|
|
78
|
+
await testLiveConnection();
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
process.stderr.write(formatWarning('No active server. Run `rackmind connect <alias>` to connect.\n\n'));
|
|
83
|
+
}
|
|
84
|
+
// -- Server list ---------------------------------------------------------
|
|
85
|
+
if (profiles.length > 0) {
|
|
86
|
+
process.stderr.write(chalk.bold(`Servers (${profiles.length})\n`));
|
|
87
|
+
const table = new Table({
|
|
88
|
+
head: ['Alias', 'Platform', 'Host', 'SSH', 'Active'],
|
|
89
|
+
style: { head: ['cyan'] },
|
|
90
|
+
chars: {
|
|
91
|
+
top: '─',
|
|
92
|
+
'top-mid': '┬',
|
|
93
|
+
'top-left': '┌',
|
|
94
|
+
'top-right': '┐',
|
|
95
|
+
bottom: '─',
|
|
96
|
+
'bottom-mid': '┴',
|
|
97
|
+
'bottom-left': '└',
|
|
98
|
+
'bottom-right': '┘',
|
|
99
|
+
left: '│',
|
|
100
|
+
'left-mid': '├',
|
|
101
|
+
mid: '─',
|
|
102
|
+
'mid-mid': '┼',
|
|
103
|
+
right: '│',
|
|
104
|
+
'right-mid': '┤',
|
|
105
|
+
middle: '│',
|
|
106
|
+
},
|
|
107
|
+
});
|
|
108
|
+
for (const profile of profiles) {
|
|
109
|
+
const isActive = profile.alias === config.activeServer;
|
|
110
|
+
const sshInfo = profile.sshHost
|
|
111
|
+
? `${profile.sshUsername}@${profile.sshHost}:${profile.sshPort}`
|
|
112
|
+
: chalk.dim('--');
|
|
113
|
+
table.push([
|
|
114
|
+
isActive ? chalk.green(profile.alias) : profile.alias,
|
|
115
|
+
profile.platform,
|
|
116
|
+
`${profile.host}:${profile.port}`,
|
|
117
|
+
sshInfo,
|
|
118
|
+
isActive ? chalk.green('*') : '',
|
|
119
|
+
]);
|
|
120
|
+
}
|
|
121
|
+
process.stderr.write(table.toString() + '\n\n');
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
process.stderr.write(formatDim('No servers configured.\n\n'));
|
|
125
|
+
}
|
|
126
|
+
// -- Preferences ---------------------------------------------------------
|
|
127
|
+
process.stderr.write(chalk.bold('Preferences\n'));
|
|
128
|
+
process.stderr.write(formatKeyValue(' Model', config.preferences.model) + '\n');
|
|
129
|
+
process.stderr.write(formatKeyValue(' Theme', config.preferences.theme) + '\n');
|
|
130
|
+
process.stderr.write(formatKeyValue(' Confirm Destructive', config.preferences.confirmDestructive ? 'yes' : 'no') + '\n');
|
|
131
|
+
process.stderr.write(formatKeyValue(' Timestamps', config.preferences.timestampMessages ? 'yes' : 'no') + '\n');
|
|
132
|
+
process.stderr.write('\n');
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Test live Proxmox API and SSH connections for the active profile.
|
|
136
|
+
*/
|
|
137
|
+
async function testLiveConnection() {
|
|
138
|
+
const profile = getActiveProfile();
|
|
139
|
+
if (!profile)
|
|
140
|
+
return;
|
|
141
|
+
process.stderr.write(chalk.bold('Connection Status\n'));
|
|
142
|
+
process.stderr.write(chalk.dim(' Testing connections...\n'));
|
|
143
|
+
let client = null;
|
|
144
|
+
try {
|
|
145
|
+
client = new ServerClient(profile);
|
|
146
|
+
const status = await client.getStatus();
|
|
147
|
+
// API status
|
|
148
|
+
if (status.apiConnected && status.proxmoxVersion) {
|
|
149
|
+
process.stderr.write(formatKeyValue(' Proxmox API', formatSuccess(`connected (v${status.proxmoxVersion.version}-${status.proxmoxVersion.release})`)) + '\n');
|
|
150
|
+
}
|
|
151
|
+
else {
|
|
152
|
+
const apiError = status.errors.find((e) => e.startsWith('API:'));
|
|
153
|
+
process.stderr.write(formatKeyValue(' Proxmox API', chalk.red(apiError ?? 'disconnected')) + '\n');
|
|
154
|
+
}
|
|
155
|
+
// SSH status
|
|
156
|
+
if (status.sshConnected && status.sshHostname) {
|
|
157
|
+
process.stderr.write(formatKeyValue(' SSH', formatSuccess(`connected (${status.sshHostname})`)) + '\n');
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
const sshError = status.errors.find((e) => e.startsWith('SSH:'));
|
|
161
|
+
process.stderr.write(formatKeyValue(' SSH', chalk.red(sshError ?? 'disconnected')) + '\n');
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
catch (err) {
|
|
165
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
166
|
+
logger.error('Connection test failed', { error: message });
|
|
167
|
+
process.stderr.write(formatKeyValue(' Status', chalk.red(`error: ${message}`)) + '\n');
|
|
168
|
+
}
|
|
169
|
+
finally {
|
|
170
|
+
client?.dispose();
|
|
171
|
+
}
|
|
172
|
+
process.stderr.write('\n');
|
|
173
|
+
}
|
|
174
|
+
//# sourceMappingURL=status.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status.js","sourceRoot":"","sources":["../../src/commands/status.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vms.d.ts","sourceRoot":"","sources":["../../src/commands/vms.ts"],"names":[],"mappings":"AAuDA;;GAEG;AACH,wBAAsB,UAAU,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CA2D7D"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// rackmind vms (alias: vm) — list all QEMU virtual machines
|
|
3
|
+
// ---------------------------------------------------------------------------
|
|
4
|
+
// Fetches VMs from Proxmox API and displays them in a formatted table.
|
|
5
|
+
// Supports --json for machine-readable output.
|
|
6
|
+
// ---------------------------------------------------------------------------
|
|
7
|
+
import chalk from 'chalk';
|
|
8
|
+
import { ServerClient } from '../server/client.js';
|
|
9
|
+
import { createTable, formatBytes, formatPercent, formatUptime, colorStatus, truncate, } from '../utils/table.js';
|
|
10
|
+
import { formatError } from '../utils/format.js';
|
|
11
|
+
import { logger } from '../utils/logger.js';
|
|
12
|
+
function toVMRow(vm) {
|
|
13
|
+
return {
|
|
14
|
+
vmid: vm.vmid,
|
|
15
|
+
name: vm.name,
|
|
16
|
+
status: vm.status,
|
|
17
|
+
cpus: vm.cpus,
|
|
18
|
+
memUsed: vm.mem,
|
|
19
|
+
memMax: vm.maxmem,
|
|
20
|
+
memPercent: vm.maxmem > 0 ? vm.mem / vm.maxmem : 0,
|
|
21
|
+
diskUsed: vm.disk,
|
|
22
|
+
diskMax: vm.maxdisk,
|
|
23
|
+
diskPercent: vm.maxdisk > 0 ? vm.disk / vm.maxdisk : 0,
|
|
24
|
+
uptime: vm.uptime,
|
|
25
|
+
netIn: vm.netin,
|
|
26
|
+
netOut: vm.netout,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* List all QEMU VMs on the active server.
|
|
31
|
+
*/
|
|
32
|
+
export async function vmsCommand(json) {
|
|
33
|
+
let client = null;
|
|
34
|
+
try {
|
|
35
|
+
client = ServerClient.fromActiveProfile();
|
|
36
|
+
}
|
|
37
|
+
catch (err) {
|
|
38
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
39
|
+
process.stderr.write(formatError(message) + '\n');
|
|
40
|
+
process.exit(1);
|
|
41
|
+
}
|
|
42
|
+
try {
|
|
43
|
+
const node = await client.getNodeName();
|
|
44
|
+
const vms = await client.listVMs(node);
|
|
45
|
+
// Sort by VMID
|
|
46
|
+
vms.sort((a, b) => a.vmid - b.vmid);
|
|
47
|
+
if (json) {
|
|
48
|
+
const rows = vms.map(toVMRow);
|
|
49
|
+
process.stdout.write(JSON.stringify(rows, null, 2) + '\n');
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
if (vms.length === 0) {
|
|
53
|
+
process.stderr.write(chalk.dim('No QEMU virtual machines found.\n'));
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
process.stderr.write(chalk.cyan.bold(`\nQEMU Virtual Machines`) + chalk.dim(` (${vms.length})\n\n`));
|
|
57
|
+
const table = createTable(['VMID', 'Name', 'Status', 'CPUs', 'Memory', 'Disk', 'Uptime']);
|
|
58
|
+
for (const vm of vms) {
|
|
59
|
+
const memRatio = vm.maxmem > 0 ? vm.mem / vm.maxmem : 0;
|
|
60
|
+
const diskRatio = vm.maxdisk > 0 ? vm.disk / vm.maxdisk : 0;
|
|
61
|
+
table.push([
|
|
62
|
+
String(vm.vmid),
|
|
63
|
+
truncate(vm.name || chalk.dim('(unnamed)'), 24),
|
|
64
|
+
colorStatus(vm.status),
|
|
65
|
+
String(vm.cpus),
|
|
66
|
+
`${formatBytes(vm.mem)} / ${formatBytes(vm.maxmem)} ${formatPercent(memRatio)}`,
|
|
67
|
+
`${formatBytes(vm.disk)} / ${formatBytes(vm.maxdisk)} ${formatPercent(diskRatio)}`,
|
|
68
|
+
formatUptime(vm.uptime),
|
|
69
|
+
]);
|
|
70
|
+
}
|
|
71
|
+
process.stderr.write(table.toString() + '\n\n');
|
|
72
|
+
}
|
|
73
|
+
catch (err) {
|
|
74
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
75
|
+
logger.error('VMs command failed', { error: message });
|
|
76
|
+
process.stderr.write(formatError(message) + '\n');
|
|
77
|
+
process.exit(1);
|
|
78
|
+
}
|
|
79
|
+
finally {
|
|
80
|
+
client.dispose();
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=vms.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vms.js","sourceRoot":"","sources":["../../src/commands/vms.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Encrypt a plaintext string. Returns a hex-encoded string containing
|
|
3
|
+
* the IV, auth tag, and ciphertext: `iv:authTag:ciphertext`
|
|
4
|
+
*
|
|
5
|
+
* Returns an empty string if the input is empty.
|
|
6
|
+
*/
|
|
7
|
+
export declare function encrypt(plaintext: string): string;
|
|
8
|
+
/**
|
|
9
|
+
* Decrypt a string that was encrypted with `encrypt()`.
|
|
10
|
+
* Expects the format `iv:authTag:ciphertext` (all hex-encoded).
|
|
11
|
+
*
|
|
12
|
+
* Returns an empty string if the input is empty.
|
|
13
|
+
* Throws on invalid or corrupted data.
|
|
14
|
+
*/
|
|
15
|
+
export declare function decrypt(encrypted: string): string;
|
|
16
|
+
/**
|
|
17
|
+
* Test whether a string looks like an encrypted value (iv:authTag:ciphertext).
|
|
18
|
+
*/
|
|
19
|
+
export declare function isEncrypted(value: string): boolean;
|
|
20
|
+
//# sourceMappingURL=crypto.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"crypto.d.ts","sourceRoot":"","sources":["../../src/config/crypto.ts"],"names":[],"mappings":"AA0BA;;;;;GAKG;AACH,wBAAgB,OAAO,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAWjD;AAED;;;;;;GAMG;AACH,wBAAgB,OAAO,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAwBjD;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAMlD"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// Credential encryption — AES-256-GCM with machine-derived key
|
|
3
|
+
// ---------------------------------------------------------------------------
|
|
4
|
+
// Encrypts sensitive values (API tokens, passwords) at rest in config.json.
|
|
5
|
+
// The encryption key is derived from a combination of hostname + username,
|
|
6
|
+
// making it tied to the current machine and user. Not bullet-proof against
|
|
7
|
+
// targeted attacks, but prevents casual reading of credentials from disk.
|
|
8
|
+
// ---------------------------------------------------------------------------
|
|
9
|
+
import { createCipheriv, createDecipheriv, randomBytes, createHash } from 'node:crypto';
|
|
10
|
+
import os from 'node:os';
|
|
11
|
+
const ALGORITHM = 'aes-256-gcm';
|
|
12
|
+
const IV_LENGTH = 16;
|
|
13
|
+
const AUTH_TAG_LENGTH = 16;
|
|
14
|
+
const KEY_SALT = 'rackmind-cli-v1';
|
|
15
|
+
/**
|
|
16
|
+
* Derive a 32-byte encryption key from machine-specific values.
|
|
17
|
+
* Uses SHA-256(hostname + username + salt) to produce a deterministic key.
|
|
18
|
+
*/
|
|
19
|
+
function deriveKey() {
|
|
20
|
+
const material = [os.hostname(), os.userInfo().username, KEY_SALT].join(':');
|
|
21
|
+
return createHash('sha256').update(material).digest();
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Encrypt a plaintext string. Returns a hex-encoded string containing
|
|
25
|
+
* the IV, auth tag, and ciphertext: `iv:authTag:ciphertext`
|
|
26
|
+
*
|
|
27
|
+
* Returns an empty string if the input is empty.
|
|
28
|
+
*/
|
|
29
|
+
export function encrypt(plaintext) {
|
|
30
|
+
if (!plaintext)
|
|
31
|
+
return '';
|
|
32
|
+
const key = deriveKey();
|
|
33
|
+
const iv = randomBytes(IV_LENGTH);
|
|
34
|
+
const cipher = createCipheriv(ALGORITHM, key, iv);
|
|
35
|
+
const encrypted = Buffer.concat([cipher.update(plaintext, 'utf8'), cipher.final()]);
|
|
36
|
+
const authTag = cipher.getAuthTag();
|
|
37
|
+
return [iv.toString('hex'), authTag.toString('hex'), encrypted.toString('hex')].join(':');
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Decrypt a string that was encrypted with `encrypt()`.
|
|
41
|
+
* Expects the format `iv:authTag:ciphertext` (all hex-encoded).
|
|
42
|
+
*
|
|
43
|
+
* Returns an empty string if the input is empty.
|
|
44
|
+
* Throws on invalid or corrupted data.
|
|
45
|
+
*/
|
|
46
|
+
export function decrypt(encrypted) {
|
|
47
|
+
if (!encrypted)
|
|
48
|
+
return '';
|
|
49
|
+
const parts = encrypted.split(':');
|
|
50
|
+
if (parts.length !== 3) {
|
|
51
|
+
throw new Error('Invalid encrypted value format — expected iv:authTag:ciphertext');
|
|
52
|
+
}
|
|
53
|
+
const [ivHex, authTagHex, ciphertextHex] = parts;
|
|
54
|
+
const key = deriveKey();
|
|
55
|
+
const iv = Buffer.from(ivHex, 'hex');
|
|
56
|
+
const authTag = Buffer.from(authTagHex, 'hex');
|
|
57
|
+
const ciphertext = Buffer.from(ciphertextHex, 'hex');
|
|
58
|
+
if (authTag.length !== AUTH_TAG_LENGTH) {
|
|
59
|
+
throw new Error('Invalid auth tag length');
|
|
60
|
+
}
|
|
61
|
+
const decipher = createDecipheriv(ALGORITHM, key, iv);
|
|
62
|
+
decipher.setAuthTag(authTag);
|
|
63
|
+
const decrypted = Buffer.concat([decipher.update(ciphertext), decipher.final()]);
|
|
64
|
+
return decrypted.toString('utf8');
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Test whether a string looks like an encrypted value (iv:authTag:ciphertext).
|
|
68
|
+
*/
|
|
69
|
+
export function isEncrypted(value) {
|
|
70
|
+
if (!value)
|
|
71
|
+
return false;
|
|
72
|
+
const parts = value.split(':');
|
|
73
|
+
if (parts.length !== 3)
|
|
74
|
+
return false;
|
|
75
|
+
// Each part should be valid hex
|
|
76
|
+
return parts.every((p) => /^[0-9a-f]+$/i.test(p));
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=crypto.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"crypto.js","sourceRoot":"","sources":["../../src/config/crypto.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type RackMindConfig, type ServerProfile } from './types.js';
|
|
2
|
+
export declare function getConfigDir(): string;
|
|
3
|
+
export declare function getConfigPath(): string;
|
|
4
|
+
export declare function configExists(): boolean;
|
|
5
|
+
export declare function readConfig(): RackMindConfig;
|
|
6
|
+
export declare function writeConfig(config: RackMindConfig): void;
|
|
7
|
+
export declare function getServerProfile(alias: string): ServerProfile | null;
|
|
8
|
+
export declare function getActiveProfile(): ServerProfile | null;
|
|
9
|
+
export declare function setActiveServer(alias: string): void;
|
|
10
|
+
export declare function saveServerProfile(profile: ServerProfile): void;
|
|
11
|
+
export declare function removeServerProfile(alias: string): boolean;
|
|
12
|
+
export declare function listServerProfiles(): ServerProfile[];
|
|
13
|
+
export declare function encryptCredential(plaintext: string): string;
|
|
14
|
+
export declare function decryptCredential(encrypted: string): string;
|
|
15
|
+
export declare function getAnthropicApiKey(): string;
|
|
16
|
+
export declare function setAnthropicApiKey(apiKey: string): void;
|
|
17
|
+
export declare function needsSetup(): boolean;
|
|
18
|
+
export type { RackMindConfig, ServerProfile, Preferences } from './types.js';
|
|
19
|
+
export { getDefaultConfig, getDefaultServerProfile } from './types.js';
|
|
20
|
+
export type { ServerPlatform, SSHAuthType, ConnectionStatus } from './types.js';
|
|
21
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/config/index.ts"],"names":[],"mappings":"AAUA,OAAO,EAGH,KAAK,cAAc,EACnB,KAAK,aAAa,EACrB,MAAM,YAAY,CAAC;AAepB,wBAAgB,YAAY,IAAI,MAAM,CAErC;AAED,wBAAgB,aAAa,IAAI,MAAM,CAEtC;AAED,wBAAgB,YAAY,IAAI,OAAO,CAEtC;AAMD,wBAAgB,UAAU,IAAI,cAAc,CAuB3C;AAMD,wBAAgB,WAAW,CAAC,MAAM,EAAE,cAAc,GAAG,IAAI,CAcxD;AAMD,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI,CAGpE;AAED,wBAAgB,gBAAgB,IAAI,aAAa,GAAG,IAAI,CAIvD;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAOnD;AAED,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,aAAa,GAAG,IAAI,CAU9D;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAc1D;AAED,wBAAgB,kBAAkB,IAAI,aAAa,EAAE,CAGpD;AAMD,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAE3D;AAED,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAE3D;AAMD,wBAAgB,kBAAkB,IAAI,MAAM,CAS3C;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAIvD;AAMD,wBAAgB,UAAU,IAAI,OAAO,CAIpC;AAMD,YAAY,EAAE,cAAc,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAC7E,OAAO,EAAE,gBAAgB,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAC;AACvE,YAAY,EAAE,cAAc,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// Config module — read/write/validate ~/.config/rackmind/config.json
|
|
3
|
+
// ---------------------------------------------------------------------------
|
|
4
|
+
// Atomic writes (tmp + rename). Zod validation on read. Encrypted credentials.
|
|
5
|
+
// Falls back to defaults on parse failure so the CLI never crashes on bad config.
|
|
6
|
+
// ---------------------------------------------------------------------------
|
|
7
|
+
import fs from 'node:fs';
|
|
8
|
+
import path from 'node:path';
|
|
9
|
+
import os from 'node:os';
|
|
10
|
+
import { RackMindConfigSchema, getDefaultConfig, } from './types.js';
|
|
11
|
+
import { encrypt, decrypt } from './crypto.js';
|
|
12
|
+
import { logger } from '../utils/logger.js';
|
|
13
|
+
// ---------------------------------------------------------------------------
|
|
14
|
+
// Paths (XDG-compliant: ~/.config/rackmind/)
|
|
15
|
+
// ---------------------------------------------------------------------------
|
|
16
|
+
function getXdgConfigHome() {
|
|
17
|
+
return process.env['XDG_CONFIG_HOME'] ?? path.join(os.homedir(), '.config');
|
|
18
|
+
}
|
|
19
|
+
const CONFIG_DIR = path.join(getXdgConfigHome(), 'rackmind');
|
|
20
|
+
const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');
|
|
21
|
+
export function getConfigDir() {
|
|
22
|
+
return CONFIG_DIR;
|
|
23
|
+
}
|
|
24
|
+
export function getConfigPath() {
|
|
25
|
+
return CONFIG_FILE;
|
|
26
|
+
}
|
|
27
|
+
export function configExists() {
|
|
28
|
+
return fs.existsSync(CONFIG_FILE);
|
|
29
|
+
}
|
|
30
|
+
// ---------------------------------------------------------------------------
|
|
31
|
+
// Read
|
|
32
|
+
// ---------------------------------------------------------------------------
|
|
33
|
+
export function readConfig() {
|
|
34
|
+
if (!configExists()) {
|
|
35
|
+
return getDefaultConfig();
|
|
36
|
+
}
|
|
37
|
+
try {
|
|
38
|
+
const raw = fs.readFileSync(CONFIG_FILE, 'utf-8');
|
|
39
|
+
const parsed = JSON.parse(raw);
|
|
40
|
+
const result = RackMindConfigSchema.safeParse(parsed);
|
|
41
|
+
if (!result.success) {
|
|
42
|
+
logger.warn('Config validation failed, using defaults', {
|
|
43
|
+
errors: result.error.issues.map((i) => i.message),
|
|
44
|
+
});
|
|
45
|
+
return getDefaultConfig();
|
|
46
|
+
}
|
|
47
|
+
return result.data;
|
|
48
|
+
}
|
|
49
|
+
catch (err) {
|
|
50
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
51
|
+
logger.error('Failed to read config', { error: message });
|
|
52
|
+
return getDefaultConfig();
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
// ---------------------------------------------------------------------------
|
|
56
|
+
// Write (atomic: write to temp file, then rename)
|
|
57
|
+
// ---------------------------------------------------------------------------
|
|
58
|
+
export function writeConfig(config) {
|
|
59
|
+
try {
|
|
60
|
+
fs.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
61
|
+
const tmpFile = CONFIG_FILE + '.tmp';
|
|
62
|
+
fs.writeFileSync(tmpFile, JSON.stringify(config, null, 4) + '\n', 'utf-8');
|
|
63
|
+
fs.renameSync(tmpFile, CONFIG_FILE);
|
|
64
|
+
logger.info('Config written successfully');
|
|
65
|
+
}
|
|
66
|
+
catch (err) {
|
|
67
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
68
|
+
logger.error('Failed to write config', { error: message });
|
|
69
|
+
throw new Error(`Failed to write config: ${message}`, { cause: err });
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
// ---------------------------------------------------------------------------
|
|
73
|
+
// Server profile helpers
|
|
74
|
+
// ---------------------------------------------------------------------------
|
|
75
|
+
export function getServerProfile(alias) {
|
|
76
|
+
const config = readConfig();
|
|
77
|
+
return config.servers[alias] ?? null;
|
|
78
|
+
}
|
|
79
|
+
export function getActiveProfile() {
|
|
80
|
+
const config = readConfig();
|
|
81
|
+
if (!config.activeServer)
|
|
82
|
+
return null;
|
|
83
|
+
return config.servers[config.activeServer] ?? null;
|
|
84
|
+
}
|
|
85
|
+
export function setActiveServer(alias) {
|
|
86
|
+
const config = readConfig();
|
|
87
|
+
if (!config.servers[alias]) {
|
|
88
|
+
throw new Error(`Server "${alias}" not found in config`);
|
|
89
|
+
}
|
|
90
|
+
config.activeServer = alias;
|
|
91
|
+
writeConfig(config);
|
|
92
|
+
}
|
|
93
|
+
export function saveServerProfile(profile) {
|
|
94
|
+
const config = readConfig();
|
|
95
|
+
config.servers[profile.alias] = profile;
|
|
96
|
+
// Auto-set as active if it's the only server
|
|
97
|
+
if (Object.keys(config.servers).length === 1) {
|
|
98
|
+
config.activeServer = profile.alias;
|
|
99
|
+
}
|
|
100
|
+
writeConfig(config);
|
|
101
|
+
}
|
|
102
|
+
export function removeServerProfile(alias) {
|
|
103
|
+
const config = readConfig();
|
|
104
|
+
if (!config.servers[alias])
|
|
105
|
+
return false;
|
|
106
|
+
delete config.servers[alias];
|
|
107
|
+
// Clear active server if it was the deleted one
|
|
108
|
+
if (config.activeServer === alias) {
|
|
109
|
+
const remaining = Object.keys(config.servers);
|
|
110
|
+
config.activeServer = remaining[0] ?? null;
|
|
111
|
+
}
|
|
112
|
+
writeConfig(config);
|
|
113
|
+
return true;
|
|
114
|
+
}
|
|
115
|
+
export function listServerProfiles() {
|
|
116
|
+
const config = readConfig();
|
|
117
|
+
return Object.values(config.servers).sort((a, b) => a.createdAt - b.createdAt);
|
|
118
|
+
}
|
|
119
|
+
// ---------------------------------------------------------------------------
|
|
120
|
+
// Credential helpers (encrypt on save, decrypt on read)
|
|
121
|
+
// ---------------------------------------------------------------------------
|
|
122
|
+
export function encryptCredential(plaintext) {
|
|
123
|
+
return encrypt(plaintext);
|
|
124
|
+
}
|
|
125
|
+
export function decryptCredential(encrypted) {
|
|
126
|
+
return decrypt(encrypted);
|
|
127
|
+
}
|
|
128
|
+
// ---------------------------------------------------------------------------
|
|
129
|
+
// Anthropic API key helpers
|
|
130
|
+
// ---------------------------------------------------------------------------
|
|
131
|
+
export function getAnthropicApiKey() {
|
|
132
|
+
const config = readConfig();
|
|
133
|
+
if (!config.encryptedAnthropicApiKey)
|
|
134
|
+
return '';
|
|
135
|
+
try {
|
|
136
|
+
return decrypt(config.encryptedAnthropicApiKey);
|
|
137
|
+
}
|
|
138
|
+
catch {
|
|
139
|
+
logger.warn('Failed to decrypt Anthropic API key');
|
|
140
|
+
return '';
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
export function setAnthropicApiKey(apiKey) {
|
|
144
|
+
const config = readConfig();
|
|
145
|
+
config.encryptedAnthropicApiKey = encrypt(apiKey);
|
|
146
|
+
writeConfig(config);
|
|
147
|
+
}
|
|
148
|
+
// ---------------------------------------------------------------------------
|
|
149
|
+
// First-time setup detection
|
|
150
|
+
// ---------------------------------------------------------------------------
|
|
151
|
+
export function needsSetup() {
|
|
152
|
+
if (!configExists())
|
|
153
|
+
return true;
|
|
154
|
+
const config = readConfig();
|
|
155
|
+
return Object.keys(config.servers).length === 0;
|
|
156
|
+
}
|
|
157
|
+
export { getDefaultConfig, getDefaultServerProfile } from './types.js';
|
|
158
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/config/index.ts"],"names":[],"mappings":"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"}
|