@hashgraphonline/conversational-agent 0.1.211 → 0.1.213

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 (55) hide show
  1. package/cli/dist/CLIApp.d.ts +9 -0
  2. package/cli/dist/CLIApp.js +127 -0
  3. package/cli/dist/LocalConversationalAgent.d.ts +37 -0
  4. package/cli/dist/LocalConversationalAgent.js +58 -0
  5. package/cli/dist/app.d.ts +16 -0
  6. package/cli/dist/app.js +13 -0
  7. package/cli/dist/cli.d.ts +2 -0
  8. package/cli/dist/cli.js +51 -0
  9. package/cli/dist/components/AppContainer.d.ts +16 -0
  10. package/cli/dist/components/AppContainer.js +24 -0
  11. package/cli/dist/components/AppScreens.d.ts +2 -0
  12. package/cli/dist/components/AppScreens.js +259 -0
  13. package/cli/dist/components/ChatScreen.d.ts +15 -0
  14. package/cli/dist/components/ChatScreen.js +39 -0
  15. package/cli/dist/components/DebugLoadingScreen.d.ts +5 -0
  16. package/cli/dist/components/DebugLoadingScreen.js +31 -0
  17. package/cli/dist/components/LoadingScreen.d.ts +2 -0
  18. package/cli/dist/components/LoadingScreen.js +16 -0
  19. package/cli/dist/components/LoadingScreenDebug.d.ts +5 -0
  20. package/cli/dist/components/LoadingScreenDebug.js +27 -0
  21. package/cli/dist/components/MCPConfigScreen.d.ts +28 -0
  22. package/cli/dist/components/MCPConfigScreen.js +168 -0
  23. package/cli/dist/components/ScreenRouter.d.ts +12 -0
  24. package/cli/dist/components/ScreenRouter.js +22 -0
  25. package/cli/dist/components/SetupScreen.d.ts +15 -0
  26. package/cli/dist/components/SetupScreen.js +65 -0
  27. package/cli/dist/components/SingleLoadingScreen.d.ts +5 -0
  28. package/cli/dist/components/SingleLoadingScreen.js +27 -0
  29. package/cli/dist/components/StatusBadge.d.ts +7 -0
  30. package/cli/dist/components/StatusBadge.js +28 -0
  31. package/cli/dist/components/TerminalWindow.d.ts +8 -0
  32. package/cli/dist/components/TerminalWindow.js +24 -0
  33. package/cli/dist/components/WelcomeScreen.d.ts +11 -0
  34. package/cli/dist/components/WelcomeScreen.js +47 -0
  35. package/cli/dist/context/AppContext.d.ts +68 -0
  36. package/cli/dist/context/AppContext.js +363 -0
  37. package/cli/dist/hooks/useInitializeAgent.d.ts +19 -0
  38. package/cli/dist/hooks/useInitializeAgent.js +28 -0
  39. package/cli/dist/hooks/useStableState.d.ts +38 -0
  40. package/cli/dist/hooks/useStableState.js +68 -0
  41. package/cli/dist/managers/AgentManager.d.ts +57 -0
  42. package/cli/dist/managers/AgentManager.js +119 -0
  43. package/cli/dist/managers/ConfigManager.d.ts +53 -0
  44. package/cli/dist/managers/ConfigManager.js +173 -0
  45. package/cli/dist/types.d.ts +31 -0
  46. package/cli/dist/types.js +19 -0
  47. package/dist/cjs/conversational-agent.d.ts +0 -8
  48. package/dist/cjs/index.cjs +1 -1
  49. package/dist/cjs/index.cjs.map +1 -1
  50. package/dist/esm/index6.js +11 -25
  51. package/dist/esm/index6.js.map +1 -1
  52. package/dist/types/conversational-agent.d.ts +0 -8
  53. package/package.json +27 -29
  54. package/src/conversational-agent.ts +14 -30
  55. package/cli/readme.md +0 -181
