maverick-ai-cli 1.0.0
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 +39 -0
- package/prisma/migrations/20251116054155_start/migration.sql +6 -0
- package/prisma/migrations/20251116060620_authentication/migration.sql +69 -0
- package/prisma/migrations/20251116104449_device_flow/migration.sql +15 -0
- package/prisma/migrations/20251117173518_migraton_ai_conversation/migration.sql +31 -0
- package/prisma/migrations/migration_lock.toml +3 -0
- package/prisma/schema.prisma +112 -0
- package/src/cli/ai/google-service.js +99 -0
- package/src/cli/chat/chat-with-ai.js +293 -0
- package/src/cli/commands/ai/wakeUp.js +83 -0
- package/src/cli/commands/auth/login.js +296 -0
- package/src/cli/main.js +56 -0
- package/src/config/google.config.js +9 -0
- package/src/config/tool.config.js +106 -0
- package/src/index.js +47 -0
- package/src/lib/auth.js +26 -0
- package/src/lib/db.js +9 -0
- package/src/lib/token.js +104 -0
- package/src/service/chat.service.js +117 -0
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import { Command } from "commander";
|
|
3
|
+
import yoctoSpinner from "yocto-spinner"
|
|
4
|
+
import { requireAuth } from "../../../lib/token.js";
|
|
5
|
+
import prisma from "../../../lib/db.js";
|
|
6
|
+
import { select } from "@clack/prompts";
|
|
7
|
+
import { startChat } from "../../chat/chat-with-ai.js";
|
|
8
|
+
|
|
9
|
+
const wakeUpAction = async () => {
|
|
10
|
+
const token = await requireAuth();
|
|
11
|
+
|
|
12
|
+
const spinner = yoctoSpinner({ text: "Fetching user information....." })
|
|
13
|
+
spinner.start()
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
const user = await prisma.user.findFirst({
|
|
17
|
+
where: {
|
|
18
|
+
sessions: {
|
|
19
|
+
some: {
|
|
20
|
+
token: token.access_token
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
select: {
|
|
25
|
+
id: true,
|
|
26
|
+
name: true,
|
|
27
|
+
email: true,
|
|
28
|
+
image: true
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
spinner.stop()
|
|
34
|
+
|
|
35
|
+
if (!user) {
|
|
36
|
+
console.log(chalk.red("User not found"))
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
console.log(chalk.green(`Welcome back, ${user.name}! \n`))
|
|
41
|
+
|
|
42
|
+
const choice = await select({
|
|
43
|
+
message: "Select an option:",
|
|
44
|
+
options: [
|
|
45
|
+
{
|
|
46
|
+
value: "chat",
|
|
47
|
+
label: "Chat",
|
|
48
|
+
hint: "Simple chat with AI"
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
value: "tool",
|
|
52
|
+
label: "Tool Calling",
|
|
53
|
+
hint: "Chat with tools (Google Search, Code Execution)"
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
value: "agent",
|
|
57
|
+
label: "Agent",
|
|
58
|
+
hint: "Advanced AI agent (coming soon)"
|
|
59
|
+
},
|
|
60
|
+
],
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
switch (choice) {
|
|
64
|
+
case "chat":
|
|
65
|
+
console.log("chat is selected")
|
|
66
|
+
await startChat("chat")
|
|
67
|
+
break;
|
|
68
|
+
case "tool":
|
|
69
|
+
console.log(chalk.green("Tool calling is selected"))
|
|
70
|
+
break;
|
|
71
|
+
case "agent":
|
|
72
|
+
console.log(chalk.yellow("Agentic mode coming soon"))
|
|
73
|
+
break;
|
|
74
|
+
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export const wakeup = new Command("wakeup")
|
|
82
|
+
.description("wake up the ai")
|
|
83
|
+
.action(wakeUpAction)
|
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { cancel, confirm, intro, isCancel, outro } from "@clack/prompts";
|
|
4
|
+
import { logger } from "better-auth";
|
|
5
|
+
import { createAuthClient } from "better-auth/client";
|
|
6
|
+
import { deviceAuthorizationClient } from "better-auth/client/plugins";
|
|
7
|
+
|
|
8
|
+
import chalk from "chalk";
|
|
9
|
+
import { Command } from "commander";
|
|
10
|
+
import open from "open";
|
|
11
|
+
import os from "os";
|
|
12
|
+
import path from "path";
|
|
13
|
+
import yoctoSpinner from "yocto-spinner";
|
|
14
|
+
import * as z from "zod";
|
|
15
|
+
import dotenv from "dotenv";
|
|
16
|
+
import { clearStoredToken, getStoredToken, isTokenExpired, requireAuth, storeToken } from "../../../lib/token.js";
|
|
17
|
+
import prisma from "../../../lib/db.js";
|
|
18
|
+
|
|
19
|
+
dotenv.config();
|
|
20
|
+
|
|
21
|
+
const URL = "https://maverick-cli-backend.onrender.com";
|
|
22
|
+
const CLIENT_ID = process.env.GITHUB_CLIENT_ID;
|
|
23
|
+
|
|
24
|
+
// ~/.better-auth/token.json
|
|
25
|
+
export const CONFIG_DIR = path.join(os.homedir(), ".better-auth");
|
|
26
|
+
export const TOKEN_FILE = path.join(CONFIG_DIR, "token.json");
|
|
27
|
+
|
|
28
|
+
export async function loginAction(opts) {
|
|
29
|
+
// Validate CLI args
|
|
30
|
+
const OptionsSchema = z.object({
|
|
31
|
+
serverUrl: z.string().optional(),
|
|
32
|
+
clientId: z.string().optional(),
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
const parsed = OptionsSchema.parse(opts);
|
|
36
|
+
|
|
37
|
+
const serverUrl = parsed.serverUrl || URL;
|
|
38
|
+
const clientId = parsed.clientId || CLIENT_ID;
|
|
39
|
+
|
|
40
|
+
intro(chalk.cyan("🔐 Better Auth – Device Login"));
|
|
41
|
+
|
|
42
|
+
// Token check (optional)
|
|
43
|
+
const existingToken = await getStoredToken();
|
|
44
|
+
const expired = await isTokenExpired()
|
|
45
|
+
|
|
46
|
+
if (existingToken && !expired) {
|
|
47
|
+
const reAuth = await confirm({
|
|
48
|
+
message: "You are already logged in. Login again?",
|
|
49
|
+
initialValue: false,
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
if (isCancel(reAuth) || !reAuth) {
|
|
53
|
+
cancel("Login cancelled");
|
|
54
|
+
process.exit(0);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Create auth client
|
|
59
|
+
const authClient = createAuthClient({
|
|
60
|
+
baseURL: serverUrl,
|
|
61
|
+
plugins: [deviceAuthorizationClient()],
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
const spinner = yoctoSpinner({ text: "Requesting device authorization..." });
|
|
65
|
+
spinner.start();
|
|
66
|
+
|
|
67
|
+
try {
|
|
68
|
+
// Request device code
|
|
69
|
+
const { data, error } = await authClient.device.code({
|
|
70
|
+
client_id: clientId,
|
|
71
|
+
scope: "openid profile email",
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
spinner.stop();
|
|
75
|
+
|
|
76
|
+
if (!data || error) {
|
|
77
|
+
logger.error(
|
|
78
|
+
`Failed to request device authorization: ${error?.error_description || error?.error
|
|
79
|
+
}`
|
|
80
|
+
);
|
|
81
|
+
process.exit(1);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const {
|
|
85
|
+
device_code,
|
|
86
|
+
user_code,
|
|
87
|
+
verification_uri,
|
|
88
|
+
verification_uri_complete,
|
|
89
|
+
interval = 5,
|
|
90
|
+
expires_in,
|
|
91
|
+
} = data;
|
|
92
|
+
|
|
93
|
+
console.log(chalk.cyan("\n🔗 Device Authorization Required"));
|
|
94
|
+
console.log(
|
|
95
|
+
`Visit: ${chalk.underline.blue(
|
|
96
|
+
verification_uri_complete || verification_uri
|
|
97
|
+
)}`
|
|
98
|
+
);
|
|
99
|
+
console.log(`Code: ${chalk.bold.green(user_code)}\n`);
|
|
100
|
+
|
|
101
|
+
// Auto-open browser
|
|
102
|
+
const shouldOpen = await confirm({
|
|
103
|
+
message: "Open browser automatically?",
|
|
104
|
+
initialValue: true,
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
if (!isCancel(shouldOpen) && shouldOpen) {
|
|
108
|
+
await open(verification_uri_complete || verification_uri);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
console.log(
|
|
112
|
+
chalk.gray(
|
|
113
|
+
`⏳ Waiting for authorization (expires in ${Math.floor(
|
|
114
|
+
expires_in / 60
|
|
115
|
+
)}m)...`
|
|
116
|
+
)
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
const token = await pollForToken(
|
|
120
|
+
authClient,
|
|
121
|
+
device_code,
|
|
122
|
+
clientId,
|
|
123
|
+
interval
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
if (token) {
|
|
127
|
+
const saved = await storeToken(token);
|
|
128
|
+
|
|
129
|
+
if (!saved) {
|
|
130
|
+
console.log(chalk.yellow("⚠️ Warning: Could not save authentication token"));
|
|
131
|
+
console.log(chalk.yellow("You may need to login again on next use."));
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
outro(chalk.green("✔ Logged in successfully!"));
|
|
135
|
+
|
|
136
|
+
// Todo : Get the user data
|
|
137
|
+
}
|
|
138
|
+
} catch (err) {
|
|
139
|
+
spinner.stop();
|
|
140
|
+
process.exit(1);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// --------------------------------------
|
|
145
|
+
// POLLING FOR TOKEN
|
|
146
|
+
// --------------------------------------
|
|
147
|
+
|
|
148
|
+
async function pollForToken(authClient, deviceCode, clientId, interval) {
|
|
149
|
+
let pollInterval = interval;
|
|
150
|
+
|
|
151
|
+
return new Promise((resolve, reject) => {
|
|
152
|
+
const spinner = yoctoSpinner({ text: "", color: "cyan" });
|
|
153
|
+
let dots = 0;
|
|
154
|
+
|
|
155
|
+
const poll = async () => {
|
|
156
|
+
dots = (dots + 1) % 4;
|
|
157
|
+
spinner.text = chalk.gray(
|
|
158
|
+
`Polling for authorization ${".".repeat(dots)}${" ".repeat(3 - dots)}`
|
|
159
|
+
);
|
|
160
|
+
|
|
161
|
+
if (!spinner.isSpinning) spinner.start();
|
|
162
|
+
|
|
163
|
+
try {
|
|
164
|
+
const { data, error } = await authClient.device.token({
|
|
165
|
+
grant_type: "urn:ietf:params:oauth:grant-type:device_code",
|
|
166
|
+
device_code: deviceCode,
|
|
167
|
+
client_id: clientId,
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
if (data?.access_token) {
|
|
171
|
+
spinner.stop();
|
|
172
|
+
resolve(data);
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
if (error) {
|
|
177
|
+
switch (error.error) {
|
|
178
|
+
case "authorization_pending":
|
|
179
|
+
// Keep polling
|
|
180
|
+
break;
|
|
181
|
+
case "slow_down":
|
|
182
|
+
pollInterval += 5;
|
|
183
|
+
break;
|
|
184
|
+
case "access_denied":
|
|
185
|
+
spinner.stop();
|
|
186
|
+
console.error(chalk.red("✖ Access denied."));
|
|
187
|
+
reject(error);
|
|
188
|
+
return;
|
|
189
|
+
case "expired_token":
|
|
190
|
+
spinner.stop();
|
|
191
|
+
console.error(chalk.red("✖ Device code expired."));
|
|
192
|
+
reject(error);
|
|
193
|
+
return;
|
|
194
|
+
default:
|
|
195
|
+
spinner.stop();
|
|
196
|
+
reject(error);
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
} catch (err) {
|
|
201
|
+
spinner.stop();
|
|
202
|
+
reject(err);
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
setTimeout(poll, pollInterval * 1000);
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
poll();
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
export async function logoutAction() {
|
|
214
|
+
intro(chalk.bold("👋🏻 Logout"))
|
|
215
|
+
|
|
216
|
+
const token = await getStoredToken();
|
|
217
|
+
|
|
218
|
+
if (!token) {
|
|
219
|
+
console.log(chalk.yellow("You'r are not logged in"));
|
|
220
|
+
process.exit(0);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const shouldLogout = await confirm({
|
|
224
|
+
message: "Are you sure you want to logout?",
|
|
225
|
+
initialValue: false
|
|
226
|
+
})
|
|
227
|
+
|
|
228
|
+
if (isCancel(shouldLogout) || !shouldLogout) {
|
|
229
|
+
cancel("Loggout Cancelled")
|
|
230
|
+
process.exit(0)
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
const cleared = await clearStoredToken();
|
|
234
|
+
|
|
235
|
+
if (cleared) {
|
|
236
|
+
outro(chalk.green("✅ Successfully logged out!"))
|
|
237
|
+
} else {
|
|
238
|
+
console.log(chalk.yellow("⚠️ Could not clear token file."))
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
export async function whoamiAction(opts) {
|
|
246
|
+
const token = await requireAuth();
|
|
247
|
+
|
|
248
|
+
if (!token?.access_token) {
|
|
249
|
+
console.log("No access token found. Please login.")
|
|
250
|
+
process.exit(1)
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
const user = await prisma.user.findFirst({
|
|
254
|
+
where: {
|
|
255
|
+
sessions: {
|
|
256
|
+
some: {
|
|
257
|
+
token: token.access_token,
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
},
|
|
261
|
+
select: {
|
|
262
|
+
id: true,
|
|
263
|
+
name: true,
|
|
264
|
+
email: true,
|
|
265
|
+
image: true
|
|
266
|
+
},
|
|
267
|
+
})
|
|
268
|
+
|
|
269
|
+
console.log(
|
|
270
|
+
chalk.bold.greenBright(`\n USER: ${user.name}
|
|
271
|
+
📧 EMAIL: ${user.email}
|
|
272
|
+
🆔 ID : ${user.id}`)
|
|
273
|
+
);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
// --------------------------------------
|
|
279
|
+
// COMMANDER SETUP
|
|
280
|
+
// --------------------------------------
|
|
281
|
+
|
|
282
|
+
export const login = new Command("login")
|
|
283
|
+
.description("Login to Better Auth")
|
|
284
|
+
.option("--server-url <url>", "The Better Auth server URL", URL)
|
|
285
|
+
.option("--client-id <id>", "The OAuth client ID", CLIENT_ID)
|
|
286
|
+
.action(loginAction);
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
export const logout = new Command("logout")
|
|
290
|
+
.description("Logout and Clear stored credentials")
|
|
291
|
+
.action(logoutAction)
|
|
292
|
+
|
|
293
|
+
export const whoami = new Command("whoami")
|
|
294
|
+
.description("Show current authenticated user")
|
|
295
|
+
.option("--server-url <url>", "The Better Auth server URL", URL)
|
|
296
|
+
.action(whoamiAction)
|
package/src/cli/main.js
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import dotenv from "dotenv";
|
|
4
|
+
import chalk from "chalk";
|
|
5
|
+
import figlet from "figlet";
|
|
6
|
+
import { Command, program } from "commander";
|
|
7
|
+
import { login, logout, whoami } from "./commands/auth/login.js";
|
|
8
|
+
import { wakeup } from "./commands/ai/wakeUp.js";
|
|
9
|
+
|
|
10
|
+
import path from "path";
|
|
11
|
+
import { fileURLToPath } from "url";
|
|
12
|
+
|
|
13
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
14
|
+
const __dirname = path.dirname(__filename);
|
|
15
|
+
|
|
16
|
+
dotenv.config({ path: path.join(__dirname, "../../.env") });
|
|
17
|
+
|
|
18
|
+
async function main() {
|
|
19
|
+
// Sexy Banner
|
|
20
|
+
console.log(
|
|
21
|
+
chalk.hex("#6A5ACD")(
|
|
22
|
+
figlet.textSync("Maverick", {
|
|
23
|
+
font: "ANSI Shadow",
|
|
24
|
+
horizontalLayout: "default",
|
|
25
|
+
verticalLayout: "default",
|
|
26
|
+
width: 120
|
|
27
|
+
})
|
|
28
|
+
)
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
console.log(
|
|
32
|
+
chalk.greenBright("<---- Intelligent Command Line Interface ---->\n")
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
const program = new Command("Maverick");
|
|
37
|
+
program.version("1.4.3")
|
|
38
|
+
.description("Maverick Cli - A cli based AI tool")
|
|
39
|
+
.addCommand(login)
|
|
40
|
+
.addCommand(logout)
|
|
41
|
+
.addCommand(whoami)
|
|
42
|
+
.addCommand(wakeup)
|
|
43
|
+
|
|
44
|
+
program.action(() => {
|
|
45
|
+
program.help();
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
program.parse()
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
main().catch((err) => {
|
|
54
|
+
console.log(chalk.red("Error : Maverick don't wanna shown UP..!!; Because of this ->"), err)
|
|
55
|
+
process.exit(1)
|
|
56
|
+
});
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { google } from "@ai-sdk/google";
|
|
2
|
+
import chalk from "chalk";
|
|
3
|
+
|
|
4
|
+
export const availableTools = [
|
|
5
|
+
{
|
|
6
|
+
id: "google_search",
|
|
7
|
+
name: "Google Search",
|
|
8
|
+
description: "Search the web using Google Search",
|
|
9
|
+
getTool: () => google({}),
|
|
10
|
+
enabled: true,
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
id: 'code_execution',
|
|
14
|
+
name: 'Code Execution',
|
|
15
|
+
description: 'Generate and execute python code to perform calculations, solve problems, or provide accurate results.',
|
|
16
|
+
getTool: () => google.tools.codeExecution({}),
|
|
17
|
+
enabled: true,
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
id: 'url_context',
|
|
21
|
+
name: 'URL Context',
|
|
22
|
+
description: 'Provide specific URLs that you want to analyse directly from the propmt. Suports upto 20 URLs per request.',
|
|
23
|
+
getTool: () => google.tools.urlContext({}),
|
|
24
|
+
enabled: true,
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
export function getEnabledTools() {
|
|
32
|
+
const tools = {}
|
|
33
|
+
try {
|
|
34
|
+
for (const tool of availableTools) {
|
|
35
|
+
if (tool.enabled) {
|
|
36
|
+
tools[tool.id] = tool.getTool()
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
if (Object.keys(tools).length > 0) {
|
|
40
|
+
console.log(chalk.green(`[DEBUG] Enabled ${Object.keys(tools).join(", ")}`))
|
|
41
|
+
} else {
|
|
42
|
+
console.log(chalk.yellow(`[DEBUG] No tools enabled`))
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return Object.keys(tools).length > 0 ? tools : undefined
|
|
46
|
+
} catch (error) {
|
|
47
|
+
console.error(chalk.red("Error getting enabled tools"), error)
|
|
48
|
+
console.error(chalk.red(`[ERROR] Error getting enabled tools`), error.message)
|
|
49
|
+
console.error(chalk.yellow('Make sure you have @ai-sdk/google version 2.0+ installed'))
|
|
50
|
+
console.error(chalk.yellow('You can install it using npm install @ai-sdk/google@latest'))
|
|
51
|
+
return undefined
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
export function toggleTool(toolsId) {
|
|
58
|
+
|
|
59
|
+
const tool = availableTools.find(t => t.id === toolsId)
|
|
60
|
+
|
|
61
|
+
if (tool) {
|
|
62
|
+
tool.enabled = !tool.enabled
|
|
63
|
+
console.log(chalk.gray(`[DEBUG] Tool ${toolsId} toggled to ${tool.enabled}`))
|
|
64
|
+
|
|
65
|
+
return tool.enabled
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
console.log(chalk.red(`[ERROR] Tool ${toolsId} not found`))
|
|
69
|
+
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
export function enableTools(toolsId) {
|
|
75
|
+
|
|
76
|
+
console.log(chalk.gray('[DEBUG] enableTools called with:'), toolsId)
|
|
77
|
+
|
|
78
|
+
availableTools.forEach(tool => {
|
|
79
|
+
const wasEnabled = tool.enabled
|
|
80
|
+
|
|
81
|
+
tool.enabled = toolsId.includes(tool.id)
|
|
82
|
+
|
|
83
|
+
if (tool.enabled !== wasEnabled) {
|
|
84
|
+
console.log(chalk.gray(`[DEBUG] Tool ${tool.id} ${wasEnabled} -> ${tool.enabled}`))
|
|
85
|
+
|
|
86
|
+
}
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
const enabledCount = availableTools.filter(t => t.enabled).length;
|
|
90
|
+
console.log(chalk.gray(`[DEBUG] Enabled ${enabledCount}/${availableTools.length} tools`))
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
export function getEnabledToolNames() {
|
|
97
|
+
const enabledTools = availableTools.filter(t => t.enabled).map(t => t.name)
|
|
98
|
+
console.log(chalk.gray('[DEBUG] getEnabledToolNames returning:'), enabledTools);
|
|
99
|
+
return enabledTools
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export function resetTools() {
|
|
103
|
+
availableTools.forEach(tool => tool.enabled = false);
|
|
104
|
+
|
|
105
|
+
console.log(chalk.gray('[DEBUG] ALl tools have been reset (disabled)'))
|
|
106
|
+
}
|
package/src/index.js
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import express from "express";
|
|
2
|
+
import dotenv from "dotenv";
|
|
3
|
+
import { toNodeHandler, fromNodeHeaders } from "better-auth/node";
|
|
4
|
+
import cors from "cors";
|
|
5
|
+
import { auth } from "./lib/auth.js"
|
|
6
|
+
|
|
7
|
+
dotenv.config()
|
|
8
|
+
|
|
9
|
+
const app = express()
|
|
10
|
+
|
|
11
|
+
// *----------- CORS --------------*
|
|
12
|
+
app.use(
|
|
13
|
+
cors({
|
|
14
|
+
origin: "https://maverick-cli.vercel.app",
|
|
15
|
+
methods: ["GET", "POST", "PUT", "DELETE"],
|
|
16
|
+
credentials: true,
|
|
17
|
+
})
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
app.all("/api/auth/*splat", toNodeHandler(auth))
|
|
21
|
+
|
|
22
|
+
app.use(express.json());
|
|
23
|
+
|
|
24
|
+
app.get("/api/me", async (req, res) => {
|
|
25
|
+
const session = await auth.api.getSession({
|
|
26
|
+
headers: fromNodeHeaders(req.headers),
|
|
27
|
+
});
|
|
28
|
+
return res.json(session);
|
|
29
|
+
})
|
|
30
|
+
app.head('/', (req, res) => {
|
|
31
|
+
res.status(200).end();
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
app.get("/device", async (req, res) => {
|
|
36
|
+
const { user_code } = req.query
|
|
37
|
+
res.redirect(`https://maverick-cli.vercel.app/device?user_code=${user_code}`)
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
app.get('/health', (req, res) => {
|
|
42
|
+
res.send("Backend is running")
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
app.listen(process.env.PORT, () => {
|
|
46
|
+
console.log(`running on PORT : http://localhost:${process.env.PORT}`)
|
|
47
|
+
})
|
package/src/lib/auth.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { betterAuth } from "better-auth";
|
|
2
|
+
import { prismaAdapter } from "better-auth/adapters/prisma";
|
|
3
|
+
import { deviceAuthorization } from "better-auth/plugins";
|
|
4
|
+
import prisma from "./db.js"
|
|
5
|
+
|
|
6
|
+
export const auth = betterAuth({
|
|
7
|
+
database: prismaAdapter(prisma, {
|
|
8
|
+
provider: "postgresql",
|
|
9
|
+
}),
|
|
10
|
+
basePath: "api/auth",
|
|
11
|
+
trustedOrigins: ["https://maverick-cli.vercel.app"],
|
|
12
|
+
socialProviders: {
|
|
13
|
+
github: {
|
|
14
|
+
clientId: process.env.GITHUB_CLIENT_ID,
|
|
15
|
+
clientSecret: process.env.GITHUB_CLIENT_SECRET
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
plugins: [
|
|
19
|
+
deviceAuthorization({
|
|
20
|
+
expiresIn: "30m",
|
|
21
|
+
interval: "5s",
|
|
22
|
+
}),
|
|
23
|
+
],
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
|