auq-mcp-server 1.2.6 → 1.2.8
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/bin/auq.js +9 -169
- package/dist/bin/tui-app.js +160 -0
- package/dist/package.json +1 -1
- package/package.json +1 -1
package/dist/bin/auq.js
CHANGED
|
@@ -3,14 +3,7 @@
|
|
|
3
3
|
import { readFileSync } from "fs";
|
|
4
4
|
import { dirname, join } from "path";
|
|
5
5
|
import { fileURLToPath } from "url";
|
|
6
|
-
import {
|
|
7
|
-
import React, { useEffect, useState } from "react";
|
|
8
|
-
import { ensureDirectoryExists, getSessionDirectory, } from "../src/session/utils.js";
|
|
9
|
-
import { Header } from "../src/tui/components/Header.js";
|
|
10
|
-
import { StepperView } from "../src/tui/components/StepperView.js";
|
|
11
|
-
import { Toast } from "../src/tui/components/Toast.js";
|
|
12
|
-
import { WaitingScreen } from "../src/tui/components/WaitingScreen.js";
|
|
13
|
-
import { createTUIWatcher } from "../src/tui/session-watcher.js";
|
|
6
|
+
import { getSessionDirectory } from "../src/session/utils.js";
|
|
14
7
|
// import { goodbyeText } from "../src/tui/utils/gradientText.js";
|
|
15
8
|
// Handle command-line arguments
|
|
16
9
|
const args = process.argv.slice(2);
|
|
@@ -180,164 +173,11 @@ if (command === "ask") {
|
|
|
180
173
|
process.exit(1);
|
|
181
174
|
}
|
|
182
175
|
}
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
// Auto-hide session log after 3 seconds
|
|
192
|
-
useEffect(() => {
|
|
193
|
-
const timer = setTimeout(() => {
|
|
194
|
-
setShowSessionLog(false);
|
|
195
|
-
}, 3000);
|
|
196
|
-
return () => clearTimeout(timer);
|
|
197
|
-
}, []);
|
|
198
|
-
// Initialize: Load existing sessions + start persistent watcher
|
|
199
|
-
useEffect(() => {
|
|
200
|
-
let watcherInstance = null;
|
|
201
|
-
const initialize = async () => {
|
|
202
|
-
try {
|
|
203
|
-
// Step 0: Ensure session directory exists
|
|
204
|
-
await ensureDirectoryExists(sessionDir);
|
|
205
|
-
// Step 1: Load existing pending sessions
|
|
206
|
-
const watcher = createTUIWatcher();
|
|
207
|
-
const sessionIds = await watcher.getPendingSessions();
|
|
208
|
-
const sessionData = await Promise.all(sessionIds.map(async (sessionId) => {
|
|
209
|
-
const sessionRequest = await watcher.getSessionRequest(sessionId);
|
|
210
|
-
if (!sessionRequest)
|
|
211
|
-
return null;
|
|
212
|
-
return {
|
|
213
|
-
sessionId,
|
|
214
|
-
sessionRequest,
|
|
215
|
-
timestamp: new Date(sessionRequest.timestamp),
|
|
216
|
-
};
|
|
217
|
-
}));
|
|
218
|
-
// Filter out null entries and sort by timestamp (FIFO - oldest first)
|
|
219
|
-
const validSessions = sessionData
|
|
220
|
-
.filter((s) => s !== null)
|
|
221
|
-
.sort((a, b) => a.timestamp.getTime() - b.timestamp.getTime());
|
|
222
|
-
setSessionQueue(validSessions);
|
|
223
|
-
setIsInitialized(true);
|
|
224
|
-
// Step 2: Start persistent watcher for new sessions
|
|
225
|
-
watcherInstance = createTUIWatcher({ autoLoadData: true });
|
|
226
|
-
watcherInstance.startEnhancedWatching((event) => {
|
|
227
|
-
// Add new session to queue (FIFO - append to end)
|
|
228
|
-
setSessionQueue((prev) => {
|
|
229
|
-
// Check for duplicates
|
|
230
|
-
if (prev.some((s) => s.sessionId === event.sessionId)) {
|
|
231
|
-
return prev;
|
|
232
|
-
}
|
|
233
|
-
// Add to end of queue
|
|
234
|
-
return [
|
|
235
|
-
...prev,
|
|
236
|
-
{
|
|
237
|
-
sessionId: event.sessionId,
|
|
238
|
-
sessionRequest: event.sessionRequest,
|
|
239
|
-
timestamp: new Date(event.timestamp),
|
|
240
|
-
},
|
|
241
|
-
];
|
|
242
|
-
});
|
|
243
|
-
});
|
|
244
|
-
}
|
|
245
|
-
catch (error) {
|
|
246
|
-
console.error("Failed to initialize:", error);
|
|
247
|
-
setIsInitialized(true); // Continue even if initialization fails
|
|
248
|
-
}
|
|
249
|
-
};
|
|
250
|
-
initialize();
|
|
251
|
-
// Cleanup: stop watcher on unmount
|
|
252
|
-
return () => {
|
|
253
|
-
if (watcherInstance) {
|
|
254
|
-
watcherInstance.stop();
|
|
255
|
-
}
|
|
256
|
-
};
|
|
257
|
-
}, []);
|
|
258
|
-
// Auto-transition: WAITING → PROCESSING when queue has items
|
|
259
|
-
useEffect(() => {
|
|
260
|
-
if (!isInitialized)
|
|
261
|
-
return;
|
|
262
|
-
if (state.mode === "WAITING" && sessionQueue.length > 0) {
|
|
263
|
-
const [nextSession, ...rest] = sessionQueue;
|
|
264
|
-
setSessionQueue(rest);
|
|
265
|
-
setState({ mode: "PROCESSING", session: nextSession });
|
|
266
|
-
}
|
|
267
|
-
}, [state, sessionQueue, isInitialized]);
|
|
268
|
-
// Show toast notification
|
|
269
|
-
const showToast = (message, type = "success", title) => {
|
|
270
|
-
setToast({ message, type, title });
|
|
271
|
-
};
|
|
272
|
-
// Handle session completion
|
|
273
|
-
const handleSessionComplete = (wasRejected = false, rejectionReason) => {
|
|
274
|
-
// Show appropriate toast
|
|
275
|
-
if (wasRejected) {
|
|
276
|
-
if (rejectionReason) {
|
|
277
|
-
showToast(`Rejection reason: ${rejectionReason}`, "info", "Question set rejected");
|
|
278
|
-
}
|
|
279
|
-
else {
|
|
280
|
-
showToast("", "info", "Question set rejected");
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
else {
|
|
284
|
-
showToast("✓ Answers submitted successfully!", "success");
|
|
285
|
-
}
|
|
286
|
-
if (sessionQueue.length > 0) {
|
|
287
|
-
// Auto-load next session
|
|
288
|
-
const [nextSession, ...rest] = sessionQueue;
|
|
289
|
-
setSessionQueue(rest);
|
|
290
|
-
setState({ mode: "PROCESSING", session: nextSession });
|
|
291
|
-
}
|
|
292
|
-
else {
|
|
293
|
-
// Return to WAITING
|
|
294
|
-
setState({ mode: "WAITING" });
|
|
295
|
-
}
|
|
296
|
-
};
|
|
297
|
-
// Render based on state
|
|
298
|
-
if (!isInitialized) {
|
|
299
|
-
return React.createElement(Text, null, "Loading...");
|
|
300
|
-
}
|
|
301
|
-
let mainContent;
|
|
302
|
-
if (state.mode === "WAITING") {
|
|
303
|
-
mainContent = React.createElement(WaitingScreen, { queueCount: sessionQueue.length });
|
|
304
|
-
}
|
|
305
|
-
else {
|
|
306
|
-
// PROCESSING mode
|
|
307
|
-
const { session } = state;
|
|
308
|
-
mainContent = (React.createElement(StepperView, { key: session.sessionId, onComplete: handleSessionComplete, sessionId: session.sessionId, sessionRequest: session.sessionRequest }));
|
|
309
|
-
}
|
|
310
|
-
// Render with header, toast overlay, and main content
|
|
311
|
-
return (React.createElement(Box, { flexDirection: "column", paddingX: 1 },
|
|
312
|
-
React.createElement(Header, { pendingCount: sessionQueue.length }),
|
|
313
|
-
toast && (React.createElement(Box, { marginBottom: 1, marginTop: 1 },
|
|
314
|
-
React.createElement(Toast, { message: toast.message, onDismiss: () => setToast(null), type: toast.type, title: toast.title }))),
|
|
315
|
-
mainContent,
|
|
316
|
-
showSessionLog && (React.createElement(Box, { marginTop: 1 },
|
|
317
|
-
React.createElement(Text, { dimColor: true },
|
|
318
|
-
"[AUQ] Session directory: ",
|
|
319
|
-
sessionDir)))));
|
|
320
|
-
};
|
|
321
|
-
// Clear terminal before showing app
|
|
322
|
-
console.clear();
|
|
323
|
-
const { waitUntilExit } = render(React.createElement(App, null));
|
|
324
|
-
// Periodically clear performance measures to prevent memory leak warnings
|
|
325
|
-
// React development mode creates performance marks that can accumulate over time
|
|
326
|
-
const clearPerformanceBuffer = () => {
|
|
327
|
-
if (typeof performance !== 'undefined' && performance.clearMeasures) {
|
|
328
|
-
performance.clearMeasures();
|
|
329
|
-
}
|
|
330
|
-
};
|
|
331
|
-
// Clear performance buffer every 5 minutes to prevent accumulation
|
|
332
|
-
const performanceCleanupInterval = setInterval(clearPerformanceBuffer, 5 * 60 * 1000);
|
|
333
|
-
// Handle Ctrl+C gracefully
|
|
334
|
-
process.on("SIGINT", () => {
|
|
335
|
-
clearInterval(performanceCleanupInterval);
|
|
336
|
-
process.exit(0);
|
|
337
|
-
});
|
|
338
|
-
// Show goodbye after Ink unmounts
|
|
339
|
-
waitUntilExit().then(() => {
|
|
340
|
-
clearInterval(performanceCleanupInterval);
|
|
341
|
-
process.stdout.write("\n");
|
|
342
|
-
console.log("👋 Goodbye! See you next time.");
|
|
343
|
-
});
|
|
176
|
+
// Default: Start TUI
|
|
177
|
+
// Important: Lazy-load Ink/React so non-interactive commands (ask/server) don't pull them in.
|
|
178
|
+
// Also force production mode before importing React/Ink to avoid perf_hooks measure accumulation warnings.
|
|
179
|
+
if (!process.env.NODE_ENV) {
|
|
180
|
+
process.env.NODE_ENV = "production";
|
|
181
|
+
}
|
|
182
|
+
const { runTui } = await import("./tui-app.js");
|
|
183
|
+
runTui();
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { Box, render, Text } from "ink";
|
|
2
|
+
import React, { useEffect, useState } from "react";
|
|
3
|
+
import { ensureDirectoryExists, getSessionDirectory, } from "../src/session/utils.js";
|
|
4
|
+
import { Header } from "../src/tui/components/Header.js";
|
|
5
|
+
import { StepperView } from "../src/tui/components/StepperView.js";
|
|
6
|
+
import { Toast } from "../src/tui/components/Toast.js";
|
|
7
|
+
import { WaitingScreen } from "../src/tui/components/WaitingScreen.js";
|
|
8
|
+
import { createTUIWatcher } from "../src/tui/session-watcher.js";
|
|
9
|
+
const App = () => {
|
|
10
|
+
const [state, setState] = useState({ mode: "WAITING" });
|
|
11
|
+
const [sessionQueue, setSessionQueue] = useState([]);
|
|
12
|
+
const [isInitialized, setIsInitialized] = useState(false);
|
|
13
|
+
const [toast, setToast] = useState(null);
|
|
14
|
+
const [showSessionLog, setShowSessionLog] = useState(true);
|
|
15
|
+
// Get session directory for logging
|
|
16
|
+
const sessionDir = getSessionDirectory();
|
|
17
|
+
// Auto-hide session log after 3 seconds
|
|
18
|
+
useEffect(() => {
|
|
19
|
+
const timer = setTimeout(() => {
|
|
20
|
+
setShowSessionLog(false);
|
|
21
|
+
}, 3000);
|
|
22
|
+
return () => clearTimeout(timer);
|
|
23
|
+
}, []);
|
|
24
|
+
// Initialize: Load existing sessions + start persistent watcher
|
|
25
|
+
useEffect(() => {
|
|
26
|
+
let watcherInstance = null;
|
|
27
|
+
const initialize = async () => {
|
|
28
|
+
try {
|
|
29
|
+
// Step 0: Ensure session directory exists
|
|
30
|
+
await ensureDirectoryExists(sessionDir);
|
|
31
|
+
// Step 1: Load existing pending sessions
|
|
32
|
+
const watcher = createTUIWatcher();
|
|
33
|
+
const sessionIds = await watcher.getPendingSessions();
|
|
34
|
+
const sessionData = await Promise.all(sessionIds.map(async (sessionId) => {
|
|
35
|
+
const sessionRequest = await watcher.getSessionRequest(sessionId);
|
|
36
|
+
if (!sessionRequest)
|
|
37
|
+
return null;
|
|
38
|
+
return {
|
|
39
|
+
sessionId,
|
|
40
|
+
sessionRequest,
|
|
41
|
+
timestamp: new Date(sessionRequest.timestamp),
|
|
42
|
+
};
|
|
43
|
+
}));
|
|
44
|
+
// Filter out null entries and sort by timestamp (FIFO - oldest first)
|
|
45
|
+
const validSessions = sessionData
|
|
46
|
+
.filter((s) => s !== null)
|
|
47
|
+
.sort((a, b) => a.timestamp.getTime() - b.timestamp.getTime());
|
|
48
|
+
setSessionQueue(validSessions);
|
|
49
|
+
setIsInitialized(true);
|
|
50
|
+
// Step 2: Start persistent watcher for new sessions
|
|
51
|
+
watcherInstance = createTUIWatcher({ autoLoadData: true });
|
|
52
|
+
watcherInstance.startEnhancedWatching((event) => {
|
|
53
|
+
// Add new session to queue (FIFO - append to end)
|
|
54
|
+
setSessionQueue((prev) => {
|
|
55
|
+
// Check for duplicates
|
|
56
|
+
if (prev.some((s) => s.sessionId === event.sessionId)) {
|
|
57
|
+
return prev;
|
|
58
|
+
}
|
|
59
|
+
// Add to end of queue
|
|
60
|
+
return [
|
|
61
|
+
...prev,
|
|
62
|
+
{
|
|
63
|
+
sessionId: event.sessionId,
|
|
64
|
+
sessionRequest: event.sessionRequest,
|
|
65
|
+
timestamp: new Date(event.timestamp),
|
|
66
|
+
},
|
|
67
|
+
];
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
console.error("Failed to initialize:", error);
|
|
73
|
+
setIsInitialized(true); // Continue even if initialization fails
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
initialize();
|
|
77
|
+
// Cleanup: stop watcher on unmount
|
|
78
|
+
return () => {
|
|
79
|
+
if (watcherInstance) {
|
|
80
|
+
watcherInstance.stop();
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
}, []);
|
|
84
|
+
// Auto-transition: WAITING → PROCESSING when queue has items
|
|
85
|
+
useEffect(() => {
|
|
86
|
+
if (!isInitialized)
|
|
87
|
+
return;
|
|
88
|
+
if (state.mode === "WAITING" && sessionQueue.length > 0) {
|
|
89
|
+
const [nextSession, ...rest] = sessionQueue;
|
|
90
|
+
setSessionQueue(rest);
|
|
91
|
+
setState({ mode: "PROCESSING", session: nextSession });
|
|
92
|
+
}
|
|
93
|
+
}, [state, sessionQueue, isInitialized]);
|
|
94
|
+
// Show toast notification
|
|
95
|
+
const showToast = (message, type = "success", title) => {
|
|
96
|
+
setToast({ message, type, title });
|
|
97
|
+
};
|
|
98
|
+
// Handle session completion
|
|
99
|
+
const handleSessionComplete = (wasRejected = false, rejectionReason) => {
|
|
100
|
+
// Show appropriate toast
|
|
101
|
+
if (wasRejected) {
|
|
102
|
+
if (rejectionReason) {
|
|
103
|
+
showToast(`Rejection reason: ${rejectionReason}`, "info", "Question set rejected");
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
showToast("", "info", "Question set rejected");
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
showToast("✓ Answers submitted successfully!", "success");
|
|
111
|
+
}
|
|
112
|
+
if (sessionQueue.length > 0) {
|
|
113
|
+
// Auto-load next session
|
|
114
|
+
const [nextSession, ...rest] = sessionQueue;
|
|
115
|
+
setSessionQueue(rest);
|
|
116
|
+
setState({ mode: "PROCESSING", session: nextSession });
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
// Return to WAITING
|
|
120
|
+
setState({ mode: "WAITING" });
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
// Render based on state
|
|
124
|
+
if (!isInitialized) {
|
|
125
|
+
return React.createElement(Text, null, "Loading...");
|
|
126
|
+
}
|
|
127
|
+
let mainContent;
|
|
128
|
+
if (state.mode === "WAITING") {
|
|
129
|
+
mainContent = React.createElement(WaitingScreen, { queueCount: sessionQueue.length });
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
// PROCESSING mode
|
|
133
|
+
const { session } = state;
|
|
134
|
+
mainContent = (React.createElement(StepperView, { key: session.sessionId, onComplete: handleSessionComplete, sessionId: session.sessionId, sessionRequest: session.sessionRequest }));
|
|
135
|
+
}
|
|
136
|
+
// Render with header, toast overlay, and main content
|
|
137
|
+
return (React.createElement(Box, { flexDirection: "column", paddingX: 1 },
|
|
138
|
+
React.createElement(Header, { pendingCount: sessionQueue.length }),
|
|
139
|
+
toast && (React.createElement(Box, { marginBottom: 1, marginTop: 1 },
|
|
140
|
+
React.createElement(Toast, { message: toast.message, onDismiss: () => setToast(null), type: toast.type, title: toast.title }))),
|
|
141
|
+
mainContent,
|
|
142
|
+
showSessionLog && (React.createElement(Box, { marginTop: 1 },
|
|
143
|
+
React.createElement(Text, { dimColor: true },
|
|
144
|
+
"[AUQ] Session directory: ",
|
|
145
|
+
sessionDir)))));
|
|
146
|
+
};
|
|
147
|
+
export const runTui = () => {
|
|
148
|
+
// Clear terminal before showing app
|
|
149
|
+
console.clear();
|
|
150
|
+
const { waitUntilExit } = render(React.createElement(App, null));
|
|
151
|
+
// Handle Ctrl+C gracefully
|
|
152
|
+
process.on("SIGINT", () => {
|
|
153
|
+
process.exit(0);
|
|
154
|
+
});
|
|
155
|
+
// Show goodbye after Ink unmounts
|
|
156
|
+
waitUntilExit().then(() => {
|
|
157
|
+
process.stdout.write("\n");
|
|
158
|
+
console.log("👋 Goodbye! See you next time.");
|
|
159
|
+
});
|
|
160
|
+
};
|
package/dist/package.json
CHANGED