rackmind-cli 0.2.0 → 0.3.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/LICENSE +101 -21
- package/README.md +7 -1
- package/dist/ai/tool-executor.d.ts +10 -0
- package/dist/ai/tool-executor.d.ts.map +1 -1
- package/dist/ai/tool-executor.js +73 -2
- package/dist/ai/tool-executor.js.map +1 -1
- package/dist/commands/config.d.ts.map +1 -1
- package/dist/commands/config.js +37 -7
- package/dist/commands/config.js.map +1 -1
- package/dist/commands/status.d.ts.map +1 -1
- package/dist/commands/status.js +55 -4
- package/dist/commands/status.js.map +1 -1
- package/dist/config/crypto.d.ts +10 -0
- package/dist/config/crypto.d.ts.map +1 -1
- package/dist/config/crypto.js +47 -2
- package/dist/config/crypto.js.map +1 -1
- package/dist/config/index.d.ts +41 -0
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/index.js +140 -17
- package/dist/config/index.js.map +1 -1
- package/dist/index.js +26 -1
- package/dist/index.js.map +1 -1
- package/dist/interactive/useRackMind.d.ts.map +1 -1
- package/dist/interactive/useRackMind.js +16 -2
- package/dist/interactive/useRackMind.js.map +1 -1
- package/dist/utils/command-safety.d.ts.map +1 -1
- package/dist/utils/command-safety.js +157 -5
- package/dist/utils/command-safety.js.map +1 -1
- package/dist/utils/logger.d.ts +20 -0
- package/dist/utils/logger.d.ts.map +1 -1
- package/dist/utils/logger.js +68 -1
- package/dist/utils/logger.js.map +1 -1
- package/package.json +3 -2
|
@@ -3,20 +3,172 @@
|
|
|
3
3
|
// ---------------------------------------------------------------------------
|
|
4
4
|
// Shared between `exec` command and AI tool executor to prevent accidental
|
|
5
5
|
// or AI-initiated destructive operations without explicit user consent.
|
|
6
|
+
//
|
|
7
|
+
// The blocklist is intentionally regex-based (not literal-string-match) so
|
|
8
|
+
// that flag reordering (`pct destroy --purge 100` vs `pct destroy 100 --purge`)
|
|
9
|
+
// cannot trivially bypass the gate. Patterns are evaluated in order; the
|
|
10
|
+
// first match wins, so more-specific patterns are listed first when label
|
|
11
|
+
// specificity matters.
|
|
6
12
|
// ---------------------------------------------------------------------------
|
|
7
13
|
/** Patterns that match potentially destructive shell commands */
|
|
8
14
|
export const DANGEROUS_PATTERNS = [
|
|
15
|
+
// -----------------------------------------------------------------------
|
|
16
|
+
// Filesystem destruction
|
|
17
|
+
// -----------------------------------------------------------------------
|
|
18
|
+
// `rm -rf /` and "rm root" variants — match `rm` with at least one
|
|
19
|
+
// recursive/force flag AND a path that targets the system root.
|
|
20
|
+
// Listed BEFORE the generic rm pattern so this more-specific label wins.
|
|
9
21
|
{
|
|
10
|
-
pattern: /\brm\s+(
|
|
22
|
+
pattern: /\brm\s+(?:-[a-zA-Z]*[rfR][a-zA-Z]*|--recursive|--force|--no-preserve-root)(?:\s+(?:-[a-zA-Z-]+|--[a-zA-Z-]+))*\s+\/(?:\s|$|\*|;|&|\||>)/i,
|
|
23
|
+
label: 'rm -rf / (root filesystem destruction)',
|
|
24
|
+
},
|
|
25
|
+
// Generic rm -rf / rm -r / rm -f / rm --force / rm --recursive
|
|
26
|
+
{
|
|
27
|
+
pattern: /\brm\s+(?:-[a-zA-Z]*(?:f|r|R)[a-zA-Z]*|--force|--recursive)\b/i,
|
|
11
28
|
label: 'rm with force/recursive',
|
|
12
29
|
},
|
|
13
|
-
|
|
30
|
+
// find … -delete or find … -exec rm — recursive deletion via find
|
|
31
|
+
{
|
|
32
|
+
pattern: /\bfind\b[^|;&]*\s(?:-delete\b|-exec\s+rm\b)/i,
|
|
33
|
+
label: 'find with -delete or -exec rm (mass deletion)',
|
|
34
|
+
},
|
|
35
|
+
// chmod 000 -R / chmod -R 000 / chmod 0 recursive — permission wipe
|
|
36
|
+
{
|
|
37
|
+
pattern: /\bchmod\b[^|;&]*\s(?:-R\b|--recursive\b)[^|;&]*\s0{1,4}\b/i,
|
|
38
|
+
label: 'chmod recursive 000 (permission wipe)',
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
pattern: /\bchmod\b[^|;&]*\s0{2,4}\s+(?:-R\b|--recursive\b)/i,
|
|
42
|
+
label: 'chmod recursive 000 (permission wipe)',
|
|
43
|
+
},
|
|
44
|
+
// chown -R … / — recursive ownership change starting at /
|
|
45
|
+
{
|
|
46
|
+
pattern: /\bchown\b[^|;&]*\s(?:-R\b|--recursive\b)[^|;&]*\s\/(?:\s|$)/i,
|
|
47
|
+
label: 'chown -R targeting / (recursive ownership change)',
|
|
48
|
+
},
|
|
49
|
+
// -----------------------------------------------------------------------
|
|
50
|
+
// Disk / partition / filesystem operations
|
|
51
|
+
// -----------------------------------------------------------------------
|
|
52
|
+
{ pattern: /\bmkfs(?:\.[a-z0-9]+)?\b/i, label: 'mkfs (format filesystem)' },
|
|
14
53
|
{ pattern: /\bdd\s+.*of=/i, label: 'dd (raw disk write)' },
|
|
15
54
|
{ pattern: /\bfdisk\b/i, label: 'fdisk (partition table)' },
|
|
16
|
-
{ pattern:
|
|
17
|
-
{ pattern: /\
|
|
18
|
-
{ pattern: /\
|
|
55
|
+
{ pattern: /\bsgdisk\b/i, label: 'sgdisk (partition table)' },
|
|
56
|
+
{ pattern: /\bparted\b/i, label: 'parted (partition table)' },
|
|
57
|
+
{ pattern: /\bwipefs\b/i, label: 'wipefs (wipe filesystem signature)' },
|
|
58
|
+
{ pattern: /\b(?:lvremove|vgremove|pvremove)\b/i, label: 'LVM remove (storage destruction)' },
|
|
59
|
+
{ pattern: /\bzpool\s+destroy\b/i, label: 'zpool destroy (ZFS pool destruction)' },
|
|
60
|
+
{ pattern: /\bzfs\s+destroy\b/i, label: 'zfs destroy (ZFS dataset destruction)' },
|
|
61
|
+
{ pattern: /\bmdadm\s+(?:--stop|--remove|--zero-superblock)\b/i, label: 'mdadm stop/remove' },
|
|
62
|
+
// -----------------------------------------------------------------------
|
|
63
|
+
// Container / VM destruction (Proxmox + Docker + libvirt)
|
|
64
|
+
// -----------------------------------------------------------------------
|
|
65
|
+
{ pattern: /\bpct\s+(?:destroy|delete)\b/i, label: 'pct destroy/delete (LXC destruction)' },
|
|
66
|
+
{ pattern: /\bqm\s+(?:destroy|delete)\b/i, label: 'qm destroy/delete (QEMU VM destruction)' },
|
|
67
|
+
{ pattern: /\bvirsh\s+(?:destroy|undefine)\b/i, label: 'virsh destroy/undefine' },
|
|
68
|
+
{ pattern: /\bdocker\s+(?:system\s+)?prune\b[^|;&]*-[a-z]*a/i, label: 'docker prune -a' },
|
|
69
|
+
{ pattern: /\bdocker\s+rm\s+-f\b/i, label: 'docker rm -f' },
|
|
70
|
+
{ pattern: /\bdocker\s+rmi\s+-f\b/i, label: 'docker rmi -f' },
|
|
71
|
+
// -----------------------------------------------------------------------
|
|
72
|
+
// Network blackholing
|
|
73
|
+
// -----------------------------------------------------------------------
|
|
74
|
+
{
|
|
75
|
+
pattern: /\biptables\s+(?:-F|--flush|-X|--delete-chain|-Z|--zero)\b/i,
|
|
76
|
+
label: 'iptables flush/delete chain (network blackhole)',
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
pattern: /\bip6tables\s+(?:-F|--flush|-X|--delete-chain|-Z|--zero)\b/i,
|
|
80
|
+
label: 'ip6tables flush/delete chain',
|
|
81
|
+
},
|
|
82
|
+
{ pattern: /\bnft\s+flush\b/i, label: 'nft flush (nftables ruleset wipe)' },
|
|
83
|
+
{
|
|
84
|
+
pattern: /\bip\s+link\s+set\s+\S+\s+down\b/i,
|
|
85
|
+
label: 'ip link set <iface> down (network interface shutdown)',
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
pattern: /\bifconfig\s+\S+\s+down\b/i,
|
|
89
|
+
label: 'ifconfig <iface> down (network interface shutdown)',
|
|
90
|
+
},
|
|
91
|
+
{ pattern: /\bip\s+route\s+(?:flush|del)\b/i, label: 'ip route flush/del (routing wipe)' },
|
|
92
|
+
// -----------------------------------------------------------------------
|
|
93
|
+
// User / authentication destruction
|
|
94
|
+
// -----------------------------------------------------------------------
|
|
95
|
+
{ pattern: /\buserdel\b/i, label: 'userdel (delete user account)' },
|
|
96
|
+
{ pattern: /\bgroupdel\b/i, label: 'groupdel (delete group)' },
|
|
97
|
+
{ pattern: /\bdeluser\b/i, label: 'deluser (delete user account)' },
|
|
98
|
+
{ pattern: /\bdelgroup\b/i, label: 'delgroup (delete group)' },
|
|
99
|
+
{
|
|
100
|
+
pattern: /\bpasswd\s+(?:-l|--lock|-d|--delete)\b/i,
|
|
101
|
+
label: 'passwd lock/delete (lock or wipe password)',
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
pattern: /\bchage\s+(?:-E\s+0|-E0)\b/i,
|
|
105
|
+
label: 'chage -E 0 (expire account immediately)',
|
|
106
|
+
},
|
|
107
|
+
{ pattern: /\busermod\s+(?:-L|--lock)\b/i, label: 'usermod --lock (lock account)' },
|
|
108
|
+
{
|
|
109
|
+
pattern: />\s*\/etc\/(?:passwd|shadow|sudoers|group|gshadow)\b/i,
|
|
110
|
+
label: 'write to /etc/passwd|shadow|sudoers (auth file overwrite)',
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
pattern: /\brm\b[^|;&]*\/etc\/(?:passwd|shadow|sudoers|group|gshadow)\b/i,
|
|
114
|
+
label: 'rm /etc/passwd|shadow|sudoers (auth file deletion)',
|
|
115
|
+
},
|
|
116
|
+
// -----------------------------------------------------------------------
|
|
117
|
+
// Process disruption — kill init / PID 1
|
|
118
|
+
// -----------------------------------------------------------------------
|
|
119
|
+
{ pattern: /\bkill\s+(?:-[a-zA-Z0-9]+\s+)?-?1\b(?!\d)/i, label: 'kill PID 1 (init kill)' },
|
|
120
|
+
{ pattern: /\bkillall\s+(?:-[a-zA-Z0-9]+\s+)?init\b/i, label: 'killall init' },
|
|
121
|
+
{ pattern: /\bkillall\s+(?:-[a-zA-Z0-9]+\s+)?systemd\b/i, label: 'killall systemd' },
|
|
122
|
+
{ pattern: /\bpkill\b[^|;&]*\s-1\b(?!\d)/i, label: 'pkill -1 (signal all processes)' },
|
|
123
|
+
{ pattern: /\bpkill\s+(?:-[a-zA-Z0-9]+\s+)?init\b/i, label: 'pkill init' },
|
|
124
|
+
{ pattern: /\bpkill\s+(?:-[a-zA-Z0-9]+\s+)?systemd\b/i, label: 'pkill systemd' },
|
|
125
|
+
// -----------------------------------------------------------------------
|
|
126
|
+
// System shutdown / reboot
|
|
127
|
+
// -----------------------------------------------------------------------
|
|
128
|
+
{ pattern: /\b(?:shutdown|reboot|poweroff|halt)\b/i, label: 'shutdown/reboot/poweroff' },
|
|
129
|
+
{
|
|
130
|
+
pattern: /\bsystemctl\s+(?:stop|disable|mask|isolate)\b/i,
|
|
131
|
+
label: 'systemctl stop/disable/mask',
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
pattern: /\bsystemctl\s+(?:reboot|poweroff|halt|emergency|rescue)\b/i,
|
|
135
|
+
label: 'systemctl reboot/poweroff/halt',
|
|
136
|
+
},
|
|
137
|
+
{ pattern: /\binit\s+0\b/i, label: 'init 0 (shutdown)' },
|
|
138
|
+
{ pattern: /\binit\s+6\b/i, label: 'init 6 (reboot)' },
|
|
139
|
+
{ pattern: /\btelinit\s+(?:0|6)\b/i, label: 'telinit 0/6 (shutdown/reboot)' },
|
|
140
|
+
// -----------------------------------------------------------------------
|
|
141
|
+
// Raw device writes
|
|
142
|
+
// -----------------------------------------------------------------------
|
|
19
143
|
{ pattern: />\s*\/dev\//i, label: 'write to /dev/' },
|
|
144
|
+
// -----------------------------------------------------------------------
|
|
145
|
+
// Fork bomb
|
|
146
|
+
// -----------------------------------------------------------------------
|
|
147
|
+
// Matches the classic `:(){ :|:& };:` shape — a function named `:` that
|
|
148
|
+
// pipes itself into itself recursively in the background. Tolerates
|
|
149
|
+
// whitespace and minor formatting variation.
|
|
150
|
+
{
|
|
151
|
+
pattern: /:\s*\(\s*\)\s*\{[^}]*:\s*\|\s*:[^}]*&[^}]*\}\s*;\s*:/,
|
|
152
|
+
label: 'fork bomb',
|
|
153
|
+
},
|
|
154
|
+
// Backstop: matches the bare function-definition shape that pipes itself
|
|
155
|
+
// in background. Catches variants where the inner body is condensed.
|
|
156
|
+
{ pattern: /:\(\)\s*\{[\s\S]{0,40}:\|:&/, label: 'fork bomb' },
|
|
157
|
+
// -----------------------------------------------------------------------
|
|
158
|
+
// Curl/wget piped to a shell — remote code execution
|
|
159
|
+
// -----------------------------------------------------------------------
|
|
160
|
+
{
|
|
161
|
+
pattern: /\b(?:curl|wget)\b[^|;&]*\|\s*(?:sudo\s+)?(?:bash|sh|zsh|ksh|dash|python|perl)\b/i,
|
|
162
|
+
label: 'curl|wget piped to shell (remote code execution)',
|
|
163
|
+
},
|
|
164
|
+
// -----------------------------------------------------------------------
|
|
165
|
+
// Crypto / key destruction
|
|
166
|
+
// -----------------------------------------------------------------------
|
|
167
|
+
{ pattern: /\brm\b[^|;&]*~\/\.ssh\b/i, label: 'rm ~/.ssh (SSH key destruction)' },
|
|
168
|
+
{
|
|
169
|
+
pattern: /\brm\b[^|;&]*\/root\/\.ssh\b/i,
|
|
170
|
+
label: 'rm /root/.ssh (root SSH key destruction)',
|
|
171
|
+
},
|
|
20
172
|
];
|
|
21
173
|
/**
|
|
22
174
|
* Check a command string against the dangerous patterns blocklist.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"command-safety.js","sourceRoot":"","sources":["../../src/utils/command-safety.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,yDAAyD;AACzD,8EAA8E;AAC9E,2EAA2E;AAC3E,wEAAwE;AACxE,8EAA8E;AAE9E,iEAAiE;AACjE,MAAM,CAAC,MAAM,kBAAkB,GAAyC;IACpE;QACI,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"command-safety.js","sourceRoot":"","sources":["../../src/utils/command-safety.ts"],"names":[],"mappings":"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"}
|
package/dist/utils/logger.d.ts
CHANGED
|
@@ -1,3 +1,16 @@
|
|
|
1
|
+
declare function getLogFile(): string;
|
|
2
|
+
/**
|
|
3
|
+
* Rotate `logFile` if it exceeds MAX_FILE_BYTES.
|
|
4
|
+
*
|
|
5
|
+
* Strategy: shift `.N -> .N+1`, then rename the live file to `.1`. The next
|
|
6
|
+
* write to `logFile` will create a fresh, empty file.
|
|
7
|
+
*
|
|
8
|
+
* This is intentionally synchronous (CLI hot path, rare event, tiny rename
|
|
9
|
+
* cost) and best-effort — any failure here must NOT throw, so that a broken
|
|
10
|
+
* filesystem state cannot crash the CLI or the MCP. We swallow errors and let
|
|
11
|
+
* the caller fall back to appending to the (possibly oversized) file.
|
|
12
|
+
*/
|
|
13
|
+
declare function rotateIfNeeded(logFile: string): void;
|
|
1
14
|
export type LogLevel = 'debug' | 'info' | 'warn' | 'error';
|
|
2
15
|
export declare function setLogLevel(level: LogLevel): void;
|
|
3
16
|
export declare const logger: {
|
|
@@ -6,4 +19,11 @@ export declare const logger: {
|
|
|
6
19
|
warn: (message: string, meta?: Record<string, unknown>) => void;
|
|
7
20
|
error: (message: string, meta?: Record<string, unknown>) => void;
|
|
8
21
|
};
|
|
22
|
+
export declare const __testing: {
|
|
23
|
+
MAX_FILE_BYTES: number;
|
|
24
|
+
MAX_BACKUPS: number;
|
|
25
|
+
rotateIfNeeded: typeof rotateIfNeeded;
|
|
26
|
+
getLogFile: typeof getLogFile;
|
|
27
|
+
};
|
|
28
|
+
export {};
|
|
9
29
|
//# sourceMappingURL=logger.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/utils/logger.ts"],"names":[],"mappings":"AAmBA,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;AAW3D,wBAAgB,WAAW,CAAC,KAAK,EAAE,QAAQ,GAAG,IAAI,CAEjD;
|
|
1
|
+
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/utils/logger.ts"],"names":[],"mappings":"AAmBA,iBAAS,UAAU,IAAI,MAAM,CAG5B;AAMD;;;;;;;;;;GAUG;AACH,iBAAS,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAmC7C;AAED,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;AAW3D,wBAAgB,WAAW,CAAC,KAAK,EAAE,QAAQ,GAAG,IAAI,CAEjD;AAyBD,eAAO,MAAM,MAAM;qBACE,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;oBACvC,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;oBACtC,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;qBACrC,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;CAC1D,CAAC;AAIF,eAAO,MAAM,SAAS;;;;;CAKrB,CAAC"}
|
package/dist/utils/logger.js
CHANGED
|
@@ -2,6 +2,14 @@ import fs from 'node:fs';
|
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import os from 'node:os';
|
|
4
4
|
const LOG_DIR = path.join(os.homedir(), '.rackmind', 'logs');
|
|
5
|
+
// Rotation policy: cap each log file at MAX_FILE_BYTES, keep MAX_BACKUPS rotated
|
|
6
|
+
// copies (`<file>.1` is the most recent rotation, `<file>.<MAX_BACKUPS>` is the
|
|
7
|
+
// oldest and is deleted on the next rotation).
|
|
8
|
+
// One file maxed out + N backups bounds disk usage at roughly
|
|
9
|
+
// MAX_FILE_BYTES * (MAX_BACKUPS + 1)
|
|
10
|
+
// = 10 MB * 6 = 60 MB per log basename.
|
|
11
|
+
const MAX_FILE_BYTES = 10 * 1024 * 1024;
|
|
12
|
+
const MAX_BACKUPS = 5;
|
|
5
13
|
function ensureLogDir() {
|
|
6
14
|
fs.mkdirSync(LOG_DIR, { recursive: true });
|
|
7
15
|
}
|
|
@@ -12,6 +20,55 @@ function getLogFile() {
|
|
|
12
20
|
function formatTimestamp() {
|
|
13
21
|
return new Date().toISOString();
|
|
14
22
|
}
|
|
23
|
+
/**
|
|
24
|
+
* Rotate `logFile` if it exceeds MAX_FILE_BYTES.
|
|
25
|
+
*
|
|
26
|
+
* Strategy: shift `.N -> .N+1`, then rename the live file to `.1`. The next
|
|
27
|
+
* write to `logFile` will create a fresh, empty file.
|
|
28
|
+
*
|
|
29
|
+
* This is intentionally synchronous (CLI hot path, rare event, tiny rename
|
|
30
|
+
* cost) and best-effort — any failure here must NOT throw, so that a broken
|
|
31
|
+
* filesystem state cannot crash the CLI or the MCP. We swallow errors and let
|
|
32
|
+
* the caller fall back to appending to the (possibly oversized) file.
|
|
33
|
+
*/
|
|
34
|
+
function rotateIfNeeded(logFile) {
|
|
35
|
+
try {
|
|
36
|
+
const stat = fs.statSync(logFile);
|
|
37
|
+
if (stat.size <= MAX_FILE_BYTES)
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
// File doesn't exist yet — nothing to rotate.
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
try {
|
|
45
|
+
// Drop the oldest backup if it exists.
|
|
46
|
+
const oldest = `${logFile}.${MAX_BACKUPS}`;
|
|
47
|
+
try {
|
|
48
|
+
fs.unlinkSync(oldest);
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
// ignore — may not exist
|
|
52
|
+
}
|
|
53
|
+
// Shift .N -> .N+1, starting from the second-oldest.
|
|
54
|
+
for (let i = MAX_BACKUPS - 1; i >= 1; i--) {
|
|
55
|
+
const src = `${logFile}.${i}`;
|
|
56
|
+
const dest = `${logFile}.${i + 1}`;
|
|
57
|
+
try {
|
|
58
|
+
fs.renameSync(src, dest);
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
// ignore — backup at this index doesn't exist
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
// Live file -> .1. Atomic rename on POSIX.
|
|
65
|
+
fs.renameSync(logFile, `${logFile}.1`);
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
// Rotation failed — fall through, caller will append to the existing
|
|
69
|
+
// file. Bounded growth is preferred to a crashed CLI.
|
|
70
|
+
}
|
|
71
|
+
}
|
|
15
72
|
const LOG_LEVELS = {
|
|
16
73
|
debug: 0,
|
|
17
74
|
info: 1,
|
|
@@ -30,13 +87,15 @@ function writeLog(level, message, meta) {
|
|
|
30
87
|
return;
|
|
31
88
|
try {
|
|
32
89
|
ensureLogDir();
|
|
90
|
+
const file = getLogFile();
|
|
91
|
+
rotateIfNeeded(file);
|
|
33
92
|
const entry = {
|
|
34
93
|
timestamp: formatTimestamp(),
|
|
35
94
|
level,
|
|
36
95
|
message,
|
|
37
96
|
...(meta ? { meta } : {}),
|
|
38
97
|
};
|
|
39
|
-
fs.appendFileSync(
|
|
98
|
+
fs.appendFileSync(file, JSON.stringify(entry) + '\n');
|
|
40
99
|
}
|
|
41
100
|
catch {
|
|
42
101
|
// Silently fail — logging should never crash the CLI
|
|
@@ -48,4 +107,12 @@ export const logger = {
|
|
|
48
107
|
warn: (message, meta) => writeLog('warn', message, meta),
|
|
49
108
|
error: (message, meta) => writeLog('error', message, meta),
|
|
50
109
|
};
|
|
110
|
+
// Exported for tests only. Keep undocumented in public README — the rotation
|
|
111
|
+
// policy is an implementation detail.
|
|
112
|
+
export const __testing = {
|
|
113
|
+
MAX_FILE_BYTES,
|
|
114
|
+
MAX_BACKUPS,
|
|
115
|
+
rotateIfNeeded,
|
|
116
|
+
getLogFile,
|
|
117
|
+
};
|
|
51
118
|
//# sourceMappingURL=logger.js.map
|
package/dist/utils/logger.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/utils/logger.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,MAAM,SAAS,CAAC;AAEzB,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,WAAW,EAAE,MAAM,CAAC,CAAC;AAE7D,SAAS,YAAY;IACjB,EAAE,CAAC,SAAS,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;AAC/C,CAAC;AAED,SAAS,UAAU;IACf,MAAM,IAAI,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IACpD,OAAO,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,YAAY,IAAI,MAAM,CAAC,CAAC;AACtD,CAAC;AAED,SAAS,eAAe;IACpB,OAAO,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;AACpC,CAAC;AAID,MAAM,UAAU,GAA6B;IACzC,KAAK,EAAE,CAAC;IACR,IAAI,EAAE,CAAC;IACP,IAAI,EAAE,CAAC;IACP,KAAK,EAAE,CAAC;CACX,CAAC;AAEF,IAAI,YAAY,GAAa,MAAM,CAAC;AAEpC,MAAM,UAAU,WAAW,CAAC,KAAe;IACvC,YAAY,GAAG,KAAK,CAAC;AACzB,CAAC;AAED,SAAS,SAAS,CAAC,KAAe;IAC9B,OAAO,UAAU,CAAC,KAAK,CAAC,IAAI,UAAU,CAAC,YAAY,CAAC,CAAC;AACzD,CAAC;AAED,SAAS,QAAQ,CAAC,KAAe,EAAE,OAAe,EAAE,IAA8B;IAC9E,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC;QAAE,OAAO;IAE9B,IAAI,CAAC;QACD,YAAY,EAAE,CAAC;QACf,MAAM,KAAK,GAAG;YACV,SAAS,EAAE,eAAe,EAAE;YAC5B,KAAK;YACL,OAAO;YACP,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAC5B,CAAC;QACF,EAAE,CAAC,cAAc,CAAC,
|
|
1
|
+
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/utils/logger.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rackmind-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "CLI interface for RackMind — Claude Code-style terminal experience for managing Proxmox servers, LXC containers, and QEMU VMs from your terminal.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -74,6 +74,7 @@
|
|
|
74
74
|
"format:check": "prettier --check .",
|
|
75
75
|
"type-check": "tsc --noEmit -p tsconfig.typecheck.json",
|
|
76
76
|
"test": "vitest run",
|
|
77
|
+
"test:homelab": "./scripts/test-against-homelab.sh",
|
|
77
78
|
"check": "npm run lint && npm run format:check && npm run type-check && npm test",
|
|
78
79
|
"prepare": "husky || true",
|
|
79
80
|
"prepublishOnly": "npm run check && npm run build"
|
|
@@ -88,7 +89,7 @@
|
|
|
88
89
|
]
|
|
89
90
|
},
|
|
90
91
|
"author": "RackMind <hello@rackmind.ai>",
|
|
91
|
-
"license": "
|
|
92
|
+
"license": "BUSL-1.1",
|
|
92
93
|
"engines": {
|
|
93
94
|
"node": ">=20.0.0"
|
|
94
95
|
},
|