@dubeyvishal/orbital-cli 1.0.3 → 1.0.5

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 (32) hide show
  1. package/README.md +13 -0
  2. package/package.json +2 -1
  3. package/server/src/cli/ai/googleService.js +3 -4
  4. package/server/src/cli/chat/chat-with-ai-agent.js +45 -53
  5. package/server/src/cli/chat/chat-with-ai-tools.js +72 -84
  6. package/server/src/cli/chat/chat-with-ai.js +58 -67
  7. package/server/src/cli/commands/General/openApp.js +3 -20
  8. package/server/src/cli/commands/ai/wakeUp.js +11 -22
  9. package/server/src/cli/commands/auth/aboutMe.js +2 -1
  10. package/server/src/cli/commands/auth/login.js +34 -11
  11. package/server/src/cli/commands/config/setkey.js +12 -5
  12. package/server/src/cli/main.js +1 -1
  13. package/server/src/cli/utils/apiClient.js +66 -0
  14. package/server/src/config/agentConfig.js +22 -17
  15. package/server/src/config/api.js +22 -0
  16. package/server/src/config/env.js +2 -16
  17. package/server/src/config/toolConfig.js +7 -0
  18. package/server/src/controllers/aiController.js +58 -0
  19. package/server/src/controllers/cliController.js +77 -0
  20. package/server/src/db/prisma.js +3 -0
  21. package/server/src/index.js +12 -6
  22. package/server/src/lib/auth.js +4 -4
  23. package/server/src/lib/credentialStore.js +47 -0
  24. package/server/src/lib/orbitalConfig.js +165 -17
  25. package/server/src/lib/token.js +8 -14
  26. package/server/src/middleware/auth.js +39 -0
  27. package/server/src/middleware/errorHandler.js +11 -0
  28. package/server/src/routes/authRoutes.js +14 -0
  29. package/server/src/routes/cliRoutes.js +18 -0
  30. package/server/src/services/aiService.js +10 -0
  31. package/server/src/services/chatService.js +1 -0
  32. package/server/src/types/express.d.ts +19 -0
@@ -2,10 +2,9 @@ import chalk from "chalk";
2
2
  import { Command } from "commander";
3
3
  import yoctoSpinner from "yocto-spinner";
4
4
  import { getStoredToken } from "../../../lib/token.js";
5
- import prisma from "../../../lib/db.js";
6
- import { ensureDbConnection } from "../../../lib/dbHealth.js";
7
5
  import { select } from "@clack/prompts";
8
6
  import {openGithub , openLinkedin , openLeetcode , openGmail , openWhatsApp} from "../../../cli/generalApp/Apps.js"
7
+ import { apiRequestSafe } from "../../utils/apiClient.js";
9
8
 
