rax-flow 0.1.8 → 0.2.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.
Files changed (224) hide show
  1. package/dist/bin.js +8 -6
  2. package/dist/bin.js.map +1 -1
  3. package/dist/hub/commands/index.d.ts.map +1 -1
  4. package/dist/hub/commands/index.js +7 -4
  5. package/dist/hub/commands/index.js.map +1 -1
  6. package/dist/hub/event-listener.d.ts +6 -0
  7. package/dist/hub/event-listener.d.ts.map +1 -1
  8. package/dist/hub/event-listener.js +10 -6
  9. package/dist/hub/event-listener.js.map +1 -1
  10. package/dist/hub/index.d.ts.map +1 -1
  11. package/dist/hub/index.js +12 -0
  12. package/dist/hub/index.js.map +1 -1
  13. package/dist/setup/components/ApiKeyInput.d.ts +25 -0
  14. package/dist/setup/components/ApiKeyInput.d.ts.map +1 -0
  15. package/dist/setup/components/ApiKeyInput.js +54 -0
  16. package/dist/setup/components/ApiKeyInput.js.map +1 -0
  17. package/dist/setup/components/AsciiBanner.d.ts +22 -0
  18. package/dist/setup/components/AsciiBanner.d.ts.map +1 -0
  19. package/dist/setup/components/AsciiBanner.js +55 -0
  20. package/dist/setup/components/AsciiBanner.js.map +1 -0
  21. package/dist/setup/components/CliDetector.d.ts +17 -0
  22. package/dist/setup/components/CliDetector.d.ts.map +1 -0
  23. package/dist/setup/components/CliDetector.js +79 -0
  24. package/dist/setup/components/CliDetector.js.map +1 -0
  25. package/dist/setup/components/ModeSelector.d.ts +8 -0
  26. package/dist/setup/components/ModeSelector.d.ts.map +1 -0
  27. package/dist/setup/components/ModeSelector.js +76 -0
  28. package/dist/setup/components/ModeSelector.js.map +1 -0
  29. package/dist/setup/components/ProviderSelector.d.ts +18 -0
  30. package/dist/setup/components/ProviderSelector.d.ts.map +1 -0
  31. package/dist/setup/components/ProviderSelector.js +97 -0
  32. package/dist/setup/components/ProviderSelector.js.map +1 -0
  33. package/dist/setup/components/SetupWizard.d.ts +2 -0
  34. package/dist/setup/components/SetupWizard.d.ts.map +1 -0
  35. package/dist/setup/components/SetupWizard.js +212 -0
  36. package/dist/setup/components/SetupWizard.js.map +1 -0
  37. package/dist/setup/components/StepIndicator.d.ts +13 -0
  38. package/dist/setup/components/StepIndicator.d.ts.map +1 -0
  39. package/dist/setup/components/StepIndicator.js +18 -0
  40. package/dist/setup/components/StepIndicator.js.map +1 -0
  41. package/dist/setup/components/SuccessScreen.d.ts +18 -0
  42. package/dist/setup/components/SuccessScreen.d.ts.map +1 -0
  43. package/dist/setup/components/SuccessScreen.js +38 -0
  44. package/dist/setup/components/SuccessScreen.js.map +1 -0
  45. package/dist/setup/index.d.ts +12 -0
  46. package/dist/setup/index.d.ts.map +1 -0
  47. package/dist/setup/index.js +29 -0
  48. package/dist/setup/index.js.map +1 -0
  49. package/dist/setup/utils/cli-detection.d.ts +12 -0
  50. package/dist/setup/utils/cli-detection.d.ts.map +1 -0
  51. package/dist/setup/utils/cli-detection.js +83 -0
  52. package/dist/setup/utils/cli-detection.js.map +1 -0
  53. package/dist/setup/utils/config-writer.d.ts +43 -0
  54. package/dist/setup/utils/config-writer.d.ts.map +1 -0
  55. package/dist/setup/utils/config-writer.js +180 -0
  56. package/dist/setup/utils/config-writer.js.map +1 -0
  57. package/dist/tui/App.d.ts +2 -1
  58. package/dist/tui/App.d.ts.map +1 -1
  59. package/dist/tui/App.js +88 -20
  60. package/dist/tui/App.js.map +1 -1
  61. package/dist/tui/components/AgentStateIcon.d.ts +18 -0
  62. package/dist/tui/components/AgentStateIcon.d.ts.map +1 -0
  63. package/dist/tui/components/AgentStateIcon.js +57 -0
  64. package/dist/tui/components/AgentStateIcon.js.map +1 -0
  65. package/dist/tui/components/AnimatedBranch.d.ts +39 -0
  66. package/dist/tui/components/AnimatedBranch.d.ts.map +1 -0
  67. package/dist/tui/components/AnimatedBranch.js +64 -0
  68. package/dist/tui/components/AnimatedBranch.js.map +1 -0
  69. package/dist/tui/components/ChatPanel.d.ts +2 -1
  70. package/dist/tui/components/ChatPanel.d.ts.map +1 -1
  71. package/dist/tui/components/ChatPanel.js +47 -28
  72. package/dist/tui/components/ChatPanel.js.map +1 -1
  73. package/dist/tui/components/DAGPanel.d.ts +14 -2
  74. package/dist/tui/components/DAGPanel.d.ts.map +1 -1
  75. package/dist/tui/components/DAGPanel.js +48 -27
  76. package/dist/tui/components/DAGPanel.js.map +1 -1
  77. package/dist/tui/components/ExecutionTimeline.d.ts +34 -0
  78. package/dist/tui/components/ExecutionTimeline.d.ts.map +1 -0
  79. package/dist/tui/components/ExecutionTimeline.js +67 -0
  80. package/dist/tui/components/ExecutionTimeline.js.map +1 -0
  81. package/dist/tui/components/Header.d.ts +2 -1
  82. package/dist/tui/components/Header.d.ts.map +1 -1
  83. package/dist/tui/components/Header.js +26 -19
  84. package/dist/tui/components/Header.js.map +1 -1
  85. package/dist/tui/components/HelpOverlay.d.ts +2 -1
  86. package/dist/tui/components/HelpOverlay.d.ts.map +1 -1
  87. package/dist/tui/components/HelpOverlay.js +59 -41
  88. package/dist/tui/components/HelpOverlay.js.map +1 -1
  89. package/dist/tui/components/InputBar.d.ts +3 -1
  90. package/dist/tui/components/InputBar.d.ts.map +1 -1
  91. package/dist/tui/components/InputBar.js +12 -7
  92. package/dist/tui/components/InputBar.js.map +1 -1
  93. package/dist/tui/components/LogsPanel.d.ts +9 -0
  94. package/dist/tui/components/LogsPanel.d.ts.map +1 -0
  95. package/dist/tui/components/LogsPanel.js +56 -0
  96. package/dist/tui/components/LogsPanel.js.map +1 -0
  97. package/dist/tui/components/MemoryPanel.d.ts +21 -0
  98. package/dist/tui/components/MemoryPanel.d.ts.map +1 -0
  99. package/dist/tui/components/MemoryPanel.js +51 -0
  100. package/dist/tui/components/MemoryPanel.js.map +1 -0
  101. package/dist/tui/components/MetricsPanel.d.ts +18 -0
  102. package/dist/tui/components/MetricsPanel.d.ts.map +1 -0
  103. package/dist/tui/components/MetricsPanel.js +27 -0
  104. package/dist/tui/components/MetricsPanel.js.map +1 -0
  105. package/dist/tui/components/StatusPanel.d.ts +3 -1
  106. package/dist/tui/components/StatusPanel.d.ts.map +1 -1
  107. package/dist/tui/components/StatusPanel.js +20 -18
  108. package/dist/tui/components/StatusPanel.js.map +1 -1
  109. package/dist/tui/components/TaskTree.d.ts +28 -0
  110. package/dist/tui/components/TaskTree.d.ts.map +1 -0
  111. package/dist/tui/components/TaskTree.js +29 -0
  112. package/dist/tui/components/TaskTree.js.map +1 -0
  113. package/dist/tui/components/animations/ProgressBar.d.ts +39 -0
  114. package/dist/tui/components/animations/ProgressBar.d.ts.map +1 -0
  115. package/dist/tui/components/animations/ProgressBar.js +39 -0
  116. package/dist/tui/components/animations/ProgressBar.js.map +1 -0
  117. package/dist/tui/components/animations/Pulse.d.ts +17 -0
  118. package/dist/tui/components/animations/Pulse.d.ts.map +1 -0
  119. package/dist/tui/components/animations/Pulse.js +47 -0
  120. package/dist/tui/components/animations/Pulse.js.map +1 -0
  121. package/dist/tui/components/animations/Spinner.d.ts +13 -0
  122. package/dist/tui/components/animations/Spinner.d.ts.map +1 -0
  123. package/dist/tui/components/animations/Spinner.js +36 -0
  124. package/dist/tui/components/animations/Spinner.js.map +1 -0
  125. package/dist/tui/components/animations/StatusAnimator.d.ts +27 -0
  126. package/dist/tui/components/animations/StatusAnimator.d.ts.map +1 -0
  127. package/dist/tui/components/animations/StatusAnimator.js +85 -0
  128. package/dist/tui/components/animations/StatusAnimator.js.map +1 -0
  129. package/dist/tui/components/animations/TypingEffect.d.ts +26 -0
  130. package/dist/tui/components/animations/TypingEffect.d.ts.map +1 -0
  131. package/dist/tui/components/animations/TypingEffect.js +59 -0
  132. package/dist/tui/components/animations/TypingEffect.js.map +1 -0
  133. package/dist/tui/components/animations/index.d.ts +8 -0
  134. package/dist/tui/components/animations/index.d.ts.map +1 -0
  135. package/dist/tui/components/animations/index.js +6 -0
  136. package/dist/tui/components/animations/index.js.map +1 -0
  137. package/dist/tui/hooks/useAnimation.d.ts +42 -0
  138. package/dist/tui/hooks/useAnimation.d.ts.map +1 -0
  139. package/dist/tui/hooks/useAnimation.js +212 -0
  140. package/dist/tui/hooks/useAnimation.js.map +1 -0
  141. package/dist/tui/hooks/useAppState.d.ts +10 -0
  142. package/dist/tui/hooks/useAppState.d.ts.map +1 -1
  143. package/dist/tui/hooks/useAppState.js +178 -75
  144. package/dist/tui/hooks/useAppState.js.map +1 -1
  145. package/dist/tui/services/orchestrator.d.ts +16 -0
  146. package/dist/tui/services/orchestrator.d.ts.map +1 -0
  147. package/dist/tui/services/orchestrator.js +152 -0
  148. package/dist/tui/services/orchestrator.js.map +1 -0
  149. package/dist/tui/styles/borders.d.ts +31 -0
  150. package/dist/tui/styles/borders.d.ts.map +1 -0
  151. package/dist/tui/styles/borders.js +47 -0
  152. package/dist/tui/styles/borders.js.map +1 -0
  153. package/dist/tui/styles/colors.d.ts +18 -0
  154. package/dist/tui/styles/colors.d.ts.map +1 -0
  155. package/dist/tui/styles/colors.js +18 -0
  156. package/dist/tui/styles/colors.js.map +1 -0
  157. package/dist/tui/styles/index.d.ts +6 -0
  158. package/dist/tui/styles/index.d.ts.map +1 -0
  159. package/dist/tui/styles/index.js +6 -0
  160. package/dist/tui/styles/index.js.map +1 -0
  161. package/dist/tui/styles/indicators.d.ts +67 -0
  162. package/dist/tui/styles/indicators.d.ts.map +1 -0
  163. package/dist/tui/styles/indicators.js +42 -0
  164. package/dist/tui/styles/indicators.js.map +1 -0
  165. package/dist/tui/styles/layout.d.ts +21 -0
  166. package/dist/tui/styles/layout.d.ts.map +1 -0
  167. package/dist/tui/styles/layout.js +42 -0
  168. package/dist/tui/styles/layout.js.map +1 -0
  169. package/dist/tui/styles/providers.d.ts +77 -0
  170. package/dist/tui/styles/providers.d.ts.map +1 -0
  171. package/dist/tui/styles/providers.js +31 -0
  172. package/dist/tui/styles/providers.js.map +1 -0
  173. package/dist/tui/utils/animation.d.ts +44 -0
  174. package/dist/tui/utils/animation.d.ts.map +1 -0
  175. package/dist/tui/utils/animation.js +107 -0
  176. package/dist/tui/utils/animation.js.map +1 -0
  177. package/package.json +8 -8
  178. package/src/bin.ts +8 -5
  179. package/src/hub/commands/index.ts +7 -4
  180. package/src/hub/event-listener.ts +14 -10
  181. package/src/hub/index.ts +14 -2
  182. package/src/setup/components/ApiKeyInput.tsx +158 -0
  183. package/src/setup/components/AsciiBanner.tsx +125 -0
  184. package/src/setup/components/CliDetector.tsx +230 -0
  185. package/src/setup/components/ModeSelector.tsx +137 -0
  186. package/src/setup/components/ProviderSelector.tsx +174 -0
  187. package/src/setup/components/SetupWizard.tsx +368 -0
  188. package/src/setup/components/StepIndicator.tsx +74 -0
  189. package/src/setup/components/SuccessScreen.tsx +229 -0
  190. package/src/setup/index.ts +34 -0
  191. package/src/setup/utils/cli-detection.ts +99 -0
  192. package/src/setup/utils/config-writer.ts +249 -0
  193. package/src/tui/App.tsx +117 -53
  194. package/src/tui/components/AgentStateIcon.tsx +84 -0
  195. package/src/tui/components/AnimatedBranch.tsx +134 -0
  196. package/src/tui/components/ChatPanel.tsx +85 -43
  197. package/src/tui/components/DAGPanel.tsx +150 -58
  198. package/src/tui/components/ExecutionTimeline.tsx +225 -0
  199. package/src/tui/components/Header.tsx +76 -43
  200. package/src/tui/components/HelpOverlay.tsx +118 -61
  201. package/src/tui/components/InputBar.tsx +33 -19
  202. package/src/tui/components/LogsPanel.tsx +129 -0
  203. package/src/tui/components/MemoryPanel.tsx +163 -0
  204. package/src/tui/components/MetricsPanel.tsx +149 -0
  205. package/src/tui/components/StatusPanel.tsx +84 -37
  206. package/src/tui/components/TaskTree.tsx +159 -0
  207. package/src/tui/components/animations/ProgressBar.tsx +160 -0
  208. package/src/tui/components/animations/Pulse.tsx +73 -0
  209. package/src/tui/components/animations/Spinner.tsx +54 -0
  210. package/src/tui/components/animations/StatusAnimator.tsx +153 -0
  211. package/src/tui/components/animations/TypingEffect.tsx +119 -0
  212. package/src/tui/components/animations/index.ts +16 -0
  213. package/src/tui/hooks/useAnimation.ts +290 -0
  214. package/src/tui/hooks/useAppState.ts +206 -67
  215. package/src/tui/services/orchestrator.ts +195 -0
  216. package/src/tui/styles/borders.ts +51 -0
  217. package/src/tui/styles/colors.ts +19 -0
  218. package/src/tui/styles/index.ts +20 -0
  219. package/src/tui/styles/indicators.ts +54 -0
  220. package/src/tui/styles/layout.ts +44 -0
  221. package/src/tui/styles/providers.ts +32 -0
  222. package/src/tui/utils/animation.ts +124 -0
  223. package/LICENSE +0 -21
  224. package/tsconfig.tsbuildinfo +0 -1
