4runr-os 1.0.9 → 1.0.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -8,6 +8,9 @@ import * as fs from 'fs';
8
8
  import * as path from 'path';
9
9
  import * as os from 'os';
10
10
  import { GatewayClient } from './gateway-client.js';
11
+ import { executeLocalModel, verifyLocalModelServer } from './local-model-executor.js';
12
+ import { checkOllamaInstallation, installOllama, startOllamaServer, downloadModel, verifyLocalExecution } from './local-setup.js';
13
+ import { loadSession, clearSession, createSession, isSessionValid, getSessionTimeRemaining, getUserAgentsPath, getUserToolsPath } from './auth.js';
11
14
  // ANSI colors
12
15
  const colors = {
13
16
  reset: '\x1b[0m',
@@ -23,13 +26,23 @@ const colors = {
23
26
  brightGreen: '\x1b[1m\x1b[32m'
24
27
  };
25
28
  const { cyan, green, yellow, blue, magenta, red, gray, bright, reset, dim, brightGreen } = colors;
26
- let client;
29
+ let client = null;
27
30
  let systemReady = false;
31
+ let gatewayConnected = false;
32
+ let localMode = false; // Run without gateway (local-only)
33
+ let currentUser = null; // Current logged-in user
34
+ let sessionCheckInterval = null; // Session expiry checker
28
35
  // Gateway URL - configurable via GATEWAY_URL environment variable or config file
29
- // Default: Official 4Runr server
36
+ // Default: Official 4Runr server (but connection requires login)
30
37
  // Override: Set GATEWAY_URL environment variable
31
38
  // Local dev: Set GATEWAY_URL=http://localhost:3001
32
39
  let gatewayUrl = process.env.GATEWAY_URL || 'http://44.222.212.152';
40
+ // Parse command line args
41
+ const args = process.argv.slice(2);
42
+ if (args.includes('--local')) {
43
+ localMode = true;
44
+ gatewayUrl = '';
45
+ }
33
46
  // Chat mode state
34
47
  let chatMode = false;
35
48
  let chatAgent = null;
@@ -51,8 +64,11 @@ function getConfigDir() {
51
64
  }
52
65
  return configDir;
53
66
  }
54
- // Get agents file path
67
+ // Get agents file path (user-specific if logged in)
55
68
  function getAgentsFilePath() {
69
+ if (currentUser) {
70
+ return getUserAgentsPath(currentUser.username);
71
+ }
56
72
  return path.join(getConfigDir(), 'agents.json');
57
73
  }
58
74
  // Load custom agents from file
@@ -82,8 +98,11 @@ function saveCustomAgents() {
82
98
  console.error(`Failed to save agents: ${error.message}`);
83
99
  }
84
100
  }
85
- // Get tools file path
101
+ // Get tools file path (user-specific if logged in)
86
102
  function getToolsFilePath() {
103
+ if (currentUser) {
104
+ return getUserToolsPath(currentUser.username);
105
+ }
87
106
  return path.join(getConfigDir(), 'tools.json');
88
107
  }
89
108
  // Load custom tools from file
