pulse-cli-divakar 0.0.0-stage → 1.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.
@@ -0,0 +1,307 @@
1
+ import chalk from "chalk";
2
+ import boxen from "boxen"
3
+ import { text,isCancel,cancel,intro,outro, box, multiselect } from "@clack/prompts";
4
+ import yoctoSpinner from "yocto-spinner";
5
+ import {marked} from "marked"
6
+ import {markedTerminal} from "marked-terminal"
7
+ import { AIService } from "../ai/google-service.js";
8
+ import { ChatService } from "../../service/chat.service.js";
9
+ import { getStoredToken } from "../../lib/token.js";
10
+ import {prisma} from "../../lib/db.js"
11
+
12
+ import {availableTools,getEnabledToolNames,getEnabledTools,enableTools,resetTools} from "../../config/tool.config.js"
13
+ import { displayMessages, getUserFromToken ,updateConversationTitle,saveMessage} from "./utils/chataiutils.js";
14
+
15
+ const aiService=new AIService()
16
+ const chatService=new ChatService()
17
+
18
+ async function selectTools() {
19
+ const toolOptions=availableTools.map(tool=>({
20
+ value:tool.id,
21
+ label:tool.description,
22
+ hint:tool.description
23
+ }))
24
+
25
+ const selectedTools=await multiselect({
26
+ message: chalk.cyan(
27
+ "Select tools to enable (Space to select, Enter to confirm):"
28
+ ),
29
+ options: toolOptions,
30
+ required: false,
31
+ })
32
+
33
+ if (isCancel(selectedTools)) {
34
+ cancel(chalk.yellow("Tool selection cancelled"));
35
+ process.exit(0);
36
+ }
37
+
38
+ enableTools(selectedTools);
39
+
40
+ if(selectedTools.length===0){
41
+ console.log(
42
+ chalk.yellow("\n⚠️ No tools selected. AI will work without tools.\n")
43
+ );
44
+ }
45
+ else{
46
+ const toolsBox = boxen(
47
+ chalk.green(
48
+ `✅ Enabled tools:\n${selectedTools
49
+ .map((id) => {
50
+ const tool = availableTools.find((t) => t.id === id);
51
+ return ` • ${tool.name}`;
52
+ })
53
+ .join("\n")}`
54
+ ),
55
+ {
56
+ padding: 1,
57
+ margin: { top: 1, bottom: 1 },
58
+ borderStyle: "round",
59
+ borderColor: "green",
60
+ title: "🛠️ Active Tools",
61
+ titleAlignment: "center",
62
+ }
63
+ );
64
+ console.log(toolsBox);
65
+ }
66
+
67
+ return selectTools.length > 0;
68
+ }
69
+
70
+
71
+ export async function startToolChat(conversationId=null) {
72
+ try {
73
+ intro(
74
+ boxen(chalk.bold.cyan("🛠️ Pulse AI - Tool Calling Mode"),{
75
+ padding: 1,
76
+ borderStyle: "double",
77
+ borderColor: "cyan",
78
+ })
79
+ )
80
+
81
+ const user=await getUserFromToken();
82
+ await selectTools();
83
+ const conversation=await initConversation(user.id,conversationId,"tool")
84
+ await chatLoop(conversation)
85
+
86
+ resetTools()
87
+
88
+ outro(chalk.green("✨Thanks for using tools"))
89
+
90
+
91
+ } catch (error) {
92
+ const errorBox = boxen(chalk.red(`❌ Error: ${error.message}`), {
93
+ padding: 1,
94
+ margin: 1,
95
+ borderStyle: "round",
96
+ borderColor: "red",
97
+ });
98
+ console.log(errorBox);
99
+ resetTools();
100
+ process.exit(1);
101
+ }
102
+ }
103
+
104
+ export async function initConversation(userId,conversationId=null,mode="tool") {
105
+ const spinner=yoctoSpinner({text:"Loading conversation..."}).start()
106
+
107
+ const conversation=await chatService.getOrCreateConversation(
108
+ userId,
109
+ conversationId,
110
+ mode
111
+ )
112
+ spinner.success("Conversation Loaded")
113
+
114
+ const enabledToolNames = getEnabledToolNames();
115
+ const toolsDisplay =
116
+ enabledToolNames.length > 0
117
+ ? `\n${chalk.gray("Active Tools:")} ${enabledToolNames.join(", ")}`
118
+ : `\n${chalk.gray("No tools enabled")}`;
119
+
120
+
121
+
122
+ const conversationInfo = boxen(
123
+ `${chalk.bold("Conversation")}: ${conversation.title}\n${chalk.gray("ID: " + conversation.id)}\n${chalk.gray("Mode: " + conversation.mode)}`,
124
+ {
125
+ padding: 1,
126
+ margin: { top: 1, bottom: 1 },
127
+ borderStyle: "round",
128
+ borderColor: "cyan",
129
+ title: "💬 Chat Session",
130
+ titleAlignment: "center",
131
+ }
132
+ );
133
+
134
+ console.log(conversationInfo)
135
+
136
+ if(conversation.messages?.length>0){
137
+ console.log(chalk.yellow("📜 Previous messages:\n"));
138
+ displayMessages(conversation.messages);
139
+ }
140
+
141
+ return conversation
142
+ }
143
+
144
+
145
+ async function chatLoop(conversation) {
146
+ const enabledToolNames=getEnabledToolNames()
147
+ const helpBox = boxen(
148
+ `${chalk.gray("• Type your message and press Enter")}\n${chalk.gray(
149
+ "• AI has access to:"
150
+ )} ${
151
+ enabledToolNames.length > 0 ? enabledToolNames.join(", ") : "No tools"
152
+ }\n${chalk.gray('• Type "exit" to end conversation')}\n${chalk.gray(
153
+ "• Press Ctrl+C to quit anytime"
154
+ )}`,
155
+ {
156
+ padding: 1,
157
+ margin: { bottom: 1 },
158
+ borderStyle: "round",
159
+ borderColor: "gray",
160
+ dimBorder: true,
161
+ }
162
+ );
163
+
164
+ console.log(helpBox)
165
+
166
+ while(true){
167
+ const userInput = await text({
168
+ message: chalk.blue("💬 Your message"),
169
+ placeholder: "Type your message...",
170
+ validate(value) {
171
+ if (!value || value.trim().length === 0) {
172
+ return "Message cannot be empty";
173
+ }
174
+ },
175
+ });
176
+
177
+ if (isCancel(userInput)) {
178
+ const exitBox = boxen(chalk.yellow("Chat session ended. Goodbye! 👋"), {
179
+ padding: 1,
180
+ margin: 1,
181
+ borderStyle: "round",
182
+ borderColor: "yellow",
183
+ });
184
+ console.log(exitBox);
185
+ process.exit(0);
186
+ }
187
+
188
+ if (userInput.toLowerCase() === "exit") {
189
+ const exitBox = boxen(chalk.yellow("Chat session ended. Goodbye! 👋"), {
190
+ padding: 1,
191
+ margin: 1,
192
+ borderStyle: "round",
193
+ borderColor: "yellow",
194
+ });
195
+ console.log(exitBox);
196
+ break;
197
+ }
198
+
199
+ const userBox = boxen(chalk.white(userInput), {
200
+ padding: 1,
201
+ margin: { left: 2, top: 1, bottom: 1 },
202
+ borderStyle: "round",
203
+ borderColor: "blue",
204
+ title: "👤 You",
205
+ titleAlignment: "left",
206
+ });
207
+ console.log(userBox);
208
+
209
+ await saveMessage(conversation.id, "user", userInput);
210
+
211
+ const messages = await chatService.getMessages(conversation.id);
212
+
213
+ const aiResponse = await getAIResponse(conversation.id);
214
+
215
+ await saveMessage(conversation.id, "assistant", aiResponse);
216
+
217
+ await updateConversationTitle(conversation.id, userInput, messages.length);
218
+
219
+ }
220
+ }
221
+
222
+ async function getAIResponse(conversationId) {
223
+ const spinner=yoctoSpinner({
224
+ text:"AI is thinking...",
225
+ color:"cyan",
226
+ }).start();
227
+
228
+ const dbMessages=await chatService.getMessages(conversationId)
229
+ const aiMessages=chatService.formatMessageForAI(dbMessages)
230
+
231
+ const tools=getEnabledTools();
232
+
233
+ let fullResponse=""
234
+ let isFirstChunk=true
235
+ const toolCallsDetected=[]
236
+
237
+ try {
238
+ const result=await aiService.sendMessage(aiMessages,(chunk)=>{
239
+ if(isFirstChunk){
240
+ spinner.stop();
241
+ console.log("\n");
242
+ const header=chalk.green.bold("🤖 Assistant")
243
+ console.log(header)
244
+ console.log(chalk.gray("-".repeat(60)))
245
+ isFirstChunk=false
246
+ }
247
+ fullResponse+=chunk;
248
+ },
249
+ tools,(toolCall)=>{
250
+ toolCallsDetected.push(toolCall)
251
+ }
252
+ );
253
+
254
+ if (toolCallsDetected.length > 0) {
255
+ console.log("\n");
256
+ const toolCallBox = boxen(
257
+ toolCallsDetected.map(tc =>
258
+ `${chalk.cyan("🔧 Tool:")} ${tc.toolName}\n${chalk.gray("Args:")} ${JSON.stringify(tc.args, null, 2)}`
259
+ ).join("\n\n"),
260
+ {
261
+ padding: 1,
262
+ margin: 1,
263
+ borderStyle: "round",
264
+ borderColor: "cyan",
265
+ title: "🛠️ Tool Calls",
266
+ }
267
+ );
268
+ console.log(toolCallBox);
269
+ }
270
+
271
+ if (result.toolResults && result.toolResults.length > 0) {
272
+ const toolResultBox = boxen(
273
+ result.toolResults.map(tr =>
274
+ `${chalk.green("✅ Tool:")} ${tr.toolName}\n${chalk.gray("Result:")} ${JSON.stringify(tr.result, null, 2).slice(0, 200)}...`
275
+ ).join("\n\n"),
276
+ {
277
+ padding: 1,
278
+ margin: 1,
279
+ borderStyle: "round",
280
+ borderColor: "green",
281
+ title: "📊 Tool Results",
282
+ }
283
+ );
284
+ console.log(toolResultBox);
285
+ }
286
+
287
+ // Render markdown response
288
+ console.log("\n");
289
+ const renderedMarkdown = marked.parse(fullResponse);
290
+ console.log(renderedMarkdown);
291
+ console.log(chalk.gray("─".repeat(60)));
292
+ console.log("\n");
293
+
294
+ return result.content;
295
+ } catch (error) {
296
+ spinner.error("Failed to get AI response");
297
+ throw error;
298
+ }
299
+
300
+ }
301
+
302
+
303
+
304
+
305
+
306
+
307
+
@@ -0,0 +1,212 @@
1
+ import yoctoSpinner from "yocto-spinner";
2
+ import { getStoredToken } from "../../../lib/token.js";
3
+ import { prisma } from "../../../lib/db.js";
4
+ import { AIService } from "../../ai/google-service.js";
5
+ import { ChatService } from "../../../service/chat.service.js";
6
+ import { isCancel, text } from "@clack/prompts";
7
+ import chalk from "chalk";
8
+ import boxen from "boxen";
9
+ import { marked } from "marked";
10
+
11
+ const aiService=new AIService()
12
+ const chatService=new ChatService()
13
+
14
+ export async function getUserFromToken() {
15
+ const token=await getStoredToken();
16
+ if(!token.access_token){
17
+ throw new Error("Not authenticated. Please run 'pulse login' first");
18
+ }
19
+ const spinner=yoctoSpinner({text:"Authenticating..."});
20
+ spinner.start()
21
+
22
+ const user=await prisma.user.findFirst({
23
+ where:{
24
+ sessions:{
25
+ some:{token:token.access_token},
26
+ }
27
+ }
28
+ })
29
+ if(!user){
30
+ spinner.error("User not found")
31
+ throw new Error("User not found.Please login again")
32
+ }
33
+ spinner.success(`Welcome back, ${user.name}!`)
34
+ return user;
35
+ }
36
+
37
+
38
+ export async function getAIResponse(conversationId) {
39
+ const spinner = yoctoSpinner({
40
+ text: "AI is thinking...",
41
+ color: "cyan"
42
+ }).start();
43
+
44
+ const dbMessages=await chatService.getMessages(conversationId)
45
+ const aiMessages=chatService.formatMessageForAI(dbMessages)
46
+
47
+ let fullResponse=""
48
+
49
+ let isFirstChunk=true;
50
+
51
+ try {
52
+ const result=await aiService.sendMessage(aiMessages,(chunk)=>{
53
+ if (isFirstChunk) {
54
+ spinner.stop();
55
+ console.log("\n");
56
+ const header = chalk.green.bold("🤖 Assistant:");
57
+ console.log(header);
58
+ console.log(chalk.gray("─".repeat(60)));
59
+ isFirstChunk = false;
60
+ }
61
+ fullResponse += chunk;
62
+ })
63
+
64
+ console.log("\n");
65
+ const renderedMarkdown = marked.parse(fullResponse);
66
+ console.log(renderedMarkdown);
67
+ console.log(chalk.gray("─".repeat(60)));
68
+ console.log("\n");
69
+
70
+ return result.content;
71
+
72
+ } catch (error) {
73
+ spinner.error("Failed to get AI response");
74
+ throw error;
75
+ }
76
+ }
77
+
78
+ export async function saveMessage(conversationId,role,content) {
79
+ return await chatService.addMessage(conversationId,role,content)
80
+ }
81
+
82
+ export async function updateConversationTitle(conversationId, userInput, messageCount) {
83
+ if (messageCount === 1) {
84
+ const title = userInput.slice(0, 50) + (userInput.length > 50 ? "..." : "");
85
+ await chatService.updateTitle(conversationId, title);
86
+ }
87
+ }
88
+
89
+
90
+ export async function initConversation(userId,conversationId=null,mode="chat") {
91
+ const spinner=yoctoSpinner({text:"Loading conversation..."}).start()
92
+
93
+ const conversation=await chatService.getOrCreateConversation(
94
+ userId,
95
+ conversationId,
96
+ mode
97
+ )
98
+
99
+ spinner.success("Conversation Loaded")
100
+
101
+ const conversationInfo = boxen(
102
+ `${chalk.bold("Conversation")}: ${conversation.title}\n${chalk.gray("ID: " + conversation.id)}\n${chalk.gray("Mode: " + conversation.mode)}`,
103
+ {
104
+ padding: 1,
105
+ margin: { top: 1, bottom: 1 },
106
+ borderStyle: "round",
107
+ borderColor: "cyan",
108
+ title: "💬 Chat Session",
109
+ titleAlignment: "center",
110
+ }
111
+ );
112
+
113
+ console.log(conversationInfo)
114
+
115
+ if(conversation.messages?.length>0){
116
+ console.log(chalk.yellow("📜 Previous messages:\n"));
117
+ displayMessages(conversation.messages);
118
+ }
119
+
120
+ return conversation
121
+ }
122
+
123
+
124
+ export function displayMessages(messages) {
125
+ messages.forEach((msg) => {
126
+ if (msg.role === "user") {
127
+ const userBox = boxen(chalk.white(msg.content), {
128
+ padding: 1,
129
+ margin: { left: 2, bottom: 1 },
130
+ borderStyle: "round",
131
+ borderColor: "blue",
132
+ title: "👤 You",
133
+ titleAlignment: "left",
134
+ });
135
+ console.log(userBox);
136
+ } else {
137
+ // Render markdown for assistant messages
138
+ const renderedContent = marked.parse(msg.content);
139
+ const assistantBox = boxen(renderedContent.trim(), {
140
+ padding: 1,
141
+ margin: { left: 2, bottom: 1 },
142
+ borderStyle: "round",
143
+ borderColor: "green",
144
+ title: "🤖 Assistant",
145
+ titleAlignment: "left",
146
+ });
147
+ console.log(assistantBox);
148
+ }
149
+ });
150
+ }
151
+
152
+
153
+ export async function chatLoop(conversation) {
154
+ const helpBox = boxen(
155
+ `${chalk.gray('• Type your message and press Enter')}\n${chalk.gray('• Markdown formatting is supported in responses')}\n${chalk.gray('• Type "exit" to end conversation')}\n${chalk.gray('• Press Ctrl+C to quit anytime')}`,
156
+ {
157
+ padding: 1,
158
+ margin: { bottom: 1 },
159
+ borderStyle: "round",
160
+ borderColor: "gray",
161
+ dimBorder: true,
162
+ }
163
+ );
164
+
165
+ console.log(helpBox)
166
+
167
+ while(true){
168
+ const userInput=await text({
169
+ message:chalk.blue("Your message"),
170
+ placeholder:"Type your message...",
171
+ validate(value){
172
+ if(!value || value.trim().length===0){
173
+ return "Message cannot be empty"
174
+ }
175
+ }
176
+ })
177
+
178
+ if(isCancel(userInput)){
179
+ const exitBox = boxen(chalk.yellow("Chat session ended. Goodbye! 👋"), {
180
+ padding: 1,
181
+ margin: 1,
182
+ borderStyle: "round",
183
+ borderColor: "yellow",
184
+ });
185
+ console.log(exitBox);
186
+ process.exit(0);
187
+ }
188
+
189
+ if(userInput.toLowerCase()==="exit"){
190
+ const exitBox = boxen(chalk.yellow("Chat session ended. Goodbye! 👋"), {
191
+ padding: 1,
192
+ margin: 1,
193
+ borderStyle: "round",
194
+ borderColor: "yellow",
195
+ });
196
+ console.log(exitBox);
197
+ break;
198
+ }
199
+
200
+ await saveMessage(conversation.id , "user" , userInput);
201
+
202
+ const messages = await chatService.getMessages(conversation.id)
203
+
204
+ const aiResponse = await getAIResponse(conversation.id)
205
+
206
+ await saveMessage(conversation.id , "assistant" , aiResponse)
207
+
208
+ await updateConversationTitle(conversation.id , userInput , messages.length)
209
+
210
+ }
211
+
212
+ }
@@ -0,0 +1,85 @@
1
+ import chalk from "chalk"
2
+ import { Command } from "commander"
3
+ import yoctoSpinner from "yocto-spinner"
4
+ import { getStoredToken } from "../../../lib/token.js"
5
+ import { select } from "@clack/prompts"
6
+ import { prisma } from "../../../lib/db.js"
7
+
8
+
9
+
10
+ const wakeUpAction=async ()=>{
11
+ const token=await getStoredToken()
12
+ if(!token?.access_token){
13
+ console.log(chalk.red("Not Authenticated.Please login"))
14
+ return;
15
+ }
16
+
17
+ const spinner=yoctoSpinner({text:"Fetching user information..."})
18
+ spinner.start()
19
+
20
+ const user=await prisma.user.findFirst({
21
+ where:{
22
+ sessions:{
23
+ some:{
24
+ token:token.access_token,
25
+ }
26
+ }
27
+ },
28
+ select:{
29
+ id:true,
30
+ name:true,
31
+ email:true,
32
+ image:true
33
+ }
34
+ })
35
+
36
+ if(!user){
37
+ console.log(chalk.red("User not Found."))
38
+ return;
39
+ }
40
+
41
+ spinner.stop()
42
+ console.log(chalk.green(`Welcome back, ${user.name}!\n`))
43
+
44
+ const choice=await select({
45
+ message:"Select an Option:",
46
+ options:[
47
+ {
48
+ value:"chat",
49
+ label:"Chat",
50
+ hint:"Simple chat with AI"
51
+ },
52
+ {
53
+ value:"tool",
54
+ label:"Tool Calling",
55
+ hint:"Chat with tools.(Google Search,Code Execution)"
56
+ },
57
+ {
58
+ value:"agent",
59
+ label:"Agentic Mode",
60
+ hint:"Advanced AI agent."
61
+ }
62
+ ]
63
+ })
64
+
65
+ switch(choice){
66
+ case "chat":
67
+ const { startChat } = await import("../../chat/chat-with-ai.js");
68
+ await startChat("chat");
69
+ break;
70
+ case "tool":
71
+ const { startToolChat } = await import("../../chat/chat-with-aitool.js");
72
+
73
+ await startToolChat();
74
+ break;
75
+ case "agent":
76
+ const { startAgentChat } = await import("../../chat/chat-with-aiAgent.js");
77
+ await startAgentChat();
78
+ break;
79
+ }
80
+ }
81
+
82
+
83
+ export const wakeUp=new Command("wakeup")
84
+ .description("Wake up the AI.")
85
+ .action(wakeUpAction)