clippy-test 1.0.9 → 2.0.1
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/LICENSE +21 -0
- package/README.md +172 -0
- package/bin/oncall.js +396 -0
- package/dist/api.js +10 -1
- package/dist/cli.js +20 -20
- package/dist/config.js +7 -7
- package/dist/helpers/cli-helpers.d.ts +25 -0
- package/dist/helpers/cli-helpers.js +329 -0
- package/dist/helpers/config-helpers.js +189 -0
- package/dist/helpers/ripgrep-tool.d.ts +15 -0
- package/dist/helpers/ripgrep-tool.js +126 -0
- package/dist/index.js +225 -62
- package/dist/logsManager.d.ts +31 -0
- package/dist/logsManager.js +90 -0
- package/dist/postinstall.js +20 -0
- package/dist/tools/ripgrep.js +3 -3
- package/dist/useWebSocket.d.ts +14 -6
- package/dist/useWebSocket.js +290 -45
- package/dist/utils/version-check.d.ts +2 -0
- package/dist/utils/version-check.js +124 -0
- package/dist/utils.d.ts +16 -0
- package/dist/utils.js +125 -4
- package/dist/websocket-server.d.ts +24 -0
- package/dist/websocket-server.js +235 -0
- package/package.json +18 -5
- package/bin/clippy.js +0 -109
- package/dist/api.js.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/code_hierarchy.js.map +0 -1
- package/dist/config.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/tools/code_hierarchy.d.ts +0 -8
- package/dist/tools/code_hierarchy.js +0 -78
- package/dist/tools/code_hierarchy.js.map +0 -1
- package/dist/tools/fetch_context.d.ts +0 -1
- package/dist/tools/fetch_context.js +0 -8
- package/dist/tools/fetch_context.js.map +0 -1
- package/dist/tools/ripgrep.js.map +0 -1
- package/dist/ui-graph.js.map +0 -1
- package/dist/useWebSocket.js.map +0 -1
- package/dist/utils.js.map +0 -1
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Singleton Logs Manager
|
|
3
|
+
* Maintains logs data
|
|
4
|
+
*/
|
|
5
|
+
export class LogsManager {
|
|
6
|
+
constructor() {
|
|
7
|
+
this.logs = "";
|
|
8
|
+
// Private constructor to prevent direct instantiation
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Get the singleton instance
|
|
12
|
+
*/
|
|
13
|
+
static getInstance() {
|
|
14
|
+
if (!LogsManager.instance) {
|
|
15
|
+
LogsManager.instance = new LogsManager();
|
|
16
|
+
}
|
|
17
|
+
return LogsManager.instance;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Append Logs
|
|
21
|
+
* @param chunk
|
|
22
|
+
*/
|
|
23
|
+
addChunk(chunk) {
|
|
24
|
+
this.logs += chunk;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Retrieves logs
|
|
28
|
+
*/
|
|
29
|
+
getLogs() {
|
|
30
|
+
return this.logs;
|
|
31
|
+
}
|
|
32
|
+
getTailLogs(n) {
|
|
33
|
+
const logs = this.getLogs() || "";
|
|
34
|
+
const linesArr = logs.split("\n");
|
|
35
|
+
const tail = linesArr.slice(-n).join("\n");
|
|
36
|
+
return tail;
|
|
37
|
+
}
|
|
38
|
+
getGrepLogs(pattern, before = 5, after = 5) {
|
|
39
|
+
const logs = this.getLogs() || "";
|
|
40
|
+
if (!pattern || !pattern.trim()) {
|
|
41
|
+
return "";
|
|
42
|
+
}
|
|
43
|
+
const lines = logs.split("\n");
|
|
44
|
+
const normalizedPattern = pattern.toLowerCase();
|
|
45
|
+
const beforeCount = before;
|
|
46
|
+
const afterCount = after;
|
|
47
|
+
const blocks = [];
|
|
48
|
+
for (let i = 0; i < lines.length; i++) {
|
|
49
|
+
const line = lines[i];
|
|
50
|
+
if (line.toLowerCase().includes(normalizedPattern)) {
|
|
51
|
+
const start = Math.max(0, i - beforeCount);
|
|
52
|
+
const end = Math.min(lines.length, i + afterCount + 1);
|
|
53
|
+
const contextBlock = lines.slice(start, end).join("\n");
|
|
54
|
+
blocks.push(`block (match line ${i + 1}): ${contextBlock}`);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
if (blocks.length === 0) {
|
|
58
|
+
return `No matches found for pattern "${pattern}" in logs.`;
|
|
59
|
+
}
|
|
60
|
+
return blocks.join("\n\n");
|
|
61
|
+
}
|
|
62
|
+
getRecentErrors(n) {
|
|
63
|
+
const logs = this.getLogs() || "";
|
|
64
|
+
const lines = logs.split("\n");
|
|
65
|
+
const matched = [];
|
|
66
|
+
if (!Number.isFinite(n) || n <= 0) {
|
|
67
|
+
return "Invalid value for n. Please provide a positive number of error lines to fetch.";
|
|
68
|
+
}
|
|
69
|
+
const errorRegex = /\b(ERROR|ERR|FATAL|CRITICAL|WARN|WARNING|SEVERE|ALERT|PANIC|EMERGENCY)\b|(Exception|Unhandled|Uncaught|Traceback|stacktrace|Caused by:)|(TypeError|ReferenceError|RangeError|SyntaxError|RuntimeError|ValueError|NullPointerException|IllegalArgument)|(timeout|timed out|connection refused|connection reset|ECONNRESET|ECONNREFUSED|ETIMEDOUT|EAI_AGAIN)|(HTTP\s(4\d\d|5\d\d)|\b5\d\d\b|\b429\b|\b503\b)|(OOM|out of memory|disk full|quota exceeded|rate limited|deadlock|segfault|SIGKILL|panic)/i;
|
|
70
|
+
for (let i = lines.length - 1; i >= 0 && matched.length < n; i--) {
|
|
71
|
+
const line = lines[i];
|
|
72
|
+
if (errorRegex.test(line)) {
|
|
73
|
+
matched.push(line);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
if (matched.length === 0) {
|
|
77
|
+
return "No recent error log lines found";
|
|
78
|
+
}
|
|
79
|
+
return matched.reverse().join("\n");
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Clear all logs
|
|
83
|
+
*/
|
|
84
|
+
clearAll() {
|
|
85
|
+
this.logs = "";
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
// Export the singleton instance
|
|
89
|
+
export default LogsManager.getInstance();
|
|
90
|
+
//# sourceMappingURL=logsManager.js.map
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { execSync } from 'child_process';
|
|
2
|
+
import { existsSync } from 'fs';
|
|
3
|
+
import { join } from 'path';
|
|
4
|
+
|
|
5
|
+
try {
|
|
6
|
+
const patchPackagePath = join(process.cwd(), 'node_modules', '.bin', 'patch-package');
|
|
7
|
+
const patchesDir = join(process.cwd(), 'patches');
|
|
8
|
+
|
|
9
|
+
if (existsSync(patchPackagePath) && existsSync(patchesDir)) {
|
|
10
|
+
execSync('patch-package', { stdio: 'ignore', timeout: 5000 });
|
|
11
|
+
}
|
|
12
|
+
} catch (error) {
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
console.log(`
|
|
16
|
+
OnCall installed.
|
|
17
|
+
|
|
18
|
+
Run:
|
|
19
|
+
oncall --help
|
|
20
|
+
to get started.`);
|
package/dist/tools/ripgrep.js
CHANGED
|
@@ -29,11 +29,11 @@ export async function ripgrepSearch(query, options = {}) {
|
|
|
29
29
|
}
|
|
30
30
|
const searchDir = workingDirectory || (typeof process !== "undefined" ? process.cwd() : undefined);
|
|
31
31
|
if (!searchDir) {
|
|
32
|
-
console.error("[ripgrep] workingDirectory is required for client-side usage");
|
|
32
|
+
// console.error("[ripgrep] workingDirectory is required for client-side usage");
|
|
33
33
|
return [];
|
|
34
34
|
}
|
|
35
35
|
try {
|
|
36
|
-
let rg = new RipGrep(query,
|
|
36
|
+
let rg = new RipGrep(query, searchDir);
|
|
37
37
|
rg.withFilename().lineNumber();
|
|
38
38
|
if (!caseSensitive) {
|
|
39
39
|
rg.ignoreCase();
|
|
@@ -82,7 +82,7 @@ export async function ripgrepSearch(query, options = {}) {
|
|
|
82
82
|
error.code === 1) {
|
|
83
83
|
return [];
|
|
84
84
|
}
|
|
85
|
-
console.error("[ripgrep] Error executing ripgrep:", error.message);
|
|
85
|
+
// console.error("[ripgrep] Error executing ripgrep:", error.message);
|
|
86
86
|
return [];
|
|
87
87
|
}
|
|
88
88
|
}
|
package/dist/useWebSocket.d.ts
CHANGED
|
@@ -1,10 +1,15 @@
|
|
|
1
|
-
|
|
1
|
+
import WebSocket from "ws";
|
|
2
|
+
import { BaseMessage } from "langchain";
|
|
3
|
+
import { LogsManager } from "./logsManager.js";
|
|
4
|
+
export declare function useWebSocket(url: string, rawLogData: LogsManager): {
|
|
2
5
|
connectWebSocket: () => void;
|
|
3
6
|
socketId: string;
|
|
4
|
-
sendQuery: (
|
|
5
|
-
chatResponseMessages:
|
|
6
|
-
|
|
7
|
-
|
|
7
|
+
sendQuery: (messages: BaseMessage[], architecture: string, logs?: string, planningDoc?: string) => void;
|
|
8
|
+
chatResponseMessages: BaseMessage<import("@langchain/core/messages").MessageStructure, import("@langchain/core/messages").MessageType>[];
|
|
9
|
+
visibleChats: BaseMessage<import("@langchain/core/messages").MessageStructure, import("@langchain/core/messages").MessageType>[];
|
|
10
|
+
setVisibleChats: import("react").Dispatch<import("react").SetStateAction<BaseMessage<import("@langchain/core/messages").MessageStructure, import("@langchain/core/messages").MessageType>[]>>;
|
|
11
|
+
setChatResponseMessages: import("react").Dispatch<import("react").SetStateAction<BaseMessage<import("@langchain/core/messages").MessageStructure, import("@langchain/core/messages").MessageType>[]>>;
|
|
12
|
+
setTrimmedChats: import("react").Dispatch<import("react").SetStateAction<BaseMessage<import("@langchain/core/messages").MessageStructure, import("@langchain/core/messages").MessageType>[]>>;
|
|
8
13
|
isConnected: boolean;
|
|
9
14
|
connectionError: string;
|
|
10
15
|
isLoading: boolean;
|
|
@@ -15,5 +20,8 @@ export declare function useWebSocket(url: string): {
|
|
|
15
20
|
setIsLoading: import("react").Dispatch<import("react").SetStateAction<boolean>>;
|
|
16
21
|
setShowControlR: import("react").Dispatch<import("react").SetStateAction<boolean>>;
|
|
17
22
|
showControlR: boolean;
|
|
18
|
-
setCompleteChatHistory: import("react").Dispatch<import("react").SetStateAction<
|
|
23
|
+
setCompleteChatHistory: import("react").Dispatch<import("react").SetStateAction<BaseMessage<import("@langchain/core/messages").MessageStructure, import("@langchain/core/messages").MessageType>[]>>;
|
|
24
|
+
customMessage: string;
|
|
25
|
+
graphState: any;
|
|
26
|
+
setGraphState: import("react").Dispatch<any>;
|
|
19
27
|
};
|
package/dist/useWebSocket.js
CHANGED
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
import { useCallback, useEffect, useRef, useState } from "react";
|
|
2
|
+
import WebSocket from "ws";
|
|
2
3
|
import { getContextLines } from "./utils.js";
|
|
3
4
|
import { toolFunctionCall } from "./api.js";
|
|
4
5
|
import fs from "fs";
|
|
5
6
|
import os from "os";
|
|
6
7
|
import path from "path";
|
|
7
|
-
|
|
8
|
+
import { mapChatMessagesToStoredMessages, mapStoredMessagesToChatMessages, SystemMessage, } from "@langchain/core/messages";
|
|
9
|
+
import { ripgrepSearch } from "./helpers/ripgrep-tool.js";
|
|
10
|
+
import { loadProjectMetadata, logd } from "./helpers/cli-helpers.js";
|
|
11
|
+
// Load OnCall config from ~/.oncall/config
|
|
8
12
|
const HOME_DIR = os.homedir();
|
|
9
|
-
const
|
|
10
|
-
const CONFIG_PATH = path.join(
|
|
13
|
+
const ONCALL_DIR = path.join(HOME_DIR, ".oncall");
|
|
14
|
+
const CONFIG_PATH = path.join(ONCALL_DIR, "config");
|
|
11
15
|
let API_KEY = "";
|
|
12
16
|
try {
|
|
13
17
|
const configText = fs.readFileSync(CONFIG_PATH, "utf8");
|
|
@@ -16,33 +20,54 @@ try {
|
|
|
16
20
|
API_KEY = match[1].trim();
|
|
17
21
|
}
|
|
18
22
|
else {
|
|
19
|
-
console.log("No API_KEY found in ~/.
|
|
23
|
+
console.log("No API_KEY found in ~/.oncall/config");
|
|
20
24
|
}
|
|
21
25
|
}
|
|
22
26
|
catch (err) {
|
|
23
|
-
console.log("Failed to read
|
|
27
|
+
console.log("Failed to read OnCall config:", err);
|
|
24
28
|
}
|
|
25
|
-
export function useWebSocket(url) {
|
|
29
|
+
export function useWebSocket(url, rawLogData) {
|
|
26
30
|
//refs
|
|
27
31
|
const socketRef = useRef(null);
|
|
28
32
|
const [socketId, setSocketId] = useState(null);
|
|
29
33
|
const [chatResponseMessages, setChatResponseMessages] = useState([]);
|
|
30
34
|
const [trimmedChats, setTrimmedChats] = useState([]);
|
|
31
|
-
const
|
|
35
|
+
const initialAssistantMessage = new SystemMessage("I’m watching your app run locally. You can ask me about errors, logs, performance,or anything else related to this run.");
|
|
36
|
+
const [visibleChats, setVisibleChats] = useState([
|
|
37
|
+
initialAssistantMessage,
|
|
38
|
+
]);
|
|
32
39
|
const [isConnected, setIsConnected] = useState(false);
|
|
33
40
|
const [connectionError, setConnectionError] = useState(null);
|
|
34
41
|
const [isLoading, setIsLoading] = useState(false);
|
|
35
42
|
const [showControlR, setShowControlR] = useState(false);
|
|
43
|
+
const [customMessage, setCustomMessage] = useState(null);
|
|
44
|
+
const [graphState, setGraphState] = useState(null);
|
|
36
45
|
const authKey = API_KEY;
|
|
46
|
+
const initialProjectMetadata = loadProjectMetadata();
|
|
47
|
+
const getProjectMetadata = () => loadProjectMetadata() || initialProjectMetadata;
|
|
48
|
+
const getServiceId = () => getProjectMetadata()?.window_id;
|
|
49
|
+
const hasLogsAccess = () => {
|
|
50
|
+
const value = getProjectMetadata()?.logs_available;
|
|
51
|
+
return value === undefined ? true : value;
|
|
52
|
+
};
|
|
53
|
+
const hasCodeAccess = () => {
|
|
54
|
+
const value = getProjectMetadata()?.code_available;
|
|
55
|
+
return value === undefined ? true : value;
|
|
56
|
+
};
|
|
37
57
|
useEffect(() => {
|
|
38
58
|
if (API_KEY === "") {
|
|
39
|
-
setConnectionError("No API_KEY found in ~/.
|
|
40
|
-
console.log("No API_KEY found in ~/.
|
|
59
|
+
setConnectionError("No API_KEY found in ~/.oncall/config");
|
|
60
|
+
console.log("No API_KEY found in ~/.oncall/config");
|
|
41
61
|
process.exit();
|
|
42
62
|
}
|
|
43
63
|
}, [API_KEY]);
|
|
44
64
|
//web socket connection
|
|
45
65
|
const connectWebSocket = useCallback(() => {
|
|
66
|
+
// const d = fs.readFileSync('logs')
|
|
67
|
+
// fs.writeFileSync('logs', `${d} inside connectWS \n message: ${url} ${authKey} ${serviceId}`)
|
|
68
|
+
// if (!url || !authKey || serviceId.trim() === "") {
|
|
69
|
+
// return;
|
|
70
|
+
// }
|
|
46
71
|
if (socketRef.current) {
|
|
47
72
|
socketRef.current.close();
|
|
48
73
|
}
|
|
@@ -52,7 +77,17 @@ export function useWebSocket(url) {
|
|
|
52
77
|
setIsConnected(false);
|
|
53
78
|
socket.onopen = () => {
|
|
54
79
|
try {
|
|
55
|
-
|
|
80
|
+
const md = getProjectMetadata();
|
|
81
|
+
if (!md?.window_id) {
|
|
82
|
+
setConnectionError("Missing serviceId (window_id) in oncall.yaml");
|
|
83
|
+
console.log("Missing serviceId (window_id) in oncall.yaml");
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
socket.send(JSON.stringify({
|
|
87
|
+
type: "recurring_connection",
|
|
88
|
+
authKey,
|
|
89
|
+
serviceId: md.window_id,
|
|
90
|
+
}));
|
|
56
91
|
}
|
|
57
92
|
catch (error) {
|
|
58
93
|
setConnectionError("error");
|
|
@@ -83,7 +118,8 @@ export function useWebSocket(url) {
|
|
|
83
118
|
return; // ignore non-JSON frames
|
|
84
119
|
}
|
|
85
120
|
const data = JSON.parse(text);
|
|
86
|
-
if (data.type === "user_assigned" && data.socketId) {
|
|
121
|
+
// if (data.type === "user_assigned" && data.socketId) {
|
|
122
|
+
if (data.type === "user_assigned") {
|
|
87
123
|
setIsConnected(true);
|
|
88
124
|
setSocketId(data.socketId);
|
|
89
125
|
}
|
|
@@ -99,46 +135,126 @@ export function useWebSocket(url) {
|
|
|
99
135
|
return;
|
|
100
136
|
}
|
|
101
137
|
if (data.type === "tool_function_call") {
|
|
102
|
-
if (data.function_name === "
|
|
138
|
+
if (data.function_name === "read_file") {
|
|
139
|
+
logd(`[read_file] 📥 Tool call received: tool_call_id=${data.tool_call_id}, args=${JSON.stringify(data.args)}`);
|
|
140
|
+
if (!hasCodeAccess()) {
|
|
141
|
+
logd(`[read_file] ⚠️ No code access, denying tool`);
|
|
142
|
+
await denyToolAccess("read_file", data);
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
103
145
|
const argsData = {
|
|
104
|
-
|
|
146
|
+
filePath: data.args.filePath,
|
|
105
147
|
lineNumber: data.args.lineNumber,
|
|
106
|
-
before: data.args.before,
|
|
107
|
-
after: data.args.after,
|
|
148
|
+
before: data.args.before || 30,
|
|
149
|
+
after: data.args.after || 30,
|
|
108
150
|
};
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
151
|
+
logd(`[read_file] 📋 Extracted args: filePath=${argsData.filePath}, lineNumber=${argsData.lineNumber}, before=${argsData.before}, after=${argsData.after}`);
|
|
152
|
+
logd(`[read_file] 🔍 Calling getContextLines...`);
|
|
153
|
+
const result = getContextLines(argsData.filePath, argsData.lineNumber, argsData.before, argsData.after);
|
|
154
|
+
logd(`[read_file] ✅ File content extracted: resultLength=${result.length}, resultLines=${result.split("\n").length}, isEmpty=${result.trim().length === 0}`);
|
|
155
|
+
logd(`[read_file] 📄 Result preview (first 300 chars): ${result.substring(0, 300)}${result.length > 300 ? "..." : ""}`);
|
|
156
|
+
await postToolCallResult(data, result, setChatResponseMessages);
|
|
157
|
+
logd(`[read_file] 📤 Result sent to backend successfully`);
|
|
158
|
+
// await redisClient.set(data.tool_call_id, result, { EX: 120 });
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
if (data.function_name === "grep_search") {
|
|
162
|
+
if (!hasCodeAccess()) {
|
|
163
|
+
await denyToolAccess("grep_search", data);
|
|
164
|
+
return;
|
|
112
165
|
}
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
166
|
+
await grepSearch(data.args.searchTerm, data);
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
if (data.function_name === "read_logs") {
|
|
170
|
+
if (!hasLogsAccess()) {
|
|
171
|
+
await denyToolAccess("read_logs", data);
|
|
172
|
+
return;
|
|
118
173
|
}
|
|
119
|
-
|
|
174
|
+
await readLogs(data.args.pageNumber, data);
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
if (data.function_name === "tail_logs") {
|
|
178
|
+
if (!hasLogsAccess()) {
|
|
179
|
+
await denyToolAccess("tail_logs", data);
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
await tailLogs(data.args.n, data);
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
if (data.function_name === "grep_logs") {
|
|
186
|
+
if (!hasLogsAccess()) {
|
|
187
|
+
await denyToolAccess("grep_logs", data);
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
await grepLogs(data.args.pattern, data.args.before, data.args.after, data);
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
if (data.function_name === "get_recent_errors") {
|
|
194
|
+
if (!hasLogsAccess()) {
|
|
195
|
+
await denyToolAccess("get_recent_errors", data);
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
await getRecentErrors(data.args.n, data);
|
|
199
|
+
return;
|
|
120
200
|
}
|
|
121
|
-
return;
|
|
122
201
|
}
|
|
123
|
-
|
|
124
|
-
|
|
202
|
+
// trimmed - imp msgs
|
|
203
|
+
// visible - shown
|
|
204
|
+
// chatresponses - all
|
|
125
205
|
// add non-progress responses to trimmedChats
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
206
|
+
// @todo this is where the messages get trimmed
|
|
207
|
+
// if (data.type !== "progress") {
|
|
208
|
+
// setTrimmedChats((prevTrimmed) => {
|
|
209
|
+
// const updated = [...prevTrimmed, data];
|
|
210
|
+
// // update visible chats with all trimmed messages
|
|
211
|
+
// setVisibleChats(updated);
|
|
212
|
+
// return updated;
|
|
213
|
+
// });
|
|
214
|
+
// }
|
|
215
|
+
if (data.type === "response") {
|
|
216
|
+
let messages = data.data.messages ? data.data.messages : [];
|
|
217
|
+
messages = mapStoredMessagesToChatMessages(messages);
|
|
218
|
+
switch (data.data.type) {
|
|
219
|
+
case "messages":
|
|
220
|
+
if (data.data.sender !== "toolNode") {
|
|
221
|
+
setVisibleChats(old => [...old, ...messages]);
|
|
222
|
+
}
|
|
223
|
+
// logd(`LOGGING RESPONSE SENDER: ${data.data.sender}`);
|
|
224
|
+
break;
|
|
225
|
+
case "values":
|
|
226
|
+
if (data.data.state) {
|
|
227
|
+
setGraphState(data.data.state);
|
|
228
|
+
}
|
|
229
|
+
break;
|
|
230
|
+
case "updates":
|
|
231
|
+
if (data.data.sender !== "toolNode" && data.data.sender !== "routerNode") {
|
|
232
|
+
setChatResponseMessages(old => [...old, ...messages]);
|
|
233
|
+
}
|
|
234
|
+
if (data.data.sender === "answerNode") {
|
|
235
|
+
setTrimmedChats(prev => {
|
|
236
|
+
setVisibleChats([...prev, ...messages]);
|
|
237
|
+
return [...prev, ...messages];
|
|
238
|
+
});
|
|
239
|
+
setIsLoading(false);
|
|
240
|
+
}
|
|
241
|
+
if (data.data.sender === "userNode") {
|
|
242
|
+
setIsLoading(false);
|
|
243
|
+
}
|
|
244
|
+
break;
|
|
245
|
+
default:
|
|
246
|
+
break;
|
|
247
|
+
}
|
|
133
248
|
}
|
|
134
|
-
if (data.type === "
|
|
135
|
-
data.type === "error" ||
|
|
249
|
+
if (data.type === "error" ||
|
|
136
250
|
data.type === "ask_user") {
|
|
137
251
|
setIsLoading(false);
|
|
252
|
+
setCustomMessage(null);
|
|
138
253
|
}
|
|
139
254
|
}
|
|
140
255
|
catch (error) {
|
|
141
|
-
|
|
256
|
+
setIsLoading(false);
|
|
257
|
+
// console.warn("WebSocket message handling warning:", error);
|
|
142
258
|
return;
|
|
143
259
|
}
|
|
144
260
|
};
|
|
@@ -159,34 +275,160 @@ export function useWebSocket(url) {
|
|
|
159
275
|
// };
|
|
160
276
|
// }
|
|
161
277
|
}, [url, authKey]);
|
|
162
|
-
const sendQuery = useCallback((
|
|
278
|
+
const sendQuery = useCallback((messages, architecture, logs, planningDoc) => {
|
|
163
279
|
const socket = socketRef.current;
|
|
164
280
|
if (socket && socket.readyState === WebSocket.OPEN) {
|
|
165
281
|
setIsLoading(true);
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
282
|
+
setCustomMessage("🤔 Thinking...");
|
|
283
|
+
const md = getProjectMetadata();
|
|
284
|
+
const payload = {
|
|
285
|
+
type: "query",
|
|
286
|
+
authKey,
|
|
287
|
+
serviceId: md?.window_id,
|
|
288
|
+
userQuery: {
|
|
289
|
+
messages: graphState
|
|
290
|
+
? mapChatMessagesToStoredMessages([messages[messages.length - 1]])
|
|
291
|
+
: mapChatMessagesToStoredMessages(messages),
|
|
292
|
+
architecture: architecture,
|
|
293
|
+
logs,
|
|
294
|
+
planningDoc: "",
|
|
295
|
+
},
|
|
296
|
+
planningDoc,
|
|
297
|
+
graphState: graphState || undefined,
|
|
298
|
+
};
|
|
169
299
|
socket.send(JSON.stringify(payload));
|
|
170
300
|
}
|
|
171
301
|
else {
|
|
172
302
|
setConnectionError("WebSocket not connected");
|
|
173
303
|
throw new Error("Cannot send the message: Web socket not connected");
|
|
174
304
|
}
|
|
175
|
-
}, [socketId]);
|
|
305
|
+
}, [socketId, graphState]);
|
|
176
306
|
// Clean up on unmount
|
|
177
307
|
useEffect(() => {
|
|
178
308
|
return () => {
|
|
179
309
|
if (socketRef.current) {
|
|
180
|
-
socketRef.current.close();
|
|
310
|
+
// socketRef.current.close();
|
|
181
311
|
}
|
|
182
312
|
};
|
|
183
313
|
}, []);
|
|
314
|
+
async function grepSearch(searchTerm, data) {
|
|
315
|
+
logd(`[grepSearch] 🔍 Grep search invoked: searchTerm=${searchTerm}, tool_call_id=${data.tool_call_id}, args=${JSON.stringify(data.args)}`);
|
|
316
|
+
const projectMetadata = getProjectMetadata();
|
|
317
|
+
const workingDirectory = projectMetadata?.path || process.cwd();
|
|
318
|
+
logd(`[grepSearch] 📁 Working directory: workingDirectory=${workingDirectory}, projectPath=${projectMetadata?.path || 'N/A'}, processCwd=${process.cwd()}`);
|
|
319
|
+
const maxResults = data.args?.max_results || 20;
|
|
320
|
+
const caseSensitive = data.args?.case_sensitive || false;
|
|
321
|
+
const fileTypes = data.args?.file_types || [];
|
|
322
|
+
logd(`[grepSearch] ⚙️ Search options: maxResults=${maxResults}, caseSensitive=${caseSensitive}, fileTypes=${JSON.stringify(fileTypes)}`);
|
|
323
|
+
try {
|
|
324
|
+
const results = await ripgrepSearch(searchTerm, {
|
|
325
|
+
maxResults,
|
|
326
|
+
caseSensitive,
|
|
327
|
+
fileTypes,
|
|
328
|
+
workingDirectory,
|
|
329
|
+
});
|
|
330
|
+
logd(`[grepSearch] ✅ Search completed: resultCount=${results.length}, results=${JSON.stringify(results.map((r) => ({
|
|
331
|
+
filePath: r.filePath,
|
|
332
|
+
line: r.line,
|
|
333
|
+
previewLength: r.preview?.length || 0,
|
|
334
|
+
})))}`);
|
|
335
|
+
await postToolCallResult(data, results, setChatResponseMessages);
|
|
336
|
+
}
|
|
337
|
+
catch (error) {
|
|
338
|
+
logd(`[grepSearch] ❌ Error during grep search: ${error instanceof Error ? error.message : String(error)}, stack=${error instanceof Error ? error.stack : "N/A"}`);
|
|
339
|
+
await postToolCallResult(data, [], setChatResponseMessages);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
async function readLogs(pageNumber, data) {
|
|
343
|
+
logd(`Triggered readLogs: ${pageNumber}`);
|
|
344
|
+
pageNumber = parseInt(pageNumber.toString());
|
|
345
|
+
const lines = rawLogData
|
|
346
|
+
.getLogs()
|
|
347
|
+
.split("\n")
|
|
348
|
+
.slice(-50 * pageNumber)
|
|
349
|
+
.join("\n");
|
|
350
|
+
logd(`Triggered readLogs - Posting result: ${lines.length}`);
|
|
351
|
+
await postToolCallResult(data, lines, setChatResponseMessages);
|
|
352
|
+
logd(`Triggered readLogs - done Posting result`);
|
|
353
|
+
}
|
|
354
|
+
async function tailLogs(n, data) {
|
|
355
|
+
try {
|
|
356
|
+
logd(`Triggered tailLogs: ${n}`);
|
|
357
|
+
n = parseInt(n.toString());
|
|
358
|
+
if (Number.isNaN(n) || n <= 0) {
|
|
359
|
+
await postToolCallResult(data, "Invalid value for n. Please provide a positive number of log lines to fetch.", setChatResponseMessages);
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
362
|
+
const tail = rawLogData.getTailLogs(n);
|
|
363
|
+
logd(`Triggered tailLogs - Posting result: ${tail.length}`);
|
|
364
|
+
await postToolCallResult(data, tail, setChatResponseMessages);
|
|
365
|
+
logd(`Triggered tailLogs - done Posting result`);
|
|
366
|
+
}
|
|
367
|
+
catch (error) {
|
|
368
|
+
logd(`Triggered tailLogs - error: ${JSON.stringify(error)}`);
|
|
369
|
+
await postToolCallResult(data, "Failed to fetch tail logs from CLI.", setChatResponseMessages);
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
async function grepLogs(pattern, before, after, data) {
|
|
373
|
+
try {
|
|
374
|
+
logd(`Triggered grepLogs: pattern=${pattern}, before=${before}, after=${after}`);
|
|
375
|
+
if (!pattern || !pattern.trim()) {
|
|
376
|
+
await postToolCallResult(data, "Invalid pattern for grep_logs. Please provide a non-empty pattern.", setChatResponseMessages);
|
|
377
|
+
return;
|
|
378
|
+
}
|
|
379
|
+
const beforeCount = before !== undefined ? parseInt(before.toString()) : 5;
|
|
380
|
+
const afterCount = after !== undefined ? parseInt(after.toString()) : 5;
|
|
381
|
+
const result = rawLogData.getGrepLogs(pattern, beforeCount, afterCount);
|
|
382
|
+
await postToolCallResult(data, result, setChatResponseMessages);
|
|
383
|
+
logd(`Triggered grepLogs - done Posting result of length ${result.length}`);
|
|
384
|
+
}
|
|
385
|
+
catch (error) {
|
|
386
|
+
logd(`Triggered grepLogs - error: ${JSON.stringify(error)}`);
|
|
387
|
+
await postToolCallResult(data, "Failed to search logs with grep_logs.", setChatResponseMessages);
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
async function getRecentErrors(n, data) {
|
|
391
|
+
try {
|
|
392
|
+
logd(`Triggered getRecentErrors: n=${n}`);
|
|
393
|
+
n = parseInt(n.toString());
|
|
394
|
+
if (Number.isNaN(n) || n <= 0) {
|
|
395
|
+
await postToolCallResult(data, "Invalid value for n. Number Provided :" + n.toString(), setChatResponseMessages);
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
398
|
+
const result = rawLogData.getRecentErrors(n);
|
|
399
|
+
await postToolCallResult(data, result, setChatResponseMessages);
|
|
400
|
+
logd(`Triggered getRecentErrors - done Posting result of length ${result.length}`);
|
|
401
|
+
}
|
|
402
|
+
catch (error) {
|
|
403
|
+
logd(`Triggered getRecentErrors - error: ${JSON.stringify(error)}`);
|
|
404
|
+
await postToolCallResult(data, "Failed to fetch recent error logs.", setChatResponseMessages);
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
async function postToolCallResult(data, result, setChatResponseMessages) {
|
|
408
|
+
try {
|
|
409
|
+
await toolFunctionCall(data?.tool_call_id, result, data?.args, "tool_function_call");
|
|
410
|
+
}
|
|
411
|
+
catch (error) {
|
|
412
|
+
logd(`Triggered readLogs - failer podting: ${JSON.stringify(error)}`);
|
|
413
|
+
// setChatResponseMessages((prev) => [
|
|
414
|
+
// ...prev,
|
|
415
|
+
// error?.response?.message || "Error, please try again",
|
|
416
|
+
// ]);
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
async function denyToolAccess(functionName, data) {
|
|
420
|
+
const serviceId = getServiceId();
|
|
421
|
+
const message = `No Access to execute ${functionName}. with the serviceId of ${serviceId ?? "unknown"}.`;
|
|
422
|
+
await postToolCallResult(data, message, setChatResponseMessages);
|
|
423
|
+
}
|
|
184
424
|
return {
|
|
185
425
|
connectWebSocket,
|
|
186
426
|
socketId,
|
|
187
427
|
sendQuery,
|
|
188
|
-
chatResponseMessages:
|
|
189
|
-
|
|
428
|
+
chatResponseMessages: chatResponseMessages, // Full history including ToolMessage (needed for backend)
|
|
429
|
+
visibleChats: visibleChats, // Filtered for UI display (excludes ToolMessage except reflections)
|
|
430
|
+
setVisibleChats: setVisibleChats,
|
|
431
|
+
setChatResponseMessages,
|
|
190
432
|
setTrimmedChats,
|
|
191
433
|
isConnected,
|
|
192
434
|
connectionError,
|
|
@@ -199,6 +441,9 @@ export function useWebSocket(url) {
|
|
|
199
441
|
setShowControlR,
|
|
200
442
|
showControlR,
|
|
201
443
|
setCompleteChatHistory: setChatResponseMessages,
|
|
444
|
+
customMessage,
|
|
445
|
+
graphState,
|
|
446
|
+
setGraphState,
|
|
202
447
|
};
|
|
203
448
|
}
|
|
204
449
|
//# sourceMappingURL=useWebSocket.js.map
|