10
9
  const openAppAction = async () => {
11
10
  const token = await getStoredToken();
@@ -15,29 +14,13 @@ const openAppAction = async () => {
15
14
  return;
16
15
  }
17
16
 
18
- const dbOk = await ensureDbConnection();
19
- if (!dbOk) return;
20
-
21
17
  const spinner = yoctoSpinner({ text: "Fetching user information..." });
22
18
  spinner.start();
23
19
 
24
20
  let user;
25
21
  try {
26
- user = await prisma.user.findFirst({
27
- where: {
28
- sessions: {
29
- some: {
30
- token: token.access_token,
31
- },
32
- },
33
- },
34
- select: {
35
- id: true,
36
- name: true,
37
- email: true,
38
- image: true,
39
- },
40
- });
22
+ const result = await apiRequestSafe("/api/cli/me");
23
+ user = result?.user;
41
24
  } finally {
42
25
  spinner.stop();
43
26
  }
@@ -2,46 +2,35 @@ import chalk from "chalk";
2
2
  import {Command} from "commander";
3
3
  import yoctoSpinner from "yocto-spinner";
4
4
  import {getStoredToken} from "../../../lib/token.js"
5
- import prisma from "../../../lib/db.js"
6
- import { ensureDbConnection } from "../../../lib/dbHealth.js";
7
5
  import {select} from "@clack/prompts";
8
6
  import {startChat} from "../../../cli/chat/chat-with-ai.js";
9
7
  import {startToolChat} from "../../../cli/chat/chat-with-ai-tools.js";
10
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";
11
11
 
12
12
 
13
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
+
14
21
  const token = await getStoredToken();
15
22
  if(!token?.access_token){
16
23
  console.log(chalk.red("Not Authenticated. please login"))
17
24
  return ;
18
25
  }
19
26
 
20
- const dbOk = await ensureDbConnection();
21
- if(!dbOk){
22
- return;
23
- }
24
-
25
27
  const spinner = yoctoSpinner({text: "Fetching user information ..."})
26
28
  spinner.start()
27
29
 
28
30
  let user;
29
31
  try{
30
- user = await prisma.user.findFirst({
31
- where : {
32
- sessions : {
33
- some : {
34
- token : token.access_token
35
- }
36
- }
37
- },
38
- select : {
39
- id : true ,
40
- name : true ,
41
- email : true ,
42
- image : true
43
- }
44
- });
32
+ const result = await apiRequestSafe("/api/cli/me");
33
+ user = result?.user;
45
34
  }
46
35
  finally{
47
36
  spinner.stop();
@@ -1,8 +1,9 @@
1
1
  import chalk from "chalk";
2
2
  import { Command } from "commander";
3
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";
@@ -11,24 +12,36 @@ import { deviceAuthorizationClient } from "better-auth/client/plugins";
11
12
  import { logger } from "better-auth";
12
13
  import { fileURLToPath } from "url";
13
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
 
@@ -238,5 +261,5 @@ const pollForToken = async (
238
261
  export const login = new Command("login")
239
262
  .description("Login to Better Auth")
240
263
  .option("--server-url <url>", "The Better Auth server URL", URL)
241
- .option("--client-id <id>", "The OAuth client ID", CLIENT_ID)
264
+ .option("--client-id <id>", "The OAuth client ID")
242
265
  .action(loginAction);
@@ -1,19 +1,26 @@
1
1
  import { Command } from "commander";
2
2
  import chalk from "chalk";
3
- import { setGeminiApiKey, ORBITAL_CONFIG_FILE } from "../../../lib/orbitalConfig.js";
3
+ import { setGeminiApiKey } from "../../../lib/orbitalConfig.js";
4
+ import { getCredentialServiceName } from "../../../lib/credentialStore.js";
4
5
 
5
6
  const setKeyAction = async (apiKey) => {
6
7
  try {
7
8
  await setGeminiApiKey(apiKey);
8
9
  console.log(chalk.green("Gemini API key saved."));
9
- console.log(chalk.gray(`Stored at: ${ORBITAL_CONFIG_FILE}`));
10
+ console.log(
11
+ chalk.gray(
12
+ `Stored securely in your OS credential manager (service: ${getCredentialServiceName()}).`
13
+ )
14
+ );
10
15
  } catch (err) {
11
16
  console.log(chalk.red("Failed to save key:"), err?.message || err);
12
17
  process.exit(1);
13
18
  }
14
19
  };
15
20
 
16
- export const setkey = new Command("setkey")
17
- .description("Set Gemini API key for Orbital")
18
- .argument("<apiKey>", "Your Gemini API key")
21
+ export const setkey = new Command("set-key")
22
+ .description("Store your Gemini API key securely (keytar)")
23
+ .argument("<API_KEY>", "Your Gemini API key")
24
+ .alias("set")
25
+ .alias("setkey")
19
26
  .action(setKeyAction);
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env node
2
2
 
3
3
  import "../config/env.js";
4
4
  import chalk from "chalk";
@@ -0,0 +1,66 @@
1
+ import chalk from "chalk";
2
+ import { API_BASE } from "../../config/api.js";
3
+ import { getStoredToken, requireAuth } from "../../lib/token.js";
4
+
5
+ const buildUrl = (path) => {
6
+ const normalized = path.startsWith("/") ? path : `/${path}`;
7
+ return `${API_BASE}${normalized}`;
8
+ };
9
+
10
+ const getAuthHeader = async (isRequired) => {
11
+ const token = isRequired ? await requireAuth() : await getStoredToken();
12
+ if (!token?.access_token) return {};
13
+
14
+ const headerValue = token.token_type
15
+ ? `${token.token_type} ${token.access_token}`
16
+ : `Bearer ${token.access_token}`;
17
+
18
+ return { Authorization: headerValue };
19
+ };
20
+
21
+ export const apiRequest = async (
22
+ path,
23
+ { method = "GET", body, requireAuth: isRequired = true } = {}
24
+ ) => {
25
+ const headers = {
26
+ "content-type": "application/json",
27
+ ...(await getAuthHeader(isRequired)),
28
+ };
29
+
30
+ let response;
31
+ try {
32
+ response = await fetch(buildUrl(path), {
33
+ method,
34
+ headers,
35
+ body: body ? JSON.stringify(body) : undefined,
36
+ });
37
+ } catch (error) {
38
+ throw new Error(`Failed to reach server at ${API_BASE}`);
39
+ }
40
+
41
+ const text = await response.text();
42
+ let data = null;
43
+ if (text) {
44
+ try {
45
+ data = JSON.parse(text);
46
+ } catch {
47
+ data = null;
48
+ }
49
+ }
50
+
51
+ if (!response.ok) {
52
+ const errorMessage = data?.error || data?.message || response.statusText;
53
+ throw new Error(`${errorMessage} (HTTP ${response.status})`);
54
+ }
55
+
56
+ return data;
57
+ };
58
+
59
+ export const apiRequestSafe = async (...args) => {
60
+ try {
61
+ return await apiRequest(...args);
62
+ } catch (error) {
63
+ console.error(chalk.red(error?.message || "Request failed"));
64
+ throw error;
65
+ }
66
+ };
@@ -8,7 +8,7 @@ import { z } from "zod";
8
8
  const genenateObject = generateObject;
9
9
 
10
10
 
11
- const applicationSchema = z.object({
11
+ export const applicationSchema = z.object({
12
12
  folderName: z.string().describe("Kebab-Case folder name for the application"),
13
13
  description: z.string().describe("Brief description of what was created"),
14
14
  files: z.array(
@@ -35,7 +35,7 @@ const printSystem = (message) => {
35
35
  console.log(message);
36
36
  }
37
37
 
38
- const displayFileTree = (files, folderName) => {
38
+ export const displayFileTree = (files, folderName) => {
39
39
  printSystem(chalk.cyan("📂 Project Structure:"));
40
40
  printSystem(chalk.white(`${folderName}/`));
41
41
 
@@ -77,7 +77,7 @@ const displayFileTree = (files, folderName) => {
77
77
  printTree(tree, " ");
78
78
  }
79
79
 
80
- const createApplicationFiles = async(baseDir , folderName , files)=>{
80
+ export const createApplicationFiles = async(baseDir , folderName , files)=>{
81
81
  const appDir = path.join(baseDir , folderName)
82
82
 
83
83
  await fs.mkdir(appDir , {recursive : true});
@@ -97,18 +97,11 @@ const createApplicationFiles = async(baseDir , folderName , files)=>{
97
97
 
98
98
  }
99
99
 
100
-
101
- export const generateApplication = async (description, aiService, cwd = process.cwd()) => {
102
- try {
103
- printSystem(chalk.cyan(`\n Agent Mode: Generating your application...\n`));
104
- printSystem(chalk.gray(`Request: ${description}\n`));
105
-
106
- printSystem(chalk.magenta("Agent Response: \n"));
107
-
108
- const { object: application } = await generateObject({
109
- model: aiService.model,
110
- schema: applicationSchema,
111
- prompt: `Create a complete, production-ready application for: ${description}
100
+ export const generateApplicationPlan = async (description, aiService) => {
101
+ const { object: application } = await generateObject({
102
+ model: aiService.model,
103
+ schema: applicationSchema,
104
+ prompt: `Create a complete, production-ready application for: ${description}
112
105
 
113
106
  CRITICAL REQUIREMENTS:
114
107
  1. Generate ALL files needed for the application to run
@@ -125,9 +118,21 @@ Provide:
125
118
  - A meaningful kebab-case folder name
126
119
  - All necessary files with complete content
127
120
  - Setup commands (cd folder, npm install, npm run dev, etc.)
128
- - All dependencies with versions`
121
+ - All dependencies with versions`,
122
+ });
129
123
 
130
- });
124
+ return application;
125
+ };
126
+
127
+
128
+ export const generateApplication = async (description, aiService, cwd = process.cwd()) => {
129
+ try {
130
+ printSystem(chalk.cyan(`\n Agent Mode: Generating your application...\n`));
131
+ printSystem(chalk.gray(`Request: ${description}\n`));
132
+
133
+ printSystem(chalk.magenta("Agent Response: \n"));
134
+
135
+ const application = await generateApplicationPlan(description, aiService);
131
136
 
132
137
  printSystem(chalk.green(`\n Generated: ${application.folderName}`));
133
138
  printSystem(chalk.green(`\n Description: ${application.description}`));
@@ -0,0 +1,22 @@
1
+ const DEFAULT_API_BASE = "https://smart-cli-based-agent.onrender.com";
2
+ const DEFAULT_FRONTEND_URL = "https://smart-cli-based-agent-t7x4.vercel.app";
3
+
4
+ const stripTrailingSlash = (value) => {
5
+ if (typeof value !== "string") return value;
6
+ return value.replace(/\/+$/, "");
7
+ };
8
+
9
+ export const API_BASE = stripTrailingSlash(
10
+ process.env.ORBITAL_SERVER_URL ||
11
+ process.env.BACKEND_URL ||
12
+ process.env.SERVER_URL ||
13
+ DEFAULT_API_BASE
14
+ );
15
+
16
+ export const FRONTEND_URL = stripTrailingSlash(
17
+ process.env.FRONTEND_URL || process.env.CLIENT_ORIGIN || DEFAULT_FRONTEND_URL
18
+ );
19
+
20
+ export const AUTH_BASE_URL = stripTrailingSlash(
21
+ process.env.BETTER_AUTH_BASE_URL || API_BASE
22
+ );
@@ -1,8 +1,6 @@
1
1
  import dotenv from "dotenv";
2
2
  import path from "path";
3
3
  import { fileURLToPath } from "url";
4
- import fs from "fs";
5
- import os from "os";
6
4
 
7
5
  const __filename = fileURLToPath(import.meta.url);
8
6
  const __dirname = path.dirname(__filename);
@@ -12,20 +10,8 @@ const serverEnvPath = path.resolve(__dirname, "../../.env");
12
10
 
13
11
  dotenv.config({ path: serverEnvPath });
14
12
 
15
- // Load Orbital user config (stored in the user's home directory) and
16
- // hydrate env vars if they are not already set.
17
- try {
18
- const orbitalConfigPath = path.join(os.homedir(), ".orbital", "config.json");
19
- if (!process.env.GOOGLE_GENERATIVE_AI_API_KEY && fs.existsSync(orbitalConfigPath)) {
20
- const raw = fs.readFileSync(orbitalConfigPath, "utf-8");
21
- const parsed = JSON.parse(raw);
22
- if (parsed?.geminiApiKey && typeof parsed.geminiApiKey === "string") {
23
- process.env.GOOGLE_GENERATIVE_AI_API_KEY = parsed.geminiApiKey.trim();
24
- }
25
- }
26
- } catch {
27
- // ignore
28
- }
13
+ // Note: The Gemini API key is stored in the OS credential manager via keytar.
14
+ // CLI code will hydrate GOOGLE_GENERATIVE_AI_API_KEY from keytar when needed.
29
15
 
30
16
  const stripWrappingQuotes = (value) => {
31
17
  if (typeof value !== "string") return value;
@@ -1,5 +1,6 @@
1
1
  import { google } from "@ai-sdk/google";
2
2
  import chalk from "chalk";
3
+ import { requireGeminiApiKeySync } from "../lib/orbitalConfig.js";
3
4
 
4
5
  export const availableTools = [
5
6
  {
@@ -32,6 +33,12 @@ export const getEnabledTools = () => {
32
33
  const tools = {};
33
34
 
34
35
  try {
36
+ const enabledToolCount = availableTools.filter((t) => t.enabled).length;
37
+ if (enabledToolCount > 0 && !process.env.GOOGLE_GENERATIVE_AI_API_KEY) {
38
+
39
+ requireGeminiApiKeySync();
40
+ }
41
+
35
42
  for (const toolConfig of availableTools) {
36
43
  if (toolConfig.enabled) {
37
44
  tools[toolConfig.id] = toolConfig.getTool();
@@ -0,0 +1,58 @@
1
+ import { ChatService } from "../services/chatService.js";
2
+ import { getAIService } from "../services/aiService.js";
3
+ import { enableTools, getEnabledTools, resetTools } from "../config/toolConfig.js";
4
+ import { generateApplicationPlan } from "../config/agentConfig.js";
5
+
6
+ const chatService = new ChatService();
7
+
8
+ export const respond = async (req, res, next) => {
9
+ try {
10
+ const { conversationId, mode, toolIds } = req.body || {};
11
+
12
+ if (!conversationId) {
13
+ return res.status(400).json({ error: "conversationId is required" });
14
+ }
15
+
16
+ const messages = await chatService.getMessages(conversationId);
17
+ const aiMessages = chatService.formatMessageForAI(messages);
18
+
19
+ let tools;
20
+ if (mode === "tool") {
21
+ resetTools();
22
+ if (Array.isArray(toolIds)) {
23
+ enableTools(toolIds);
24
+ }
25
+ tools = getEnabledTools();
26
+ }
27
+
28
+ const aiService = getAIService();
29
+ const result = await aiService.sendMessage(aiMessages, null, tools);
30
+
31
+ await chatService.addMessage(conversationId, "assistant", result.content);
32
+
33
+ return res.json({
34
+ content: result.content,
35
+ toolCalls: result.toolCalls || [],
36
+ toolResults: result.toolResults || [],
37
+ });
38
+ } catch (error) {
39
+ return next(error);
40
+ }
41
+ };
42
+
43
+ export const generateAgentPlan = async (req, res, next) => {
44
+ try {
45
+ const { description } = req.body || {};
46
+
47
+ if (!description || typeof description !== "string") {
48
+ return res.status(400).json({ error: "description is required" });
49
+ }
50
+
51
+ const aiService = getAIService();
52
+ const application = await generateApplicationPlan(description, aiService);
53
+
54
+ return res.json({ application });
55
+ } catch (error) {
56
+ return next(error);
57
+ }
58
+ };
@@ -0,0 +1,77 @@
1
+ import { ChatService } from "../services/chatService.js";
2
+
3
+ const chatService = new ChatService();
4
+
5
+ export const getMe = async (req, res) => {
6
+ return res.json({
7
+ user: req.user,
8
+ session: {
9
+ id: req.session?.id,
10
+ expiresAt: req.session?.expiresAt,
11
+ },
12
+ });
13
+ };
14
+
15
+ export const initConversation = async (req, res, next) => {
16
+ try {
17
+ const { conversationId, mode } = req.body || {};
18
+
19
+ const conversation = await chatService.getOrCreateConversation(
20
+ req.user.id,
21
+ conversationId || null,
22
+ mode || "chat"
23
+ );
24
+
25
+ const messages = await chatService.getMessages(conversation.id);
26
+
27
+ return res.json({ conversation, messages });
28
+ } catch (error) {
29
+ return next(error);
30
+ }
31
+ };
32
+
33
+ export const addMessage = async (req, res, next) => {
34
+ try {
35
+ const { conversationId, role, content } = req.body || {};
36
+
37
+ if (!conversationId || !role) {
38
+ return res.status(400).json({ error: "conversationId and role are required" });
39
+ }
40
+
41
+ const message = await chatService.addMessage(conversationId, role, content);
42
+ return res.json({ message });
43
+ } catch (error) {
44
+ return next(error);
45
+ }
46
+ };
47
+
48
+ export const getMessages = async (req, res, next) => {
49
+ try {
50
+ const { conversationId } = req.query || {};
51
+
52
+ if (!conversationId) {
53
+ return res.status(400).json({ error: "conversationId is required" });
54
+ }
55
+
56
+ const messages = await chatService.getMessages(conversationId);
57
+ return res.json({ messages });
58
+ } catch (error) {
59
+ return next(error);
60
+ }
61
+ };
62
+
63
+ export const updateTitle = async (req, res, next) => {
64
+ try {
65
+ const { id } = req.params || {};
66
+ const { title } = req.body || {};
67
+
68
+ if (!id || !title) {
69
+ return res.status(400).json({ error: "title is required" });
70
+ }
71
+
72
+ const conversation = await chatService.updateTitle(id, title);
73
+ return res.json({ conversation });
74
+ } catch (error) {
75
+ return next(error);
76
+ }
77
+ };
@@ -0,0 +1,3 @@
1
+ import prisma from "../lib/db.js";
2
+
3
+ export default prisma;
@@ -5,14 +5,14 @@ import cors from "cors";
5
5
  import { auth } from "./lib/auth.js";
6
6
  import prisma from "./lib/db.js";
7
7
  import { ensureDbConnectionOrExit } from "./lib/dbHealth.js";
8
-
8
+ import { FRONTEND_URL } from "./config/api.js";
9
+ import cliRoutes from "./routes/cliRoutes.js";
10
+ import authRoutes from "./routes/authRoutes.js";
11
+ import { errorHandler } from "./middleware/errorHandler.js";
9
12
 
10
13
  const app = express();
11
14
  const PORT = process.env.PORT || 8080;
12
- const CLIENT_ORIGIN =
13
- process.env.CLIENT_ORIGIN ||
14
- process.env.FRONTEND_URL ||
15
- "https://smart-cli-based-agent-t7x4.vercel.app";
15
+ const CLIENT_ORIGIN = FRONTEND_URL;
16
16
 
17
17
  app.use(express.json());
18
18
 
@@ -20,12 +20,14 @@ app.use(
20
20
  cors({
21
21
  origin: CLIENT_ORIGIN,
22
22
  methods: ["GET", "POST", "PUT", "DELETE"],
23
- credentials: true,
23
+ credentials: true,
24
24
  })
25
25
  );
26
26
 
27
27
  app.all("/api/auth/*splat", toNodeHandler(auth));
28
28
 
29
+ app.use("/auth", authRoutes);
30
+
29
31
  app.get("/api/me" , async (req, res)=>{
30
32
  const session = await auth.api.getSession({
31
33
  headers : fromNodeHeaders (req.headers),
@@ -72,6 +74,10 @@ app.get("/device" , async(req , res)=>{
72
74
  res.redirect(`${CLIENT_ORIGIN}/device?user_code=${user_code}`)
73
75
  });
74
76
 
77
+ app.use("/api/cli", cliRoutes);
78
+
79
+ app.use(errorHandler);
80
+
75
81
  const start = async () => {
76
82
  await ensureDbConnectionOrExit({
77
83
  retries: Number(process.env.DB_CONNECT_RETRIES || 10),
@@ -3,6 +3,7 @@ import { betterAuth } from "better-auth";
3
3
  import { prismaAdapter } from "better-auth/adapters/prisma";
4
4
  import { deviceAuthorization } from "better-auth/plugins";
5
5
  import prisma from "./db.js";
6
+ import { FRONTEND_URL } from "../config/api.js";
6
7
 
7
8
  export const auth = betterAuth({
8
9
  database: prismaAdapter(prisma, {
@@ -12,16 +13,15 @@ export const auth = betterAuth({
12
13
  // auth cookies are set on the frontend origin. The OAuth callback must therefore
13
14
  // also land on the frontend origin to avoid `state_mismatch`.
14
15
  baseURL:
15
- process.env.BETTER_AUTH_BASE_URL ||
16
16
  process.env.FRONTEND_URL ||
17
17
  process.env.CLIENT_ORIGIN ||
18
- "http://localhost:3000",
18
+ FRONTEND_URL,
19
19
  basePath:"/api/auth" ,
20
20
  trustedOrigins: [
21
21
  process.env.CLIENT_ORIGIN ||
22
22
  process.env.FRONTEND_URL ||
23
- "https://smart-cli-based-agent-t7x4.vercel.app",
24
- "http://localhost:3000",
23
+ FRONTEND_URL,
24
+ FRONTEND_URL,
25
25
  ],
26
26
  plugins: [
27
27
  deviceAuthorization({