@dubeyvishal/orbital-cli 1.0.4 → 1.0.6

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.
Files changed (55) hide show
  1. package/images/pic1.png +0 -0
  2. package/images/pic2.png +0 -0
  3. package/images/pic3.png +0 -0
  4. package/package.json +10 -11
  5. package/server/prisma/migrations/20260105143219_test_migration/migration.sql +7 -0
  6. package/server/prisma/migrations/20260105151026_authentication/migration.sql +78 -0
  7. package/server/prisma/migrations/20260114105919_add_devicecode_conversation_message/migration.sql +50 -0
  8. package/server/prisma/migrations/migration_lock.toml +3 -0
  9. package/server/prisma/schema.prisma +117 -0
  10. package/server/{ai → src/cli/ai}/googleService.js +4 -5
  11. package/server/{chat → src/cli/chat}/chat-with-ai-agent.js +43 -50
  12. package/server/{chat → src/cli/chat}/chat-with-ai-tools.js +70 -83
  13. package/server/{chat → src/cli/chat}/chat-with-ai.js +57 -69
  14. package/server/{commands → src/cli/commands}/General/openApp.js +13 -8
  15. package/server/{commands → src/cli/commands}/ai/wakeUp.js +18 -20
  16. package/server/{commands → src/cli/commands}/auth/aboutMe.js +3 -2
  17. package/server/{commands → src/cli/commands}/auth/login.js +35 -17
  18. package/server/{commands → src/cli/commands}/auth/logout.js +1 -1
  19. package/server/src/cli/commands/config/setkey.js +26 -0
  20. package/server/{main.js → src/cli/main.js} +1 -1
  21. package/server/src/cli/utils/apiClient.js +66 -0
  22. package/server/{config → src/config}/agentConfig.js +22 -17
  23. package/server/src/config/api.js +22 -0
  24. package/server/src/config/env.js +86 -0
  25. package/server/{config → src/config}/toolConfig.js +7 -0
  26. package/server/src/controllers/aiController.js +58 -0
  27. package/server/src/controllers/cliController.js +77 -0
  28. package/server/src/db/prisma.js +3 -0
  29. package/server/src/index.js +108 -0
  30. package/server/src/lib/auth.js +37 -0
  31. package/server/src/lib/credentialStore.js +47 -0
  32. package/server/src/lib/db.js +18 -0
  33. package/server/src/lib/dbHealth.js +106 -0
  34. package/server/src/lib/orbitalConfig.js +192 -0
  35. package/server/{utils → src/lib}/token.js +8 -14
  36. package/server/src/middleware/auth.js +39 -0
  37. package/server/src/middleware/errorHandler.js +11 -0
  38. package/server/src/prisma/migrations/20260107093841_device_flow/migration.sql +94 -0
  39. package/server/src/prisma/migrations/migration_lock.toml +3 -0
  40. package/server/src/prisma/schema.prisma +115 -0
  41. package/server/src/routes/authRoutes.js +14 -0
  42. package/server/src/routes/cliRoutes.js +18 -0
  43. package/server/src/service/chatService.js +156 -0
  44. package/server/src/services/aiService.js +10 -0
  45. package/server/src/services/chatService.js +1 -0
  46. package/server/src/types/express.d.ts +19 -0
  47. package/server/commands/config/setkey.js +0 -19
  48. package/server/config/env.js +0 -20
  49. package/server/utils/apiClient.js +0 -40
  50. package/server/utils/chatServiceClient.js +0 -73
  51. package/server/utils/orbitalConfig.js +0 -44
  52. /package/server/{commands → src/cli/commands}/General/playSong.js +0 -0
  53. /package/server/{commands → src/cli/commands}/General/searchYoutube.js +0 -0
  54. /package/server/{generalApp → src/cli/generalApp}/Apps.js +0 -0
  55. /package/server/{config → src/config}/googleConfig.js +0 -0
