@dubeyvishal/orbital-cli 1.6.12 → 1.6.14
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/package.json +4 -2
- package/server/src/cli/ai/googleService.js +175 -63
- package/server/src/cli/chat/chat-with-ai-agent.js +40 -20
- package/server/src/cli/chat/chat-with-ai-tools.js +108 -46
- package/server/src/cli/chat/chat-with-ai.js +232 -197
- package/server/src/cli/commands/ai/wakeUp.js +139 -78
- package/server/src/cli/commands/auth/login.js +1 -7
- package/server/src/cli/commands/config/setkey.js +57 -6
- package/server/src/cli/main.js +6 -2
- package/server/src/config/aiConfig.js +159 -0
- package/server/src/config/googleConfig.js +1 -1
- package/server/src/config/toolConfig.js +25 -44
- package/server/src/controllers/aiController.js +10 -6
- package/server/src/lib/credentialStore.js +47 -14
- package/server/src/lib/orbitalConfig.js +144 -38
- package/server/src/service/aiService.js +5 -5
|
@@ -1,85 +1,98 @@
|
|
|
1
1
|
import chalk from "chalk";
|
|
2
2
|
import boxen from "boxen";
|
|
3
|
-
import {text
|
|
3
|
+
import { text, isCancel, intro, outro } from "@clack/prompts";
|
|
4
4
|
import yoctoSpinner from "yocto-spinner";
|
|
5
|
-
import {marked} from "marked";
|
|
6
|
-
import {markedTerminal} from "marked-terminal";
|
|
7
|
-
import {getStoredToken} from "../../lib/token.js";
|
|
5
|
+
import { marked } from "marked";
|
|
6
|
+
import { markedTerminal } from "marked-terminal";
|
|
7
|
+
import { getStoredToken } from "../../lib/token.js";
|
|
8
8
|
import { apiRequestSafe } from "../utils/apiClient.js";
|
|
9
9
|
import { AIService } from "../ai/googleService.js";
|
|
10
|
-
import {
|
|
10
|
+
import { requireApiKey, hydrateApiKeyEnv } from "../../lib/orbitalConfig.js";
|
|
11
|
+
import { parseModelChoice } from "../../config/aiConfig.js";
|
|
12
|
+
|
|
11
13
|
|
|
12
14
|
marked.use(
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
)
|
|
15
|
+
markedTerminal({
|
|
16
|
+
code: chalk.cyan,
|
|
17
|
+
blockquote: chalk.gray.italic,
|
|
18
|
+
heading: chalk.green.bold,
|
|
19
|
+
firstHeading: chalk.magenta.underline.bold,
|
|
20
|
+
hr: chalk.reset,
|
|
21
|
+
listitem: chalk.reset,
|
|
22
|
+
list: chalk.reset,
|
|
23
|
+
paragraph: chalk.reset,
|
|
24
|
+
strong: chalk.bold,
|
|
25
|
+
em: chalk.italic,
|
|
26
|
+
codespan: chalk.yellow.bgBlack,
|
|
27
|
+
del: chalk.dim.gray.strikethrough,
|
|
28
|
+
link: chalk.blue.underline,
|
|
29
|
+
href: chalk.blue.underline,
|
|
30
|
+
})
|
|
31
|
+
);
|
|
30
32
|
|
|
31
|
-
const getUserFromToken = async()=>{
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
33
|
+
const getUserFromToken = async () => {
|
|
34
|
+
const token = await getStoredToken();
|
|
35
|
+
if (!token?.access_token) {
|
|
36
|
+
throw new Error("Not authenticated. Please run 'orbital login' first.");
|
|
37
|
+
}
|
|
36
38
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
}
|
|
45
|
-
spinner.success(`Welcome back , ${user.name}!`);
|
|
46
|
-
return user;
|
|
47
|
-
} finally {
|
|
48
|
-
spinner.stop();
|
|
39
|
+
const spinner = yoctoSpinner({ text: "Authenticating..." }).start();
|
|
40
|
+
try {
|
|
41
|
+
const result = await apiRequestSafe("/api/cli/me");
|
|
42
|
+
const user = result?.user;
|
|
43
|
+
if (!user) {
|
|
44
|
+
spinner.error("User not found");
|
|
45
|
+
throw new Error("User not found. Please login again");
|
|
49
46
|
}
|
|
50
|
-
}
|
|
47
|
+
spinner.success(`Welcome back, ${user.name}!`);
|
|
48
|
+
return user;
|
|
49
|
+
} finally {
|
|
50
|
+
spinner.stop();
|
|
51
|
+
}
|
|
52
|
+
};
|
|
51
53
|
|
|
52
|
-
const initConversation = async(
|
|
53
|
-
|
|
54
|
+
const initConversation = async (
|
|
55
|
+
userId,
|
|
56
|
+
conversationId = null,
|
|
57
|
+
mode = "chat",
|
|
58
|
+
modelDisplayName = null
|
|
59
|
+
) => {
|
|
60
|
+
const spinner = yoctoSpinner({ text: "Loading conversation..." }).start();
|
|
54
61
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
+
const result = await apiRequestSafe("/api/cli/conversations/init", {
|
|
63
|
+
method: "POST",
|
|
64
|
+
body: { conversationId, mode },
|
|
65
|
+
});
|
|
66
|
+
const conversation = result?.conversation;
|
|
67
|
+
const messages = result?.messages || [];
|
|
68
|
+
spinner.success("Conversation Loaded");
|
|
62
69
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
padding : 1 ,
|
|
67
|
-
margin : {top: 1 , bottom : 1} ,
|
|
68
|
-
borderStyle : "round" ,
|
|
69
|
-
borderColor : "cyan" ,
|
|
70
|
-
title : "Chat Settion",
|
|
71
|
-
titleAlignment : "center"
|
|
72
|
-
}
|
|
73
|
-
);
|
|
74
|
-
console.log(conversationInfo);
|
|
70
|
+
const modelLine = modelDisplayName
|
|
71
|
+
? `\n ${chalk.cyan("Model: " + modelDisplayName)}`
|
|
72
|
+
: "";
|
|
75
73
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
74
|
+
const conversationInfo = boxen(
|
|
75
|
+
`${chalk.bold("Conversation")}: ${conversation.title}\n ${chalk.gray(
|
|
76
|
+
"ID: " + conversation.id
|
|
77
|
+
)}\n ${chalk.gray("Mode: " + conversation.mode)}${modelLine}`,
|
|
78
|
+
{
|
|
79
|
+
padding: 1,
|
|
80
|
+
margin: { top: 1, bottom: 1 },
|
|
81
|
+
borderStyle: "round",
|
|
82
|
+
borderColor: "cyan",
|
|
83
|
+
title: "Chat Session",
|
|
84
|
+
titleAlignment: "center",
|
|
79
85
|
}
|
|
86
|
+
);
|
|
87
|
+
console.log(conversationInfo);
|
|
80
88
|
|
|
81
|
-
|
|
82
|
-
|
|
89
|
+
if (messages.length > 0) {
|
|
90
|
+
console.log(chalk.yellow("Previous Messages:\n"));
|
|
91
|
+
displayMessages(messages);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return conversation;
|
|
95
|
+
};
|
|
83
96
|
|
|
84
97
|
const displayMessages = (messages) => {
|
|
85
98
|
messages.forEach((msg) => {
|
|
@@ -108,153 +121,175 @@ const displayMessages = (messages) => {
|
|
|
108
121
|
});
|
|
109
122
|
};
|
|
110
123
|
|
|
111
|
-
const saveMessage = async(conversationId
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
}
|
|
124
|
+
const saveMessage = async (conversationId, role, content) => {
|
|
125
|
+
return await apiRequestSafe("/api/cli/messages", {
|
|
126
|
+
method: "POST",
|
|
127
|
+
body: { conversationId, role, content },
|
|
128
|
+
});
|
|
129
|
+
};
|
|
117
130
|
|
|
118
|
-
const getAIResponse
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
color: "cyan"
|
|
122
|
-
}).start();
|
|
131
|
+
const getAIResponse = async (conversationId, modelConfig = null) => {
|
|
132
|
+
const aiService = new AIService(modelConfig);
|
|
133
|
+
await requireApiKey(aiService.provider);
|
|
123
134
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
{ method: "GET" }
|
|
129
|
-
);
|
|
135
|
+
const spinner = yoctoSpinner({
|
|
136
|
+
text: `${aiService.getDisplayName()} is thinking...`,
|
|
137
|
+
color: "cyan",
|
|
138
|
+
}).start();
|
|
130
139
|
|
|
131
|
-
|
|
132
|
-
|
|
140
|
+
let fullResponse = "";
|
|
141
|
+
try {
|
|
142
|
+
const messageResult = await apiRequestSafe(
|
|
143
|
+
`/api/cli/messages?conversationId=${encodeURIComponent(conversationId)}`,
|
|
144
|
+
{ method: "GET" }
|
|
145
|
+
);
|
|
133
146
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
147
|
+
const messages = Array.isArray(messageResult?.messages)
|
|
148
|
+
? messageResult.messages
|
|
149
|
+
: [];
|
|
150
|
+
const aiMessages = messages.map((m) => ({
|
|
151
|
+
role: m.role,
|
|
152
|
+
content: m.content,
|
|
153
|
+
}));
|
|
137
154
|
|
|
138
|
-
|
|
139
|
-
console.log("\n");
|
|
140
|
-
const header = chalk.green.bold("Assistent: ");
|
|
141
|
-
console.log(header);
|
|
142
|
-
console.log(chalk.gray("-".repeat(60)));
|
|
155
|
+
const result = await aiService.sendMessage(aiMessages);
|
|
143
156
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
console.log("\n");
|
|
157
|
+
spinner.stop();
|
|
158
|
+
console.log("\n");
|
|
159
|
+
const header = chalk.green.bold(`Assistant (${aiService.getDisplayName()}):`);
|
|
160
|
+
console.log(header);
|
|
161
|
+
console.log(chalk.gray("-".repeat(60)));
|
|
150
162
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
}
|
|
163
|
+
fullResponse = result?.content || "";
|
|
164
|
+
console.log("\n");
|
|
165
|
+
const renderMarkdown = marked.parse(fullResponse);
|
|
166
|
+
console.log(renderMarkdown);
|
|
167
|
+
console.log(chalk.gray("-".repeat(60)));
|
|
168
|
+
console.log("\n");
|
|
158
169
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
});
|
|
166
|
-
}
|
|
167
|
-
}
|
|
170
|
+
return fullResponse;
|
|
171
|
+
} catch (error) {
|
|
172
|
+
spinner.error("Failed to get AI response");
|
|
173
|
+
throw error;
|
|
174
|
+
}
|
|
175
|
+
};
|
|
168
176
|
|
|
169
|
-
const
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
);
|
|
181
|
-
|
|
177
|
+
const updateConversationTitle = async (
|
|
178
|
+
conversationId,
|
|
179
|
+
userInput,
|
|
180
|
+
messageCount
|
|
181
|
+
) => {
|
|
182
|
+
if (messageCount === 1) {
|
|
183
|
+
const title =
|
|
184
|
+
userInput.slice(0, 50) + (userInput.length > 50 ? "..." : "");
|
|
185
|
+
await apiRequestSafe(`/api/cli/conversations/${conversationId}/title`, {
|
|
186
|
+
method: "PATCH",
|
|
187
|
+
body: { title },
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
};
|
|
182
191
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
192
|
+
const chatLoop = async (conversation, modelConfig = null) => {
|
|
193
|
+
const helpBox = boxen(
|
|
194
|
+
`${chalk.gray(`* Type your message and press Enter`)}\n${chalk.gray(
|
|
195
|
+
`* Markdown formatting is supported in response`
|
|
196
|
+
)}\n${chalk.gray(`* Type "exit" to end conversation`)}\n${chalk.gray(
|
|
197
|
+
`* Press Ctrl + C to quit anytime`
|
|
198
|
+
)}`,
|
|
199
|
+
{
|
|
200
|
+
padding: 1,
|
|
201
|
+
margin: { bottom: 1 },
|
|
202
|
+
borderStyle: "round",
|
|
203
|
+
borderColor: "gray",
|
|
204
|
+
dimBorder: true,
|
|
205
|
+
}
|
|
206
|
+
);
|
|
207
|
+
console.log(helpBox);
|
|
193
208
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
console.log(exitBox);
|
|
202
|
-
process.exit(0);
|
|
209
|
+
while (true) {
|
|
210
|
+
const userInput = await text({
|
|
211
|
+
message: chalk.blue("Your message"),
|
|
212
|
+
placeholder: "Type your message...",
|
|
213
|
+
validate(value) {
|
|
214
|
+
if (!value || value.trim().length === 0) {
|
|
215
|
+
return "Message cannot be empty";
|
|
203
216
|
}
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
padding : 1 ,
|
|
207
|
-
margin : 1,
|
|
208
|
-
borderStyle : "round" ,
|
|
209
|
-
borderColor : "yellow" ,
|
|
210
|
-
});
|
|
211
|
-
console.log(exitBox);
|
|
212
|
-
break ;
|
|
213
|
-
}
|
|
214
|
-
await saveMessage(conversation.id , "user" , userInput);
|
|
215
|
-
const messageResult = await apiRequestSafe(
|
|
216
|
-
`/api/cli/messages?conversationId=${encodeURIComponent(conversation.id)}`,
|
|
217
|
-
{ method: "GET" }
|
|
218
|
-
);
|
|
219
|
-
|
|
220
|
-
const aiResponse = await getAIResponse(conversation.id);
|
|
221
|
-
await saveMessage(conversation.id , "assistant" , aiResponse);
|
|
217
|
+
},
|
|
218
|
+
});
|
|
222
219
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
220
|
+
if (isCancel(userInput)) {
|
|
221
|
+
const exitBox = boxen(chalk.yellow("Chat session ended. Goodbye!"), {
|
|
222
|
+
padding: 1,
|
|
223
|
+
margin: 1,
|
|
224
|
+
borderStyle: "round",
|
|
225
|
+
borderColor: "yellow",
|
|
226
|
+
});
|
|
227
|
+
console.log(exitBox);
|
|
228
|
+
process.exit(0);
|
|
229
|
+
}
|
|
230
|
+
if (userInput.trim().toLowerCase() === "exit") {
|
|
231
|
+
const exitBox = boxen(chalk.yellow("Chat session ended. Goodbye!"), {
|
|
232
|
+
padding: 1,
|
|
233
|
+
margin: 1,
|
|
234
|
+
borderStyle: "round",
|
|
235
|
+
borderColor: "yellow",
|
|
236
|
+
});
|
|
237
|
+
console.log(exitBox);
|
|
238
|
+
break;
|
|
228
239
|
}
|
|
229
|
-
|
|
240
|
+
await saveMessage(conversation.id, "user", userInput);
|
|
241
|
+
const messageResult = await apiRequestSafe(
|
|
242
|
+
`/api/cli/messages?conversationId=${encodeURIComponent(conversation.id)}`,
|
|
243
|
+
{ method: "GET" }
|
|
244
|
+
);
|
|
230
245
|
|
|
246
|
+
const aiResponse = await getAIResponse(conversation.id, modelConfig);
|
|
247
|
+
await saveMessage(conversation.id, "assistant", aiResponse);
|
|
231
248
|
|
|
249
|
+
await updateConversationTitle(
|
|
250
|
+
conversation.id,
|
|
251
|
+
userInput,
|
|
252
|
+
messageResult?.messages?.length || 0
|
|
253
|
+
);
|
|
254
|
+
}
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
export const startChat = async (
|
|
258
|
+
mode = "chat",
|
|
259
|
+
conversationId = null,
|
|
260
|
+
modelConfig = null
|
|
261
|
+
) => {
|
|
262
|
+
try {
|
|
263
|
+
intro(
|
|
264
|
+
boxen(chalk.bold.cyan("Orbital AI Chat"), {
|
|
265
|
+
padding: 1,
|
|
266
|
+
borderStyle: "double",
|
|
267
|
+
borderColor: "cyan",
|
|
268
|
+
})
|
|
269
|
+
);
|
|
232
270
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
boxen(chalk.bold.cyan("Orbital AI Chat") , {
|
|
237
|
-
padding: 1 ,
|
|
238
|
-
borderStyle : "double",
|
|
239
|
-
borderColor: "cyan"
|
|
240
|
-
})
|
|
241
|
-
)
|
|
271
|
+
const { provider } = parseModelChoice(modelConfig);
|
|
272
|
+
await hydrateApiKeyEnv(provider);
|
|
273
|
+
const aiService = new AIService(modelConfig);
|
|
242
274
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
275
|
+
const user = await getUserFromToken();
|
|
276
|
+
const conversation = await initConversation(
|
|
277
|
+
user.id,
|
|
278
|
+
conversationId,
|
|
279
|
+
mode,
|
|
280
|
+
aiService.getDisplayName()
|
|
281
|
+
);
|
|
282
|
+
await chatLoop(conversation, modelConfig);
|
|
246
283
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
}
|
|
260
|
-
}
|
|
284
|
+
outro(chalk.green("Thanks for chatting"));
|
|
285
|
+
} catch (error) {
|
|
286
|
+
const errorBox = boxen(chalk.red(`Error: ${error.message}`), {
|
|
287
|
+
padding: 1,
|
|
288
|
+
margin: 1,
|
|
289
|
+
borderStyle: "round",
|
|
290
|
+
borderColor: "red",
|
|
291
|
+
});
|
|
292
|
+
console.log(errorBox);
|
|
293
|
+
process.exit(1);
|
|
294
|
+
}
|
|
295
|
+
};
|