@@ -143,41 +162,90 @@ ${reset}`);
143
162
  await progress(['gateway', 'sentinel', 'shield', 'devkit', 'metrics', 'ai-providers']);
144
163
  console.log(`${gray}[BOOT] Starting services...${reset}`);
145
164
  await sleep(200);
146
- // Check gateway connection
147
- process.stdout.write(`${gray}[BOOT] Connecting to gateway (${gatewayUrl})... ${reset}`);
148
- try {
149
- client = new GatewayClient({ gatewayUrl });
150
- const health = await client.health();
151
- console.log(`${green}✓${reset}`);
152
- // Check database
153
- process.stdout.write(`${gray}[BOOT] Connecting to database... ${reset}`);
154
- await sleep(200);
155
- console.log(`${green}✓${reset} ${gray}(${health.persistence})${reset}`);
156
- // Check Redis
157
- process.stdout.write(`${gray}[BOOT] Connecting to cache... ${reset}`);
158
- await sleep(200);
159
- console.log(`${green}✓${reset} ${gray}(redis)${reset}`);
160
- // Check Sentinel
161
- process.stdout.write(`${gray}[BOOT] Starting Sentinel monitor... ${reset}`);
162
- try {
163
- const sentinelRes = await fetch(`${gatewayUrl}/sentinel/health`);
164
- const sentinel = await sentinelRes.json();
165
- console.log(`${green}✓${reset} ${gray}(${sentinel?.status || 'unknown'})${reset}`);
165
+ // Check for existing session
166
+ const savedSession = loadSession();
167
+ if (savedSession && isSessionValid(savedSession)) {
168
+ currentUser = savedSession;
169
+ console.log(`${dim}[BOOT] Session restored: ${green}${savedSession.username}${reset}${dim} (${getSessionTimeRemaining(savedSession)}m remaining)${reset}`);
170
+ // Try to reconnect to gateway
171
+ if (!localMode && (savedSession.gatewayUrl || gatewayUrl)) {
172
+ const urlToUse = savedSession.gatewayUrl || gatewayUrl;
173
+ process.stdout.write(`${gray}[BOOT] Reconnecting to gateway... ${reset}`);
174
+ try {
175
+ client = new GatewayClient({ gatewayUrl: urlToUse });
176
+ const health = await client.health();
177
+ console.log(`${green}✓${reset}`);
178
+ gatewayConnected = true;
179
+ gatewayUrl = urlToUse;
180
+ }
181
+ catch (error) {
182
+ console.log(`${yellow}✗${reset} ${dim}(offline)${reset}`);
183
+ gatewayConnected = false;
184
+ }
185
+ }
186
+ }
187
+ else {
188
+ // No session - user needs to login
189
+ if (localMode) {
190
+ console.log(`${dim}[BOOT] Mode: ${green}Local-only${reset} ${dim}(no login required)${reset}`);
166
191
  }
167
- catch {
168
- console.log(`${yellow}⚠${reset} ${gray}(not available)${reset}`);
192
+ else {
193
+ console.log(`${dim}[BOOT] Not connected${reset}`);
194
+ console.log(`${dim}Use ${bright}connect <username>${reset}${dim} to access mainframe${reset}`);
195
+ console.log(`${dim}Or run in ${bright}--local${reset}${dim} mode for offline use${reset}`);
169
196
  }
170
- await sleep(300);
171
- console.log(`\n${green}${bright}[READY] 4Runr OS is ready!${reset}\n`);
172
- systemReady = true;
173
197
  }
174
- catch (error) {
175
- console.log(`${red}✗${reset}`);
176
- console.log(`${red}[ERROR] Failed to connect to gateway at ${gatewayUrl}${reset}`);
177
- console.log(`${yellow}Make sure the gateway is running on the Ubuntu server${reset}`);
178
- process.exit(1);
198
+ await sleep(300);
199
+ console.log(`\n${green}${bright}[READY] 4Runr OS is ready!${reset}`);
200
+ if (currentUser) {
201
+ console.log(`${dim}Connected as: ${green}${currentUser.username}${reset}${dim} | Session: ${getSessionTimeRemaining(currentUser)}m${reset}`);
202
+ }
203
+ else if (!localMode) {
204
+ console.log(`${dim}Mode: ${yellow}Not connected${reset} ${dim}(local mode only)${reset}`);
205
+ console.log(`${dim}Type ${bright}connect <username>${reset}${dim} to access mainframe${reset}`);
206
+ }
207
+ else {
208
+ console.log(`${dim}Mode: ${green}Local-only${reset} ${dim}(no remote features)${reset}`);
209
+ }
210
+ console.log();
211
+ systemReady = true;
212
+ // Start session expiry checker
213
+ if (currentUser && !localMode) {
214
+ startSessionChecker();
179
215
  }
180
216
  }
217
+ /**
218
+ * Start session expiry checker
219
+ */
220
+ function startSessionChecker() {
221
+ // Check every 5 minutes
222
+ sessionCheckInterval = setInterval(() => {
223
+ if (currentUser && !isSessionValid(currentUser)) {
224
+ console.log(`\n${yellow}[ ${brightGreen}SESSION${reset}${yellow} ] Link expired${reset}`);
225
+ console.log(`${dim}Reconnect: ${bright}connect <username>${reset}\n`);
226
+ handleLogout();
227
+ }
228
+ }, 5 * 60 * 1000);
229
+ }
230
+ /**
231
+ * Stop session checker
232
+ */
233
+ function stopSessionChecker() {
234
+ if (sessionCheckInterval) {
235
+ clearInterval(sessionCheckInterval);
236
+ sessionCheckInterval = null;
237
+ }
238
+ }
239
+ /**
240
+ * Handle logout
241
+ */
242
+ function handleLogout() {
243
+ stopSessionChecker();
244
+ clearSession();
245
+ currentUser = null;
246
+ gatewayConnected = false;
247
+ client = null;
248
+ }
181
249
  async function progress(items) {
182
250
  for (const item of items) {
183
251
  process.stdout.write(`${gray} - ${item}${reset}`);
@@ -255,8 +323,14 @@ const commands = {
255
323
  console.log(` ${cyan}get${reset} Get run details`);
256
324
  console.log(` ${cyan}list${reset} List all runs`);
257
325
  console.log();
326
+ console.log(`${magenta}Connection:${reset}`);
327
+ console.log(` ${cyan}connect${reset} Connect to mainframe (required for remote features)`);
328
+ console.log(` ${cyan}disconnect${reset} Disconnect from mainframe`);
329
+ console.log(` ${cyan}whoami${reset} Show current connection and session info`);
330
+ console.log();
258
331
  console.log(`${magenta}System:${reset}`);
259
332
  console.log(` ${cyan}status${reset} Show system status`);
333
+ console.log(` ${cyan}gateway${reset} Manage gateway connection (connect/disconnect/status)`);
260
334
  console.log(` ${cyan}sentinel${reset} Show Sentinel monitoring`);
261
335
  console.log(` ${cyan}metrics${reset} Show system metrics`);
262
336
  console.log(` ${cyan}clear${reset} Clear the screen`);
@@ -497,11 +571,96 @@ const commands = {
497
571
  console.log(`${red}Invalid choice${reset}\n`);
498
572
  return;
499
573
  }
500
- localModelName = await prompt(rl, `${cyan}Model name (e.g., llama2, mistral, gpt-3.5-turbo):${reset} `);
574
+ // Integrated setup - automatically install and configure
575
+ console.log(`\n${bright}Setting up local model environment...${reset}`);
576
+ if (localModelProvider === 'ollama') {
577
+ // Check if Ollama is installed
578
+ const ollamaCheck = await checkOllamaInstallation();
579
+ if (!ollamaCheck.ollamaInstalled) {
580
+ console.log(`${yellow}Ollama not found. Installing automatically...${reset}`);
581
+ const installResult = await installOllama();
582
+ if (!installResult.success) {
583
+ console.log(`${red}✗ ${installResult.message}${reset}\n`);
584
+ const continueChoice = await prompt(rl, `${yellow}Continue anyway? (y/n):${reset} `);
585
+ if (continueChoice?.toLowerCase() !== 'y') {
586
+ console.log(`${gray}Agent creation cancelled${reset}\n`);
587
+ return;
588
+ }
589
+ }
590
+ else {
591
+ console.log(`${green}✓ ${installResult.message}${reset}`);
592
+ }
593
+ }
594
+ // Start server if not running
595
+ const serverCheck = await checkOllamaInstallation();
596
+ if (!serverCheck.success) {
597
+ console.log(`${dim}Starting Ollama server...${reset}`);
598
+ const startResult = await startOllamaServer();
599
+ if (startResult.success) {
600
+ console.log(`${green}✓ ${startResult.message}${reset}`);
601
+ }
602
+ else {
603
+ console.log(`${yellow}⚠ ${startResult.message}${reset}`);
604
+ }
605
+ }
606
+ // Get available models
607
+ const finalCheck = await checkOllamaInstallation();
608
+ if (finalCheck.modelsAvailable && finalCheck.modelsAvailable.length > 0) {
609
+ console.log(`${green}✓ Ollama is ready${reset}`);
610
+ console.log(`${dim}Available models: ${finalCheck.modelsAvailable.join(', ')}${reset}`);
611
+ }
612
+ else {
613
+ console.log(`${yellow}⚠ No models installed yet${reset}`);
614
+ }
615
+ }
616
+ else if (localModelProvider === 'lm-studio') {
617
+ // LM Studio - check if running
618
+ const verification = await verifyLocalModelServer('lm-studio', localModelUrl);
619
+ if (!verification.available) {
620
+ console.log(`${yellow}⚠ LM Studio server not running${reset}`);
621
+ console.log(`${dim}Please start LM Studio and click "Start Server" in the Chat tab${reset}`);
622
+ const continueChoice = await prompt(rl, `${yellow}Continue anyway? (y/n):${reset} `);
623
+ if (continueChoice?.toLowerCase() !== 'y') {
624
+ console.log(`${gray}Agent creation cancelled${reset}\n`);
625
+ return;
626
+ }
627
+ }
628
+ else {
629
+ console.log(`${green}✓ LM Studio server is running${reset}`);
630
+ if (verification.models && verification.models.length > 0) {
631
+ console.log(`${dim}Available models: ${verification.models.join(', ')}${reset}`);
632
+ }
633
+ }
634
+ }
635
+ // Get model name
636
+ localModelName = await prompt(rl, `\n${cyan}Model name (e.g., llama2, mistral, gpt-3.5-turbo):${reset} `);
501
637
  if (!localModelName) {
502
638
  console.log(`${red}Model name required${reset}\n`);
503
639
  return;
504
640
  }
641
+ // Auto-download model if using Ollama and model not found
642
+ if (localModelProvider === 'ollama') {
643
+ const finalCheck = await checkOllamaInstallation();
644
+ if (finalCheck.modelsAvailable && !finalCheck.modelsAvailable.includes(localModelName)) {
645
+ console.log(`\n${yellow}Model '${localModelName}' not found${reset}`);
646
+ const downloadChoice = await prompt(rl, `${cyan}Download it now? (y/n):${reset} `);
647
+ if (downloadChoice?.toLowerCase() === 'y') {
648
+ console.log(`\n${dim}Downloading model (this may take several minutes)...${reset}`);
649
+ const downloadResult = await downloadModel(localModelName);
650
+ if (downloadResult.success) {
651
+ console.log(`${green}✓ ${downloadResult.message}${reset}`);
652
+ }
653
+ else {
654
+ console.log(`${red}✗ ${downloadResult.message}${reset}`);
655
+ const continueChoice = await prompt(rl, `${yellow}Continue anyway? (y/n):${reset} `);
656
+ if (continueChoice?.toLowerCase() !== 'y') {
657
+ console.log(`${gray}Agent creation cancelled${reset}\n`);
658
+ return;
659
+ }
660
+ }
661
+ }
662
+ }
663
+ }
505
664
  baseAgent = `local:${localModelProvider}:${localModelName}`;
506
665
  }
507
666
  else {
@@ -718,7 +877,13 @@ const commands = {
718
877
  let agentConfig = null;
719
878
  if (customAgent) {
720
879
  // Use custom agent
721
- actualAgentId = customAgent.baseAgent;
880
+ // For local models, use gpt-3.5-turbo as the base executor (gateway handles local models)
881
+ if (customAgent.useLocalModel) {
882
+ actualAgentId = 'gpt-3.5-turbo';
883
+ }
884
+ else {
885
+ actualAgentId = customAgent.baseAgent;
886
+ }
722
887
  agentConfig = customAgent;
723
888
  }
724
889
  else {
@@ -760,6 +925,23 @@ const commands = {
760
925
  usage: 'status',
761
926
  handler: async () => {
762
927
  console.log(`\n${bright}System Status:${reset}`);
928
+ // Connection status
929
+ if (currentUser) {
930
+ console.log(` ${green}Link:${reset} Connected as ${currentUser.username}`);
931
+ console.log(` ${green}Session:${reset} ${getSessionTimeRemaining(currentUser)}m remaining`);
932
+ }
933
+ else {
934
+ console.log(` ${yellow}Link:${reset} Not connected`);
935
+ }
936
+ // Gateway status
937
+ if (!client || !gatewayConnected) {
938
+ console.log(` ${yellow}Mainframe:${reset} Offline`);
939
+ if (!currentUser && !localMode) {
940
+ console.log(` ${dim}Use ${bright}connect <username>${reset}${dim} to access mainframe${reset}`);
941
+ }
942
+ console.log();
943
+ return;
944
+ }
763
945
  const health = await client.health();
764
946
  console.log(` Gateway: ${green}✓${reset} alive`);
765
947
  console.log(` Database: ${green}✓${reset} ${health.persistence}`);
@@ -780,6 +962,10 @@ const commands = {
780
962
  description: 'Create a new run',
781
963
  usage: 'create <name> [agent-id]',
782
964
  handler: async (args) => {
965
+ if (!client || !gatewayConnected) {
966
+ console.log(`${red}Gateway not connected. Use ${bright}gateway connect${reset}${red} first.${reset}\n`);
967
+ return;
968
+ }
783
969
  if (args.length === 0) {
784
970
  console.log(`${red}Usage: create <name> [agent-id]${reset}\n`);
785
971
  return;
@@ -806,6 +992,10 @@ const commands = {
806
992
  description: 'Start a run',
807
993
  usage: 'start <run-id>',
808
994
  handler: async (args) => {
995
+ if (!client || !gatewayConnected) {
996
+ console.log(`${red}Gateway not connected. Use ${bright}gateway connect${reset}${red} first.${reset}\n`);
997
+ return;
998
+ }
809
999
  if (args.length === 0) {
810
1000
  console.log(`${red}Usage: start <run-id>${reset}\n`);
811
1001
  return;
@@ -826,6 +1016,10 @@ const commands = {
826
1016
  description: 'Get run details',
827
1017
  usage: 'get <run-id>',
828
1018
  handler: async (args) => {
1019
+ if (!client || !gatewayConnected) {
1020
+ console.log(`${red}Gateway not connected. Use ${bright}gateway connect${reset}${red} first.${reset}\n`);
1021
+ return;
1022
+ }
829
1023
  if (args.length === 0) {
830
1024
  console.log(`${red}Usage: get <run-id>${reset}\n`);
831
1025
  return;
@@ -851,6 +1045,10 @@ const commands = {
851
1045
  description: 'List all runs',
852
1046
  usage: 'list [limit]',
853
1047
  handler: async (args) => {
1048
+ if (!client || !gatewayConnected) {
1049
+ console.log(`${red}Gateway not connected. Use ${bright}gateway connect${reset}${red} first.${reset}\n`);
1050
+ return;
1051
+ }
854
1052
  const limit = args[0] ? parseInt(args[0]) : 10;
855
1053
  const runs = await client.runs.list({ limit });
856
1054
  if (runs.length === 0) {
@@ -895,6 +1093,10 @@ const commands = {
895
1093
  description: 'Show system metrics',
896
1094
  usage: 'metrics',
897
1095
  handler: async () => {
1096
+ if (!client || !gatewayConnected) {
1097
+ console.log(`${red}Gateway not connected. Use ${bright}gateway connect${reset}${red} first.${reset}\n`);
1098
+ return;
1099
+ }
898
1100
  console.log(`${gray}Fetching metrics...${reset}\n`);
899
1101
  try {
900
1102
  const metrics = await client.metrics();
@@ -925,6 +1127,147 @@ const commands = {
925
1127
  process.exit(0);
926
1128
  }
927
1129
  },
1130
+ connect: {
1131
+ description: 'Connect to mainframe (required for remote features)',
1132
+ usage: 'connect <username>',
1133
+ handler: async (args, rl) => {
1134
+ if (localMode) {
1135
+ console.log(`${yellow}Cannot connect in local-only mode${reset}\n`);
1136
+ return;
1137
+ }
1138
+ if (currentUser) {
1139
+ console.log(`${yellow}Already connected as ${currentUser.username}${reset}`);
1140
+ console.log(`${dim}Use ${bright}disconnect${reset}${dim} first to switch users${reset}\n`);
1141
+ return;
1142
+ }
1143
+ if (args.length === 0) {
1144
+ console.log(`${red}Usage: connect <username>${reset}\n`);
1145
+ return;
1146
+ }
1147
+ const username = args.join(' ').trim();
1148
+ console.log(`\n${dim}[ ${brightGreen}CONNECT${reset}${dim} ] Establishing link to mainframe...${reset}`);
1149
+ await sleep(500);
1150
+ console.log(`${dim}[ ${brightGreen}AUTH${reset}${dim} ] Authenticating ${cyan}${username}${reset}${dim}...${reset}`);
1151
+ await sleep(300);
1152
+ // Create session
1153
+ currentUser = createSession(username, gatewayUrl);
1154
+ // Load user-specific data
1155
+ loadCustomAgents();
1156
+ loadCustomTools();
1157
+ // Try to connect to gateway
1158
+ if (gatewayUrl) {
1159
+ try {
1160
+ client = new GatewayClient({ gatewayUrl });
1161
+ const health = await client.health();
1162
+ gatewayConnected = true;
1163
+ console.log(`${green}[ ${brightGreen}CONNECTED${reset}${green} ] Link established${reset}`);
1164
+ console.log(`${dim}User: ${green}${username}${reset}${dim} | Session: ${getSessionTimeRemaining(currentUser)}m${reset}\n`);
1165
+ // Start session checker
1166
+ startSessionChecker();
1167
+ }
1168
+ catch (error) {
1169
+ console.log(`${yellow}[ ${brightGreen}PARTIAL${reset}${yellow} ] Connected, but mainframe unavailable${reset}`);
1170
+ console.log(`${dim}Local features only${reset}\n`);
1171
+ gatewayConnected = false;
1172
+ }
1173
+ }
1174
+ else {
1175
+ console.log(`${green}[ ${brightGreen}CONNECTED${reset}${green} ] Local mode${reset}\n`);
1176
+ }
1177
+ }
1178
+ },
1179
+ disconnect: {
1180
+ description: 'Disconnect from mainframe',
1181
+ usage: 'disconnect',
1182
+ handler: async () => {
1183
+ if (!currentUser) {
1184
+ console.log(`${yellow}Not connected${reset}\n`);
1185
+ return;
1186
+ }
1187
+ const username = currentUser.username;
1188
+ console.log(`\n${dim}[ ${brightGreen}DISCONNECT${reset}${dim} ] Terminating link for ${cyan}${username}${reset}${dim}...${reset}`);
1189
+ await sleep(300);
1190
+ handleLogout();
1191
+ console.log(`${green}[ ${brightGreen}DISCONNECTED${reset}${green} ] Link terminated${reset}`);
1192
+ console.log(`${dim}Use ${bright}connect <username>${reset}${dim} to reconnect${reset}\n`);
1193
+ }
1194
+ },
1195
+ whoami: {
1196
+ description: 'Show current connection and session info',
1197
+ usage: 'whoami',
1198
+ handler: async () => {
1199
+ console.log(`\n${bright}Connection Status:${reset}`);
1200
+ if (currentUser) {
1201
+ console.log(` ${green}User:${reset} ${currentUser.username}`);
1202
+ console.log(` ${green}Link:${reset} Connected`);
1203
+ console.log(` ${green}Session:${reset} ${getSessionTimeRemaining(currentUser)}m remaining`);
1204
+ console.log(` ${green}Mainframe:${reset} ${gatewayConnected ? 'Online' : 'Offline'}`);
1205
+ }
1206
+ else {
1207
+ console.log(` ${yellow}Link:${reset} Not connected`);
1208
+ console.log(` ${yellow}Mode:${reset} ${localMode ? 'Local-only' : 'Offline'}`);
1209
+ }
1210
+ console.log();
1211
+ }
1212
+ },
1213
+ gateway: {
1214
+ description: 'Manage gateway connection',
1215
+ usage: 'gateway [connect|disconnect|status] [url]',
1216
+ handler: async (args, rl) => {
1217
+ const action = args[0] || 'status';
1218
+ if (action === 'status') {
1219
+ console.log(`\n${bright}Gateway Status:${reset}`);
1220
+ if (localMode) {
1221
+ console.log(` ${yellow}Mode: Local-only (gateway disabled)${reset}`);
1222
+ }
1223
+ else if (gatewayConnected && client) {
1224
+ console.log(` ${green}Status: Connected${reset}`);
1225
+ console.log(` ${dim}URL: ${gatewayUrl}${reset}`);
1226
+ try {
1227
+ const health = await client.health();
1228
+ console.log(` ${green}✓ Gateway is responding${reset}`);
1229
+ }
1230
+ catch (e) {
1231
+ console.log(` ${red}✗ Gateway not responding${reset}`);
1232
+ gatewayConnected = false;
1233
+ }
1234
+ }
1235
+ else {
1236
+ console.log(` ${yellow}Status: Disconnected (offline mode)${reset}`);
1237
+ console.log(` ${dim}Use ${bright}gateway connect${reset}${dim} to connect${reset}`);
1238
+ }
1239
+ console.log();
1240
+ }
1241
+ else if (action === 'connect') {
1242
+ const url = args[1] || gatewayUrl || 'http://44.222.212.152';
1243
+ console.log(`\n${dim}Connecting to gateway at ${url}...${reset}`);
1244
+ try {
1245
+ gatewayUrl = url;
1246
+ client = new GatewayClient({ gatewayUrl: url });
1247
+ const health = await client.health();
1248
+ console.log(`${green}✓ Connected to gateway${reset}`);
1249
+ console.log(`${dim}URL: ${url}${reset}\n`);
1250
+ gatewayConnected = true;
1251
+ localMode = false;
1252
+ }
1253
+ catch (error) {
1254
+ console.log(`${red}✗ Failed to connect: ${error.message}${reset}`);
1255
+ console.log(`${dim}Staying in offline mode${reset}\n`);
1256
+ gatewayConnected = false;
1257
+ }
1258
+ }
1259
+ else if (action === 'disconnect') {
1260
+ console.log(`\n${dim}Disconnecting from gateway...${reset}`);
1261
+ gatewayConnected = false;
1262
+ client = null;
1263
+ console.log(`${green}✓ Disconnected (offline mode active)${reset}\n`);
1264
+ }
1265
+ else {
1266
+ console.log(`${red}Unknown action: ${action}${reset}`);
1267
+ console.log(`${dim}Usage: gateway [connect|disconnect|status]${reset}\n`);
1268
+ }
1269
+ }
1270
+ },
928
1271
  tools: {
929
1272
  description: 'Manage tools for agents',
930
1273
  usage: 'tools [list|create|edit|delete|test] [tool-name]',
@@ -1206,50 +1549,109 @@ async function startREPL() {
1206
1549
  try {
1207
1550
  // Get custom agent config if available
1208
1551
  const agentConfig = rl.chatAgentConfig;
1209
- // Create and execute run
1210
- const run = await client.runs.create({
1211
- name: `${chatAgent}: ${message.substring(0, 50)}...`,
1212
- input: {
1213
- agent_id: chatAgent,
1214
- data: {
1215
- prompt: message,
1216
- // Include custom agent settings
1217
- ...(agentConfig?.systemPrompt && { systemPrompt: agentConfig.systemPrompt }),
1218
- ...(agentConfig?.temperature !== undefined && { temperature: agentConfig.temperature }),
1219
- ...(agentConfig?.maxTokens !== undefined && { maxTokens: agentConfig.maxTokens }),
1220
- ...(conversationHistory.length > 0 && { conversationHistory })
1221
- }
1552
+ // Check if this is a local model agent - execute directly (bypass gateway)
1553
+ if (agentConfig?.useLocalModel && agentConfig.localModelProvider && agentConfig.localModelName) {
1554
+ // Execute locally - this is the REAL local execution
1555
+ const messages = [];
1556
+ // Add system prompt
1557
+ if (agentConfig.systemPrompt) {
1558
+ messages.push({ role: 'system', content: agentConfig.systemPrompt });
1222
1559
  }
1223
- });
1224
- await client.runs.start(run.id);
1225
- // Monitor execution
1226
- let status = await client.runs.get(run.id);
1227
- while (status.status === 'running' || status.status === 'queued') {
1228
- await sleep(500);
1229
- status = await client.runs.get(run.id);
1230
- }
1231
- // Clear thinking indicator
1232
- process.stdout.write(`\r${' '.repeat(20)}\r`);
1233
- if (status.output) {
1234
- const output = status.output;
1235
- const response = output.result || output.message || JSON.stringify(output, null, 2);
1560
+ // Add conversation history
1561
+ if (conversationHistory.length > 0) {
1562
+ messages.push(...conversationHistory);
1563
+ }
1564
+ // Add current message
1565
+ messages.push({ role: 'user', content: message });
1566
+ // Execute local model directly
1567
+ const localConfig = {
1568
+ provider: agentConfig.localModelProvider,
1569
+ model: agentConfig.localModelName,
1570
+ baseUrl: agentConfig.localModelUrl,
1571
+ temperature: agentConfig.temperature,
1572
+ maxTokens: agentConfig.maxTokens,
1573
+ topP: agentConfig.topP,
1574
+ frequencyPenalty: agentConfig.frequencyPenalty,
1575
+ presencePenalty: agentConfig.presencePenalty,
1576
+ };
1577
+ // PROOF: Verify execution is truly local before executing
1578
+ const localProof = await verifyLocalExecution();
1579
+ if (!localProof.isLocal) {
1580
+ console.log(`${red}⚠ WARNING: Local execution verification failed!${reset}`);
1581
+ console.log(`${yellow}This may indicate the request is going through a remote server.${reset}\n`);
1582
+ }
1583
+ const result = await executeLocalModel(localConfig, messages);
1584
+ // Clear thinking indicator
1585
+ process.stdout.write(`\r${' '.repeat(20)}\r`);
1236
1586
  // Update conversation history
1237
1587
  conversationHistory.push({ role: 'user', content: message });
1238
- conversationHistory.push({ role: 'assistant', content: response });
1239
- // Show response with friendly name
1240
- const agentConfig = rl.chatAgentConfig;
1241
- const friendlyName = agentConfig ? agentConfig.name :
1242
- chatAgent === 'gpt-3.5-turbo' ? 'Assistant' :
1243
- chatAgent === 'gpt-4' ? 'GPT-4' :
1244
- chatAgent;
1245
- console.log(`${green}${friendlyName}:${reset} ${response}\n`);
1246
- // Show usage if available
1247
- if (output.usage) {
1248
- console.log(`${gray}Tokens: ${output.usage.totalTokens || 'N/A'} | Cost: $${(output.cost || 0).toFixed(6)}${reset}\n`);
1588
+ conversationHistory.push({ role: 'assistant', content: result.result });
1589
+ // Show response with proof it's local
1590
+ console.log(`${green}${agentConfig.name}:${reset} ${result.result}\n`);
1591
+ // Show usage and proof
1592
+ if (result.usage) {
1593
+ console.log(`${gray}Tokens: ${result.usage.totalTokens || 'N/A'} | Model: ${result.model} | Cost: $0.000000 (local)${reset}`);
1594
+ console.log(`${dim}✓ Executed locally (${localProof.proof.executionPath}) | Response time: ${localProof.proof.responseTime}ms${reset}\n`);
1249
1595
  }
1250
1596
  }
1251
1597
  else {
1252
- console.log(`${yellow}No response received${reset}\n`);
1598
+ // Remote model - execute through gateway
1599
+ if (!client || !gatewayConnected) {
1600
+ console.log(`${red}Mainframe not connected. Use ${bright}connect <username>${reset}${red} for remote agents.${reset}\n`);
1601
+ rl.prompt();
1602
+ return;
1603
+ }
1604
+ const run = await client.runs.create({
1605
+ name: `${chatAgent}: ${message.substring(0, 50)}...`,
1606
+ input: {
1607
+ agent_id: chatAgent,
1608
+ data: {
1609
+ prompt: message,
1610
+ // Include custom agent settings
1611
+ ...(agentConfig?.systemPrompt && { systemPrompt: agentConfig.systemPrompt }),
1612
+ ...(agentConfig?.temperature !== undefined && { temperature: agentConfig.temperature }),
1613
+ ...(agentConfig?.maxTokens !== undefined && { maxTokens: agentConfig.maxTokens }),
1614
+ // Advanced parameters
1615
+ ...(agentConfig?.topP !== undefined && { topP: agentConfig.topP }),
1616
+ ...(agentConfig?.frequencyPenalty !== undefined && { frequencyPenalty: agentConfig.frequencyPenalty }),
1617
+ ...(agentConfig?.presencePenalty !== undefined && { presencePenalty: agentConfig.presencePenalty }),
1618
+ // Tools
1619
+ ...(agentConfig?.tools && agentConfig.tools.length > 0 && { tools: agentConfig.tools }),
1620
+ // Always include conversation history for context
1621
+ conversationHistory: conversationHistory.length > 0 ? conversationHistory : []
1622
+ }
1623
+ }
1624
+ });
1625
+ await client.runs.start(run.id);
1626
+ // Monitor execution
1627
+ let status = await client.runs.get(run.id);
1628
+ while (status.status === 'running' || status.status === 'queued') {
1629
+ await sleep(500);
1630
+ status = await client.runs.get(run.id);
1631
+ }
1632
+ // Clear thinking indicator
1633
+ process.stdout.write(`\r${' '.repeat(20)}\r`);
1634
+ if (status.output) {
1635
+ const output = status.output;
1636
+ const response = output.result || output.message || JSON.stringify(output, null, 2);
1637
+ // Update conversation history
1638
+ conversationHistory.push({ role: 'user', content: message });
1639
+ conversationHistory.push({ role: 'assistant', content: response });
1640
+ // Show response with friendly name
1641
+ const agentConfig = rl.chatAgentConfig;
1642
+ const friendlyName = agentConfig ? agentConfig.name :
1643
+ chatAgent === 'gpt-3.5-turbo' ? 'Assistant' :
1644
+ chatAgent === 'gpt-4' ? 'GPT-4' :
1645
+ chatAgent;
1646
+ console.log(`${green}${friendlyName}:${reset} ${response}\n`);
1647
+ // Show usage if available
1648
+ if (output.usage) {
1649
+ console.log(`${gray}Tokens: ${output.usage.totalTokens || 'N/A'} | Cost: $${(output.cost || 0).toFixed(6)}${reset}\n`);
1650
+ }
1651
+ }
1652
+ else {
1653
+ console.log(`${yellow}No response received${reset}\n`);
1654
+ }
1253
1655
  }
1254
1656
  }
1255
1657
  catch (error) {