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.
Files changed (183) hide show
  1. package/README.md +179 -0
  2. package/bin/rackmind.js +6 -0
  3. package/dist/ai/client.d.ts +71 -0
  4. package/dist/ai/client.d.ts.map +1 -0
  5. package/dist/ai/client.js +198 -0
  6. package/dist/ai/client.js.map +1 -0
  7. package/dist/ai/system-prompt.d.ts +20 -0
  8. package/dist/ai/system-prompt.d.ts.map +1 -0
  9. package/dist/ai/system-prompt.js +169 -0
  10. package/dist/ai/system-prompt.js.map +1 -0
  11. package/dist/ai/tool-executor.d.ts +27 -0
  12. package/dist/ai/tool-executor.d.ts.map +1 -0
  13. package/dist/ai/tool-executor.js +184 -0
  14. package/dist/ai/tool-executor.js.map +1 -0
  15. package/dist/ai/tools.d.ts +7 -0
  16. package/dist/ai/tools.d.ts.map +1 -0
  17. package/dist/ai/tools.js +203 -0
  18. package/dist/ai/tools.js.map +1 -0
  19. package/dist/commands/config.d.ts +21 -0
  20. package/dist/commands/config.d.ts.map +1 -0
  21. package/dist/commands/config.js +169 -0
  22. package/dist/commands/config.js.map +1 -0
  23. package/dist/commands/connect.d.ts +7 -0
  24. package/dist/commands/connect.d.ts.map +1 -0
  25. package/dist/commands/connect.js +117 -0
  26. package/dist/commands/connect.js.map +1 -0
  27. package/dist/commands/containers.d.ts +5 -0
  28. package/dist/commands/containers.d.ts.map +1 -0
  29. package/dist/commands/containers.js +83 -0
  30. package/dist/commands/containers.js.map +1 -0
  31. package/dist/commands/exec.d.ts +5 -0
  32. package/dist/commands/exec.d.ts.map +1 -0
  33. package/dist/commands/exec.js +133 -0
  34. package/dist/commands/exec.js.map +1 -0
  35. package/dist/commands/lifecycle.d.ts +7 -0
  36. package/dist/commands/lifecycle.d.ts.map +1 -0
  37. package/dist/commands/lifecycle.js +213 -0
  38. package/dist/commands/lifecycle.js.map +1 -0
  39. package/dist/commands/logs.d.ts +5 -0
  40. package/dist/commands/logs.d.ts.map +1 -0
  41. package/dist/commands/logs.js +117 -0
  42. package/dist/commands/logs.js.map +1 -0
  43. package/dist/commands/report.d.ts +6 -0
  44. package/dist/commands/report.d.ts.map +1 -0
  45. package/dist/commands/report.js +203 -0
  46. package/dist/commands/report.js.map +1 -0
  47. package/dist/commands/servers.d.ts +17 -0
  48. package/dist/commands/servers.d.ts.map +1 -0
  49. package/dist/commands/servers.js +116 -0
  50. package/dist/commands/servers.js.map +1 -0
  51. package/dist/commands/status.d.ts +5 -0
  52. package/dist/commands/status.d.ts.map +1 -0
  53. package/dist/commands/status.js +174 -0
  54. package/dist/commands/status.js.map +1 -0
  55. package/dist/commands/vms.d.ts +5 -0
  56. package/dist/commands/vms.d.ts.map +1 -0
  57. package/dist/commands/vms.js +83 -0
  58. package/dist/commands/vms.js.map +1 -0
  59. package/dist/config/crypto.d.ts +20 -0
  60. package/dist/config/crypto.d.ts.map +1 -0
  61. package/dist/config/crypto.js +78 -0
  62. package/dist/config/crypto.js.map +1 -0
  63. package/dist/config/index.d.ts +21 -0
  64. package/dist/config/index.d.ts.map +1 -0
  65. package/dist/config/index.js +158 -0
  66. package/dist/config/index.js.map +1 -0
  67. package/dist/config/types.d.ts +263 -0
  68. package/dist/config/types.d.ts.map +1 -0
  69. package/dist/config/types.js +83 -0
  70. package/dist/config/types.js.map +1 -0
  71. package/dist/globals.d.ts +22 -0
  72. package/dist/globals.d.ts.map +1 -0
  73. package/dist/globals.js +43 -0
  74. package/dist/globals.js.map +1 -0
  75. package/dist/index.d.ts +2 -0
  76. package/dist/index.d.ts.map +1 -0
  77. package/dist/index.js +399 -0
  78. package/dist/index.js.map +1 -0
  79. package/dist/interactive/App.d.ts +3 -0
  80. package/dist/interactive/App.d.ts.map +1 -0
  81. package/dist/interactive/App.js +103 -0
  82. package/dist/interactive/App.js.map +1 -0
  83. package/dist/interactive/components/Header.d.ts +8 -0
  84. package/dist/interactive/components/Header.d.ts.map +1 -0
  85. package/dist/interactive/components/Header.js +20 -0
  86. package/dist/interactive/components/Header.js.map +1 -0
  87. package/dist/interactive/components/InputBar.d.ts +9 -0
  88. package/dist/interactive/components/InputBar.d.ts.map +1 -0
  89. package/dist/interactive/components/InputBar.js +89 -0
  90. package/dist/interactive/components/InputBar.js.map +1 -0
  91. package/dist/interactive/components/MessageList.d.ts +9 -0
  92. package/dist/interactive/components/MessageList.d.ts.map +1 -0
  93. package/dist/interactive/components/MessageList.js +28 -0
  94. package/dist/interactive/components/MessageList.js.map +1 -0
  95. package/dist/interactive/components/Spinner.d.ts +7 -0
  96. package/dist/interactive/components/Spinner.d.ts.map +1 -0
  97. package/dist/interactive/components/Spinner.js +19 -0
  98. package/dist/interactive/components/Spinner.js.map +1 -0
  99. package/dist/interactive/components/StatusBar.d.ts +10 -0
  100. package/dist/interactive/components/StatusBar.d.ts.map +1 -0
  101. package/dist/interactive/components/StatusBar.js +7 -0
  102. package/dist/interactive/components/StatusBar.js.map +1 -0
  103. package/dist/interactive/components/StreamingMessage.d.ts +7 -0
  104. package/dist/interactive/components/StreamingMessage.d.ts.map +1 -0
  105. package/dist/interactive/components/StreamingMessage.js +9 -0
  106. package/dist/interactive/components/StreamingMessage.js.map +1 -0
  107. package/dist/interactive/components/ToolIndicator.d.ts +7 -0
  108. package/dist/interactive/components/ToolIndicator.d.ts.map +1 -0
  109. package/dist/interactive/components/ToolIndicator.js +10 -0
  110. package/dist/interactive/components/ToolIndicator.js.map +1 -0
  111. package/dist/interactive/components/ToolOutput.d.ts +8 -0
  112. package/dist/interactive/components/ToolOutput.d.ts.map +1 -0
  113. package/dist/interactive/components/ToolOutput.js +9 -0
  114. package/dist/interactive/components/ToolOutput.js.map +1 -0
  115. package/dist/interactive/components/WelcomeScreen.d.ts +9 -0
  116. package/dist/interactive/components/WelcomeScreen.d.ts.map +1 -0
  117. package/dist/interactive/components/WelcomeScreen.js +8 -0
  118. package/dist/interactive/components/WelcomeScreen.js.map +1 -0
  119. package/dist/interactive/launch.d.ts +6 -0
  120. package/dist/interactive/launch.d.ts.map +1 -0
  121. package/dist/interactive/launch.js +30 -0
  122. package/dist/interactive/launch.js.map +1 -0
  123. package/dist/interactive/slashCommands.d.ts +13 -0
  124. package/dist/interactive/slashCommands.d.ts.map +1 -0
  125. package/dist/interactive/slashCommands.js +102 -0
  126. package/dist/interactive/slashCommands.js.map +1 -0
  127. package/dist/interactive/useRackMind.d.ts +30 -0
  128. package/dist/interactive/useRackMind.d.ts.map +1 -0
  129. package/dist/interactive/useRackMind.js +241 -0
  130. package/dist/interactive/useRackMind.js.map +1 -0
  131. package/dist/oneshot/run.d.ts +2 -0
  132. package/dist/oneshot/run.d.ts.map +1 -0
  133. package/dist/oneshot/run.js +195 -0
  134. package/dist/oneshot/run.js.map +1 -0
  135. package/dist/server/client.d.ts +89 -0
  136. package/dist/server/client.d.ts.map +1 -0
  137. package/dist/server/client.js +239 -0
  138. package/dist/server/client.js.map +1 -0
  139. package/dist/server/proxmox.d.ts +160 -0
  140. package/dist/server/proxmox.d.ts.map +1 -0
  141. package/dist/server/proxmox.js +219 -0
  142. package/dist/server/proxmox.js.map +1 -0
  143. package/dist/server/ssh.d.ts +80 -0
  144. package/dist/server/ssh.d.ts.map +1 -0
  145. package/dist/server/ssh.js +262 -0
  146. package/dist/server/ssh.js.map +1 -0
  147. package/dist/utils/ascii.d.ts +3 -0
  148. package/dist/utils/ascii.d.ts.map +1 -0
  149. package/dist/utils/ascii.js +16 -0
  150. package/dist/utils/ascii.js.map +1 -0
  151. package/dist/utils/format.d.ts +7 -0
  152. package/dist/utils/format.d.ts.map +1 -0
  153. package/dist/utils/format.js +20 -0
  154. package/dist/utils/format.js.map +1 -0
  155. package/dist/utils/history.d.ts +23 -0
  156. package/dist/utils/history.d.ts.map +1 -0
  157. package/dist/utils/history.js +107 -0
  158. package/dist/utils/history.js.map +1 -0
  159. package/dist/utils/logger.d.ts +9 -0
  160. package/dist/utils/logger.d.ts.map +1 -0
  161. package/dist/utils/logger.js +51 -0
  162. package/dist/utils/logger.js.map +1 -0
  163. package/dist/utils/markdown.d.ts +12 -0
  164. package/dist/utils/markdown.d.ts.map +1 -0
  165. package/dist/utils/markdown.js +76 -0
  166. package/dist/utils/markdown.js.map +1 -0
  167. package/dist/utils/prompt.d.ts +25 -0
  168. package/dist/utils/prompt.d.ts.map +1 -0
  169. package/dist/utils/prompt.js +133 -0
  170. package/dist/utils/prompt.js.map +1 -0
  171. package/dist/utils/retry.d.ts +37 -0
  172. package/dist/utils/retry.d.ts.map +1 -0
  173. package/dist/utils/retry.js +122 -0
  174. package/dist/utils/retry.js.map +1 -0
  175. package/dist/utils/shutdown.d.ts +22 -0
  176. package/dist/utils/shutdown.d.ts.map +1 -0
  177. package/dist/utils/shutdown.js +94 -0
  178. package/dist/utils/shutdown.js.map +1 -0
  179. package/dist/utils/table.d.ts +38 -0
  180. package/dist/utils/table.d.ts.map +1 -0
  181. package/dist/utils/table.js +150 -0
  182. package/dist/utils/table.js.map +1 -0
  183. package/package.json +71 -0
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Render a markdown string to terminal-formatted output.
3
+ * Handles code blocks, bold, italic, lists, headers, links, etc.
4
+ */
5
+ export declare function renderMarkdown(text: string): string;
6
+ /**
7
+ * Render markdown for streaming output — handles partial markdown gracefully.
8
+ * This is less aggressive than full rendering to avoid broken partial blocks.
9
+ * Applies inline formatting only (bold, italic, code spans).
10
+ */
11
+ export declare function renderMarkdownInline(text: string): string;
12
+ //# sourceMappingURL=markdown.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"markdown.d.ts","sourceRoot":"","sources":["../../src/utils/markdown.ts"],"names":[],"mappings":"AA4CA;;;GAGG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAiBnD;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAYzD"}
@@ -0,0 +1,76 @@
1
+ // ---------------------------------------------------------------------------
2
+ // Markdown rendering — converts AI markdown responses to terminal output
3
+ // ---------------------------------------------------------------------------
4
+ // Uses `marked` + `marked-terminal` for rendering markdown with proper
5
+ // formatting: headers, bold, italic, lists, code blocks with syntax
6
+ // highlighting via `marked-terminal`.
7
+ // ---------------------------------------------------------------------------
8
+ import { marked } from 'marked';
9
+ import { markedTerminal } from 'marked-terminal';
10
+ import chalk from 'chalk';
11
+ // ---------------------------------------------------------------------------
12
+ // Initialization — configure marked with terminal renderer
13
+ // ---------------------------------------------------------------------------
14
+ let initialized = false;
15
+ function ensureInitialized() {
16
+ if (initialized)
17
+ return;
18
+ // marked-terminal returns an object with { renderer, useNewRenderer }
19
+ // but @types/marked-terminal declares it as returning TerminalRenderer.
20
+ // Cast to MarkedExtension to satisfy TypeScript — runtime is correct.
21
+ const terminalExtension = markedTerminal({
22
+ // Style overrides for terminal rendering
23
+ strong: chalk.bold,
24
+ em: chalk.italic,
25
+ heading: chalk.cyan.bold,
26
+ hr: () => chalk.dim('-'.repeat(60)),
27
+ tableOptions: {
28
+ style: { head: ['cyan'] },
29
+ },
30
+ });
31
+ marked.use(terminalExtension);
32
+ initialized = true;
33
+ }
34
+ // ---------------------------------------------------------------------------
35
+ // Public API
36
+ // ---------------------------------------------------------------------------
37
+ /**
38
+ * Render a markdown string to terminal-formatted output.
39
+ * Handles code blocks, bold, italic, lists, headers, links, etc.
40
+ */
41
+ export function renderMarkdown(text) {
42
+ ensureInitialized();
43
+ try {
44
+ const rendered = marked.parse(text);
45
+ if (typeof rendered !== 'string') {
46
+ // marked.parse can return a Promise if async extensions are used;
47
+ // we only use synchronous extensions, so this shouldn't happen
48
+ return text;
49
+ }
50
+ // Trim trailing whitespace but preserve structure
51
+ return rendered.replace(/\n{3,}/g, '\n\n').trimEnd();
52
+ }
53
+ catch {
54
+ // If rendering fails, return the raw text
55
+ return text;
56
+ }
57
+ }
58
+ /**
59
+ * Render markdown for streaming output — handles partial markdown gracefully.
60
+ * This is less aggressive than full rendering to avoid broken partial blocks.
61
+ * Applies inline formatting only (bold, italic, code spans).
62
+ */
63
+ export function renderMarkdownInline(text) {
64
+ ensureInitialized();
65
+ try {
66
+ const rendered = marked.parseInline(text);
67
+ if (typeof rendered !== 'string') {
68
+ return text;
69
+ }
70
+ return rendered;
71
+ }
72
+ catch {
73
+ return text;
74
+ }
75
+ }
76
+ //# sourceMappingURL=markdown.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"markdown.js","sourceRoot":"","sources":["../../src/utils/markdown.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,yEAAyE;AACzE,8EAA8E;AAC9E,uEAAuE;AACvE,oEAAoE;AACpE,sCAAsC;AACtC,8EAA8E;AAE9E,OAAO,EAAE,MAAM,EAAwB,MAAM,QAAQ,CAAC;AACtD,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,8EAA8E;AAC9E,2DAA2D;AAC3D,8EAA8E;AAE9E,IAAI,WAAW,GAAG,KAAK,CAAC;AAExB,SAAS,iBAAiB;IACtB,IAAI,WAAW;QAAE,OAAO;IAExB,sEAAsE;IACtE,wEAAwE;IACxE,sEAAsE;IACtE,MAAM,iBAAiB,GAAG,cAAc,CAAC;QACrC,yCAAyC;QACzC,MAAM,EAAE,KAAK,CAAC,IAAI;QAClB,EAAE,EAAE,KAAK,CAAC,MAAM;QAChB,OAAO,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI;QACxB,EAAE,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QACnC,YAAY,EAAE;YACV,KAAK,EAAE,EAAE,IAAI,EAAE,CAAC,MAAM,CAAC,EAAE;SAC5B;KACJ,CAA+B,CAAC;IAEjC,MAAM,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC;IAE9B,WAAW,GAAG,IAAI,CAAC;AACvB,CAAC;AAED,8EAA8E;AAC9E,aAAa;AACb,8EAA8E;AAE9E;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,IAAY;IACvC,iBAAiB,EAAE,CAAC;IAEpB,IAAI,CAAC;QACD,MAAM,QAAQ,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACpC,IAAI,OAAO,QAAQ,KAAK,QAAQ,EAAE,CAAC;YAC/B,kEAAkE;YAClE,+DAA+D;YAC/D,OAAO,IAAI,CAAC;QAChB,CAAC;QAED,kDAAkD;QAClD,OAAO,QAAQ,CAAC,OAAO,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC,OAAO,EAAE,CAAC;IACzD,CAAC;IAAC,MAAM,CAAC;QACL,0CAA0C;QAC1C,OAAO,IAAI,CAAC;IAChB,CAAC;AACL,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,oBAAoB,CAAC,IAAY;IAC7C,iBAAiB,EAAE,CAAC;IAEpB,IAAI,CAAC;QACD,MAAM,QAAQ,GAAG,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;QAC1C,IAAI,OAAO,QAAQ,KAAK,QAAQ,EAAE,CAAC;YAC/B,OAAO,IAAI,CAAC;QAChB,CAAC;QACD,OAAO,QAAQ,CAAC;IACpB,CAAC;IAAC,MAAM,CAAC;QACL,OAAO,IAAI,CAAC;IAChB,CAAC;AACL,CAAC"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Prompt for a text value. Returns the trimmed input or the default value.
3
+ */
4
+ export declare function promptText(message: string, defaultValue?: string): Promise<string>;
5
+ /**
6
+ * Prompt for a number. Returns the parsed number or the default value.
7
+ */
8
+ export declare function promptNumber(message: string, defaultValue: number): Promise<number>;
9
+ /**
10
+ * Prompt for a secret value (password, token). Masks input with asterisks.
11
+ */
12
+ export declare function promptSecret(message: string): Promise<string>;
13
+ /**
14
+ * Prompt for a yes/no confirmation. Returns true for yes.
15
+ */
16
+ export declare function promptConfirm(message: string, defaultYes?: boolean): Promise<boolean>;
17
+ /**
18
+ * Prompt for a selection from a list of options.
19
+ * Returns the selected option value.
20
+ */
21
+ export declare function promptSelect<T extends string>(message: string, options: Array<{
22
+ label: string;
23
+ value: T;
24
+ }>, defaultValue?: T): Promise<T>;
25
+ //# sourceMappingURL=prompt.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompt.d.ts","sourceRoot":"","sources":["../../src/utils/prompt.ts"],"names":[],"mappings":"AAgBA;;GAEG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAWlF;AAED;;GAEG;AACH,wBAAsB,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAIzF;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAkE7D;AAED;;GAEG;AACH,wBAAsB,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,UAAQ,GAAG,OAAO,CAAC,OAAO,CAAC,CAMzF;AAED;;;GAGG;AACH,wBAAsB,YAAY,CAAC,CAAC,SAAS,MAAM,EAC/C,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,KAAK,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,CAAC,CAAA;CAAE,CAAC,EAC3C,YAAY,CAAC,EAAE,CAAC,GACjB,OAAO,CAAC,CAAC,CAAC,CAsBZ"}
@@ -0,0 +1,133 @@
1
+ // ---------------------------------------------------------------------------
2
+ // Interactive prompts — minimal readline-based prompts for CLI flows
3
+ // ---------------------------------------------------------------------------
4
+ // Uses Node's built-in readline module. No external deps needed.
5
+ // ---------------------------------------------------------------------------
6
+ import readline from 'node:readline';
7
+ function createInterface() {
8
+ return readline.createInterface({
9
+ input: process.stdin,
10
+ output: process.stderr, // prompts go to stderr so stdout stays pipeable
11
+ terminal: true,
12
+ });
13
+ }
14
+ /**
15
+ * Prompt for a text value. Returns the trimmed input or the default value.
16
+ */
17
+ export function promptText(message, defaultValue) {
18
+ const rl = createInterface();
19
+ const suffix = defaultValue ? ` (${defaultValue})` : '';
20
+ return new Promise((resolve) => {
21
+ rl.question(`${message}${suffix}: `, (answer) => {
22
+ rl.close();
23
+ const trimmed = answer.trim();
24
+ resolve(trimmed || defaultValue || '');
25
+ });
26
+ });
27
+ }
28
+ /**
29
+ * Prompt for a number. Returns the parsed number or the default value.
30
+ */
31
+ export async function promptNumber(message, defaultValue) {
32
+ const raw = await promptText(message, String(defaultValue));
33
+ const parsed = parseInt(raw, 10);
34
+ return isNaN(parsed) ? defaultValue : parsed;
35
+ }
36
+ /**
37
+ * Prompt for a secret value (password, token). Masks input with asterisks.
38
+ */
39
+ export function promptSecret(message) {
40
+ return new Promise((resolve) => {
41
+ const rl = readline.createInterface({
42
+ input: process.stdin,
43
+ output: process.stderr,
44
+ terminal: true,
45
+ });
46
+ // Mask input characters
47
+ const originalWrite = rl.write.bind(rl);
48
+ let masked = false;
49
+ process.stderr.write(`${message}: `);
50
+ masked = true;
51
+ // We need to handle the raw mode ourselves for masking
52
+ if (process.stdin.isTTY) {
53
+ process.stdin.setRawMode(true);
54
+ }
55
+ let secret = '';
56
+ const onData = (key) => {
57
+ const char = key.toString('utf8');
58
+ if (char === '\n' || char === '\r') {
59
+ // Enter pressed
60
+ process.stderr.write('\n');
61
+ if (process.stdin.isTTY) {
62
+ process.stdin.setRawMode(false);
63
+ }
64
+ process.stdin.removeListener('data', onData);
65
+ rl.close();
66
+ resolve(secret);
67
+ }
68
+ else if (char === '\u0003') {
69
+ // Ctrl+C
70
+ process.stderr.write('\n');
71
+ if (process.stdin.isTTY) {
72
+ process.stdin.setRawMode(false);
73
+ }
74
+ process.stdin.removeListener('data', onData);
75
+ rl.close();
76
+ process.exit(130);
77
+ }
78
+ else if (char === '\u007F' || char === '\b') {
79
+ // Backspace
80
+ if (secret.length > 0) {
81
+ secret = secret.slice(0, -1);
82
+ process.stderr.write('\b \b');
83
+ }
84
+ }
85
+ else if (masked && char.length === 1 && char.charCodeAt(0) >= 32) {
86
+ secret += char;
87
+ process.stderr.write('*');
88
+ }
89
+ };
90
+ // Suppress the default readline behavior when in raw mode
91
+ rl.write = (...args) => {
92
+ if (!masked) {
93
+ return originalWrite(...args);
94
+ }
95
+ // Suppress default output during masked input
96
+ return undefined;
97
+ };
98
+ process.stdin.on('data', onData);
99
+ });
100
+ }
101
+ /**
102
+ * Prompt for a yes/no confirmation. Returns true for yes.
103
+ */
104
+ export async function promptConfirm(message, defaultYes = false) {
105
+ const hint = defaultYes ? '[Y/n]' : '[y/N]';
106
+ const raw = await promptText(`${message} ${hint}`);
107
+ if (!raw)
108
+ return defaultYes;
109
+ return raw.toLowerCase().startsWith('y');
110
+ }
111
+ /**
112
+ * Prompt for a selection from a list of options.
113
+ * Returns the selected option value.
114
+ */
115
+ export async function promptSelect(message, options, defaultValue) {
116
+ process.stderr.write(`\n${message}\n`);
117
+ for (let i = 0; i < options.length; i++) {
118
+ const marker = options[i].value === defaultValue ? ' (default)' : '';
119
+ process.stderr.write(` ${i + 1}. ${options[i].label}${marker}\n`);
120
+ }
121
+ const raw = await promptText('Select', defaultValue ? '1' : undefined);
122
+ const index = parseInt(raw, 10) - 1;
123
+ if (index >= 0 && index < options.length) {
124
+ return options[index].value;
125
+ }
126
+ // Try matching by value
127
+ const match = options.find((o) => o.value === raw || o.label.toLowerCase() === raw.toLowerCase());
128
+ if (match)
129
+ return match.value;
130
+ // Fall back to default
131
+ return defaultValue ?? options[0].value;
132
+ }
133
+ //# sourceMappingURL=prompt.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompt.js","sourceRoot":"","sources":["../../src/utils/prompt.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,37 @@
1
+ export interface RetryOptions {
2
+ /** Maximum number of attempts (including the first) */
3
+ maxAttempts: number;
4
+ /** Initial delay in milliseconds before first retry */
5
+ initialDelayMs: number;
6
+ /** Maximum delay in milliseconds between retries */
7
+ maxDelayMs: number;
8
+ /** Multiplier for exponential backoff (default: 2) */
9
+ backoffMultiplier?: number;
10
+ /** Optional predicate to decide if an error is retryable */
11
+ isRetryable?: (error: unknown) => boolean;
12
+ /** Optional callback called before each retry */
13
+ onRetry?: (attempt: number, error: unknown, delayMs: number) => void;
14
+ }
15
+ /**
16
+ * Check if an error is likely a transient failure worth retrying.
17
+ */
18
+ export declare function isTransientError(error: unknown): boolean;
19
+ /**
20
+ * Check if an error is an Anthropic rate limit specifically.
21
+ * These get special treatment with longer backoff.
22
+ */
23
+ export declare function isRateLimitError(error: unknown): boolean;
24
+ /**
25
+ * Execute a function with retry logic and exponential backoff.
26
+ *
27
+ * @param fn - The async function to execute
28
+ * @param options - Retry configuration
29
+ * @returns The result of the function
30
+ * @throws The last error if all attempts fail
31
+ */
32
+ export declare function withRetry<T>(fn: () => Promise<T>, options: RetryOptions): Promise<T>;
33
+ /** Retry options for Claude API calls (rate limit + overload) */
34
+ export declare const CLAUDE_API_RETRY: RetryOptions;
35
+ /** Retry options for SSH reconnection */
36
+ export declare const SSH_RECONNECT_RETRY: RetryOptions;
37
+ //# sourceMappingURL=retry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"retry.d.ts","sourceRoot":"","sources":["../../src/utils/retry.ts"],"names":[],"mappings":"AAYA,MAAM,WAAW,YAAY;IACzB,uDAAuD;IACvD,WAAW,EAAE,MAAM,CAAC;IACpB,uDAAuD;IACvD,cAAc,EAAE,MAAM,CAAC;IACvB,oDAAoD;IACpD,UAAU,EAAE,MAAM,CAAC;IACnB,sDAAsD;IACtD,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,4DAA4D;IAC5D,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,OAAO,CAAC;IAC1C,iDAAiD;IACjD,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CACxE;AAMD;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CA+BxD;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAIxD;AAMD;;;;;;;GAOG;AACH,wBAAsB,SAAS,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,CAAC,CAAC,CAuC1F;AAcD,iEAAiE;AACjE,eAAO,MAAM,gBAAgB,EAAE,YAK9B,CAAC;AAEF,yCAAyC;AACzC,eAAO,MAAM,mBAAmB,EAAE,YAejC,CAAC"}
@@ -0,0 +1,122 @@
1
+ // ---------------------------------------------------------------------------
2
+ // Retry utility — exponential backoff for transient failures
3
+ // ---------------------------------------------------------------------------
4
+ // Used for Claude API rate limits, SSH reconnects, and Proxmox API retries.
5
+ // ---------------------------------------------------------------------------
6
+ import { logger } from './logger.js';
7
+ // ---------------------------------------------------------------------------
8
+ // Default retryable error detection
9
+ // ---------------------------------------------------------------------------
10
+ /**
11
+ * Check if an error is likely a transient failure worth retrying.
12
+ */
13
+ export function isTransientError(error) {
14
+ const message = error instanceof Error ? error.message : String(error);
15
+ const lc = message.toLowerCase();
16
+ // Rate limits
17
+ if (lc.includes('429') || lc.includes('rate_limit') || lc.includes('rate limit')) {
18
+ return true;
19
+ }
20
+ // API overloaded
21
+ if (lc.includes('overloaded') || lc.includes('529') || lc.includes('503')) {
22
+ return true;
23
+ }
24
+ // Network errors
25
+ if (lc.includes('econnreset') ||
26
+ lc.includes('etimedout') ||
27
+ lc.includes('socket hang up') ||
28
+ lc.includes('epipe') ||
29
+ lc.includes('network')) {
30
+ return true;
31
+ }
32
+ // Server errors (5xx)
33
+ if (lc.includes('500') || lc.includes('502') || lc.includes('504')) {
34
+ return true;
35
+ }
36
+ return false;
37
+ }
38
+ /**
39
+ * Check if an error is an Anthropic rate limit specifically.
40
+ * These get special treatment with longer backoff.
41
+ */
42
+ export function isRateLimitError(error) {
43
+ const message = error instanceof Error ? error.message : String(error);
44
+ const lc = message.toLowerCase();
45
+ return lc.includes('429') || lc.includes('rate_limit') || lc.includes('rate limit');
46
+ }
47
+ // ---------------------------------------------------------------------------
48
+ // Retry function
49
+ // ---------------------------------------------------------------------------
50
+ /**
51
+ * Execute a function with retry logic and exponential backoff.
52
+ *
53
+ * @param fn - The async function to execute
54
+ * @param options - Retry configuration
55
+ * @returns The result of the function
56
+ * @throws The last error if all attempts fail
57
+ */
58
+ export async function withRetry(fn, options) {
59
+ const { maxAttempts, initialDelayMs, maxDelayMs, backoffMultiplier = 2 } = options;
60
+ const isRetryable = options.isRetryable ?? isTransientError;
61
+ let lastError;
62
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
63
+ try {
64
+ return await fn();
65
+ }
66
+ catch (error) {
67
+ lastError = error;
68
+ // Don't retry if we've exhausted attempts or error isn't retryable
69
+ if (attempt >= maxAttempts || !isRetryable(error)) {
70
+ throw error;
71
+ }
72
+ // Calculate delay with exponential backoff + jitter
73
+ const baseDelay = initialDelayMs * Math.pow(backoffMultiplier, attempt - 1);
74
+ const jitter = Math.random() * 0.3 * baseDelay; // 0-30% jitter
75
+ const delay = Math.min(baseDelay + jitter, maxDelayMs);
76
+ logger.info('Retrying after transient error', {
77
+ attempt,
78
+ maxAttempts,
79
+ delayMs: Math.round(delay),
80
+ error: error instanceof Error ? error.message : String(error),
81
+ });
82
+ if (options.onRetry) {
83
+ options.onRetry(attempt, error, Math.round(delay));
84
+ }
85
+ await sleep(delay);
86
+ }
87
+ }
88
+ // Should not reach here, but TypeScript needs it
89
+ throw lastError;
90
+ }
91
+ // ---------------------------------------------------------------------------
92
+ // Helpers
93
+ // ---------------------------------------------------------------------------
94
+ function sleep(ms) {
95
+ return new Promise((resolve) => setTimeout(resolve, ms));
96
+ }
97
+ // ---------------------------------------------------------------------------
98
+ // Presets
99
+ // ---------------------------------------------------------------------------
100
+ /** Retry options for Claude API calls (rate limit + overload) */
101
+ export const CLAUDE_API_RETRY = {
102
+ maxAttempts: 3,
103
+ initialDelayMs: 2000,
104
+ maxDelayMs: 30_000,
105
+ isRetryable: isTransientError,
106
+ };
107
+ /** Retry options for SSH reconnection */
108
+ export const SSH_RECONNECT_RETRY = {
109
+ maxAttempts: 3,
110
+ initialDelayMs: 1000,
111
+ maxDelayMs: 10_000,
112
+ isRetryable: (error) => {
113
+ const msg = error instanceof Error ? error.message : String(error);
114
+ return (msg.includes('ECONNRESET') ||
115
+ msg.includes('ECONNREFUSED') ||
116
+ msg.includes('ETIMEDOUT') ||
117
+ msg.includes('not connected') ||
118
+ msg.includes('SSH') ||
119
+ msg.includes('socket hang up'));
120
+ },
121
+ };
122
+ //# sourceMappingURL=retry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"retry.js","sourceRoot":"","sources":["../../src/utils/retry.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ type CleanupFn = () => void | Promise<void>;
2
+ /**
3
+ * Register a cleanup function to be called on shutdown.
4
+ * Functions are called in reverse order (LIFO).
5
+ */
6
+ export declare function onShutdown(fn: CleanupFn): void;
7
+ /**
8
+ * Remove a previously registered cleanup function.
9
+ */
10
+ export declare function removeShutdownHandler(fn: CleanupFn): void;
11
+ /**
12
+ * Run all cleanup functions. Called automatically on SIGINT/SIGTERM,
13
+ * but can also be called manually for controlled shutdown.
14
+ */
15
+ export declare function runCleanup(): Promise<void>;
16
+ /**
17
+ * Register SIGINT and SIGTERM signal handlers for graceful shutdown.
18
+ * Call once at application startup. Safe to call multiple times.
19
+ */
20
+ export declare function registerSignalHandlers(): void;
21
+ export {};
22
+ //# sourceMappingURL=shutdown.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shutdown.d.ts","sourceRoot":"","sources":["../../src/utils/shutdown.ts"],"names":[],"mappings":"AAaA,KAAK,SAAS,GAAG,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAS5C;;;GAGG;AACH,wBAAgB,UAAU,CAAC,EAAE,EAAE,SAAS,GAAG,IAAI,CAE9C;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,EAAE,EAAE,SAAS,GAAG,IAAI,CAKzD;AAED;;;GAGG;AACH,wBAAsB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAoBhD;AAQD;;;GAGG;AACH,wBAAgB,sBAAsB,IAAI,IAAI,CAqC7C"}
@@ -0,0 +1,94 @@
1
+ // ---------------------------------------------------------------------------
2
+ // Graceful shutdown — clean up resources on exit
3
+ // ---------------------------------------------------------------------------
4
+ // Registers SIGINT/SIGTERM handlers and tracks resources that need cleanup.
5
+ // Ensures SSH connections are closed and optional conversation history is saved.
6
+ // ---------------------------------------------------------------------------
7
+ import { logger } from './logger.js';
8
+ // ---------------------------------------------------------------------------
9
+ // Cleanup registry
10
+ // ---------------------------------------------------------------------------
11
+ const cleanupFns = [];
12
+ let shutdownInitiated = false;
13
+ /**
14
+ * Register a cleanup function to be called on shutdown.
15
+ * Functions are called in reverse order (LIFO).
16
+ */
17
+ export function onShutdown(fn) {
18
+ cleanupFns.push(fn);
19
+ }
20
+ /**
21
+ * Remove a previously registered cleanup function.
22
+ */
23
+ export function removeShutdownHandler(fn) {
24
+ const index = cleanupFns.indexOf(fn);
25
+ if (index >= 0) {
26
+ cleanupFns.splice(index, 1);
27
+ }
28
+ }
29
+ /**
30
+ * Run all cleanup functions. Called automatically on SIGINT/SIGTERM,
31
+ * but can also be called manually for controlled shutdown.
32
+ */
33
+ export async function runCleanup() {
34
+ if (shutdownInitiated)
35
+ return;
36
+ shutdownInitiated = true;
37
+ logger.info('Running shutdown cleanup', { handlerCount: cleanupFns.length });
38
+ // Run in reverse order (LIFO)
39
+ const fns = [...cleanupFns].reverse();
40
+ for (const fn of fns) {
41
+ try {
42
+ await fn();
43
+ }
44
+ catch (err) {
45
+ const message = err instanceof Error ? err.message : String(err);
46
+ logger.error('Cleanup handler failed', { error: message });
47
+ // Continue with other cleanup handlers
48
+ }
49
+ }
50
+ cleanupFns.length = 0;
51
+ }
52
+ // ---------------------------------------------------------------------------
53
+ // Signal handlers
54
+ // ---------------------------------------------------------------------------
55
+ let handlersRegistered = false;
56
+ /**
57
+ * Register SIGINT and SIGTERM signal handlers for graceful shutdown.
58
+ * Call once at application startup. Safe to call multiple times.
59
+ */
60
+ export function registerSignalHandlers() {
61
+ if (handlersRegistered)
62
+ return;
63
+ handlersRegistered = true;
64
+ const handleSignal = (signal) => {
65
+ logger.info('Received signal', { signal });
66
+ runCleanup()
67
+ .then(() => {
68
+ process.exit(0);
69
+ })
70
+ .catch(() => {
71
+ process.exit(1);
72
+ });
73
+ };
74
+ process.on('SIGINT', () => handleSignal('SIGINT'));
75
+ process.on('SIGTERM', () => handleSignal('SIGTERM'));
76
+ // Handle uncaught exceptions gracefully
77
+ process.on('uncaughtException', (error) => {
78
+ logger.error('Uncaught exception', { error: error.message, stack: error.stack });
79
+ runCleanup()
80
+ .then(() => {
81
+ process.exit(1);
82
+ })
83
+ .catch(() => {
84
+ process.exit(1);
85
+ });
86
+ });
87
+ // Handle unhandled promise rejections
88
+ process.on('unhandledRejection', (reason) => {
89
+ const message = reason instanceof Error ? reason.message : String(reason);
90
+ logger.error('Unhandled rejection', { error: message });
91
+ // Don't exit on unhandled rejections — log and continue
92
+ });
93
+ }
94
+ //# sourceMappingURL=shutdown.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shutdown.js","sourceRoot":"","sources":["../../src/utils/shutdown.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,38 @@
1
+ import Table from 'cli-table3';
2
+ export declare const TABLE_CHARS: Table.TableConstructorOptions['chars'];
3
+ /**
4
+ * Format bytes into a human-readable string (e.g., 1.23 GiB).
5
+ * Uses binary units (1 GiB = 1024^3 bytes).
6
+ */
7
+ export declare function formatBytes(bytes: number): string;
8
+ /**
9
+ * Format a ratio (0-1) as a colored percentage string.
10
+ * Green: 0-59%, Yellow: 60-84%, Red: 85-100%
11
+ */
12
+ export declare function formatPercent(ratio: number): string;
13
+ /**
14
+ * Format a usage bar with percentage (e.g., "45% [████████░░░░░░░]").
15
+ */
16
+ export declare function formatUsageBar(ratio: number, width?: number): string;
17
+ /**
18
+ * Format uptime in seconds into a human-readable string (e.g., "3d 12h 5m").
19
+ */
20
+ export declare function formatUptime(seconds: number): string;
21
+ /**
22
+ * Color-code a guest status string.
23
+ * running = green, stopped = yellow, paused = cyan, error/unknown = red
24
+ */
25
+ export declare function colorStatus(status: string): string;
26
+ /**
27
+ * Truncate a string to the given max length, appending an ellipsis if needed.
28
+ */
29
+ export declare function truncate(str: string, maxLen: number): string;
30
+ /**
31
+ * Create a styled cli-table3 instance with consistent formatting.
32
+ */
33
+ export declare function createTable(head: string[]): Table.Table;
34
+ /**
35
+ * Print a section header with a title and optional count.
36
+ */
37
+ export declare function printSectionHeader(title: string, count?: number): void;
38
+ //# sourceMappingURL=table.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"table.d.ts","sourceRoot":"","sources":["../../src/utils/table.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,MAAM,YAAY,CAAC;AAM/B,eAAO,MAAM,WAAW,EAAE,KAAK,CAAC,uBAAuB,CAAC,OAAO,CAgB9D,CAAC;AAMF;;;GAGG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAUjD;AAMD;;;GAGG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAOnD;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,SAAK,GAAG,MAAM,CAQhE;AAMD;;GAEG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAapD;AAMD;;;GAGG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAclD;AAMD;;GAEG;AACH,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAG5D;AAMD;;GAEG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,KAAK,CAAC,KAAK,CAMvD;AAMD;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAGtE"}