@@ -1,13 +1,17 @@
1
- export interface RuntimeEvent {
2
- type: string;
3
- timestamp: number;
4
- payload: unknown;
1
+ import { RuntimeEventBus, RuntimeEvent } from "rax-flow-core";
2
+
3
+ const globalEventBus = new RuntimeEventBus();
4
+
5
+ export type { RuntimeEvent };
6
+
7
+ export function getEventBus(): RuntimeEventBus {
8
+ return globalEventBus;
9
+ }
10
+
11
+ export function subscribeToEvents(callback: (event: RuntimeEvent) => void): () => void {
12
+ return globalEventBus.onEvent(callback);
5
13
  }
6
14
 
7
- export function subscribeToEvents(_callback: (event: RuntimeEvent) => void): () => void {
8
- // TODO: Integrate with RuntimeEventBus from core
9
- // For now, return a no-op unsubscribe
10
- return () => {
11
- // Cleanup
12
- };
15
+ export function emitEvent(event: RuntimeEvent): void {
16
+ globalEventBus.emit(event);
13
17
  }
package/src/hub/index.ts CHANGED
@@ -2,7 +2,7 @@ import * as readline from "readline";
2
2
  import { HubOptions, HubContext, Command } from "./types.js";
3
3
  import { parseCommand, getCompletions } from "./parser.js";
4
4
  import { CommandHistory } from "./history.js";
5
- import { subscribeToEvents } from "./event-listener.js";
5
+ import { subscribeToEvents, RuntimeEvent } from "./event-listener.js";
6
6
  import { getCommandRegistry } from "./commands/index.js";
7
7
  import { loadConfig } from "./config-loader.js";
8
8
 
@@ -41,7 +41,19 @@ export async function startHub(options: HubOptions): Promise<void> {
41
41
  },
42
42
  });
