mikasa-cli 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.
- package/package.json +39 -0
- package/prisma/migrations/20260122185342_trst_migration/migration.sql +7 -0
- package/prisma/migrations/20260122192114/migration.sql +78 -0
- package/prisma/migrations/20260123074430_device_flow/migration.sql +15 -0
- package/prisma/migrations/migration_lock.toml +3 -0
- package/prisma/schema.prisma +125 -0
- package/src/cli/ai/google-service.js +235 -0
- package/src/cli/chat/chat-with-ai-agent.js +219 -0
- package/src/cli/chat/chat-with-ai-tool.js +377 -0
- package/src/cli/chat/chat-with-ai.js +275 -0
- package/src/cli/commands/ai/wakeUp.js +81 -0
- package/src/cli/commands/auth/login.js +607 -0
- package/src/cli/main.js +56 -0
- package/src/config/agent.config.js +166 -0
- package/src/config/google.config.js +8 -0
- package/src/config/tool.config.js +112 -0
- package/src/index.js +119 -0
- package/src/lib/auth-client.js +9 -0
- package/src/lib/auth.js +52 -0
- package/src/lib/db.js +9 -0
- package/src/services/chat.services.js +152 -0
|
@@ -0,0 +1,607 @@
|
|
|
1
|
+
import { cancel, confirm, intro, isCancel, outro } from "@clack/prompts";
|
|
2
|
+
import { logger } from "better-auth";
|
|
3
|
+
import { createAuthClient } from "better-auth/client";
|
|
4
|
+
import { deviceAuthorizationClient } from "better-auth/client/plugins";
|
|
5
|
+
import chalk from "chalk";
|
|
6
|
+
import { Command } from "commander";
|
|
7
|
+
import fs from "fs/promises";
|
|
8
|
+
import open from "open";
|
|
9
|
+
import os from "os";
|
|
10
|
+
import path from "path";
|
|
11
|
+
import yoctoSpinner from "yocto-spinner";
|
|
12
|
+
import * as z from "zod/v4";
|
|
13
|
+
// import dotenv from "dotenv";
|
|
14
|
+
import "dotenv/config";
|
|
15
|
+
import prisma from "../../../lib/db.js";
|
|
16
|
+
|
|
17
|
+
// dotenv.config();
|
|
18
|
+
|
|
19
|
+
const DEMO_URL = "https://mikasa-cli-2.onrender.com";
|
|
20
|
+
const CLIENT_ID = process.env.GITHUB_CLIENT_ID;
|
|
21
|
+
const CONFIG_DIR = path.join(os.homedir(), ".better-auth");
|
|
22
|
+
const TOKEN_FILE = path.join(CONFIG_DIR, "token.json");
|
|
23
|
+
|
|
24
|
+
// ============================================
|
|
25
|
+
// TOKEN MANAGEMENT (Export these for use in other commands)
|
|
26
|
+
// ============================================
|
|
27
|
+
|
|
28
|
+
export async function getStoredToken() {
|
|
29
|
+
try {
|
|
30
|
+
const data = await fs.readFile(TOKEN_FILE, "utf-8");
|
|
31
|
+
const token = JSON.parse(data);
|
|
32
|
+
return token;
|
|
33
|
+
} catch (error) {
|
|
34
|
+
// File doesn't exist or can't be read
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export async function storeToken(token) {
|
|
40
|
+
try {
|
|
41
|
+
// Ensure config directory exists
|
|
42
|
+
await fs.mkdir(CONFIG_DIR, { recursive: true });
|
|
43
|
+
|
|
44
|
+
// Store token with metadata
|
|
45
|
+
const tokenData = {
|
|
46
|
+
access_token: token.access_token,
|
|
47
|
+
refresh_token: token.refresh_token, // Store if available
|
|
48
|
+
token_type: token.token_type || "Bearer",
|
|
49
|
+
scope: token.scope,
|
|
50
|
+
expires_at: token.expires_in
|
|
51
|
+
? new Date(Date.now() + token.expires_in * 1000).toISOString()
|
|
52
|
+
: null,
|
|
53
|
+
created_at: new Date().toISOString(),
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
await fs.writeFile(TOKEN_FILE, JSON.stringify(tokenData, null, 2), "utf-8");
|
|
57
|
+
return true;
|
|
58
|
+
} catch (error) {
|
|
59
|
+
console.error(chalk.red("Failed to store token:"), error.message);
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export async function clearStoredToken() {
|
|
65
|
+
try {
|
|
66
|
+
await fs.unlink(TOKEN_FILE);
|
|
67
|
+
return true;
|
|
68
|
+
} catch (error) {
|
|
69
|
+
// File doesn't exist or can't be deleted
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export async function isTokenExpired() {
|
|
75
|
+
const token = await getStoredToken();
|
|
76
|
+
if (!token || !token.expires_at) {
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const expiresAt = new Date(token.expires_at);
|
|
81
|
+
const now = new Date();
|
|
82
|
+
|
|
83
|
+
// Consider expired if less than 5 minutes remaining
|
|
84
|
+
return expiresAt.getTime() - now.getTime() < 5 * 60 * 1000;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export async function requireAuth() {
|
|
88
|
+
const token = await getStoredToken();
|
|
89
|
+
|
|
90
|
+
if (!token) {
|
|
91
|
+
console.log(
|
|
92
|
+
chalk.red("❌ Not authenticated. Please run 'MikasaCLI login' first.")
|
|
93
|
+
);
|
|
94
|
+
process.exit(1);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (await isTokenExpired()) {
|
|
98
|
+
console.log(
|
|
99
|
+
chalk.yellow("⚠️ Your session has expired. Please login again.")
|
|
100
|
+
);
|
|
101
|
+
console.log(chalk.gray(" Run: your-cli login\n"));
|
|
102
|
+
process.exit(1);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return token;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// ============================================
|
|
109
|
+
// LOGIN COMMAND
|
|
110
|
+
// ============================================
|
|
111
|
+
|
|
112
|
+
export async function loginAction(opts) {
|
|
113
|
+
const options = z
|
|
114
|
+
.object({
|
|
115
|
+
serverUrl: z.string().optional(),
|
|
116
|
+
clientId: z.string().optional(),
|
|
117
|
+
})
|
|
118
|
+
.parse(opts);
|
|
119
|
+
|
|
120
|
+
const serverUrl = options.serverUrl || DEMO_URL;
|
|
121
|
+
const clientId = options.clientId || CLIENT_ID;
|
|
122
|
+
|
|
123
|
+
intro(chalk.bold("🔐 MikasaCLI Login"));
|
|
124
|
+
|
|
125
|
+
if (!clientId) {
|
|
126
|
+
logger.error("CLIENT_ID is not set in .env file");
|
|
127
|
+
console.log(
|
|
128
|
+
chalk.red("\n❌ Please set GITHUB_CLIENT_ID in your .env file")
|
|
129
|
+
);
|
|
130
|
+
process.exit(1);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Check if already logged in
|
|
134
|
+
const existingToken = await getStoredToken();
|
|
135
|
+
const expired = await isTokenExpired();
|
|
136
|
+
|
|
137
|
+
if (existingToken && !expired) {
|
|
138
|
+
const shouldReauth = await confirm({
|
|
139
|
+
message: "You're already logged in. Do you want to log in again?",
|
|
140
|
+
initialValue: false,
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
if (isCancel(shouldReauth) || !shouldReauth) {
|
|
144
|
+
cancel("Login cancelled");
|
|
145
|
+
process.exit(0);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Create the auth client
|
|
150
|
+
const authClient = createAuthClient({
|
|
151
|
+
baseURL: serverUrl,
|
|
152
|
+
plugins: [deviceAuthorizationClient()],
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
const spinner = yoctoSpinner({ text: "Requesting device authorization..." });
|
|
156
|
+
spinner.start();
|
|
157
|
+
|
|
158
|
+
try {
|
|
159
|
+
// Request device code
|
|
160
|
+
const { data, error } = await authClient.device.code({
|
|
161
|
+
client_id: clientId,
|
|
162
|
+
scope: "openid profile email",
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
spinner.stop();
|
|
166
|
+
|
|
167
|
+
if (error || !data) {
|
|
168
|
+
logger.error(
|
|
169
|
+
`Failed to request device authorization: ${error?.error_description || error?.message || "Unknown error"
|
|
170
|
+
}`
|
|
171
|
+
);
|
|
172
|
+
|
|
173
|
+
if (error?.status === 404) {
|
|
174
|
+
console.log(chalk.red("\n❌ Device authorization endpoint not found."));
|
|
175
|
+
console.log(chalk.yellow(" Make sure your auth server is running."));
|
|
176
|
+
} else if (error?.status === 400) {
|
|
177
|
+
console.log(
|
|
178
|
+
chalk.red("\n❌ Bad request - check your CLIENT_ID configuration.")
|
|
179
|
+
);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
process.exit(1);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const {
|
|
186
|
+
device_code,
|
|
187
|
+
user_code,
|
|
188
|
+
verification_uri,
|
|
189
|
+
verification_uri_complete,
|
|
190
|
+
interval = 5,
|
|
191
|
+
expires_in,
|
|
192
|
+
} = data;
|
|
193
|
+
|
|
194
|
+
// Display authorization instructions
|
|
195
|
+
console.log("");
|
|
196
|
+
console.log(chalk.cyan("📱 Device Authorization Required"));
|
|
197
|
+
console.log("");
|
|
198
|
+
console.log(
|
|
199
|
+
`Please visit: ${chalk.underline.blue(
|
|
200
|
+
verification_uri_complete || verification_uri
|
|
201
|
+
)}`
|
|
202
|
+
);
|
|
203
|
+
console.log(`Enter code: ${chalk.bold.green(user_code)}`);
|
|
204
|
+
console.log("");
|
|
205
|
+
|
|
206
|
+
// Ask if user wants to open browser
|
|
207
|
+
const shouldOpen = await confirm({
|
|
208
|
+
message: "Open browser automatically?",
|
|
209
|
+
initialValue: true,
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
if (!isCancel(shouldOpen) && shouldOpen) {
|
|
213
|
+
const urlToOpen = verification_uri_complete || verification_uri;
|
|
214
|
+
await open(urlToOpen);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Start polling
|
|
218
|
+
console.log(
|
|
219
|
+
chalk.gray(
|
|
220
|
+
`Waiting for authorization (expires in ${Math.floor(
|
|
221
|
+
expires_in / 60
|
|
222
|
+
)} minutes)...`
|
|
223
|
+
)
|
|
224
|
+
);
|
|
225
|
+
|
|
226
|
+
const token = await pollForToken(
|
|
227
|
+
authClient,
|
|
228
|
+
device_code,
|
|
229
|
+
clientId,
|
|
230
|
+
interval
|
|
231
|
+
);
|
|
232
|
+
|
|
233
|
+
if (token) {
|
|
234
|
+
// Store the token
|
|
235
|
+
const saved = await storeToken(token);
|
|
236
|
+
|
|
237
|
+
if (!saved) {
|
|
238
|
+
console.log(
|
|
239
|
+
chalk.yellow("\n⚠️ Warning: Could not save authentication token.")
|
|
240
|
+
);
|
|
241
|
+
console.log(
|
|
242
|
+
chalk.yellow(" You may need to login again on next use.")
|
|
243
|
+
);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// Get user info
|
|
247
|
+
const { data: session } = await authClient.getSession({
|
|
248
|
+
fetchOptions: {
|
|
249
|
+
headers: {
|
|
250
|
+
Authorization: `Bearer ${token.access_token}`,
|
|
251
|
+
},
|
|
252
|
+
},
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
outro(
|
|
256
|
+
chalk.green(
|
|
257
|
+
`✅ Login successful! Welcome ${session?.user?.name || session?.user?.email || "User"
|
|
258
|
+
}`
|
|
259
|
+
)
|
|
260
|
+
);
|
|
261
|
+
|
|
262
|
+
console.log(chalk.gray(`\n📁 Token saved to: ${TOKEN_FILE}`));
|
|
263
|
+
console.log(
|
|
264
|
+
chalk.gray(" You can now use AI commands without logging in again.\n")
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
} catch (err) {
|
|
268
|
+
spinner.stop();
|
|
269
|
+
console.error(chalk.red("\nLogin failed:"), err.message);
|
|
270
|
+
process.exit(1);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
async function pollForToken(authClient, deviceCode, clientId, initialInterval) {
|
|
275
|
+
let pollingInterval = initialInterval;
|
|
276
|
+
const spinner = yoctoSpinner({ text: "", color: "cyan" });
|
|
277
|
+
let dots = 0;
|
|
278
|
+
|
|
279
|
+
return new Promise((resolve, reject) => {
|
|
280
|
+
const poll = async () => {
|
|
281
|
+
dots = (dots + 1) % 4;
|
|
282
|
+
spinner.text = chalk.gray(
|
|
283
|
+
`Polling for authorization${".".repeat(dots)}${" ".repeat(3 - dots)}`
|
|
284
|
+
);
|
|
285
|
+
if (!spinner.isSpinning) spinner.start();
|
|
286
|
+
|
|
287
|
+
try {
|
|
288
|
+
const { data, error } = await authClient.device.token({
|
|
289
|
+
grant_type: "urn:ietf:params:oauth:grant-type:device_code",
|
|
290
|
+
device_code: deviceCode,
|
|
291
|
+
client_id: clientId,
|
|
292
|
+
fetchOptions: {
|
|
293
|
+
headers: {
|
|
294
|
+
"user-agent": `Better Auth CLI`,
|
|
295
|
+
},
|
|
296
|
+
},
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
if (data?.access_token) {
|
|
300
|
+
console.log(
|
|
301
|
+
chalk.bold.yellow(`Your access token: ${data.access_token}`)
|
|
302
|
+
);
|
|
303
|
+
spinner.stop();
|
|
304
|
+
resolve(data);
|
|
305
|
+
return;
|
|
306
|
+
} else if (error) {
|
|
307
|
+
switch (error.error) {
|
|
308
|
+
case "authorization_pending":
|
|
309
|
+
// Continue polling
|
|
310
|
+
break;
|
|
311
|
+
case "slow_down":
|
|
312
|
+
pollingInterval += 5;
|
|
313
|
+
break;
|
|
314
|
+
case "access_denied":
|
|
315
|
+
spinner.stop();
|
|
316
|
+
logger.error("Access was denied by the user");
|
|
317
|
+
process.exit(1);
|
|
318
|
+
break;
|
|
319
|
+
case "expired_token":
|
|
320
|
+
spinner.stop();
|
|
321
|
+
logger.error("The device code has expired. Please try again.");
|
|
322
|
+
process.exit(1);
|
|
323
|
+
break;
|
|
324
|
+
default:
|
|
325
|
+
spinner.stop();
|
|
326
|
+
logger.error(`Error: ${error.error_description}`);
|
|
327
|
+
process.exit(1);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
} catch (err) {
|
|
331
|
+
spinner.stop();
|
|
332
|
+
logger.error(`Network error: ${err.message}`);
|
|
333
|
+
process.exit(1);
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
setTimeout(poll, pollingInterval * 1000);
|
|
337
|
+
};
|
|
338
|
+
|
|
339
|
+
setTimeout(poll, pollingInterval * 1000);
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// ============================================
|
|
344
|
+
// LOGOUT COMMAND
|
|
345
|
+
// ============================================
|
|
346
|
+
|
|
347
|
+
export async function logoutAction() {
|
|
348
|
+
intro(chalk.bold("👋 Logout"));
|
|
349
|
+
|
|
350
|
+
const token = await getStoredToken();
|
|
351
|
+
|
|
352
|
+
if (!token) {
|
|
353
|
+
console.log(chalk.yellow("You're not logged in."));
|
|
354
|
+
process.exit(0);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
const shouldLogout = await confirm({
|
|
358
|
+
message: "Are you sure you want to logout?",
|
|
359
|
+
initialValue: false,
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
if (isCancel(shouldLogout) || !shouldLogout) {
|
|
363
|
+
cancel("Logout cancelled");
|
|
364
|
+
process.exit(0);
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
const cleared = await clearStoredToken();
|
|
368
|
+
|
|
369
|
+
if (cleared) {
|
|
370
|
+
outro(chalk.green("✅ Successfully logged out!"));
|
|
371
|
+
} else {
|
|
372
|
+
console.log(chalk.yellow("⚠️ Could not clear token file."));
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
// ============================================
|
|
377
|
+
// WHOAMI COMMAND
|
|
378
|
+
// ============================================
|
|
379
|
+
|
|
380
|
+
export async function whoamiAction(opts) {
|
|
381
|
+
const token = await requireAuth();
|
|
382
|
+
if (!token?.access_token) {
|
|
383
|
+
console.log("No access token found. Please login.");
|
|
384
|
+
process.exit(1);
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
const user = await prisma.user.findFirst({
|
|
388
|
+
where: {
|
|
389
|
+
sessions: {
|
|
390
|
+
some: {
|
|
391
|
+
token: token.access_token,
|
|
392
|
+
},
|
|
393
|
+
},
|
|
394
|
+
},
|
|
395
|
+
select: {
|
|
396
|
+
id: true,
|
|
397
|
+
name: true,
|
|
398
|
+
email: true,
|
|
399
|
+
image: true,
|
|
400
|
+
},
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
// Output user session info
|
|
404
|
+
console.log(
|
|
405
|
+
chalk.bold.greenBright(`\n👤 User: ${user.name}
|
|
406
|
+
📧 Email: ${user.email}
|
|
407
|
+
👤 ID: ${user.id}`)
|
|
408
|
+
);
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
// ============================================
|
|
412
|
+
// COMMANDER SETUP
|
|
413
|
+
// ============================================
|
|
414
|
+
|
|
415
|
+
export const login = new Command("login")
|
|
416
|
+
.description("Login to MikasaCLI")
|
|
417
|
+
.option("--server-url <url>", "The Better Auth server URL", DEMO_URL)
|
|
418
|
+
.option("--client-id <id>", "The OAuth client ID", CLIENT_ID)
|
|
419
|
+
.action(loginAction);
|
|
420
|
+
|
|
421
|
+
export const logout = new Command("logout")
|
|
422
|
+
.description("Logout and clear stored credentials")
|
|
423
|
+
.action(logoutAction);
|
|
424
|
+
|
|
425
|
+
export const whoami = new Command("status")
|
|
426
|
+
.description("Show current authenticated user")
|
|
427
|
+
.option("--server-url <url>", "The Better Auth server URL", DEMO_URL)
|
|
428
|
+
.action(whoamiAction);
|
|
429
|
+
|
|
430
|
+
|
|
431
|
+
|
|
432
|
+
|
|
433
|
+
|
|
434
|
+
// import { cancel, confirm, intro, isCancel, outro } from "@clack/prompts";
|
|
435
|
+
// import { createAuthClient } from "better-auth/client";
|
|
436
|
+
// import { deviceAuthorizationClient } from "better-auth/client/plugins";
|
|
437
|
+
// import chalk from "chalk";
|
|
438
|
+
// import { Command } from "commander";
|
|
439
|
+
// import open from "open";
|
|
440
|
+
// import os from "os";
|
|
441
|
+
// import path from "path";
|
|
442
|
+
// import yoctoSpinner from "yocto-spinner";
|
|
443
|
+
// import * as z from "zod";
|
|
444
|
+
// import dotenv from "dotenv";
|
|
445
|
+
// import { logger } from "better-auth";
|
|
446
|
+
|
|
447
|
+
// dotenv.config();
|
|
448
|
+
|
|
449
|
+
// const DEFAULT_URL = "http://localhost:5000";
|
|
450
|
+
// const DEFAULT_CLIENT_ID = process.env.GITHUB_CLIENT_ID;
|
|
451
|
+
|
|
452
|
+
// export const CONFIG_DIR = path.join(os.homedir(), ".better-auth");
|
|
453
|
+
// export const TOKEN_FILE = path.join(CONFIG_DIR, "token.json");
|
|
454
|
+
|
|
455
|
+
// // token management (export these for use in other commands)
|
|
456
|
+
|
|
457
|
+
|
|
458
|
+
|
|
459
|
+
// export async function loginAction(opts) {
|
|
460
|
+
// const schema = z.object({
|
|
461
|
+
// serverUrl: z.string().optional(),
|
|
462
|
+
// clientId: z.string().optional(),
|
|
463
|
+
// });
|
|
464
|
+
|
|
465
|
+
// const { serverUrl, clientId } = schema.parse(opts);
|
|
466
|
+
|
|
467
|
+
// const baseURL = serverUrl || DEFAULT_URL;
|
|
468
|
+
// const clientID = clientId || DEFAULT_CLIENT_ID;
|
|
469
|
+
|
|
470
|
+
// if (!clientID) {
|
|
471
|
+
// cancel("GitHub Client ID is missing.");
|
|
472
|
+
// process.exit(1);
|
|
473
|
+
// }
|
|
474
|
+
|
|
475
|
+
// intro(chalk.green("Welcome to Mikasa CLI! Let's get you logged in."));
|
|
476
|
+
|
|
477
|
+
// const authClient = createAuthClient({
|
|
478
|
+
// baseURL,
|
|
479
|
+
// plugins: [deviceAuthorizationClient()],
|
|
480
|
+
// });
|
|
481
|
+
|
|
482
|
+
// const spinner = yoctoSpinner({ text: "Starting authentication..." });
|
|
483
|
+
// spinner.start();
|
|
484
|
+
|
|
485
|
+
// try {
|
|
486
|
+
// const { data, error } = await authClient.device.code({
|
|
487
|
+
// client_id: clientID,
|
|
488
|
+
// scope: "openid profile email",
|
|
489
|
+
// });
|
|
490
|
+
|
|
491
|
+
// spinner.stop();
|
|
492
|
+
|
|
493
|
+
// if (error || !data) {
|
|
494
|
+
// cancel("Failed to start device authorization.");
|
|
495
|
+
// process.exit(1);
|
|
496
|
+
// }
|
|
497
|
+
|
|
498
|
+
// const {
|
|
499
|
+
// device_code,
|
|
500
|
+
// user_code,
|
|
501
|
+
// verification_uri,
|
|
502
|
+
// verification_uri_complete,
|
|
503
|
+
// expires_in,
|
|
504
|
+
// interval = 5,
|
|
505
|
+
// } = data;
|
|
506
|
+
|
|
507
|
+
// console.log(chalk.cyan("\nDevice Authorization Required\n"));
|
|
508
|
+
// console.log(`Visit: ${chalk.underline.blue(verification_uri)}`);
|
|
509
|
+
// console.log(`Enter code: ${chalk.bold.yellow(user_code)}\n`);
|
|
510
|
+
|
|
511
|
+
// const shouldOpen = await confirm({
|
|
512
|
+
// message: "Open the verification URL in your browser?",
|
|
513
|
+
// initialValue: true,
|
|
514
|
+
// });
|
|
515
|
+
|
|
516
|
+
// if (!isCancel(shouldOpen) && shouldOpen) {
|
|
517
|
+
// await open(verification_uri_complete || verification_uri);
|
|
518
|
+
// }
|
|
519
|
+
|
|
520
|
+
// console.log(
|
|
521
|
+
// chalk.gray(
|
|
522
|
+
// `Waiting for authorization (expires in ${Math.floor(
|
|
523
|
+
// expires_in / 60
|
|
524
|
+
// )} minutes)...`
|
|
525
|
+
// )
|
|
526
|
+
// );
|
|
527
|
+
|
|
528
|
+
// const token = await pollForToken(
|
|
529
|
+
// authClient,
|
|
530
|
+
// device_code,
|
|
531
|
+
// interval,
|
|
532
|
+
// // expires_in,
|
|
533
|
+
// clientID
|
|
534
|
+
// );
|
|
535
|
+
|
|
536
|
+
// // outro(chalk.green("Complete authentication in the browser."));
|
|
537
|
+
// } catch (err) {
|
|
538
|
+
// spinner.stop();
|
|
539
|
+
// console.error(chalk.red("Authentication failed:"), err);
|
|
540
|
+
// process.exit(1);
|
|
541
|
+
// }
|
|
542
|
+
// }
|
|
543
|
+
|
|
544
|
+
// async function pollForToken(authClient, device_code, interval, clientID) {
|
|
545
|
+
// let pollingInterval = interval;
|
|
546
|
+
// const spinner = yoctoSpinner({ text: "", color: "cyan" });
|
|
547
|
+
// let dots = 0;
|
|
548
|
+
|
|
549
|
+
// return new Promise((resolve, reject) => {
|
|
550
|
+
// const poll = async () => {
|
|
551
|
+
// dots = (dots + 1) % 4;
|
|
552
|
+
// spinner.text = chalk.gray(`Polling for authorization${'.'.repeat(dots)}${' '.repeat(3 - dots)}`);
|
|
553
|
+
// if (!spinner.isSpinning) spinner.start();
|
|
554
|
+
// try {
|
|
555
|
+
// const { data, error } = await authClient.device.token({
|
|
556
|
+
// grant_type: "urn:ietf:params:oauth:grant-type:device_code",
|
|
557
|
+
// device_code,
|
|
558
|
+
// client_id: clientID,
|
|
559
|
+
// fatchOptions: {
|
|
560
|
+
// "user-agent": "mikasa-cli/1.0.0",
|
|
561
|
+
// },
|
|
562
|
+
// });
|
|
563
|
+
|
|
564
|
+
// if (data?.access_token) {
|
|
565
|
+
// console.log(
|
|
566
|
+
// chalk.bold.yellow(`Your access token: ${data.access_token}`)
|
|
567
|
+
// );
|
|
568
|
+
// spinner.stop();
|
|
569
|
+
// resolve(data);
|
|
570
|
+
// return;
|
|
571
|
+
// } else if (error) {
|
|
572
|
+
// switch (error.error) {
|
|
573
|
+
// case "authorization_pending":
|
|
574
|
+
// // continue polling
|
|
575
|
+
// break;
|
|
576
|
+
// case "slow_down":
|
|
577
|
+
// pollingInterval += 5;
|
|
578
|
+
// break;
|
|
579
|
+
// case "access_denied":
|
|
580
|
+
// console.error(chalk.red("Authorization denied by user."));
|
|
581
|
+
// return;
|
|
582
|
+
// case "expired_token":
|
|
583
|
+
// console.error(chalk.red("Device code has expired. Please restart the login process."));
|
|
584
|
+
// return;
|
|
585
|
+
// default:
|
|
586
|
+
// spinner.stop();
|
|
587
|
+
// logger.error(chalk.red(`Error: ${error.error_description}`));
|
|
588
|
+
// process.exit(1);
|
|
589
|
+
// }
|
|
590
|
+
// }
|
|
591
|
+
// } catch (error) {
|
|
592
|
+
// spinner.stop();
|
|
593
|
+
// logger.error(chalk.red("Polling failed:"), error);
|
|
594
|
+
// process.exit(1);
|
|
595
|
+
// }
|
|
596
|
+
// setTimeout(poll, pollingInterval * 1000);
|
|
597
|
+
// };
|
|
598
|
+
// z.set
|
|
599
|
+
// });
|
|
600
|
+
// }
|
|
601
|
+
|
|
602
|
+
// // CLI command
|
|
603
|
+
// export const login = new Command("login")
|
|
604
|
+
// .description("Login to Mikasa CLI using GitHub OAuth.")
|
|
605
|
+
// .option("--server-url <url>", "Authentication server URL")
|
|
606
|
+
// .option("--client-id <id>", "GitHub OAuth Client ID")
|
|
607
|
+
// .action(loginAction);
|
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 } from "commander";
|
|
7
|
+
import { login, logout, whoami } from "./commands/auth/login.js";
|
|
8
|
+
import { wakeUp } from "./commands/ai/wakeUp.js";
|
|
9
|
+
|
|
10
|
+
dotenv.config();
|
|
11
|
+
|
|
12
|
+
async function main() {
|
|
13
|
+
console.log(
|
|
14
|
+
chalk.green(
|
|
15
|
+
figlet.textSync("Mikasa CLI", {
|
|
16
|
+
font: "Standard",
|
|
17
|
+
horizontalLayout: "default",
|
|
18
|
+
})
|
|
19
|
+
)
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
console.log(chalk.red("Welcome to Mikasa CLI!\n"));
|
|
23
|
+
|
|
24
|
+
const program = new Command("Mikasa");
|
|
25
|
+
|
|
26
|
+
program
|
|
27
|
+
.name("mikasa")
|
|
28
|
+
.version("1.0.0")
|
|
29
|
+
.description(
|
|
30
|
+
"Mikasa CLI - A powerful AI command line tool.\n" +
|
|
31
|
+
"Built by Suman Kayal.\n" +
|
|
32
|
+
"GitHub: https://github.com/SUMANKAYALS\n" +
|
|
33
|
+
"LinkedIn: https://www.linkedin.com/in/suman-kayal10/\n"
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
// 🔥 COMMANDS
|
|
37
|
+
program
|
|
38
|
+
.addCommand(login);
|
|
39
|
+
program.addCommand(logout);
|
|
40
|
+
|
|
41
|
+
program.addCommand(whoami);
|
|
42
|
+
program.addCommand(wakeUp);
|
|
43
|
+
|
|
44
|
+
// Default action → show help
|
|
45
|
+
program.action(() => {
|
|
46
|
+
program.help();
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
// 🔥 REQUIRED
|
|
50
|
+
program.parse(process.argv);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
main().catch((err) => {
|
|
54
|
+
console.error(chalk.red("Error starting Mikasa CLI:"), err);
|
|
55
|
+
process.exit(1);
|
|
56
|
+
});
|