@mndrk/agx 1.4.19 → 1.4.20
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/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/BUILD_ID +1 -1
- package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/app-build-manifest.json +29 -29
- package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/app-path-routes-manifest.json +5 -5
- package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/build-manifest.json +2 -2
- package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/prerender-manifest.json +15 -15
- package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/_not-found.html +1 -1
- package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/_not-found.rsc +1 -1
- package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/api/audit/route_client-reference-manifest.js +1 -1
- package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/api/auth/[...nextauth]/route_client-reference-manifest.js +1 -1
- package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/api/auth/daemon-secret/route_client-reference-manifest.js +1 -1
- package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/api/auth/device/code/route_client-reference-manifest.js +1 -1
- package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/api/auth/device/token/route_client-reference-manifest.js +1 -1
- package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/api/auth/refresh/route_client-reference-manifest.js +1 -1
- package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/api/auth/status/route_client-reference-manifest.js +1 -1
- package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/api/learnings/route_client-reference-manifest.js +1 -1
- package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/api/logs/stream/route_client-reference-manifest.js +1 -1
- package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/api/orchestrator/tasks/[taskId]/cancel/route_client-reference-manifest.js +1 -1
- package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/api/orchestrator/tasks/[taskId]/signal/route_client-reference-manifest.js +1 -1
- package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/api/orchestrator/tasks/[taskId]/start/route_client-reference-manifest.js +1 -1
- package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/api/orchestrator/tasks/[taskId]/status/route_client-reference-manifest.js +1 -1
- package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/api/projects/[id]/route_client-reference-manifest.js +1 -1
- package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
- package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/api/providers/route_client-reference-manifest.js +1 -1
- package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/api/queue/complete/route_client-reference-manifest.js +1 -1
- package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/api/queue/route_client-reference-manifest.js +1 -1
- package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/api/stage-prompts/route_client-reference-manifest.js +1 -1
- package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/api/tasks/[id]/comments/[commentId]/route_client-reference-manifest.js +1 -1
- package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/api/tasks/[id]/comments/route_client-reference-manifest.js +1 -1
- package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/api/tasks/[id]/heartbeat/route_client-reference-manifest.js +1 -1
- package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/api/tasks/[id]/history/route_client-reference-manifest.js +1 -1
- package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/api/tasks/[id]/logs/route_client-reference-manifest.js +1 -1
- package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/api/tasks/[id]/route_client-reference-manifest.js +1 -1
- package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/api/tasks/route_client-reference-manifest.js +1 -1
- package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/api/tasks/stream/route_client-reference-manifest.js +1 -1
- package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/api/user-settings/route_client-reference-manifest.js +1 -1
- package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/api/workflows/[id]/nodes/route_client-reference-manifest.js +1 -1
- package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/api/workflows/[id]/route_client-reference-manifest.js +1 -1
- package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/api/workflows/route_client-reference-manifest.js +1 -1
- package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/auth/callback/route_client-reference-manifest.js +1 -1
- package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/auth/device/page.js +5 -5
- package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/auth/device/page_client-reference-manifest.js +1 -1
- package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/auth/device.html +1 -1
- package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/auth/device.rsc +2 -2
- package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/dashboard/page_client-reference-manifest.js +1 -1
- package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/dashboard.html +1 -1
- package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/dashboard.rsc +1 -1
- package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/index.html +1 -1
- package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/index.rsc +1 -1
- package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/login/page_client-reference-manifest.js +1 -1
- package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/login.html +1 -1
- package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/login.rsc +1 -1
- package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/page_client-reference-manifest.js +1 -1
- package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/projects/[slug]/page_client-reference-manifest.js +1 -1
- package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/projects/[slug]/tasks/page_client-reference-manifest.js +1 -1
- package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/projects/[slug]/workflow/page_client-reference-manifest.js +1 -1
- package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/projects/page_client-reference-manifest.js +1 -1
- package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/projects.html +1 -1
- package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/projects.rsc +1 -1
- package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/settings/page_client-reference-manifest.js +1 -1
- package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/settings.html +1 -1
- package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app/settings.rsc +1 -1
- package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/app-paths-manifest.json +5 -5
- package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/middleware-manifest.json +5 -5
- package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/pages/404.html +1 -1
- package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/pages/500.html +1 -1
- package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/server-reference-manifest.js +1 -1
- package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/server/server-reference-manifest.json +1 -1
- package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/static/chunks/app/auth/device/{page-a334052961b047e9.js → page-1191b5d80fb53701.js} +1 -1
- package/index.js +26 -0
- package/lib/cli/daemon.js +11 -0
- package/lib/cli/interactiveMenu.js +348 -0
- package/lib/cli/onboarding.js +277 -0
- package/lib/cli/providers.js +245 -0
- package/lib/cli/runCli.js +77 -1934
- package/lib/cli/skills.js +110 -0
- package/package.json +1 -1
- /package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/static/{iDPijmmtGz8j8COcWytVz → _UEH0bf2vZJdfFbGlB9rM}/_buildManifest.js +0 -0
- /package/cloud-runtime/standalone/Projects/Agents/agx-cloud/.next/static/{iDPijmmtGz8j8COcWytVz → _UEH0bf2vZJdfFbGlB9rM}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
/* eslint-disable no-console */
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const { spawn } = require('child_process');
|
|
6
|
+
|
|
7
|
+
const { c } = require('../ui/colors');
|
|
8
|
+
const { prompt, loadConfig } = require('./configStore');
|
|
9
|
+
const { detectProviders } = require('./providers');
|
|
10
|
+
const {
|
|
11
|
+
isDaemonRunning,
|
|
12
|
+
isBoardRunning,
|
|
13
|
+
isTemporalWorkerRunning,
|
|
14
|
+
startDaemon,
|
|
15
|
+
stopDaemon,
|
|
16
|
+
DAEMON_LOG_FILE,
|
|
17
|
+
WORKER_LOG_FILE,
|
|
18
|
+
BOARD_LOG_FILE,
|
|
19
|
+
} = require('./daemon');
|
|
20
|
+
|
|
21
|
+
async function runInteractiveMenu() {
|
|
22
|
+
const providers = detectProviders();
|
|
23
|
+
const config = loadConfig();
|
|
24
|
+
|
|
25
|
+
const clearScreen = () => process.stdout.write('\x1b[2J\x1b[H');
|
|
26
|
+
const hideCursor = () => process.stdout.write('\x1b[?25l');
|
|
27
|
+
const showCursor = () => process.stdout.write('\x1b[?25h');
|
|
28
|
+
|
|
29
|
+
let menuState = 'main';
|
|
30
|
+
let selectedIdx = 0;
|
|
31
|
+
let selectedProvider = null;
|
|
32
|
+
|
|
33
|
+
const buildMainMenu = () => {
|
|
34
|
+
const items = [];
|
|
35
|
+
if (providers.claude) items.push({ id: 'claude', label: 'claude', desc: 'Anthropic Claude Code', type: 'provider' });
|
|
36
|
+
if (providers.codex) items.push({ id: 'codex', label: 'codex', desc: 'OpenAI Codex', type: 'provider' });
|
|
37
|
+
if (providers.gemini) items.push({ id: 'gemini', label: 'gemini', desc: 'Google Gemini', type: 'provider' });
|
|
38
|
+
if (providers.ollama) items.push({ id: 'ollama', label: 'ollama', desc: 'Local Ollama', type: 'provider' });
|
|
39
|
+
items.push({ id: 'sep1', type: 'separator' });
|
|
40
|
+
items.push({ id: 'daemon', label: 'Daemon', desc: 'Background task runner', type: 'action' });
|
|
41
|
+
return items;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const buildActionMenu = () => [
|
|
45
|
+
{ id: 'chat', label: 'Chat', desc: 'Start interactive conversation', type: 'action' },
|
|
46
|
+
{ id: 'sep', type: 'separator' },
|
|
47
|
+
{ id: 'back', label: '← Back', desc: '', type: 'back' },
|
|
48
|
+
];
|
|
49
|
+
|
|
50
|
+
const buildDaemonMenu = () => {
|
|
51
|
+
const pid = isDaemonRunning();
|
|
52
|
+
const items = [];
|
|
53
|
+
if (pid) items.push({ id: 'stop', label: 'Stop', desc: `Stop daemon (pid ${pid})`, type: 'action' });
|
|
54
|
+
else items.push({ id: 'start', label: 'Start', desc: 'Start background daemon', type: 'action' });
|
|
55
|
+
items.push({ id: 'status', label: 'Status', desc: 'Check daemon status', type: 'action' });
|
|
56
|
+
items.push({ id: 'logs', label: 'Logs', desc: 'Show recent logs', type: 'action' });
|
|
57
|
+
items.push({ id: 'sep', type: 'separator' });
|
|
58
|
+
items.push({ id: 'back', label: '← Back', desc: '', type: 'back' });
|
|
59
|
+
return items;
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const getMenuItems = () => {
|
|
63
|
+
switch (menuState) {
|
|
64
|
+
case 'main': return buildMainMenu();
|
|
65
|
+
case 'action': return buildActionMenu();
|
|
66
|
+
case 'daemon': return buildDaemonMenu();
|
|
67
|
+
default: return buildMainMenu();
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
const render = () => {
|
|
72
|
+
const items = getMenuItems();
|
|
73
|
+
const clearLine = '\x1b[K';
|
|
74
|
+
const home = '\x1b[H';
|
|
75
|
+
const clearBelow = '\x1b[J';
|
|
76
|
+
|
|
77
|
+
const lines = [];
|
|
78
|
+
if (menuState === 'main') lines.push(`${c.bold}${c.cyan}agx${c.reset} ${c.dim}Autonomous AI Agents${c.reset}`);
|
|
79
|
+
else if (menuState === 'action' && selectedProvider) lines.push(`${c.bold}${c.cyan}agx${c.reset} ${c.dim}›${c.reset} ${c.bold}${selectedProvider}${c.reset}`);
|
|
80
|
+
else if (menuState === 'daemon') lines.push(`${c.bold}${c.cyan}agx${c.reset} ${c.dim}›${c.reset} ${c.bold}Daemon${c.reset}`);
|
|
81
|
+
lines.push('');
|
|
82
|
+
|
|
83
|
+
items.forEach((item, idx) => {
|
|
84
|
+
if (item.type === 'separator') {
|
|
85
|
+
lines.push(` ${c.dim}${'─'.repeat(40)}${c.reset}`);
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
const isSelected = idx === selectedIdx;
|
|
89
|
+
const prefix = isSelected ? `${c.cyan}❯${c.reset}` : ' ';
|
|
90
|
+
const label = isSelected ? `${c.bold}${item.label}${c.reset}` : item.label;
|
|
91
|
+
const desc = item.desc ? ` ${c.dim}${item.desc}${c.reset}` : '';
|
|
92
|
+
lines.push(`${prefix} ${label}${desc}`);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
lines.push('');
|
|
96
|
+
if (menuState === 'main') lines.push(`${c.dim}↑/↓ select · enter choose · q quit${c.reset}`);
|
|
97
|
+
else lines.push(`${c.dim}↑/↓ select · enter choose · esc back · q quit${c.reset}`);
|
|
98
|
+
|
|
99
|
+
process.stdout.write(home + lines.map((l) => l + clearLine).join('\n') + clearBelow);
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
const releaseTTY = () => {
|
|
103
|
+
if (process.stdin.isTTY && process.stdin.setRawMode) {
|
|
104
|
+
process.stdin.setRawMode(false);
|
|
105
|
+
}
|
|
106
|
+
process.stdin.pause();
|
|
107
|
+
showCursor();
|
|
108
|
+
clearScreen();
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
const handleBack = () => {
|
|
112
|
+
if (menuState === 'action' || menuState === 'daemon') {
|
|
113
|
+
menuState = 'main';
|
|
114
|
+
selectedIdx = 0;
|
|
115
|
+
render();
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
const cleanup = () => {
|
|
120
|
+
if (process.stdin.isTTY && process.stdin.setRawMode) {
|
|
121
|
+
process.stdin.setRawMode(false);
|
|
122
|
+
}
|
|
123
|
+
process.stdin.pause();
|
|
124
|
+
showCursor();
|
|
125
|
+
clearScreen();
|
|
126
|
+
process.exit(0);
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
const handleSelect = async () => {
|
|
130
|
+
const items = getMenuItems();
|
|
131
|
+
const item = items[selectedIdx];
|
|
132
|
+
if (!item || item.type === 'separator') return;
|
|
133
|
+
|
|
134
|
+
if (item.type === 'back') {
|
|
135
|
+
handleBack();
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (menuState === 'main') {
|
|
140
|
+
if (item.type === 'provider') {
|
|
141
|
+
selectedProvider = item.id;
|
|
142
|
+
menuState = 'action';
|
|
143
|
+
selectedIdx = 0;
|
|
144
|
+
render();
|
|
145
|
+
} else if (item.id === 'daemon') {
|
|
146
|
+
menuState = 'daemon';
|
|
147
|
+
selectedIdx = 0;
|
|
148
|
+
render();
|
|
149
|
+
}
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if (menuState === 'action') {
|
|
154
|
+
releaseTTY();
|
|
155
|
+
|
|
156
|
+
if (item.id === 'chat') {
|
|
157
|
+
if (selectedProvider === 'ollama') {
|
|
158
|
+
const ollamaModel = config?.ollama?.model || 'llama3.2:3b';
|
|
159
|
+
const child = spawn('claude', ['--dangerously-skip-permissions', '--model', ollamaModel], {
|
|
160
|
+
stdio: 'inherit',
|
|
161
|
+
env: {
|
|
162
|
+
...process.env,
|
|
163
|
+
ANTHROPIC_AUTH_TOKEN: 'ollama',
|
|
164
|
+
ANTHROPIC_BASE_URL: 'http://localhost:11434',
|
|
165
|
+
ANTHROPIC_API_KEY: ''
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
child.on('close', (code) => process.exit(code || 0));
|
|
169
|
+
} else {
|
|
170
|
+
const child = spawn(selectedProvider, [], { stdio: 'inherit' });
|
|
171
|
+
child.on('close', (code) => process.exit(code || 0));
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
if (menuState === 'daemon') {
|
|
178
|
+
showCursor();
|
|
179
|
+
clearScreen();
|
|
180
|
+
|
|
181
|
+
if (item.id === 'start') {
|
|
182
|
+
startDaemon();
|
|
183
|
+
console.log('');
|
|
184
|
+
await prompt(`${c.dim}Press Enter to continue...${c.reset}`);
|
|
185
|
+
hideCursor();
|
|
186
|
+
render();
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
if (item.id === 'stop') {
|
|
190
|
+
await stopDaemon();
|
|
191
|
+
console.log('');
|
|
192
|
+
await prompt(`${c.dim}Press Enter to continue...${c.reset}`);
|
|
193
|
+
hideCursor();
|
|
194
|
+
selectedIdx = 0;
|
|
195
|
+
render();
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
if (item.id === 'status') {
|
|
199
|
+
const pid = isDaemonRunning();
|
|
200
|
+
if (pid) {
|
|
201
|
+
console.log(`${c.green}Daemon running${c.reset} (pid ${pid})`);
|
|
202
|
+
console.log(`${c.dim}Logs: ${DAEMON_LOG_FILE}${c.reset}`);
|
|
203
|
+
} else {
|
|
204
|
+
console.log(`${c.yellow}Daemon not running${c.reset}`);
|
|
205
|
+
}
|
|
206
|
+
const temporalPid = isTemporalWorkerRunning();
|
|
207
|
+
if (temporalPid) {
|
|
208
|
+
console.log(`${c.green}Orchestrator worker running${c.reset} (pid ${temporalPid})`);
|
|
209
|
+
console.log(`${c.dim}Logs: ${WORKER_LOG_FILE}${c.reset}`);
|
|
210
|
+
} else {
|
|
211
|
+
console.log(`${c.yellow}Orchestrator worker not running${c.reset}`);
|
|
212
|
+
}
|
|
213
|
+
const boardPid = isBoardRunning();
|
|
214
|
+
if (boardPid) {
|
|
215
|
+
console.log(`${c.green}Board server running${c.reset} (pid ${boardPid})`);
|
|
216
|
+
console.log(`${c.dim}Logs: ${BOARD_LOG_FILE}${c.reset}`);
|
|
217
|
+
} else {
|
|
218
|
+
console.log(`${c.yellow}Board server not running${c.reset}`);
|
|
219
|
+
}
|
|
220
|
+
console.log('');
|
|
221
|
+
await prompt(`${c.dim}Press Enter to continue...${c.reset}`);
|
|
222
|
+
hideCursor();
|
|
223
|
+
render();
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
if (item.id === 'logs') {
|
|
227
|
+
if (fs.existsSync(DAEMON_LOG_FILE)) {
|
|
228
|
+
const logs = fs.readFileSync(DAEMON_LOG_FILE, 'utf8');
|
|
229
|
+
console.log(logs.split('\n').slice(-20).join('\n'));
|
|
230
|
+
} else {
|
|
231
|
+
console.log(`${c.dim}No logs yet${c.reset}`);
|
|
232
|
+
}
|
|
233
|
+
console.log('');
|
|
234
|
+
await prompt(`${c.dim}Press Enter to continue...${c.reset}`);
|
|
235
|
+
hideCursor();
|
|
236
|
+
render();
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
if (!process.stdin.isTTY) {
|
|
243
|
+
const items = buildMainMenu().filter((i) => i.type !== 'separator');
|
|
244
|
+
console.log(`${c.bold}${c.cyan}agx${c.reset} ${c.dim}Autonomous AI Agents${c.reset}\n`);
|
|
245
|
+
items.forEach((item, idx) => {
|
|
246
|
+
console.log(` ${c.cyan}${idx + 1}${c.reset}) ${item.label} ${c.dim}${item.desc}${c.reset}`);
|
|
247
|
+
});
|
|
248
|
+
console.log(` ${c.cyan}q${c.reset}) Quit\n`);
|
|
249
|
+
|
|
250
|
+
const choice = await prompt('Choice: ');
|
|
251
|
+
if (choice === 'q' || choice === 'Q') process.exit(0);
|
|
252
|
+
|
|
253
|
+
const idx = parseInt(choice, 10) - 1;
|
|
254
|
+
if (idx >= 0 && idx < items.length) {
|
|
255
|
+
const item = items[idx];
|
|
256
|
+
if (item.type === 'provider') {
|
|
257
|
+
console.log(`\n${c.bold}${item.label}${c.reset}\n`);
|
|
258
|
+
console.log(` ${c.cyan}1${c.reset}) Chat`);
|
|
259
|
+
console.log(` ${c.cyan}0${c.reset}) Back\n`);
|
|
260
|
+
const actionChoice = await prompt('Choice: ');
|
|
261
|
+
if (actionChoice === '0') {
|
|
262
|
+
spawn(process.argv[0], [process.argv[1]], { stdio: 'inherit' }).on('close', (code) => process.exit(code || 0));
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
if (actionChoice === '1') {
|
|
266
|
+
if (item.id === 'ollama') {
|
|
267
|
+
const ollamaModel = config?.ollama?.model || 'llama3.2:3b';
|
|
268
|
+
spawn('claude', ['--dangerously-skip-permissions', '--model', ollamaModel], {
|
|
269
|
+
stdio: 'inherit',
|
|
270
|
+
env: {
|
|
271
|
+
...process.env,
|
|
272
|
+
ANTHROPIC_AUTH_TOKEN: 'ollama',
|
|
273
|
+
ANTHROPIC_BASE_URL: 'http://localhost:11434',
|
|
274
|
+
ANTHROPIC_API_KEY: ''
|
|
275
|
+
}
|
|
276
|
+
}).on('close', (code) => process.exit(code || 0));
|
|
277
|
+
} else {
|
|
278
|
+
spawn(item.id, [], { stdio: 'inherit' }).on('close', (code) => process.exit(code || 0));
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
} else if (item.id === 'daemon') {
|
|
282
|
+
console.log(`\n${c.bold}Daemon${c.reset}\n`);
|
|
283
|
+
const pid = isDaemonRunning();
|
|
284
|
+
if (pid) console.log(` ${c.cyan}1${c.reset}) Stop`);
|
|
285
|
+
else console.log(` ${c.cyan}1${c.reset}) Start`);
|
|
286
|
+
console.log(` ${c.cyan}2${c.reset}) Status`);
|
|
287
|
+
console.log(` ${c.cyan}3${c.reset}) Logs`);
|
|
288
|
+
console.log(` ${c.cyan}0${c.reset}) Back\n`);
|
|
289
|
+
const dChoice = await prompt('Choice: ');
|
|
290
|
+
if (dChoice === '0') {
|
|
291
|
+
spawn(process.argv[0], [process.argv[1]], { stdio: 'inherit' }).on('close', (code) => process.exit(code || 0));
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
294
|
+
if (dChoice === '1') {
|
|
295
|
+
if (pid) await stopDaemon(); else startDaemon();
|
|
296
|
+
} else if (dChoice === '2') {
|
|
297
|
+
if (pid) console.log(`${c.green}Daemon running${c.reset} (pid ${pid})`);
|
|
298
|
+
else console.log(`${c.yellow}Daemon not running${c.reset}`);
|
|
299
|
+
} else if (dChoice === '3') {
|
|
300
|
+
if (fs.existsSync(DAEMON_LOG_FILE)) {
|
|
301
|
+
console.log(fs.readFileSync(DAEMON_LOG_FILE, 'utf8').split('\n').slice(-20).join('\n'));
|
|
302
|
+
} else {
|
|
303
|
+
console.log(`${c.dim}No logs yet${c.reset}`);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
process.exit(0);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
process.stdin.setRawMode(true);
|
|
312
|
+
process.stdin.resume();
|
|
313
|
+
hideCursor();
|
|
314
|
+
render();
|
|
315
|
+
|
|
316
|
+
process.stdin.on('data', async (key) => {
|
|
317
|
+
const k = key.toString();
|
|
318
|
+
const items = getMenuItems();
|
|
319
|
+
|
|
320
|
+
const findValidUp = (from) => {
|
|
321
|
+
let idx = from - 1;
|
|
322
|
+
while (idx >= 0 && items[idx]?.type === 'separator') idx -= 1;
|
|
323
|
+
return idx >= 0 ? idx : from;
|
|
324
|
+
};
|
|
325
|
+
|
|
326
|
+
const findValidDown = (from) => {
|
|
327
|
+
let idx = from + 1;
|
|
328
|
+
while (idx < items.length && items[idx]?.type === 'separator') idx += 1;
|
|
329
|
+
return idx < items.length ? idx : from;
|
|
330
|
+
};
|
|
331
|
+
|
|
332
|
+
if (k === 'q' || k === '\x03') { // q or ctrl-c
|
|
333
|
+
cleanup();
|
|
334
|
+
} else if (k === '\x1b[A' || k === 'k') { // up arrow or k
|
|
335
|
+
selectedIdx = findValidUp(selectedIdx);
|
|
336
|
+
render();
|
|
337
|
+
} else if (k === '\x1b[B' || k === 'j') { // down arrow or j
|
|
338
|
+
selectedIdx = findValidDown(selectedIdx);
|
|
339
|
+
render();
|
|
340
|
+
} else if (k === '\r' || k === '\n') { // enter
|
|
341
|
+
await handleSelect();
|
|
342
|
+
} else if (k === '\x1b[D' || k === 'h' || (k === '\x1b' && k.length === 1)) { // left arrow, h, or bare esc
|
|
343
|
+
handleBack();
|
|
344
|
+
}
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
module.exports = { runInteractiveMenu };
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
/* eslint-disable no-console */
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const { c } = require('../ui/colors');
|
|
5
|
+
const { loadConfig, saveConfig, prompt } = require('./configStore');
|
|
6
|
+
const {
|
|
7
|
+
PROVIDERS,
|
|
8
|
+
detectProviders,
|
|
9
|
+
printProviderStatus,
|
|
10
|
+
installProvider,
|
|
11
|
+
loginProvider,
|
|
12
|
+
getOllamaModels,
|
|
13
|
+
runAgxModelSmokeTest,
|
|
14
|
+
} = require('./providers');
|
|
15
|
+
|
|
16
|
+
async function runOnboarding() {
|
|
17
|
+
console.log(`
|
|
18
|
+
${c.bold}${c.cyan}╭─────────────────────────────────────────╮${c.reset}
|
|
19
|
+
${c.bold}${c.cyan}│${c.reset} ${c.bold}Welcome to agx${c.reset} ${c.cyan}│${c.reset}
|
|
20
|
+
${c.bold}${c.cyan}│${c.reset} ${c.dim}Unified AI Agent CLI${c.reset} ${c.cyan}│${c.reset}
|
|
21
|
+
${c.bold}${c.cyan}╰─────────────────────────────────────────╯${c.reset}
|
|
22
|
+
`);
|
|
23
|
+
|
|
24
|
+
let providers = detectProviders();
|
|
25
|
+
printProviderStatus(providers);
|
|
26
|
+
|
|
27
|
+
const missing = Object.entries(providers)
|
|
28
|
+
.filter(([_, installed]) => !installed)
|
|
29
|
+
.map(([name]) => name);
|
|
30
|
+
|
|
31
|
+
let available = Object.entries(providers)
|
|
32
|
+
.filter(([_, installed]) => installed)
|
|
33
|
+
.map(([name]) => name);
|
|
34
|
+
|
|
35
|
+
if (missing.length > 0) {
|
|
36
|
+
console.log(`\n${c.bold}Would you like to install any providers?${c.reset}\n`);
|
|
37
|
+
|
|
38
|
+
for (const provider of missing) {
|
|
39
|
+
const info = PROVIDERS[provider];
|
|
40
|
+
const answer = await prompt(` Install ${c.cyan}${provider}${c.reset} (${info.description})? [y/N]: `);
|
|
41
|
+
|
|
42
|
+
if (answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes') {
|
|
43
|
+
const success = await installProvider(provider);
|
|
44
|
+
if (success) {
|
|
45
|
+
providers[provider] = true;
|
|
46
|
+
available.push(provider);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
providers = detectProviders();
|
|
52
|
+
available = Object.entries(providers)
|
|
53
|
+
.filter(([_, installed]) => installed)
|
|
54
|
+
.map(([name]) => name);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (available.length === 0) {
|
|
58
|
+
console.log(`\n${c.yellow}⚠${c.reset} No AI providers installed.\n`);
|
|
59
|
+
console.log(`${c.dim}Run ${c.reset}agx init${c.dim} again to install providers.${c.reset}\n`);
|
|
60
|
+
process.exit(0);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
console.log(`\n${c.green}✓${c.reset} Available providers: ${c.bold}${available.join(', ')}${c.reset}`);
|
|
64
|
+
|
|
65
|
+
let defaultProvider = available[0];
|
|
66
|
+
|
|
67
|
+
if (available.length > 1) {
|
|
68
|
+
console.log(`\n${c.bold}Choose your default provider:${c.reset}`);
|
|
69
|
+
available.forEach((p, i) => {
|
|
70
|
+
console.log(` ${c.cyan}${i + 1}${c.reset}) ${p}`);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
const choice = await prompt(`\nEnter number [${c.dim}1${c.reset}]: `);
|
|
74
|
+
const idx = parseInt(choice, 10) - 1;
|
|
75
|
+
if (idx >= 0 && idx < available.length) {
|
|
76
|
+
defaultProvider = available[idx];
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
let defaultModel = '';
|
|
81
|
+
if (defaultProvider === 'ollama') {
|
|
82
|
+
const models = getOllamaModels();
|
|
83
|
+
console.log(`\n${c.bold}Choose your default model:${c.reset}`);
|
|
84
|
+
while (!defaultModel) {
|
|
85
|
+
if (models.length > 0) {
|
|
86
|
+
models.slice(0, 10).forEach((m, i) => {
|
|
87
|
+
console.log(` ${c.cyan}${i + 1}${c.reset}) ${m}`);
|
|
88
|
+
});
|
|
89
|
+
const choice = await prompt(`\nEnter number [${c.dim}1${c.reset}] or type a model name: `);
|
|
90
|
+
if (!choice) {
|
|
91
|
+
defaultModel = models[0];
|
|
92
|
+
} else if (/^\d+$/.test(choice)) {
|
|
93
|
+
const idx = parseInt(choice, 10) - 1;
|
|
94
|
+
defaultModel = (idx >= 0 && idx < models.length) ? models[idx] : '';
|
|
95
|
+
} else {
|
|
96
|
+
defaultModel = choice.trim();
|
|
97
|
+
}
|
|
98
|
+
} else {
|
|
99
|
+
defaultModel = await prompt(`\nEnter an Ollama model name (e.g. llama3.2:3b): `);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
} else {
|
|
103
|
+
while (!defaultModel) {
|
|
104
|
+
defaultModel = await prompt(`\nEnter default model for ${c.cyan}${defaultProvider}${c.reset}: `);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const models = { [defaultProvider]: defaultModel };
|
|
109
|
+
const changedAt = new Date().toISOString();
|
|
110
|
+
const config = {
|
|
111
|
+
version: 1,
|
|
112
|
+
defaultProvider,
|
|
113
|
+
models,
|
|
114
|
+
...(defaultProvider === 'ollama' ? { ollama: { model: defaultModel } } : {}),
|
|
115
|
+
settingsMeta: { provenance: 'cli', changedAt },
|
|
116
|
+
initialized: true,
|
|
117
|
+
providers: providers
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
saveConfig(config);
|
|
121
|
+
|
|
122
|
+
console.log(`\n${c.green}✓${c.reset} Configuration saved to ${c.dim}~/.agx/config.json${c.reset}`);
|
|
123
|
+
console.log(`${c.green}✓${c.reset} Default provider: ${c.bold}${c.cyan}${defaultProvider}${c.reset}`);
|
|
124
|
+
console.log(`${c.green}✓${c.reset} Default model: ${c.bold}${c.cyan}${defaultModel}${c.reset}`);
|
|
125
|
+
|
|
126
|
+
const smoke = await runAgxModelSmokeTest({ provider: defaultProvider, model: defaultModel });
|
|
127
|
+
if (smoke.timedOut || smoke.code !== 0) {
|
|
128
|
+
console.error(`\n${c.red}✗${c.reset} Smoke test failed for ${c.cyan}${defaultProvider}${c.reset} (${c.cyan}${defaultModel}${c.reset})`);
|
|
129
|
+
if (smoke.timedOut) {
|
|
130
|
+
console.error(`${c.red}Error:${c.reset} timed out after 60s`);
|
|
131
|
+
} else {
|
|
132
|
+
console.error(`${c.red}Exit code:${c.reset} ${smoke.code}${smoke.signal ? ` (signal ${smoke.signal})` : ''}`);
|
|
133
|
+
}
|
|
134
|
+
const combined = `${smoke.stderr || ''}${smoke.stdout ? (smoke.stderr ? '\n' : '') + smoke.stdout : ''}`.trim();
|
|
135
|
+
if (combined) {
|
|
136
|
+
console.error(`\n${c.dim}${combined}${c.reset}`);
|
|
137
|
+
}
|
|
138
|
+
console.error(`\n${c.dim}Fix:${c.reset} run ${c.cyan}agx add ${defaultProvider}${c.reset} then ${c.cyan}agx config${c.reset} (or re-run ${c.cyan}agx init${c.reset})`);
|
|
139
|
+
process.exit(1);
|
|
140
|
+
}
|
|
141
|
+
console.log(`${c.green}✓${c.reset} Smoke test succeeded`);
|
|
142
|
+
|
|
143
|
+
console.log(`
|
|
144
|
+
${c.bold}Quick Start:${c.reset}
|
|
145
|
+
|
|
146
|
+
${c.dim}# One-shot question${c.reset}
|
|
147
|
+
${c.cyan}agx -p "explain this code"${c.reset}
|
|
148
|
+
|
|
149
|
+
${c.dim}# Create and run a task${c.reset}
|
|
150
|
+
${c.cyan}agx new "build a REST API"${c.reset}
|
|
151
|
+
${c.cyan}agx run <task_id>${c.reset}
|
|
152
|
+
|
|
153
|
+
${c.dim}# Fully autonomous${c.reset}
|
|
154
|
+
${c.cyan}agx -a -p "refactor auth middleware"${c.reset}
|
|
155
|
+
|
|
156
|
+
${c.dim}Run ${c.reset}agx config${c.dim} anytime to reconfigure.${c.reset}
|
|
157
|
+
`);
|
|
158
|
+
|
|
159
|
+
process.exit(0);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
async function showConfigStatus() {
|
|
163
|
+
const config = loadConfig();
|
|
164
|
+
const providers = detectProviders();
|
|
165
|
+
|
|
166
|
+
console.log(`\n${c.bold}agx Configuration${c.reset}\n`);
|
|
167
|
+
|
|
168
|
+
if (config) {
|
|
169
|
+
console.log(` Config file: ${c.dim}~/.agx/config.json${c.reset}`);
|
|
170
|
+
console.log(` Default provider: ${c.cyan}${config.defaultProvider}${c.reset}`);
|
|
171
|
+
const defaultModel = (config?.models && config.defaultProvider)
|
|
172
|
+
? config.models[config.defaultProvider]
|
|
173
|
+
: (config?.defaultProvider === 'ollama' ? config?.ollama?.model : null);
|
|
174
|
+
if (defaultModel) {
|
|
175
|
+
console.log(` Default model: ${c.cyan}${defaultModel}${c.reset}`);
|
|
176
|
+
}
|
|
177
|
+
if (config?.settingsMeta?.changedAt) {
|
|
178
|
+
console.log(` Settings changed: ${c.dim}${config.settingsMeta.changedAt}${c.reset} (${config.settingsMeta.provenance || 'unknown'})`);
|
|
179
|
+
}
|
|
180
|
+
} else {
|
|
181
|
+
console.log(` ${c.yellow}Not configured${c.reset} - run ${c.cyan}agx init${c.reset}`);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
printProviderStatus(providers);
|
|
185
|
+
console.log('');
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
async function runConfigMenu() {
|
|
189
|
+
const config = loadConfig();
|
|
190
|
+
const providers = detectProviders();
|
|
191
|
+
|
|
192
|
+
console.log(`\n${c.bold}agx Configuration${c.reset}\n`);
|
|
193
|
+
|
|
194
|
+
console.log(`${c.bold}What would you like to do?${c.reset}\n`);
|
|
195
|
+
console.log(` ${c.cyan}1${c.reset}) Install a new provider`);
|
|
196
|
+
console.log(` ${c.cyan}2${c.reset}) Login to a provider`);
|
|
197
|
+
console.log(` ${c.cyan}3${c.reset}) Change default provider`);
|
|
198
|
+
console.log(` ${c.cyan}4${c.reset}) Show status`);
|
|
199
|
+
console.log(` ${c.cyan}5${c.reset}) Run full setup wizard`);
|
|
200
|
+
console.log(` ${c.cyan}q${c.reset}) Quit`);
|
|
201
|
+
|
|
202
|
+
const choice = await prompt('\nChoice: ');
|
|
203
|
+
|
|
204
|
+
switch (choice) {
|
|
205
|
+
case '1': {
|
|
206
|
+
const missing = ['claude', 'gemini', 'ollama', 'codex'].filter((p) => !providers[p]);
|
|
207
|
+
if (missing.length === 0) {
|
|
208
|
+
console.log(`\n${c.green}✓${c.reset} All providers are already installed!`);
|
|
209
|
+
break;
|
|
210
|
+
}
|
|
211
|
+
console.log(`\n${c.bold}Available to install:${c.reset}\n`);
|
|
212
|
+
missing.forEach((p, i) => {
|
|
213
|
+
console.log(` ${c.cyan}${i + 1}${c.reset}) ${p} - ${PROVIDERS[p].description}`);
|
|
214
|
+
});
|
|
215
|
+
const pChoice = await prompt('\nChoice: ');
|
|
216
|
+
const idx = parseInt(pChoice, 10) - 1;
|
|
217
|
+
if (idx >= 0 && idx < missing.length) {
|
|
218
|
+
await installProvider(missing[idx]);
|
|
219
|
+
}
|
|
220
|
+
break;
|
|
221
|
+
}
|
|
222
|
+
case '2': {
|
|
223
|
+
const installed = Object.keys(providers).filter((p) => providers[p]);
|
|
224
|
+
if (installed.length === 0) {
|
|
225
|
+
console.log(`\n${c.yellow}No providers installed.${c.reset} Install one first.`);
|
|
226
|
+
break;
|
|
227
|
+
}
|
|
228
|
+
console.log(`\n${c.bold}Login to:${c.reset}\n`);
|
|
229
|
+
installed.forEach((p, i) => {
|
|
230
|
+
console.log(` ${c.cyan}${i + 1}${c.reset}) ${p}`);
|
|
231
|
+
});
|
|
232
|
+
const pChoice = await prompt('\nChoice: ');
|
|
233
|
+
const idx = parseInt(pChoice, 10) - 1;
|
|
234
|
+
if (idx >= 0 && idx < installed.length) {
|
|
235
|
+
await loginProvider(installed[idx]);
|
|
236
|
+
}
|
|
237
|
+
break;
|
|
238
|
+
}
|
|
239
|
+
case '3': {
|
|
240
|
+
const installed = Object.keys(providers).filter((p) => providers[p]);
|
|
241
|
+
if (installed.length === 0) {
|
|
242
|
+
console.log(`\n${c.yellow}No providers installed.${c.reset}`);
|
|
243
|
+
break;
|
|
244
|
+
}
|
|
245
|
+
console.log(`\n${c.bold}Set default provider:${c.reset}\n`);
|
|
246
|
+
installed.forEach((p, i) => {
|
|
247
|
+
const current = config?.defaultProvider === p ? ` ${c.dim}(current)${c.reset}` : '';
|
|
248
|
+
console.log(` ${c.cyan}${i + 1}${c.reset}) ${p}${current}`);
|
|
249
|
+
});
|
|
250
|
+
const pChoice = await prompt('\nChoice: ');
|
|
251
|
+
const idx = parseInt(pChoice, 10) - 1;
|
|
252
|
+
if (idx >= 0 && idx < installed.length) {
|
|
253
|
+
const newConfig = { ...(config || {}), defaultProvider: installed[idx] };
|
|
254
|
+
saveConfig(newConfig);
|
|
255
|
+
console.log(`\n${c.green}✓${c.reset} Default provider set to ${c.cyan}${installed[idx]}${c.reset}`);
|
|
256
|
+
}
|
|
257
|
+
break;
|
|
258
|
+
}
|
|
259
|
+
case '4':
|
|
260
|
+
await showConfigStatus();
|
|
261
|
+
break;
|
|
262
|
+
case '5':
|
|
263
|
+
await runOnboarding();
|
|
264
|
+
break;
|
|
265
|
+
case 'q':
|
|
266
|
+
case 'Q':
|
|
267
|
+
break;
|
|
268
|
+
default:
|
|
269
|
+
console.log(`${c.yellow}Invalid choice${c.reset}`);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
console.log('');
|
|
273
|
+
process.exit(0);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
module.exports = { runOnboarding, showConfigStatus, runConfigMenu };
|
|
277
|
+
|