43
43
 
44
- const unsubscribe = subscribeToEvents((event) => {
44
+ const unsubscribe = subscribeToEvents((event: RuntimeEvent) => {
45
+ const timeStr = new Date().toLocaleTimeString("fr-FR", { hour: "2-digit", minute: "2-digit" });
46
+ switch (event.type) {
47
+ case "run_start":
48
+ console.log(`[${timeStr}] ▶ Task started: ${event.taskId}`);
49
+ break;
50
+ case "run_end":
51
+ console.log(`[${timeStr}] ✓ Task completed: ${event.taskId} (confidence: ${event.metrics.confidence.toFixed(2)})`);
52
+ break;
53
+ case "node_error":
54
+ console.log(`[${timeStr}] ⚠ Node error: ${event.agent} - ${event.message}`);
55
+ break;
56
+ }
45
57
  });
46
58
 
47
59
  rl.on("line", async (input: string) => {
@@ -0,0 +1,158 @@
1
+ import React, { useState } from "react";
2
+ import { Box, Text, useInput } from "ink";
3
+ import TextInput from "ink-text-input";
4
+
5
+ interface ApiKeyInputProps {
6
+ providerId: string;
7
+ providerName: string;
8
+ currentKey?: string;
9
+ envVar?: string;
10
+ onSetKey: (key: string) => void;
11
+ onSkip: () => void;
12
+ onBack: () => void;
13
+ }
14
+
15
+ export function ApiKeyInput({
16
+ providerId,
17
+ providerName,
18
+ currentKey,
19
+ envVar,
20
+ onSetKey,
21
+ onSkip,
22
+ onBack,
23
+ }: ApiKeyInputProps) {
24
+ const [inputValue, setInputValue] = useState(currentKey || "");
25
+ const [isEditing, setIsEditing] = useState(true);
26
+ const [showKey, setShowKey] = useState(false);
27
+ const PRIMARY = "#f97316";
28
+
29
+ const envValue = process.env[envVar || ""];
30
+
31
+ useInput((input, key) => {
32
+ if (!isEditing) {
33
+ if (key.return || input === "s") {
34
+ onSkip();
35
+ } else if (input === "b") {
36
+ onBack();
37
+ } else if (input === "e") {
38
+ setIsEditing(true);
39
+ }
40
+ }
41
+ });
42
+
43
+ const handleSubmit = (value: string) => {
44
+ if (value.trim()) {
45
+ onSetKey(value.trim());
46
+ } else {
47
+ setIsEditing(false);
48
+ }
49
+ };
50
+
51
+ const maskedValue = (key: string) => {
52
+ if (key.length <= 8) return "••••••••";
53
+ return key.slice(0, 4) + "•".repeat(8) + key.slice(-4);
54
+ };
55
+
56
+ return (
57
+ <Box flexDirection="column">
58
+ <Box marginBottom={1}>
59
+ <Text color={PRIMARY} bold>
60
+ CONFIGURE {providerName.toUpperCase()}
61
+ </Text>
62
+ </Box>
63
+
64
+ {envValue && (
65
+ <Box marginBottom={1}>
66
+ <Text color="#22c55e">✓</Text>
67
+ <Text color="#a1a1aa">
68
+ {" Found key in "}
69
+ </Text>
70
+ <Text color="#f59e0b">{envVar}</Text>
71
+ </Box>
72
+ )}
73
+
74
+ <Box marginBottom={1}>
75
+ <Text color="#a1a1aa">Enter your API key (or leave empty to skip):</Text>
76
+ </Box>
77
+
78
+ {isEditing ? (
79
+ <Box>
80
+ <Text color="#27272a">{"> "}</Text>
81
+ <TextInput
82
+ value={inputValue}
83
+ onChange={setInputValue}
84
+ onSubmit={handleSubmit}
85
+ placeholder={envValue ? "Press Enter to use env variable" : "sk-..."}
86
+ showCursor={true}
87
+ />
88
+ </Box>
89
+ ) : (
90
+ <Box flexDirection="column">
91
+ {inputValue && (
92
+ <Box>
93
+ <Text color="#22c55e">✓ Key set: </Text>
94
+ <Text color="#a1a1aa">
95
+ {showKey ? inputValue : maskedValue(inputValue)}
96
+ </Text>
97
+ </Box>
98
+ )}
99
+ </Box>
100
+ )}
101
+
102
+ <Box marginTop={1}>
103
+ <Text color="#27272a">{"─".repeat(50)}</Text>
104
+ </Box>
105
+
106
+ <Box marginTop={1}>
107
+ <Text color="#71717a">[E] Edit</Text>
108
+ <Text color="#71717a"> [S] Skip</Text>
109
+ <Text color="#71717a"> [B] Back</Text>
110
+ </Box>
111
+ </Box>
112
+ );
113
+ }
114
+
115
+ interface ApiKeyCollectorProps {
116
+ providers: Array<{ id: string; name: string; requiresApiKey: boolean }>;
117
+ currentIndex: number;
118
+ keys: Record<string, string>;
119
+ onSetKey: (providerId: string, key: string) => void;
120
+ onSkip: () => void;
121
+ onBack: () => void;
122
+ }
123
+
124
+ const ENV_VAR_MAP: Record<string, string> = {
125
+ anthropic: "ANTHROPIC_API_KEY",
126
+ openai: "OPENAI_API_KEY",
127
+ gemini: "GOOGLE_API_KEY",
128
+ mistral: "MISTRAL_API_KEY",
129
+ groq: "GROQ_API_KEY",
130
+ };
131
+
132
+ export function ApiKeyCollector({
133
+ providers,
134
+ currentIndex,
135
+ keys,
136
+ onSetKey,
137
+ onSkip,
138
+ onBack,
139
+ }: ApiKeyCollectorProps) {
140
+ const providersNeedingKeys = providers.filter((p) => p.requiresApiKey);
141
+ const currentProvider = providersNeedingKeys[currentIndex];
142
+
143
+ if (!currentProvider) {
144
+ return null;
145
+ }
146
+
147
+ return (
148
+ <ApiKeyInput
149
+ providerId={currentProvider.id}
150
+ providerName={currentProvider.name}
151
+ currentKey={keys[currentProvider.id]}
152
+ envVar={ENV_VAR_MAP[currentProvider.id]}
153
+ onSetKey={(key) => onSetKey(currentProvider.id, key)}
154
+ onSkip={onSkip}
155
+ onBack={onBack}
156
+ />
157
+ );
158
+ }
@@ -0,0 +1,125 @@
1
+ import React from "react";
2
+ import { Box, Text } from "ink";
3
+
4
+ const PRIMARY = "#f97316";
5
+ const SECONDARY = "#a1a1aa";
6
+
7
+ const RAX_FLOW_ASCII = `
8
+ ╔══════════════════════════════════════════════════════════════════════════╗
9
+ ║ ██████╗ █████╗ ██████╗ ██╗ ██╗ █████╗ ███╗ ██╗ ██████╗███████╗ ║
10
+ ║ ██╔══██╗██╔══██╗██╔════╝██║ ██╔╝██╔══██╗████╗ ██║██╔════╝██╔════╝ ║
11
+ ║ ██████╔╝███████║██║ █████╔╝ ███████║██╔██╗ ██║██║ █████╗ ║
12
+ ║ ██╔══██╗██╔══██║██║ ██╔═██╗ ██╔══██║██║╚██╗██║██║ ██╔══╝ ║
13
+ ║ ██║ ██║██║ ██║╚██████╗██║ ██╗██║ ██║██║ ╚████║╚██████╗███████╗ ║
14
+ ║ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═════╝╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═══╝ ╚═════╝╚══════╝ ║
15
+ ╠══════════════════════════════════════════════════════════════════════════╣
16
+ ║ [■■■■■■■■■■] ORCHESTRATOR THAT EVOLVES WITH YOUR HOST TOOLS [■■■■■■] ║
17
+ ╚══════════════════════════════════════════════════════════════════════════╝`;
18
+
19
+ const RAX_FLOW_ASCII_COMPACT = `
20
+ ╔═══════════════════════════════════════════════════════╗
21
+ ║ ██████╗ █████╗ ██████╗ ██╗ ██╗███████╗ ║
22
+ ║ ██╔══██╗██╔══██╗██╔════╝ ██║ ██║██╔════╝ ║
23
+ ║ ██████╔╝███████║██║ █████║█████╗ ║
24
+ ║ ██╔══██╗██╔══██║██║ ██╔═██║██╔══╝ ║
25
+ ║ ██║ ██║██║ ██║╚██████╗ ██║ ██║███████╗ ║
26
+ ║ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝ ║
27
+ ╠═══════════════════════════════════════════════════════╣
28
+ ║ [■■■■■] ORCHESTRATOR [■■■■■] ║
29
+ ╚═══════════════════════════════════════════════════════╝`;
30
+
31
+ const GEAR_PATTERN = " ⚙ ";
32
+ const CIRCUIT_PATTERN = " │├┤│ ";
33
+
34
+ interface AsciiBannerProps {
35
+ compact?: boolean;
36
+ showTagline?: boolean;
37
+ }
38
+
39
+ export function AsciiBanner({ compact = false, showTagline = true }: AsciiBannerProps) {
40
+ const ascii = compact ? RAX_FLOW_ASCII_COMPACT : RAX_FLOW_ASCII;
41
+
42
+ return (
43
+ <Box flexDirection="column" paddingX={1}>
44
+ <Box>
45
+ <Text color={PRIMARY} bold>
46
+ {ascii}
47
+ </Text>
48
+ </Box>
49
+ {showTagline && (
50
+ <Box marginTop={1} justifyContent="center">
51
+ <Text color={SECONDARY} dimColor>
52
+ {compact ? "Setup Wizard v1.0" : "Multi-Agent Orchestration Framework • Setup Wizard v1.0"}
53
+ </Text>
54
+ </Box>
55
+ )}
56
+ </Box>
57
+ );
58
+ }
59
+
60
+ export function IndustrialDivider({ width = 60 }: { width?: number }) {
61
+ const segment = "─".repeat(Math.floor(width / 3));
62
+ const full = `╟${segment}╂${segment}╂${segment}╢`;
63
+
64
+ return (
65
+ <Box>
66
+ <Text color="#27272a">{full}</Text>
67
+ </Box>
68
+ );
69
+ }
70
+
71
+ export function IndustrialBox({
72
+ children,
73
+ title,
74
+ width = 60,
75
+ active = false,
76
+ }: {
77
+ children: React.ReactNode;
78
+ title?: string;
79
+ width?: number;
80
+ active?: boolean;
81
+ }) {
82
+ const borderColor = active ? PRIMARY : "#27272a";
83
+ const innerWidth = width - 4;
84
+
85
+ return (
86
+ <Box flexDirection="column">
87
+ <Text color={borderColor}>
88
+ {`┌${title ? `─ ${title} ` : ""}${"─".repeat(innerWidth - (title?.length || 0) - 2)}┐`}
89
+ </Text>
90
+ <Box flexDirection="column" borderStyle="single" borderColor={borderColor}>
91
+ {children}
92
+ </Box>
93
+ <Text color={borderColor}>{`└${"─".repeat(innerWidth)}┘`}</Text>
94
+ </Box>
95
+ );
96
+ }
97
+
98
+ export function StatusIndicator({
99
+ status,
100
+ label,
101
+ active = false
102
+ }: {
103
+ status: "online" | "offline" | "running" | "pending" | "done" | "error";
104
+ label: string;
105
+ active?: boolean;
106
+ }) {
107
+ const indicators: Record<string, { char: string; color: string }> = {
108
+ online: { char: "●", color: "#22c55e" },
109
+ offline: { char: "○", color: "#71717a" },
110
+ running: { char: "▶", color: "#f59e0b" },
111
+ pending: { char: "◐", color: "#a1a1aa" },
112
+ done: { char: "✓", color: "#22c55e" },
113
+ error: { char: "✗", color: "#ef4444" },
114
+ };
115
+
116
+ const { char, color } = indicators[status];
117
+
118
+ return (
119
+ <Box>
120
+ <Text color={active ? PRIMARY : color}>{char}</Text>
121
+ <Text> </Text>
122
+ <Text color={active ? PRIMARY : "#ffffff"}>{label}</Text>
123
+ </Box>
124
+ );
125
+ }
@@ -0,0 +1,230 @@
1
+ import React, { useState, useEffect } from "react";
2
+ import { Box, Text, useInput } from "ink";
3
+ import Spinner from "ink-spinner";
4
+ import { detectInstalledClis, DetectedCli } from "../utils/cli-detection.js";
5
+
6
+ export type { DetectedCli };
7
+
8
+ interface CliDetectorProps {
9
+ onDetected: (clis: DetectedCli[]) => void;
10
+ onContinue: () => void;
11
+ onBack: () => void;
12
+ }
13
+
14
+ export function CliDetector({ onDetected, onContinue, onBack }: CliDetectorProps) {
15
+ const [detecting, setDetecting] = useState(true);
16
+ const [detectedClis, setDetectedClis] = useState<DetectedCli[]>([]);
17
+ const [error, setError] = useState<string | null>(null);
18
+ const PRIMARY = "#f97316";
19
+
20
+ useEffect(() => {
21
+ detectInstalledClis()
22
+ .then((clis) => {
23
+ setDetectedClis(clis);
24
+ onDetected(clis);
25
+ setDetecting(false);
26
+ })
27
+ .catch((err) => {
28
+ setError(err.message);
29
+ setDetecting(false);
30
+ });
31
+ }, [onDetected]);
32
+
33
+ useInput((input, key) => {
34
+ if (!detecting) {
35
+ if (key.return || input === "c") {
36
+ onContinue();
37
+ } else if (input === "b") {
38
+ onBack();
39
+ }
40
+ }
41
+ });
42
+
43
+ if (detecting) {
44
+ return (
45
+ <Box flexDirection="column">
46
+ <Box marginBottom={1}>
47
+ <Text color={PRIMARY} bold>
48
+ DETECTING INSTALLED TOOLS
49
+ </Text>
50
+ </Box>
51
+ <Box>
52
+ <Text color="#f59e0b">
53
+ <Spinner type="dots" />
54
+ </Text>
55
+ <Text color="#a1a1aa"> Scanning for compatible CLI tools...</Text>
56
+ </Box>
57
+ </Box>
58
+ );
59
+ }
60
+
61
+ if (error) {
62
+ return (
63
+ <Box flexDirection="column">
64
+ <Text color="#ef4444">Error detecting CLIs: {error}</Text>
65
+ <Box marginTop={1}>
66
+ <Text color="#71717a">[B] Back</Text>
67
+ </Box>
68
+ </Box>
69
+ );
70
+ }
71
+
72
+ const availableClis = detectedClis.filter((c) => c.detected);
73
+
74
+ return (
75
+ <Box flexDirection="column">
76
+ <Box marginBottom={1}>
77
+ <Text color={PRIMARY} bold>
78
+ DETECTED TOOLS
79
+ </Text>
80
+ </Box>
81
+
82
+ <Box flexDirection="column" marginBottom={1}>
83
+ {detectedClis.map((cli) => (
84
+ <Box key={cli.id}>
85
+ <Text color={cli.detected ? "#22c55e" : "#71717a"}>
86
+ {cli.detected ? "●" : "○"}
87
+ </Text>
88
+ <Text> </Text>
89
+ <Text color={cli.detected ? "#ffffff" : "#71717a"}>
90
+ {cli.name}
91
+ </Text>
92
+ {cli.version && (
93
+ <Text color="#a1a1aa"> v{cli.version.split(" ")[0]}</Text>
94
+ )}
95
+ {cli.configPath && (
96
+ <Text color="#71717a" dimColor>
97
+ {" "}
98
+ ({cli.configPath})
99
+ </Text>
100
+ )}
101
+ </Box>
102
+ ))}
103
+ </Box>
104
+
105
+ {availableClis.length === 0 && (
106
+ <Box marginBottom={1}>
107
+ <Text color="#f59e0b">
108
+ ⚠ No compatible CLI tools detected. You can still use cloud providers.
109
+ </Text>
110
+ </Box>
111
+ )}
112
+
113
+ <Box>
114
+ <Text color="#27272a">{"─".repeat(50)}</Text>
115
+ </Box>
116
+
117
+ <Box marginTop={1}>
118
+ <Text color="#22c55e">[C] Continue</Text>
119
+ <Text color="#71717a"> [B] Back</Text>
120
+ </Box>
121
+ </Box>
122
+ );
123
+ }
124
+
125
+ interface CliSelectorProps {
126
+ detectedClis: DetectedCli[];
127
+ selectedClis: string[];
128
+ onToggle: (cliId: string) => void;
129
+ onContinue: () => void;
130
+ onBack: () => void;
131
+ }
132
+
133
+ export function CliSelector({
134
+ detectedClis,
135
+ selectedClis,
136
+ onToggle,
137
+ onContinue,
138
+ onBack,
139
+ }: CliSelectorProps) {
140
+ const [focusedIndex, setFocusedIndex] = useState(0);
141
+ const availableClis = detectedClis.filter((c) => c.detected);
142
+ const PRIMARY = "#f97316";
143
+
144
+ useInput((input, key) => {
145
+ if (key.upArrow) {
146
+ setFocusedIndex((prev) =>
147
+ prev === 0 ? availableClis.length : prev - 1
148
+ );
149
+ } else if (key.downArrow) {
150
+ setFocusedIndex((prev) =>
151
+ prev === availableClis.length ? 0 : prev + 1
152
+ );
153
+ } else if (key.return) {
154
+ if (focusedIndex === availableClis.length) {
155
+ onContinue();
156
+ } else {
157
+ onToggle(availableClis[focusedIndex].id);
158
+ }
159
+ } else if (input === " ") {
160
+ if (focusedIndex < availableClis.length) {
161
+ onToggle(availableClis[focusedIndex].id);
162
+ }
163
+ } else if (input === "b") {
164
+ onBack();
165
+ }
166
+ });
167
+
168
+ if (availableClis.length === 0) {
169
+ return (
170
+ <Box flexDirection="column">
171
+ <Text color="#f59e0b">No CLI tools detected. Skipping hub setup...</Text>
172
+ <Box marginTop={1}>
173
+ <Text color="#22c55e">[C] Continue</Text>
174
+ </Box>
175
+ </Box>
176
+ );
177
+ }
178
+
179
+ return (
180
+ <Box flexDirection="column">
181
+ <Box marginBottom={1}>
182
+ <Text color={PRIMARY} bold>
183
+ SELECT TOOLS TO INTEGRATE
184
+ </Text>
185
+ </Box>
186
+
187
+ <Box marginBottom={1}>
188
+ <Text color="#a1a1aa" dimColor>
189
+ Use ↑/↓ to navigate, SPACE to toggle, ENTER to continue
190
+ </Text>
191
+ </Box>
192
+
193
+ <Box flexDirection="column">
194
+ {availableClis.map((cli, index) => {
195
+ const isSelected = selectedClis.includes(cli.id);
196
+ const isFocused = index === focusedIndex;
197
+
198
+ return (
199
+ <Box key={cli.id}>
200
+ <Text color={isFocused ? PRIMARY : "#27272a"}>
201
+ {isFocused ? "▶ " : " "}
202
+ </Text>
203
+ <Text color={isSelected ? "#22c55e" : "#a1a1aa"}>
204
+ [{isSelected ? "■" : " "}]
205
+ </Text>
206
+ <Text> </Text>
207
+ <Text color={isFocused ? "#ffffff" : "#a1a1aa"} bold={isFocused}>
208
+ {cli.name}
209
+ </Text>
210
+ {cli.version && (
211
+ <Text color="#71717a"> {cli.version.split(" ")[0]}</Text>
212
+ )}
213
+ </Box>
214
+ );
215
+ })}
216
+ </Box>
217
+
218
+ <Box marginTop={1}>
219
+ <Text color={focusedIndex === availableClis.length ? PRIMARY : "#a1a1aa"}>
220
+ {focusedIndex === availableClis.length ? "▶ " : " "}
221
+ </Text>
222
+ <Text color="#22c55e">[CONTINUE]</Text>
223
+ </Box>
224
+
225
+ <Box marginTop={1}>
226
+ <Text color="#71717a">[B] Back</Text>
227
+ </Box>
228
+ </Box>
229
+ );
230
+ }