@@ -0,0 +1,39 @@
1
+ import React from 'react';
2
+ import { Box, Text } from 'ink';
3
+ import TextInput from 'ink-text-input';
4
+ import Spinner from 'ink-spinner';
5
+ import { TerminalWindow } from './TerminalWindow.js';
6
+ import { StatusBadge } from './StatusBadge.js';
7
+ import { BRAND_COLORS } from '../types.js';
8
+ /**
9
+ * Chat screen component
10
+ */
11
+ export const ChatScreen = React.memo(({ config, messages, input, isLoading, setInput, sendMessage, showLogs, logs, }) => {
12
+ return (React.createElement(TerminalWindow, { title: `Conversational Agent Chat (${config.network})` },
13
+ React.createElement(Box, { flexDirection: "column", minHeight: 25 },
14
+ showLogs && (React.createElement(Box, { borderStyle: "single", borderColor: BRAND_COLORS.dark, marginBottom: 1, padding: 1, height: 10, flexDirection: "column", overflow: "hidden" },
15
+ React.createElement(Box, { marginBottom: 1 },
16
+ React.createElement(Text, { color: BRAND_COLORS.hedera.smoke, bold: true }, "\uD83D\uDCCB Logs")),
17
+ React.createElement(Box, { flexDirection: "column", overflow: "hidden" }, logs.slice(-8).map((log, i) => (React.createElement(Text, { key: i, color: BRAND_COLORS.comments, dimColor: true, wrap: "truncate" }, log)))))),
18
+ React.createElement(Box, { flexDirection: "column", flexGrow: 1, marginBottom: 1, overflow: "hidden" }, messages.slice(-15).map((msg, index) => (React.createElement(Box, { key: `${msg.timestamp.getTime()}-${index}`, marginBottom: 1 },
19
+ msg.role === 'user' && (React.createElement(Box, { flexDirection: "column" },
20
+ React.createElement(Box, null,
21
+ React.createElement(Text, { color: BRAND_COLORS.green }, "$ "),
22
+ React.createElement(Text, { wrap: "wrap" }, msg.content)))),
23
+ msg.role === 'assistant' && (React.createElement(Box, { flexDirection: "column" },
24
+ React.createElement(Box, null,
25
+ React.createElement(Text, { color: BRAND_COLORS.blue }, "\u2192 "),
26
+ React.createElement(Text, { wrap: "wrap" }, msg.content)))),
27
+ msg.role === 'system' && (React.createElement(Box, { flexDirection: "column" },
28
+ React.createElement(Box, null,
29
+ React.createElement(StatusBadge, { status: "info" }),
30
+ React.createElement(Text, { color: BRAND_COLORS.hedera.smoke, wrap: "wrap" }, msg.content)))))))),
31
+ React.createElement(Box, { borderStyle: "single", borderTop: true, borderColor: BRAND_COLORS.dark, paddingTop: 1, paddingX: 1 }, isLoading ? (React.createElement(Box, null,
32
+ React.createElement(Spinner, { type: "dots" }),
33
+ React.createElement(Text, { color: BRAND_COLORS.hedera.smoke }, " Processing..."))) : (React.createElement(Box, { flexDirection: "row", width: "100%" },
34
+ React.createElement(Text, { color: BRAND_COLORS.green }, "$ "),
35
+ React.createElement(Box, { flexGrow: 1 },
36
+ React.createElement(TextInput, { value: input, onChange: setInput, onSubmit: sendMessage, placeholder: "Type your message..." }))))),
37
+ React.createElement(Box, { marginTop: 1, paddingX: 1 },
38
+ React.createElement(Text, { dimColor: true }, "Ctrl+C to exit, ESC to return to menu, L to toggle logs")))));
39
+ });
@@ -0,0 +1,5 @@
1
+ import React from 'react';
2
+ /**
3
+ * Debug loading screen to trace double rendering issue
4
+ */
5
+ export declare const DebugLoadingScreen: React.FC;
@@ -0,0 +1,31 @@
1
+ import React, { useEffect, useRef } from 'react';
2
+ import { Box, Text } from 'ink';
3
+ import Spinner from 'ink-spinner';
4
+ import { BRAND_COLORS } from '../types.js';
5
+ let globalRenderCount = 0;
6
+ let globalInstanceCount = 0;
7
+ /**
8
+ * Debug loading screen to trace double rendering issue
9
+ */
10
+ export const DebugLoadingScreen = () => {
11
+ const instanceId = useRef(++globalInstanceCount);
12
+ const renderCount = useRef(0);
13
+ const mountTime = useRef(Date.now());
14
+ renderCount.current++;
15
+ globalRenderCount++;
16
+ console.error(`[LOADING-${instanceId.current}] Render #${renderCount.current} (Global: ${globalRenderCount}) at ${Date.now() - mountTime.current}ms`);
17
+ useEffect(() => {
18
+ console.error(`[LOADING-${instanceId.current}] MOUNTED`);
19
+ console.error(new Error('Mount Stack Trace').stack);
20
+ return () => {
21
+ console.error(`[LOADING-${instanceId.current}] UNMOUNTED after ${renderCount.current} renders`);
22
+ };
23
+ }, []);
24
+ return (React.createElement(Box, { flexDirection: "column", alignItems: "center", paddingY: 4 },
25
+ React.createElement(Box, { marginBottom: 2 },
26
+ React.createElement(Text, { color: BRAND_COLORS.blue }, "Initializing "),
27
+ React.createElement(Text, { color: BRAND_COLORS.purple }, "Hashgraph "),
28
+ React.createElement(Text, { color: BRAND_COLORS.green }, "Online "),
29
+ React.createElement(Text, { color: BRAND_COLORS.blue }, "Agent...")),
30
+ React.createElement(Spinner, { type: "dots" })));
31
+ };
@@ -0,0 +1,2 @@
1
+ import React from 'react';
2
+ export declare const LoadingScreen: React.FC;
@@ -0,0 +1,16 @@
1
+ import React, { useEffect } from 'react';
2
+ import { Box, Text, useStdout } from 'ink';
3
+ import Spinner from 'ink-spinner';
4
+ import { BRAND_COLORS } from '../types.js';
5
+ export const LoadingScreen = () => {
6
+ const { write } = useStdout();
7
+ useEffect(() => {
8
+ write('\x1Bc');
9
+ }, [write]);
10
+ return (React.createElement(Box, { flexDirection: "column", alignItems: "center", paddingY: 4 },
11
+ React.createElement(Box, { marginBottom: 2 },
12
+ React.createElement(Text, { color: BRAND_COLORS.blue }, "Initializing "),
13
+ React.createElement(Text, { color: BRAND_COLORS.purple }, "Conversational "),
14
+ React.createElement(Text, { color: BRAND_COLORS.green }, "Agent...")),
15
+ React.createElement(Spinner, { type: "dots" })));
16
+ };
@@ -0,0 +1,5 @@
1
+ import React from 'react';
2
+ /**
3
+ * Loading screen component with debug logging
4
+ */
5
+ export declare const LoadingScreenDebug: React.FC;
@@ -0,0 +1,27 @@
1
+ import React, { useEffect } from 'react';
2
+ import { Box, Text, Static } from 'ink';
3
+ import Spinner from 'ink-spinner';
4
+ import { BRAND_COLORS } from '../types.js';
5
+ let renderCount = 0;
6
+ /**
7
+ * Loading screen component with debug logging
8
+ */
9
+ export const LoadingScreenDebug = () => {
10
+ renderCount++;
11
+ useEffect(() => {
12
+ console.error(`[LOADING] Mounted - render #${renderCount}`);
13
+ return () => {
14
+ console.error(`[LOADING] Unmounted - after ${renderCount} renders`);
15
+ };
16
+ }, []);
17
+ useEffect(() => {
18
+ console.error(`[LOADING] Render #${renderCount}`);
19
+ });
20
+ return (React.createElement(Box, { flexDirection: "column", alignItems: "center", paddingY: 4 },
21
+ React.createElement(Static, { items: [{ id: '1' }] }, () => (React.createElement(Box, { marginBottom: 2 },
22
+ React.createElement(Text, { color: BRAND_COLORS.blue }, "Initializing "),
23
+ React.createElement(Text, { color: BRAND_COLORS.purple }, "Hashgraph "),
24
+ React.createElement(Text, { color: BRAND_COLORS.green }, "Online "),
25
+ React.createElement(Text, { color: BRAND_COLORS.blue }, "Agent...")))),
26
+ React.createElement(Spinner, { type: "dots" })));
27
+ };
@@ -0,0 +1,28 @@
1
+ import React from 'react';
2
+ import { type Screen } from '../types';
3
+ import { type MCPServerConfig } from '@hashgraphonline/conversational-agent';
4
+ interface MCPConfig {
5
+ enableFilesystem: boolean;
6
+ filesystemPath: string;
7
+ customServers: MCPServerConfig[];
8
+ addingCustom: boolean;
9
+ newServerName: string;
10
+ newServerCommand: string;
11
+ newServerArgs: string;
12
+ newServerEnv: string;
13
+ currentField: number;
14
+ }
15
+ interface Props {
16
+ mcpConfig: MCPConfig;
17
+ editingFilesystemPath: boolean;
18
+ onSetMcpConfig: (config: Partial<MCPConfig>) => void;
19
+ onSetEditingFilesystemPath: (editing: boolean) => void;
20
+ onSetScreen: (screen: Screen) => void;
21
+ onSaveMCPConfig: () => void;
22
+ getMCPConfigPath: () => string;
23
+ }
24
+ /**
25
+ * MCP Configuration screen
26
+ */
27
+ export declare const MCPConfigScreen: React.FC<Props>;
28
+ export {};
@@ -0,0 +1,168 @@
1
+ import React from 'react';
2
+ import { Box, Text, useInput } from 'ink';
3
+ import TextInput from 'ink-text-input';
4
+ import SelectInput from 'ink-select-input';
5
+ import { TerminalWindow } from './TerminalWindow.js';
6
+ import { StatusBadge } from './StatusBadge.js';
7
+ import { BRAND_COLORS } from '../types.js';
8
+ /**
9
+ * MCP Configuration screen
10
+ */
11
+ export const MCPConfigScreen = ({ mcpConfig, editingFilesystemPath, onSetMcpConfig, onSetEditingFilesystemPath, onSetScreen, onSaveMCPConfig, getMCPConfigPath, }) => {
12
+ useInput((_, key) => {
13
+ if (key.escape) {
14
+ if (mcpConfig.addingCustom) {
15
+ onSetMcpConfig({
16
+ addingCustom: false,
17
+ newServerName: '',
18
+ newServerCommand: '',
19
+ newServerArgs: '',
20
+ newServerEnv: '',
21
+ currentField: 0,
22
+ });
23
+ }
24
+ else if (editingFilesystemPath) {
25
+ onSetEditingFilesystemPath(false);
26
+ }
27
+ }
28
+ });
29
+ if (editingFilesystemPath) {
30
+ return (React.createElement(TerminalWindow, { title: "Edit Filesystem Path" },
31
+ React.createElement(Box, { flexDirection: "column" },
32
+ React.createElement(Box, { marginBottom: 2 },
33
+ React.createElement(Text, null, "Enter the directory path for the filesystem server:")),
34
+ React.createElement(TextInput, { value: mcpConfig.filesystemPath, onChange: value => onSetMcpConfig({ filesystemPath: value }), onSubmit: () => {
35
+ onSetEditingFilesystemPath(false);
36
+ onSaveMCPConfig();
37
+ }, placeholder: process.cwd() }),
38
+ React.createElement(Box, { marginTop: 2 },
39
+ React.createElement(Text, { dimColor: true }, "Press Enter to save, Escape to cancel")))));
40
+ }
41
+ if (mcpConfig.addingCustom) {
42
+ const customFields = [
43
+ 'newServerName',
44
+ 'newServerCommand',
45
+ 'newServerArgs',
46
+ 'newServerEnv',
47
+ ];
48
+ const fieldLabels = [
49
+ 'Server Name:',
50
+ 'Command:',
51
+ 'Arguments (comma-separated):',
52
+ 'Environment Variables (KEY=value, comma-separated):',
53
+ ];
54
+ const placeholders = [
55
+ 'my-server',
56
+ 'npx',
57
+ '-y, @modelcontextprotocol/server-github',
58
+ 'MCP_LOG_LEVEL=info, GIT_SIGN_COMMITS=false',
59
+ ];
60
+ return (React.createElement(TerminalWindow, { title: "Add Custom MCP Server" },
61
+ React.createElement(Box, { flexDirection: "column" },
62
+ customFields.map((field, index) => (React.createElement(Box, { key: field, marginY: 1 },
63
+ React.createElement(Text, { color: mcpConfig.currentField === index
64
+ ? BRAND_COLORS.blue
65
+ : BRAND_COLORS.hedera.smoke }, fieldLabels[index]),
66
+ mcpConfig.currentField === index ? (React.createElement(TextInput, { value: mcpConfig[field], onChange: value => onSetMcpConfig({ [field]: value }), onSubmit: () => {
67
+ if (index < customFields.length - 1) {
68
+ onSetMcpConfig({ currentField: index + 1 });
69
+ }
70
+ else if (mcpConfig.newServerName &&
71
+ mcpConfig.newServerCommand) {
72
+ const env = {};
73
+ if (mcpConfig.newServerEnv) {
74
+ mcpConfig.newServerEnv.split(',').forEach(envVar => {
75
+ const [key, value] = envVar.trim().split('=');
76
+ if (key && value) {
77
+ env[key] = value;
78
+ }
79
+ });
80
+ }
81
+ const newServer = {
82
+ name: mcpConfig.newServerName,
83
+ command: mcpConfig.newServerCommand,
84
+ args: mcpConfig.newServerArgs
85
+ .split(',')
86
+ .map(arg => arg.trim())
87
+ .filter(Boolean),
88
+ transport: 'stdio',
89
+ autoConnect: true,
90
+ ...(Object.keys(env).length > 0 && { env }),
91
+ };
92
+ const newCustomServers = [...mcpConfig.customServers, newServer];
93
+ onSetMcpConfig({
94
+ customServers: newCustomServers,
95
+ addingCustom: false,
96
+ newServerName: '',
97
+ newServerCommand: '',
98
+ newServerArgs: '',
99
+ newServerEnv: '',
100
+ currentField: 0,
101
+ });
102
+ onSaveMCPConfig();
103
+ }
104
+ }, placeholder: placeholders[index] })) : (React.createElement(Text, { color: BRAND_COLORS.hedera.smoke }, String(mcpConfig[field] || `(${placeholders[index]})`)))))),
105
+ React.createElement(Box, { marginTop: 2 },
106
+ React.createElement(Text, { dimColor: true }, "Press Tab or Enter to move between fields"),
107
+ React.createElement(Text, { dimColor: true }, "Press Enter on the last field to add the server"),
108
+ React.createElement(Text, { dimColor: true }, "Press Escape to cancel")))));
109
+ }
110
+ const menuItems = [
111
+ {
112
+ label: `Filesystem Server: ${mcpConfig.enableFilesystem ? 'Enabled' : 'Disabled'}`,
113
+ value: 'filesystem',
114
+ },
115
+ ];
116
+ if (mcpConfig.enableFilesystem) {
117
+ menuItems.push({
118
+ label: `Filesystem Path: ${mcpConfig.filesystemPath}`,
119
+ value: 'filesystem-path',
120
+ });
121
+ }
122
+ mcpConfig.customServers.forEach((server, index) => {
123
+ menuItems.push({
124
+ label: `${server.name} (${server.command})`,
125
+ value: `custom-${index}`,
126
+ });
127
+ });
128
+ menuItems.push({ label: 'Add Custom Server', value: 'add-custom' }, { label: 'Done (changes auto-saved)', value: 'save' }, { label: 'Back to Menu', value: 'back' });
129
+ return (React.createElement(TerminalWindow, { title: "MCP Server Configuration" },
130
+ React.createElement(Box, { flexDirection: "column" },
131
+ React.createElement(Box, { marginBottom: 2 },
132
+ React.createElement(StatusBadge, { status: "info" }),
133
+ React.createElement(Text, null, "Configure Model Context Protocol servers")),
134
+ React.createElement(SelectInput, { items: menuItems, onSelect: item => {
135
+ if (item.value === 'filesystem') {
136
+ const enableFilesystem = !mcpConfig.enableFilesystem;
137
+ onSetMcpConfig({ enableFilesystem });
138
+ onSaveMCPConfig();
139
+ }
140
+ else if (item.value === 'filesystem-path') {
141
+ onSetEditingFilesystemPath(true);
142
+ }
143
+ else if (item.value === 'add-custom') {
144
+ onSetMcpConfig({ addingCustom: true });
145
+ }
146
+ else if (item.value === 'save') {
147
+ onSaveMCPConfig();
148
+ onSetScreen('welcome');
149
+ }
150
+ else if (item.value === 'back') {
151
+ onSetScreen('welcome');
152
+ }
153
+ else if (item.value.startsWith('custom-')) {
154
+ const index = parseInt(item.value.replace('custom-', ''));
155
+ const newCustomServers = mcpConfig.customServers.filter((_, i) => i !== index);
156
+ onSetMcpConfig({ customServers: newCustomServers });
157
+ onSaveMCPConfig();
158
+ }
159
+ } }),
160
+ React.createElement(Box, { marginY: 2 },
161
+ React.createElement(Text, { dimColor: true }, "Press Enter to toggle/edit servers, or select actions"),
162
+ mcpConfig.enableFilesystem && (React.createElement(Text, { dimColor: true },
163
+ "Filesystem path: ",
164
+ mcpConfig.filesystemPath)),
165
+ React.createElement(Text, { dimColor: true },
166
+ "Config saved to: ",
167
+ getMCPConfigPath())))));
168
+ };
@@ -0,0 +1,12 @@
1
+ import React from 'react';
2
+ import { type Config } from '../types';
3
+ import { type AppState } from '../hooks/useStableState';
4
+ interface Props {
5
+ state: AppState;
6
+ currentConfig: Config;
7
+ actions: any;
8
+ stableHandlers: any;
9
+ exit: () => void;
10
+ }
11
+ export declare const ScreenRouter: React.FC<Props>;
12
+ export {};
@@ -0,0 +1,22 @@
1
+ import React from 'react';
2
+ import { WelcomeScreen } from './WelcomeScreen.js';
3
+ import { ChatScreen } from './ChatScreen.js';
4
+ import { LoadingScreen } from './LoadingScreen.js';
5
+ import { SetupScreen } from './SetupScreen.js';
6
+ import { MCPConfigScreen } from './MCPConfigScreen.js';
7
+ export const ScreenRouter = ({ state, currentConfig, actions, stableHandlers, exit, }) => {
8
+ switch (state.screen) {
9
+ case 'welcome':
10
+ return (React.createElement(WelcomeScreen, { config: currentConfig, onExit: exit, onInitializeAgent: stableHandlers.initializeAgent, onSetScreen: actions.setScreen }));
11
+ case 'loading':
12
+ return React.createElement(LoadingScreen, null);
13
+ case 'setup':
14
+ return (React.createElement(SetupScreen, { config: currentConfig, currentField: state.currentField, error: state.error, onUpdateConfig: stableHandlers.updateConfig, onSetCurrentField: actions.setCurrentField, onInitializeAgent: stableHandlers.initializeAgent }));
15
+ case 'mcp-config':
16
+ return (React.createElement(MCPConfigScreen, { mcpConfig: state.mcpConfig, editingFilesystemPath: state.editingFilesystemPath, onSetMcpConfig: actions.setMcpConfig, onSetEditingFilesystemPath: actions.setEditingFilesystemPath, onSetScreen: actions.setScreen, onSaveMCPConfig: stableHandlers.saveMCPConfig, getMCPConfigPath: stableHandlers.getMCPConfigPath }));
17
+ case 'chat':
18
+ return (React.createElement(ChatScreen, { config: currentConfig, messages: state.messages, input: state.input, isLoading: state.isLoading, setInput: actions.setInput, sendMessage: stableHandlers.sendMessage, showLogs: false, logs: state.logs }));
19
+ default:
20
+ return null;
21
+ }
22
+ };
@@ -0,0 +1,15 @@
1
+ import React from 'react';
2
+ import { type Config } from '../types';
3
+ interface Props {
4
+ config: Config;
5
+ currentField: number;
6
+ error: string | null;
7
+ onUpdateConfig: (field: keyof Config, value: string) => void;
8
+ onSetCurrentField: (field: number) => void;
9
+ onInitializeAgent: () => void;
10
+ }
11
+ /**
12
+ * Setup screen for configuring credentials
13
+ */
14
+ export declare const SetupScreen: React.FC<Props>;
15
+ export {};
@@ -0,0 +1,65 @@
1
+ import React from 'react';
2
+ import { Box, Text, useInput } from 'ink';
3
+ import TextInput from 'ink-text-input';
4
+ import SelectInput from 'ink-select-input';
5
+ import { TerminalWindow } from './TerminalWindow.js';
6
+ import { StatusBadge } from './StatusBadge.js';
7
+ import { BRAND_COLORS } from '../types.js';
8
+ const fields = ['accountId', 'privateKey', 'network', 'openAIApiKey'];
9
+ /**
10
+ * Setup screen for configuring credentials
11
+ */
12
+ export const SetupScreen = ({ config, currentField, error, onUpdateConfig, onSetCurrentField, onInitializeAgent, }) => {
13
+ useInput((_, key) => {
14
+ if (key.tab && !key.shift) {
15
+ const nextField = (currentField + 1) % fields.length;
16
+ onSetCurrentField(nextField);
17
+ }
18
+ else if (key.tab && key.shift) {
19
+ const prevField = currentField === 0 ? fields.length - 1 : currentField - 1;
20
+ onSetCurrentField(prevField);
21
+ }
22
+ });
23
+ return (React.createElement(TerminalWindow, { title: "Configuration" },
24
+ React.createElement(Box, { flexDirection: "column" },
25
+ React.createElement(Box, { marginBottom: 1 },
26
+ React.createElement(StatusBadge, { status: "info" }),
27
+ React.createElement(Text, null, "Configure your Hedera account credentials")),
28
+ fields.map((field, index) => (React.createElement(Box, { key: field, marginY: 1 },
29
+ React.createElement(Box, { width: 20 },
30
+ React.createElement(Text, { color: currentField === index
31
+ ? BRAND_COLORS.blue
32
+ : BRAND_COLORS.hedera.smoke },
33
+ field === 'accountId' && 'Account ID:',
34
+ field === 'privateKey' && 'Private Key:',
35
+ field === 'network' && 'Network:',
36
+ field === 'openAIApiKey' && 'OpenAI Key:')),
37
+ currentField === index ? (field === 'network' ? (React.createElement(SelectInput, { items: [
38
+ { label: 'Testnet', value: 'testnet' },
39
+ { label: 'Mainnet', value: 'mainnet' },
40
+ ], initialIndex: config.network === 'testnet' ? 0 : 1, onSelect: item => {
41
+ onUpdateConfig('network', item.value);
42
+ if (currentField < fields.length - 1) {
43
+ onSetCurrentField(currentField + 1);
44
+ }
45
+ } })) : (React.createElement(TextInput, { value: config[field], onChange: value => onUpdateConfig(field, value), onSubmit: () => {
46
+ if (currentField < fields.length - 1) {
47
+ onSetCurrentField(currentField + 1);
48
+ }
49
+ else {
50
+ onInitializeAgent();
51
+ }
52
+ }, mask: field === 'privateKey' || field === 'openAIApiKey'
53
+ ? '*'
54
+ : undefined }))) : (React.createElement(Text, { color: BRAND_COLORS.hedera.smoke }, field === 'network'
55
+ ? config[field]
56
+ : config[field]
57
+ ? '••••••••'
58
+ : '(not set)'))))),
59
+ error && (React.createElement(Box, { marginTop: 2 },
60
+ React.createElement(StatusBadge, { status: "error" }),
61
+ React.createElement(Text, { color: "red" }, error))),
62
+ React.createElement(Box, { marginTop: 2 },
63
+ React.createElement(Text, { dimColor: true }, "Press Tab to navigate fields, Enter to submit"),
64
+ React.createElement(Text, { dimColor: true }, "Complete all fields and press Enter on the last field to save and start")))));
65
+ };
@@ -0,0 +1,5 @@
1
+ import React from 'react';
2
+ /**
3
+ * Loading screen that tracks its own renders
4
+ */
5
+ export declare const SingleLoadingScreen: React.FC;
@@ -0,0 +1,27 @@
1
+ import React, { useEffect, useRef } from 'react';
2
+ import { Box, Text } from 'ink';
3
+ import Spinner from 'ink-spinner';
4
+ import { BRAND_COLORS } from '../types.js';
5
+ let globalRenderCount = 0;
6
+ /**
7
+ * Loading screen that tracks its own renders
8
+ */
9
+ export const SingleLoadingScreen = () => {
10
+ const renderCount = useRef(0);
11
+ const instanceId = useRef(Math.random());
12
+ useEffect(() => {
13
+ renderCount.current++;
14
+ globalRenderCount++;
15
+ console.error(`[LOADING] Instance ${instanceId.current.toFixed(4)} - Render #${renderCount.current} (Global: ${globalRenderCount})`);
16
+ return () => {
17
+ console.error(`[LOADING] Instance ${instanceId.current.toFixed(4)} - Unmounting after ${renderCount.current} renders`);
18
+ };
19
+ }, []);
20
+ return (React.createElement(Box, { flexDirection: "column", alignItems: "center", paddingY: 4 },
21
+ React.createElement(Box, { marginBottom: 2 },
22
+ React.createElement(Text, { color: BRAND_COLORS.blue }, "Initializing "),
23
+ React.createElement(Text, { color: BRAND_COLORS.purple }, "Hashgraph "),
24
+ React.createElement(Text, { color: BRAND_COLORS.green }, "Online "),
25
+ React.createElement(Text, { color: BRAND_COLORS.blue }, "Agent...")),
26
+ React.createElement(Spinner, { type: "dots" })));
27
+ };
@@ -0,0 +1,7 @@
1
+ import React from 'react';
2
+ /**
3
+ * Status badge component
4
+ */
5
+ export declare const StatusBadge: React.FC<{
6
+ status: 'success' | 'info' | 'warning' | 'error';
7
+ }>;
@@ -0,0 +1,28 @@
1
+ import React from 'react';
2
+ import { Box, Text } from 'ink';
3
+ const BRAND_COLORS = {
4
+ blue: '#5599fe',
5
+ green: '#48df7b',
6
+ };
7
+ /**
8
+ * Status badge component
9
+ */
10
+ export const StatusBadge = ({ status }) => {
11
+ const colors = {
12
+ success: BRAND_COLORS.green,
13
+ info: BRAND_COLORS.blue,
14
+ warning: '#FFBD2E',
15
+ error: '#FF5F57',
16
+ };
17
+ const labels = {
18
+ success: 'SUCCESS',
19
+ info: 'INFO',
20
+ warning: 'WARNING',
21
+ error: 'ERROR',
22
+ };
23
+ return (React.createElement(Box, { marginRight: 1 },
24
+ React.createElement(Text, { color: colors[status], bold: true },
25
+ "[",
26
+ labels[status],
27
+ "]")));
28
+ };
@@ -0,0 +1,8 @@
1
+ import React from 'react';
2
+ /**
3
+ * Terminal window decoration component
4
+ */
5
+ export declare const TerminalWindow: React.FC<{
6
+ title: string;
7
+ children: React.ReactNode;
8
+ }>;
@@ -0,0 +1,24 @@
1
+ import React from 'react';
2
+ import { Box, Text } from 'ink';
3
+ const BRAND_COLORS = {
4
+ blue: '#5599fe',
5
+ dark: '#3f4174',
6
+ hedera: {
7
+ smoke: '#8c8c8c',
8
+ },
9
+ };
10
+ /**
11
+ * Terminal window decoration component
12
+ */
13
+ export const TerminalWindow = ({ title, children, }) => {
14
+ return (React.createElement(Box, { flexDirection: "column", borderStyle: "round", borderColor: BRAND_COLORS.blue },
15
+ React.createElement(Box, { paddingX: 1, borderStyle: "single", borderBottom: true, borderColor: BRAND_COLORS.dark },
16
+ React.createElement(Box, { marginRight: 1 },
17
+ React.createElement(Text, { color: "red" }, "\u25CF"),
18
+ React.createElement(Text, null, " "),
19
+ React.createElement(Text, { color: "yellow" }, "\u25CF"),
20
+ React.createElement(Text, null, " "),
21
+ React.createElement(Text, { color: "green" }, "\u25CF")),
22
+ React.createElement(Text, { color: BRAND_COLORS.hedera.smoke }, title)),
23
+ React.createElement(Box, { paddingX: 2, paddingY: 1, flexDirection: "column" }, children)));
24
+ };
@@ -0,0 +1,11 @@
1
+ import React from 'react';
2
+ import { type Config, type Screen } from '../types';
3
+ /**
4
+ * Welcome screen component
5
+ */
6
+ export declare const WelcomeScreen: React.FC<{
7
+ config: Config;
8
+ onExit: () => void;
9
+ onInitializeAgent: () => void;
10
+ onSetScreen: (screen: Screen) => void;
11
+ }>;
@@ -0,0 +1,47 @@
1
+ import React from 'react';
2
+ import { Box, Text } from 'ink';
3
+ import SelectInput from 'ink-select-input';
4
+ import Gradient from 'ink-gradient';
5
+ import { BRAND_COLORS } from '../types.js';
6
+ /**
7
+ * Welcome screen component
8
+ */
9
+ export const WelcomeScreen = ({ config, onExit, onInitializeAgent, onSetScreen }) => (React.createElement(Box, { flexDirection: "column", alignItems: "center", paddingY: 2 },
10
+ React.createElement(Box, { flexDirection: "column", alignItems: "center", marginBottom: 2 },
11
+ React.createElement(Box, null,
12
+ React.createElement(Gradient, { name: "atlas" },
13
+ React.createElement(Text, { bold: true }, "\u2566 \u2566\u2554\u2550\u2557\u2554\u2550\u2557\u2566 \u2566\u2554\u2550\u2557\u2566\u2550\u2557\u2554\u2550\u2557\u2554\u2550\u2557\u2566 \u2566 \u2554\u2550\u2557\u2554\u2557\u2554\u2566 \u2566\u2554\u2557\u2554\u2554\u2550\u2557"))),
14
+ React.createElement(Box, null,
15
+ React.createElement(Gradient, { name: "atlas" },
16
+ React.createElement(Text, { bold: true }, "\u2560\u2550\u2563\u2560\u2550\u2563\u255A\u2550\u2557\u2560\u2550\u2563\u2551 \u2566\u2560\u2566\u255D\u2560\u2550\u2563\u2560\u2550\u255D\u2560\u2550\u2563 \u2551 \u2551\u2551\u2551\u2551\u2551 \u2551\u2551\u2551\u2551\u2551\u2563 "))),
17
+ React.createElement(Box, null,
18
+ React.createElement(Gradient, { name: "atlas" },
19
+ React.createElement(Text, { bold: true }, "\u2569 \u2569\u2569 \u2569\u255A\u2550\u255D\u2569 \u2569\u255A\u2550\u255D\u2569\u255A\u2550\u2569 \u2569\u2569 \u2569 \u2569 \u255A\u2550\u255D\u255D\u255A\u255D\u2569\u2550\u255D\u2569\u255D\u255A\u255D\u255A\u2550\u255D"))),
20
+ React.createElement(Text, { color: BRAND_COLORS.hedera.smoke }, "Conversational Agent CLI"),
21
+ React.createElement(Text, { color: BRAND_COLORS.hedera.smoke, dimColor: true }, "Powered by Hashgraph Online")),
22
+ React.createElement(Box, { marginY: 2 },
23
+ React.createElement(SelectInput, { items: [
24
+ { label: 'Start Chat', value: 'chat' },
25
+ { label: 'Configure', value: 'setup' },
26
+ { label: 'MCP Servers', value: 'mcp-config' },
27
+ { label: 'Exit', value: 'exit' },
28
+ ], onSelect: item => {
29
+ if (item.value === 'exit') {
30
+ onExit();
31
+ }
32
+ else if (item.value === 'chat') {
33
+ if (config.accountId &&
34
+ config.privateKey &&
35
+ config.openAIApiKey) {
36
+ onInitializeAgent();
37
+ }
38
+ else {
39
+ onSetScreen('setup');
40
+ }
41
+ }
42
+ else {
43
+ onSetScreen(item.value);
44
+ }
45
+ } })),
46
+ React.createElement(Box, { marginTop: 2 },
47
+ React.createElement(Text, { dimColor: true }, "Press \u2191/\u2193 to navigate, Enter to select"))));