@@ -11,17 +11,17 @@ import {
11
11
  import yoctoSpinner from "yocto-spinner";
12
12
  import { marked } from "marked";
13
13
  import { markedTerminal } from "marked-terminal";
14
- import { AIService } from "../ai/googleService.js";
15
- import { ChatServiceClient as ChatService } from "../utils/chatServiceClient.js";
16
- import { getStoredToken } from "../utils/token.js";
17
-
14
+ import { getStoredToken } from "../../lib/token.js";
18
15
  import {
19
16
  availableTools,
20
17
  enableTools,
21
18
  getEnabledTools,
22
19
  getEnabledToolNames,
23
20
  resetTools,
24
- } from "../config/toolConfig.js";
21
+ } from "../../config/toolConfig.js";
22
+ import { apiRequestSafe } from "../utils/apiClient.js";
23
+ import { AIService } from "../ai/googleService.js";
24
+ import { requireGeminiApiKey } from "../../lib/orbitalConfig.js";
25
25
 
26
26
  marked.use(
27
27
  markedTerminal({
@@ -42,9 +42,6 @@ marked.use(
42
42
  })
43
43
  );
44
44
 
45
- let aiService;
46
- const chatService = new ChatService();
47
-
48
45
  const getUserFromToken = async () => {
49
46
  const token = await getStoredToken();
50
47
 
@@ -54,28 +51,20 @@ const getUserFromToken = async () => {
54
51
 
55
52
  const spinner = yoctoSpinner({ text: "Authenticating..." }).start();
56
53
 
57
- let user;
58
54
  try {
59
- const response = await fetch("https://smart-cli-based-agent.onrender.com/api/me", {
60
- headers: {
61
- Authorization: `Bearer ${token.access_token}`
62
- }
63
- });
55
+ const result = await apiRequestSafe("/api/cli/me");
56
+ const user = result?.user;
64
57
 
65
- if (!response.ok) {
66
- throw new Error("Failed to authenticate.");
58
+ if (!user) {
59
+ spinner.error("User not found");
60
+ throw new Error("User not found. Please login again");
67
61
  }
68
- const data = await response.json();
69
- user = data?.user;
70
62
 
71
- if (!user) throw new Error("User not found");
72
- } catch(err) {
73
- spinner.error("Authentication failed");
74
- throw new Error("User not found or connection failed. Please login again.");
63
+ spinner.success(`Welcome back, ${user.name}!`);
64
+ return user;
65
+ } finally {
66
+ spinner.stop();
75
67
  }
76
-
77
- spinner.success(`Welcome back, ${user.name}!`);
78
- return user;
79
68
  };
80
69
 
81
70
  const selectTools = async () => {
@@ -132,17 +121,18 @@ const selectTools = async () => {
132
121
  console.log(toolBox);
133
122
  }
134
123
 
135
- return selectedTools.length > 0;
124
+ return selectedTools;
136
125
  };
137
126
 
138
127
  const initConversation = async (userId, conversationId = null, mode = "tool") => {
139
128
  const spinner = yoctoSpinner({ text: "Loading conversation..." }).start();
129
+ const result = await apiRequestSafe("/api/cli/conversations/init", {
130
+ method: "POST",
131
+ body: { conversationId, mode },
132
+ });
140
133
 
141
- const conversation = await chatService.getOrCreateConversation(
142
- userId,
143
- conversationId,
144
- mode
145
- );
134
+ const conversation = result?.conversation;
135
+ const messages = result?.messages || [];
146
136
 
147
137
  spinner.success("Conversation Loaded");
148
138
 
@@ -168,9 +158,9 @@ const initConversation = async (userId, conversationId = null, mode = "tool") =>
168
158
 
169
159
  console.log(conversationInfo);
170
160
 
171
- if (conversation.messages?.length > 0) {
161
+ if (messages.length > 0) {
172
162
  console.log(chalk.yellow("Previous Messages:\n"));
173
- displayMessages(conversation.messages);
163
+ displayMessages(messages);
174
164
  }
175
165
 
176
166
  return conversation;
@@ -206,54 +196,52 @@ const displayMessages = (messages) => {
206
196
  const updateConversationTitle = async (conversationId, userInput, messageCount) => {
207
197
  if (messageCount === 1) {
208
198
  const title = userInput.slice(0, 50) + (userInput.length > 50 ? "..." : "");
209
- await chatService.updateTitle(conversationId, title);
199
+ await apiRequestSafe(`/api/cli/conversations/${conversationId}/title`, {
200
+ method: "PATCH",
201
+ body: { title },
202
+ });
210
203
  }
211
204
  };
212
205
 
213
206
  const saveMessage = async (conversationId, role, content) => {
214
- return await chatService.addMessage(conversationId, role, content);
207
+ return await apiRequestSafe("/api/cli/messages", {
208
+ method: "POST",
209
+ body: { conversationId, role, content },
210
+ });
215
211
  };
216
212
 
217
- const getAIResponse = async (conversationId) => {
213
+ const getAIResponse = async (conversationId, toolIds = []) => {
218
214
  const spinner = yoctoSpinner({ text: "AI is thinking..." }).start();
219
-
220
- const dbMessages = await chatService.getMessages(conversationId);
221
- const aiMessages = chatService.formatMessageForAI(dbMessages);
222
-
223
- const tools = getEnabledTools();
224
-
225
215
  let fullResponse = "";
226
- let isFirstChunk = true;
227
216
  const toolCallsDetected = [];
228
217
 
229
218
  try {
230
- const result = await aiService.sendMessage(
231
- aiMessages,
232
- (chunk) => {
233
- if (isFirstChunk) {
234
- spinner.stop();
235
- console.log("\n");
236
- console.log(chalk.green.bold("Assistant: "));
237
- console.log(chalk.gray("-".repeat(60)));
238
- isFirstChunk = false;
239
- }
240
- fullResponse += chunk;
241
- },
242
- tools,
243
- (toolCall) => {
244
- toolCallsDetected.push(toolCall);
245
- }
219
+ const messageResult = await apiRequestSafe(
220
+ `/api/cli/messages?conversationId=${encodeURIComponent(conversationId)}`,
221
+ { method: "GET" }
246
222
  );
247
223
 
248
- // If the model returned without streaming any chunks, stop the spinner here
249
- // so it doesn't keep animating into the next prompt.
250
- if (isFirstChunk) {
251
- spinner.stop();
252
- console.log("\n");
253
- console.log(chalk.green.bold("Assistant: "));
254
- console.log(chalk.gray("-".repeat(60)));
255
- isFirstChunk = false;
256
- }
224
+ const messages = Array.isArray(messageResult?.messages) ? messageResult.messages : [];
225
+ const aiMessages = messages.map((m) => ({ role: m.role, content: m.content }));
226
+
227
+ // Ensure the API key is present in env before any tools are initialized.
228
+ await requireGeminiApiKey();
229
+
230
+ // Ensure tool state matches what the user selected.
231
+ resetTools();
232
+ if (Array.isArray(toolIds)) enableTools(toolIds);
233
+
234
+ const tools = getEnabledTools();
235
+ const aiService = new AIService();
236
+ const result = await aiService.sendMessage(aiMessages, null, tools);
237
+
238
+ spinner.stop();
239
+ console.log("\n");
240
+ console.log(chalk.green.bold("Assistant: "));
241
+ console.log(chalk.gray("-".repeat(60)));
242
+
243
+ fullResponse = result?.content || "";
244
+ if (Array.isArray(result?.toolCalls)) toolCallsDetected.push(...result.toolCalls);
257
245
 
258
246
  if (toolCallsDetected.length > 0) {
259
247
  console.log("\n");
@@ -305,14 +293,14 @@ const getAIResponse = async (conversationId) => {
305
293
  console.log(chalk.gray("-".repeat(60)));
306
294
  console.log("\n");
307
295
 
308
- return result?.content ?? fullResponse.trim();
296
+ return fullResponse.trim();
309
297
  } catch (error) {
310
298
  spinner.error("Failed to get AI response");
311
299
  throw error;
312
300
  }
313
301
  };
314
302
 
315
- const chatLoop = async (conversation) => {
303
+ const chatLoop = async (conversation, selectedToolIds = []) => {
316
304
  const enabledToolNames = getEnabledToolNames();
317
305
 
318
306
  const helpText = [
@@ -381,25 +369,24 @@ const chatLoop = async (conversation) => {
381
369
  );
382
370
 
383
371
  await saveMessage(conversation.id, "user", userInput);
384
- const messages = await chatService.getMessages(conversation.id);
372
+ const messageResult = await apiRequestSafe(
373
+ `/api/cli/messages?conversationId=${encodeURIComponent(conversation.id)}`,
374
+ { method: "GET" }
375
+ );
385
376
 
386
- const aiResponse = await getAIResponse(conversation.id);
377
+ const aiResponse = await getAIResponse(conversation.id, selectedToolIds);
387
378
  await saveMessage(conversation.id, "assistant", aiResponse);
388
379
 
389
- await updateConversationTitle(conversation.id, userInput, messages.length);
380
+ await updateConversationTitle(
381
+ conversation.id,
382
+ userInput,
383
+ messageResult?.messages?.length || 0
384
+ );
390
385
  }
391
386
  };
392
387
 
393
388
  export const startToolChat = async (conversationId) => {
394
389
  try {
395
- if (!process.env.GOOGLE_GENERATIVE_AI_API_KEY) {
396
- throw new Error(
397
- "Gemini API key is not set. Run: orbital setkey <your-gemini-api-key>"
398
- );
399
- }
400
-
401
- aiService = new AIService();
402
-
403
390
  intro(
404
391
  boxen(chalk.bold.cyan("Orbital AI - Tool Calling Mode"), {
405
392
  padding: 1,
@@ -410,11 +397,11 @@ export const startToolChat = async (conversationId) => {
410
397
 
411
398
  const user = await getUserFromToken();
412
399
 
413
- await selectTools();
400
+ const selectedToolIds = await selectTools();
414
401
 
415
402
  const conversation = await initConversation(user.id, conversationId, "tool");
416
403
 
417
- await chatLoop(conversation);
404
+ await chatLoop(conversation, selectedToolIds);
418
405
 
419
406
  resetTools();
420
407
  outro(chalk.green("Thanks for using tools"));
@@ -4,10 +4,10 @@ import {text , isCancel , cancel , intro , outro }from "@clack/prompts";
4
4
  import yoctoSpinner from "yocto-spinner";
5
5
  import {marked} from "marked";
6
6
  import {markedTerminal} from "marked-terminal";
7
- import {AIService} from "../ai/googleService.js";
8
- import { ChatServiceClient as ChatService } from "../utils/chatServiceClient.js";
9
- import {getStoredToken} from "../utils/token.js";
10
-
7
+ import {getStoredToken} from "../../lib/token.js";
8
+ import { apiRequestSafe } from "../utils/apiClient.js";
9
+ import { AIService } from "../ai/googleService.js";
10
+ import { requireGeminiApiKey } from "../../lib/orbitalConfig.js";
11
11
 
12
12
  marked.use(
13
13
  markedTerminal({
@@ -28,9 +28,6 @@ marked.use(
28
28
  })
29
29
  )
30
30
 
31
- let aiService;
32
- const chatService = new ChatService();
33
-
34
31
  const getUserFromToken = async()=>{
35
32
  const token = await getStoredToken()
36
33
  if(!token?.access_token){
@@ -38,37 +35,29 @@ const getUserFromToken = async()=>{
38
35
  }
39
36
 
40
37
  const spinner = yoctoSpinner({text: "Authenticating..."}).start();
41
-
42
- let user;
43
38
  try {
44
- const response = await fetch("https://smart-cli-based-agent.onrender.com/api/me", {
45
- headers: {
46
- Authorization: `Bearer ${token.access_token}`
39
+ const result = await apiRequestSafe("/api/cli/me");
40
+ const user = result?.user;
41
+ if(!user){
42
+ spinner.error("User not found");
43
+ throw new Error("User not found. Please login again");
47
44
  }
48
- });
49
- if (!response.ok) {
50
- throw new Error("Failed to authenticate.");
51
- }
52
- const data = await response.json();
53
- user = data?.user;
54
- if (!user) throw new Error("User not found");
55
- } catch(err) {
56
- spinner.error("Authentication failed");
57
- throw new Error("User not found or connection failed. Please login again.");
45
+ spinner.success(`Welcome back , ${user.name}!`);
46
+ return user;
47
+ } finally {
48
+ spinner.stop();
58
49
  }
59
-
60
- spinner.success(`Welcome back , ${user.name}!`);
61
- return user;
62
50
  }
63
51
 
64
52
  const initConversation = async(userId , conversationId = null , mode = "chat")=>{
65
53
  const spinner = yoctoSpinner({text : "Loading conservation ..."}).start();
66
54
 
67
- const conversation = await chatService.getOrCreateConversation(
68
- userId ,
69
- conversationId ,
70
- mode
71
- )
55
+ const result = await apiRequestSafe("/api/cli/conversations/init", {
56
+ method: "POST",
57
+ body: { conversationId, mode },
58
+ });
59
+ const conversation = result?.conversation;
60
+ const messages = result?.messages || [];
72
61
  spinner.success("Conversation Loaded");
73
62
 
74
63
  const conversationInfo = boxen(
@@ -84,9 +73,9 @@ const initConversation = async(userId , conversationId = null , mode = "chat")=>
84
73
  );
85
74
  console.log(conversationInfo);
86
75
 
87
- if(conversation.messages?.length > 0){
76
+ if(messages.length > 0){
88
77
  console.log(chalk.yellow("Previous Messsages: \n"));
89
- displayMessages(conversation.messages);
78
+ displayMessages(messages);
90
79
  }
91
80
 
92
81
  return conversation ;
@@ -120,7 +109,10 @@ const displayMessages = (messages) => {
120
109
  };
121
110
 
122
111
  const saveMessage = async(conversationId , role , content) =>{
123
- return await chatService.addMessage(conversationId , role , content)
112
+ return await apiRequestSafe("/api/cli/messages", {
113
+ method: "POST",
114
+ body: { conversationId, role, content },
115
+ });
124
116
  }
125
117
 
126
118
  const getAIResponse = async(conversationId)=>{
@@ -129,40 +121,34 @@ const getAIResponse = async(conversationId)=>{
129
121
  color: "cyan"
130
122
  }).start();
131
123
 
132
- const dbMessages = await chatService.getMessages(conversationId);
133
- const aiMessages = chatService.formatMessageForAI(dbMessages);
134
-
135
124
  let fullResponse = "";
136
- let isFirstChunk = true ;
137
125
  try{
138
- const result = await aiService.sendMessage(aiMessages , (chunk)=>{
139
- if(isFirstChunk){
140
- spinner.stop();
141
- console.log("\n");
142
- const header = chalk.green.bold("Assistent: ");
143
- console.log(header);
144
- console.log(chalk.gray("-".repeat(60)));
145
- isFirstChunk = false ;
146
- }
147
- fullResponse += chunk ;
148
- });
126
+ const messageResult = await apiRequestSafe(
127
+ `/api/cli/messages?conversationId=${encodeURIComponent(conversationId)}`,
128
+ { method: "GET" }
129
+ );
149
130
 
150
- if(isFirstChunk){
151
- spinner.stop();
152
- console.log("\n");
153
- const header = chalk.green.bold("Assistent: ");
154
- console.log(header);
155
- console.log(chalk.gray("-".repeat(60)));
156
- isFirstChunk = false;
157
- }
131
+ const messages = Array.isArray(messageResult?.messages) ? messageResult.messages : [];
132
+ const aiMessages = messages.map((m) => ({ role: m.role, content: m.content }));
133
+
134
+ await requireGeminiApiKey();
135
+ const aiService = new AIService();
136
+ const result = await aiService.sendMessage(aiMessages);
158
137
 
138
+ spinner.stop();
139
+ console.log("\n");
140
+ const header = chalk.green.bold("Assistent: ");
141
+ console.log(header);
142
+ console.log(chalk.gray("-".repeat(60)));
143
+
144
+ fullResponse = result?.content || "";
159
145
  console.log("\n");
160
146
  const renderMarkdown = marked.parse(fullResponse);
161
147
  console.log(renderMarkdown);
162
148
  console.log(chalk.gray("-".repeat(60)));
163
149
  console.log("\n");
164
150
 
165
- return result.content ;
151
+ return fullResponse ;
166
152
  }
167
153
  catch(error){
168
154
  spinner.error("Failed to get AI response");
@@ -173,7 +159,10 @@ const getAIResponse = async(conversationId)=>{
173
159
  const updateConversationTitle = async(conversationId , userInput , messageCount)=>{
174
160
  if(messageCount ===1){
175
161
  const title = userInput.slice(0,50) + (userInput.length > 50 ? "..." : "");
176
- await chatService.updateTitle(conversationId , title);
162
+ await apiRequestSafe(`/api/cli/conversations/${conversationId}/title`, {
163
+ method: "PATCH",
164
+ body: { title },
165
+ });
177
166
  }
178
167
  }
179
168
 
@@ -223,12 +212,19 @@ const chatLoop = async(conversation)=>{
223
212
  break ;
224
213
  }
225
214
  await saveMessage(conversation.id , "user" , userInput);
226
- const messages = await chatService.getMessages(conversation.id);
215
+ const messageResult = await apiRequestSafe(
216
+ `/api/cli/messages?conversationId=${encodeURIComponent(conversation.id)}`,
217
+ { method: "GET" }
218
+ );
227
219
 
228
220
  const aiResponse = await getAIResponse(conversation.id);
229
- await saveMessage(conversation.id , "assistant" , aiResponse);
221
+ await saveMessage(conversation.id , "assistant" , aiResponse);
230
222
 
231
- await updateConversationTitle(conversation.id , userInput , messages.length)
223
+ await updateConversationTitle(
224
+ conversation.id ,
225
+ userInput ,
226
+ messageResult?.messages?.length || 0
227
+ )
232
228
  }
233
229
  }
234
230
 
@@ -236,14 +232,6 @@ const chatLoop = async(conversation)=>{
236
232
 
237
233
  export const startChat = async(mode="chat" , conversationId = null)=>{
238
234
  try{
239
- if (!process.env.GOOGLE_GENERATIVE_AI_API_KEY) {
240
- throw new Error(
241
- "Gemini API key is not set. Run: orbital setkey <your-gemini-api-key>"
242
- );
243
- }
244
-
245
- aiService = new AIService();
246
-
247
235
  intro(
248
236
  boxen(chalk.bold.cyan("Orbital AI Chat") , {
249
237
  padding: 1 ,
@@ -1,21 +1,26 @@
1
1
  import chalk from "chalk";
2
2
  import { Command } from "commander";
3
3
  import yoctoSpinner from "yocto-spinner";
4
- import { getApiClient } from "../../utils/apiClient.js";
5
- import {openGithub , openLinkedin , openLeetcode , openGmail , openWhatsApp} from "../../generalApp/Apps.js"
4
+ import { getStoredToken } from "../../../lib/token.js";
5
+ import { select } from "@clack/prompts";
6
+ import {openGithub , openLinkedin , openLeetcode , openGmail , openWhatsApp} from "../../../cli/generalApp/Apps.js"
7
+ import { apiRequestSafe } from "../../utils/apiClient.js";
6
8
 
7
9
  const openAppAction = async () => {
10
+ const token = await getStoredToken();
11
+
12
+ if (!token?.access_token) {
13
+ console.log(chalk.red("Not Authenticated. Please login."));
14
+ return;
15
+ }
16
+
8
17
  const spinner = yoctoSpinner({ text: "Fetching user information..." });
9
18
  spinner.start();
10
19
 
11
20
  let user;
12
21
  try {
13
- const api = await getApiClient();
14
- const res = await api.get("/api/me");
15
- user = res.user;
16
- } catch (err) {
17
- console.log(chalk.red(err.message));
18
- return;
22
+ const result = await apiRequestSafe("/api/cli/me");
23
+ user = result?.user;
19
24
  } finally {
20
25
  spinner.stop();
21
26
  }
@@ -1,38 +1,36 @@
1
1
  import chalk from "chalk";
2
2
  import {Command} from "commander";
3
3
  import yoctoSpinner from "yocto-spinner";
4
- import {getStoredToken} from "../../utils/token.js"
5
-
4
+ import {getStoredToken} from "../../../lib/token.js"
6
5
  import {select} from "@clack/prompts";
7
- import {startChat} from "../../chat/chat-with-ai.js";
8
- import {startToolChat} from "../../chat/chat-with-ai-tools.js";
9
- import {startAgentChat} from "../../chat/chat-with-ai-agent.js";
6
+ import {startChat} from "../../../cli/chat/chat-with-ai.js";
7
+ import {startToolChat} from "../../../cli/chat/chat-with-ai-tools.js";
8
+ import {startAgentChat} from "../../../cli/chat/chat-with-ai-agent.js";
9
+ import { apiRequestSafe } from "../../utils/apiClient.js";
10
+ import { requireGeminiApiKey } from "../../../lib/orbitalConfig.js";
10
11
 
11
12
 
12
13
  const wakeUpAction = async()=>{
14
+ try {
15
+ await requireGeminiApiKey();
16
+ } catch {
17
+ console.log(chalk.red("Gemini API key not set. Run: orbital set-key <API_KEY>"));
18
+ return;
19
+ }
20
+
13
21
  const token = await getStoredToken();
14
22
  if(!token?.access_token){
15
23
  console.log(chalk.red("Not Authenticated. please login"))
16
24
  return ;
17
25
  }
18
26
 
27
+ const spinner = yoctoSpinner({text: "Fetching user information ..."})
28
+ spinner.start()
29
+
19
30
  let user;
20
31
  try{
21
- const response = await fetch("https://smart-cli-based-agent.onrender.com/api/me", {
22
- headers: {
23
- Authorization: `Bearer ${token.access_token}`
24
- }
25
- });
26
-
27
- if (!response.ok) {
28
- throw new Error("Failed to authenticate.");
29
- }
30
- const data = await response.json();
31
- user = data?.user;
32
-
33
- if (!user) throw new Error("User not found");
34
- } catch(err) {
35
- console.log(chalk.red("Failed to authenticate with server."));
32
+ const result = await apiRequestSafe("/api/cli/me");
33
+ user = result?.user;
36
34
  }
37
35
  finally{
38
36
  spinner.stop();
@@ -1,8 +1,9 @@
1
1
  import chalk from "chalk";
2
2
  import { Command } from "commander";
3
- import { requireAuth } from "../../utils/token.js";
3
+ import { requireAuth } from "../../../lib/token.js";
4
+ import { API_BASE } from "../../../config/api.js";
4
5
 
5
- const DEFAULT_SERVER_URL = "https://smart-cli-based-agent.onrender.com";
6
+ const DEFAULT_SERVER_URL = API_BASE;
6
7
 
7
8
  export const whoAmIAction = async (cmdOptions = {})=>{
8
9
  const token = await requireAuth();
@@ -1,3 +1,4 @@
1
+ import "../../../config/env.js";
1
2
  import { cancel, confirm, intro, outro, isCancel } from "@clack/prompts";
2
3
  import chalk from "chalk";
3
4
  import { Command } from "commander";
@@ -10,25 +11,37 @@ import { createAuthClient } from "better-auth/client";
10
11
  import { deviceAuthorizationClient } from "better-auth/client/plugins";
11
12
  import { logger } from "better-auth";
12
13
  import { fileURLToPath } from "url";
13
- import { getStoredToken, isTokenExpired, storeToken ,TOKEN_FILE } from "../../utils/token.js";
14
+ import { getStoredToken, isTokenExpired, storeToken ,TOKEN_FILE } from "../../../lib/token.js";
15
+ import { API_BASE } from "../../../config/api.js";
16
+ import { apiRequestSafe } from "../../utils/apiClient.js";
17
+ import { requireGeminiApiKey } from "../../../lib/orbitalConfig.js";
14
18
 
15
19
 
16
20
 
17
21
  const __filename = fileURLToPath(import.meta.url);
18
22
  const __dirname = path.dirname(__filename);
19
23
 
20
- const URL = "https://smart-cli-based-agent.onrender.com";
21
- const CLIENT_ID = process.env.GITHUB_CLIENT_ID;
24
+ const URL = API_BASE;
25
+
26
+ const resolveClientId = async (cliClientId) => {
27
+ const resolved = (cliClientId || "").trim();
28
+ if (resolved.length > 0) return resolved;
29
+
30
+ const response = await apiRequestSafe("/auth/github/client-id", {
31
+ method: "GET",
32
+ requireAuth: false,
33
+ });
34
+
35
+ const clientId = typeof response?.client_id === "string" ? response.client_id.trim() : "";
36
+ return clientId.length > 0 ? clientId : undefined;
37
+ };
22
38
 
23
39
 
24
40
  export const loginAction = async (cmdOptions) => {
25
- if (!process.env.GOOGLE_GENERATIVE_AI_API_KEY) {
26
- console.log(chalk.red("Gemini API key is not set."));
27
- console.log(
28
- chalk.gray(
29
- "Run: orbital setkey <your-gemini-api-key>\nThen run: orbital login",
30
- ),
31
- );
41
+ try {
42
+ await requireGeminiApiKey();
43
+ } catch {
44
+ console.log(chalk.red("Gemini API key not set. Run: orbital set-key <API_KEY>"));
32
45
  process.exit(1);
33
46
  }
34
47
 
@@ -43,7 +56,17 @@ export const loginAction = async (cmdOptions) => {
43
56
  });
44
57
 
45
58
  const serverUrl = options.serverUrl || URL;
46
- const clientId = options.clientId || CLIENT_ID;
59
+ const clientId = await resolveClientId(options.clientId);
60
+
61
+ if (!clientId) {
62
+ console.error(chalk.red("GitHub OAuth client ID is not available from the server."));
63
+ console.log(
64
+ chalk.gray(
65
+ "Make sure the backend is deployed and configured with GITHUB_CLIENT_ID.",
66
+ ),
67
+ );
68
+ process.exit(1);
69
+ }
47
70
 
48
71
  intro(chalk.bold("Auth CLI Login"));
49
72
 
@@ -143,7 +166,6 @@ export const loginAction = async (cmdOptions) => {
143
166
 
144
167
  outro(chalk.green("Login successfull !"));
145
168
 
146
- //console.log(chalk.gray(`\n Token saved to: ${TOKEN_FILE}`));
147
169
 
148
170
  console.log(
149
171
  chalk.gray("You can now use AI commands without logging in again. \n "),
@@ -189,10 +211,6 @@ const pollForToken = async (
189
211
  });
190
212
 
191
213
  if (data?.access_token) {
192
- console.log(
193
- chalk.bold.yellow(`Your access token: ${data.access_token}`),
194
- );
195
-
196
214
  spinner.stop();
197
215
  resolve(data);
198
216
  return;
@@ -238,5 +256,5 @@ const pollForToken = async (
238
256
  export const login = new Command("login")
239
257
  .description("Login to Better Auth")
240
258
  .option("--server-url <url>", "The Better Auth server URL", URL)
241
- .option("--client-id <id>", "The OAuth client ID", CLIENT_ID)
259
+ .option("--client-id <id>", "The OAuth client ID")
242
260
  .action(loginAction);