pulse-cli-divakar 0.0.0-stage → 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.
@@ -0,0 +1,157 @@
1
+ import { cancel, confirm, intro, isCancel, outro, select } 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
+
6
+ import chalk from "chalk";
7
+ import { Command, program } from "commander";
8
+ import fs from "node:fs/promises";
9
+ import open from "open";
10
+ import os from "os";
11
+ import path from "path";
12
+ import yoctoSpinner from "yocto-spinner";
13
+ import * as z from "zod/v4";
14
+ import dotenv from "dotenv";
15
+
16
+ import { prisma } from "../../../lib/db.js";
17
+ import { clearStoredToken, getStoredToken, isTokenExpired, requireAuth, storeToken } from "../../../lib/token.js";
18
+ import { pollForToken } from "../utils/polling.js";
19
+
20
+ dotenv.config();
21
+
22
+
23
+ // To store in local computer using below 4 variables
24
+
25
+
26
+ export const URL = process.env.BACK_END || "https://pulse-4t51.onrender.com";
27
+ const CLIENT_ID = process.env.GITHUB_CLIENT_ID || "Ov23lijaPrTkzWootiuZ"
28
+ // create a .better-auth folder in home directry not actual folder create just a link like C:\Users\YourUser\.better-auth
29
+ export const CONFIG_DIR = path.join(os.homedir(), ".better-auth");
30
+ // save to token.json
31
+ export const TOKEN_FILE = path.join(CONFIG_DIR, "token.json");
32
+
33
+ export async function loginAction() {
34
+ const options = z.object({
35
+ serverUrl: z.string().optional(),
36
+ clientId: z.string().optional(),
37
+ });
38
+
39
+ const serverUrl = options.serverUrl || URL;
40
+ const clientId = options.clientId || CLIENT_ID;
41
+
42
+
43
+ intro(chalk.bold("šŸ” Auth CLI Login."));
44
+
45
+ const existingToken = getStoredToken();
46
+ const expired = isTokenExpired();
47
+
48
+ if (existingToken && !expired) {
49
+ const shouldReAuth = await confirm({
50
+ message: "You are already loggedIn. Do You want to login Again?",
51
+ initialValue: false,
52
+ });
53
+ // User choose No OR Cancel the command
54
+ if (isCancel(shouldReAuth) || !shouldReAuth) {
55
+ cancel("Login Cancelled..");
56
+ process.exit(0);
57
+ }
58
+ }
59
+
60
+ const authClient = createAuthClient({
61
+ baseURL: serverUrl,
62
+ plugins: [deviceAuthorizationClient()],
63
+ });
64
+
65
+ const spinner = yoctoSpinner({ text: "Requesting device authorization..." });
66
+ spinner.start();
67
+
68
+ try {
69
+ const { data, error } = await authClient.device.code({
70
+ client_id: clientId,
71
+ scope: "openid profile email",
72
+ });
73
+ spinner.stop();
74
+
75
+ if (error || !data) {
76
+ logger.error(
77
+ `Failed to request device authorization: ${error.error_description}`,
78
+ );
79
+ process.exit(1);
80
+ }
81
+
82
+ const {
83
+ device_code,
84
+ user_code,
85
+ verification_uri,
86
+ verification_uri_complete,
87
+ expires_in,
88
+ interval,
89
+ } = data;
90
+
91
+ console.log(chalk.cyan("Device authorization is required."));
92
+
93
+ console.log(
94
+ `Please visit ${chalk.underline.blue(verification_uri || verification_uri_complete)} `,
95
+ );
96
+
97
+ console.log(`Enter Code ${chalk.bold.green(user_code)}`);
98
+
99
+ const shouldOpen = await confirm({
100
+ message: "Open Browser automatically",
101
+ initialValue: true,
102
+ });
103
+
104
+ if (!isCancel(shouldOpen) && shouldOpen) {
105
+ const urlToOpen = verification_uri_complete || verification_uri;
106
+ await open(urlToOpen);
107
+ }
108
+
109
+ console.log(
110
+ chalk.gray(
111
+ `Waiting for authorization (expires in ${Math.floor(
112
+ expires_in / 60,
113
+ )} minutes)....`,
114
+ ),
115
+ );
116
+
117
+ const token = await pollForToken(
118
+ authClient,
119
+ device_code,
120
+ clientId,
121
+ interval,
122
+ );
123
+
124
+ if(token){
125
+ const saved=await storeToken(token)
126
+ if (!saved) {
127
+ console.log(
128
+ chalk.yellow("\nāš ļø Warning: Could not save authentication token.")
129
+ );
130
+ console.log(
131
+ chalk.yellow("You may need to login again on next use.")
132
+ );
133
+ }
134
+
135
+ outro(chalk.green("Login successfull!"))
136
+
137
+ console.log(chalk.gray(`\n Token Saved to: ${TOKEN_FILE}`))
138
+ console.log(chalk.gray("You can now use Pulse Commands without logging-in again.\n "))
139
+
140
+ }
141
+
142
+ } catch (error) {
143
+ spinner.stop();
144
+ console.error(chalk.red("\nLogin failed:"), error.message);
145
+ process.exit(1);
146
+ }
147
+ }
148
+
149
+
150
+ export const login = new Command("login")
151
+ .description("Login to Better-Auth")
152
+ .option("--server-url <url>", "The Better Auth server URL", URL)
153
+ .option("--client-id <id>", "The OAuth client ID", CLIENT_ID)
154
+ .action(loginAction)
155
+
156
+
157
+
@@ -0,0 +1,42 @@
1
+ import { confirm, intro, isCancel, outro } from "@clack/prompts";
2
+ import { clearStoredToken, getStoredToken } from "../../../lib/token.js";
3
+ import chalk from "chalk";
4
+ import { Command } from "commander";
5
+ import dotenv from "dotenv";
6
+
7
+
8
+
9
+ dotenv.config();
10
+
11
+ export async function logoutAction() {
12
+ intro(chalk.bold("šŸ‘‹Logout"));
13
+
14
+ const token=await getStoredToken();
15
+ if(!token){
16
+ console.log(chalk.yellow("You're not logged in.Please LogIn First"))
17
+ process.exit(0);
18
+ }
19
+ const shouldLogout=await confirm({
20
+ message:"Are you sure , do you want to logout?",
21
+ initialValue:false
22
+ })
23
+
24
+ if(isCancel(shouldLogout) || !shouldLogout){
25
+ cancel("Logout Cancelled")
26
+ process.exit(0)
27
+ }
28
+
29
+ const cleared=await clearStoredToken();
30
+ if(cleared){
31
+ outro(chalk.green("āœ… Successfully logged out!"))
32
+ }
33
+ else{
34
+ console.log(chalk.yellow("āŒ Could not clear token file."))
35
+ }
36
+ }
37
+
38
+
39
+
40
+ export const logout = new Command("logout")
41
+ .description("Logout and Clear stored credentials")
42
+ .action(logoutAction)
@@ -0,0 +1,44 @@
1
+ import chalk from "chalk";
2
+ import { prisma } from "../../../lib/db.js";
3
+ import { requireAuth } from "../../../lib/token.js";
4
+ import { Command } from "commander";
5
+ import { URL } from "./login.js";
6
+
7
+ export async function whoamiAction() {
8
+ const token=await requireAuth();
9
+ if(!token?.access_token){
10
+ console.log("No access token found.Please login");
11
+ process.exit(1);
12
+ }
13
+
14
+ const user=await prisma.user.findFirst({
15
+ where:{
16
+ sessions:{
17
+ some:{
18
+ token:token.access_token,
19
+ }
20
+ }
21
+ },
22
+ select:{
23
+ id:true,
24
+ name:true,
25
+ email:true,
26
+ image:true
27
+ }
28
+
29
+ })
30
+
31
+ console.log(
32
+ chalk.bold.greenBright(`\n šŸ‘¤User: ${user.name}
33
+ šŸ“© Email: ${user.email}
34
+ 🪪 ID: ${user.id}
35
+ `)
36
+ )
37
+
38
+ }
39
+
40
+
41
+ export const whoami = new Command("whoami")
42
+ .description("Show current authenticated user")
43
+ .option("--server-url <url>", "The Better Auth server URL", URL)
44
+ .action(whoamiAction)
@@ -0,0 +1,77 @@
1
+ import { logger } from "better-auth";
2
+ import chalk from "chalk";
3
+ import yoctoSpinner from "yocto-spinner";
4
+
5
+
6
+ export async function pollForToken(
7
+ authClient,
8
+ deviceCode,
9
+ clientId,
10
+ initialIntervalue,
11
+ ) {
12
+ let pollingInterval = initialIntervalue;
13
+ const spinner = yoctoSpinner({ text: "", color: "cyan" });
14
+
15
+ let dots = 0;
16
+
17
+ return new Promise((resolve, reject) => {
18
+ const poll = async () => {
19
+ dots = (dots + 1) % 4;
20
+ spinner.text = chalk.gray(
21
+ `Polling for authorization${".".repeat(dots)}${" ".repeat(3 - dots)}`,
22
+ );
23
+ if (!spinner.isSpinning) spinner.start();
24
+
25
+ try {
26
+ const { data, error } = await authClient.device.token({
27
+ grant_type: "urn:ietf:params:oauth:grant-type:device_code",
28
+ device_code: deviceCode,
29
+ client_id: clientId,
30
+ fetchOptions: {
31
+ headers: {
32
+ "user-agent": `My CLI`,
33
+ },
34
+ },
35
+ });
36
+
37
+ if (data?.access_token) {
38
+ console.log(
39
+ chalk.bold.yellow(`Your acess token: ${data.access_token}`),
40
+ );
41
+ spinner.stop();
42
+ resolve(data);
43
+ return;
44
+ } else if (error) {
45
+ switch (error.error) {
46
+ case "authorization_pending":
47
+ // Continue polling
48
+ break;
49
+ case "slow_down":
50
+ pollingInterval += 5;
51
+ break;
52
+ case "access_denied":
53
+ console.error("Access was denied by the user");
54
+ return;
55
+ case "expired_token":
56
+ console.error(
57
+ "The device code has expired. Please try again.",
58
+ );
59
+ return;
60
+ default:
61
+ spinner.stop();
62
+ logger.error(`Error: ${error.error_description}`);
63
+ process.exit(1);
64
+ }
65
+ }
66
+ } catch (error) {
67
+ spinner.stop();
68
+ logger.error(`Network error: ${error.message}`);
69
+ process.exit(1);
70
+ }
71
+
72
+ setTimeout(poll, pollingInterval * 1000);
73
+ };
74
+
75
+ setTimeout(poll , pollingInterval * 1000);
76
+ });
77
+ }
@@ -0,0 +1,46 @@
1
+ #!/usr/bin/env node
2
+
3
+ import dotenv from "dotenv"
4
+ import chalk from "chalk" //to add colours
5
+ import figlet from "figlet" //to add banners
6
+
7
+ import {Command} from "commander" //to create a command
8
+ import { login} from "./commands/auth/login.js"
9
+ import { logout } from "./commands/auth/logout.js"
10
+ import { whoami } from "./commands/auth/whoami.js"
11
+ import { wakeUp } from "./commands/ai/wakeup.js"
12
+
13
+
14
+ dotenv.config()
15
+
16
+ async function main() {
17
+
18
+ console.log(
19
+ chalk.greenBright(
20
+ figlet.textSync("Pulse CLI",{
21
+ font:"Standard",
22
+ horizontalLayout:"default"
23
+ })
24
+ )
25
+ )
26
+ console.log(chalk.magenta("A Cli Based AI application.\n"))
27
+
28
+ const program=new Command("pulse")
29
+ program.version("0.0.1")
30
+ .description("Pulse is a Cli based application")
31
+ .addCommand(login)
32
+ .addCommand(logout)
33
+ .addCommand(whoami)
34
+ .addCommand(wakeUp)
35
+
36
+
37
+ program.action(()=>{
38
+ program.help();
39
+ })
40
+ await program.parseAsync(process.argv);
41
+ }
42
+
43
+ main().catch((error)=>{
44
+ console.log(chalk.red("Error running Pulse Cli:"),error);
45
+ process.exit(1);
46
+ })
@@ -0,0 +1,170 @@
1
+ import { promises as fs } from "fs";
2
+ import path from "path";
3
+ import chalk from "chalk";
4
+ import { generateText, Output } from "ai";
5
+ import { z } from "zod";
6
+
7
+ const ApplicationSchema = z.object({
8
+ folderName: z.string().describe("Kebab-case folder name for the application"),
9
+ description: z.string().describe("Brief description of what was created"),
10
+ files: z
11
+ .array(
12
+ z.object({
13
+ path: z.string().describe("Relative file path (e.g., src/App.jsx)"),
14
+ content: z.string().describe("Complete file content"),
15
+ }),
16
+ )
17
+ .describe("All files needed for the application"),
18
+ setupCommands: z
19
+ .array(z.string())
20
+ .describe(
21
+ "Bash commands to setup and run (e.g., npm install, npm run dev)",
22
+ ),
23
+ });
24
+
25
+ function printSystem(message) {
26
+ console.log(message);
27
+ }
28
+
29
+ function displayFileTree(files, folderName) {
30
+ printSystem(chalk.cyan("\nšŸ“‚ Project Structure:"));
31
+ printSystem(chalk.white(`${folderName}/`));
32
+
33
+ const filesByDir = {};
34
+ files.forEach((file) => {
35
+ const parts = file.path.split("/");
36
+ const dir = parts.length > 1 ? parts.slice(0, -1).join("/") : "";
37
+
38
+ if (!filesByDir[dir]) {
39
+ filesByDir[dir] = [];
40
+ }
41
+ filesByDir[dir].push(parts[parts.length - 1]);
42
+ });
43
+
44
+ Object.keys(filesByDir)
45
+ .sort()
46
+ .forEach((dir) => {
47
+ if (dir) {
48
+ printSystem(chalk.white(`ā”œā”€ā”€ ${dir}/`));
49
+ filesByDir[dir].forEach((file) => {
50
+ printSystem(chalk.white(`│ └── ${file}`));
51
+ });
52
+ } else {
53
+ filesByDir[dir].forEach((file) => {
54
+ printSystem(chalk.white(`ā”œā”€ā”€ ${file}`));
55
+ });
56
+ }
57
+ });
58
+ }
59
+
60
+
61
+ async function createApplicationFiles(baseDir, folderName, files) {
62
+ const appDir = path.join(baseDir, folderName);
63
+
64
+ await fs.mkdir(appDir, { recursive: true });
65
+ printSystem(chalk.cyan(`\nšŸ“ Created directory: ${folderName}/`));
66
+
67
+ for (const file of files) {
68
+ const filePath = path.join(appDir, file.path);
69
+ const fileDir = path.dirname(filePath);
70
+
71
+ await fs.mkdir(fileDir, { recursive: true });
72
+ await fs.writeFile(filePath, file.content, 'utf8');
73
+ printSystem(chalk.green(` āœ“ ${file.path}`));
74
+ }
75
+
76
+ return appDir;
77
+ }
78
+
79
+
80
+ export async function generateApplication(
81
+ description,
82
+ aiService,
83
+ cwd = process.cwd(),
84
+ ) {
85
+ try {
86
+ printSystem(
87
+ chalk.cyan("\nšŸ¤– Agent Mode: Generating your application...\n"),
88
+ );
89
+ printSystem(chalk.gray(`Request: ${description}\n`));
90
+
91
+ printSystem(chalk.magenta("šŸ¤– Generating structured output...\n"));
92
+
93
+ const result = await generateText({
94
+ model: aiService.model,
95
+ output: Output.object({
96
+ schema: ApplicationSchema,
97
+ }),
98
+ prompt: `Create a complete, production-ready application for: ${description}
99
+
100
+ CRITICAL REQUIREMENTS:
101
+ 1. Generate ALL files needed for the application to run
102
+ 2. Include package.json with ALL dependencies and correct versions (if needed)
103
+ 3. Include README.md with setup instructions
104
+ 4. Include configuration files (.gitignore, etc.) if needed
105
+ 5. Write clean, well-commented, production-ready code
106
+ 6. Include error handling and input validation
107
+ 7. Use modern JavaScript/TypeScript best practices
108
+ 8. Make sure all imports and paths are correct
109
+ 9. NO PLACEHOLDERS - everything must be complete and working
110
+ 10. For simple HTML/CSS/JS projects, you can skip package.json if not needed
111
+
112
+ Provide:
113
+ - A meaningful kebab-case folder name
114
+ - All necessary files with complete content
115
+ - Setup commands (for example: cd folder, npm install, npm run dev OR just open index.html)
116
+ - Make it visually appealing and functional`,
117
+ });
118
+
119
+ const application = result.output;
120
+
121
+ printSystem(chalk.green(`\nāœ… Generated: ${application.folderName}\n`));
122
+ printSystem(chalk.gray(`Description: ${application.description}\n`));
123
+
124
+ if (!application.files || application.files.length === 0) {
125
+ throw new Error("No files were generated");
126
+ }
127
+
128
+ printSystem(chalk.green(`Files: ${application.files.length}\n`));
129
+
130
+ displayFileTree(application.files, application.folderName);
131
+
132
+ printSystem(chalk.cyan(`\nšŸ“ Creating files...\n`));
133
+
134
+ const appDir = await createApplicationFiles(
135
+ cwd,
136
+ application.folderName,
137
+ application.files,
138
+ );
139
+
140
+ printSystem(chalk.green.bold(`\n✨ Application created successfully!\n`));
141
+ printSystem(chalk.cyan(`šŸ“ Location: ${chalk.bold(appDir)}\n`));
142
+
143
+ if (application.setupCommands && application.setupCommands.length > 0) {
144
+ printSystem(chalk.cyan("šŸ“‹ Next Steps:\n"));
145
+ printSystem(chalk.white("```bash"));
146
+ application.setupCommands.forEach((cmd) => {
147
+ printSystem(chalk.white(cmd));
148
+ });
149
+ printSystem(chalk.white("```\n"));
150
+ } else {
151
+ printSystem(chalk.yellow("ā„¹ļø No setup commands provided\n"));
152
+ }
153
+
154
+ return {
155
+ folderName: application.folderName,
156
+ appDir,
157
+ files: application.files.map((f) => f.path),
158
+ commands: application.setupCommands || [],
159
+ success: true,
160
+ };
161
+ } catch (err) {
162
+ printSystem(
163
+ chalk.red(`\nāŒ Error generating application: ${err.message}\n`),
164
+ );
165
+ if (err.stack) {
166
+ printSystem(chalk.dim(err.stack + "\n"));
167
+ }
168
+ throw err;
169
+ }
170
+ }
@@ -0,0 +1,8 @@
1
+ import dotenv from "dotenv"
2
+ dotenv.config()
3
+
4
+ export const config={
5
+ googleApiKey:process.env.GOOGLE_GENERATIVE_AI_API_KEY,
6
+ model:process.env.ORBITAL_MODEL || "gemini-2.5-flash"
7
+
8
+ }
@@ -0,0 +1,99 @@
1
+ import {google} from "@ai-sdk/google"
2
+ import chalk from "chalk"
3
+
4
+
5
+ export const availableTools=[
6
+ {
7
+ id:"google_search",
8
+ name:"Google Search",
9
+ description:"Acess the latest information using Google Search.Usefull for current events,news ,and real-time information.",
10
+ getTool:()=>google.tools.googleSearch({}),
11
+ enabled:false,
12
+ },
13
+ {
14
+ id: 'code_execution',
15
+ name: 'Code Execution',
16
+ description: 'Generate and execute Python code to perform calculations, solve problems, or provide accurate information.',
17
+ getTool: () => google.tools.codeExecution({}),
18
+ enabled: false,
19
+ },
20
+ {
21
+ id: 'url_context',
22
+ name: 'URL Context',
23
+ description: 'Provide specific URLs that you want the model to analyze directly from the prompt. Supports up to 20 URLs per request.',
24
+ getTool: () => google.tools.urlContext({}),
25
+ enabled: false,
26
+ },
27
+
28
+ ]
29
+
30
+
31
+
32
+ export function getEnabledTools(){
33
+ const tools={}
34
+
35
+ try {
36
+ for(const toolConfig of availableTools){
37
+ if(toolConfig.enabled){
38
+ tools[toolConfig.id]=toolConfig.getTool()
39
+ }
40
+ }
41
+
42
+ if (Object.keys(tools).length > 0) {
43
+ console.log(chalk.gray(`[DEBUG] Enabled tools: ${Object.keys(tools).join(', ')}`));
44
+ } else {
45
+ console.log(chalk.yellow('[DEBUG] No tools enabled'));
46
+ }
47
+
48
+ return Object.keys(tools).length > 0 ? tools : undefined;
49
+
50
+ } catch (error) {
51
+ console.error(chalk.red('[ERROR] Failed to initialize tools:'), error.message);
52
+ console.error(chalk.yellow('Make sure you have @ai-sdk/google version 2.0+ installed'));
53
+ console.error(chalk.yellow('Run: npm install @ai-sdk/google@latest'));
54
+ return undefined;
55
+ }
56
+ }
57
+
58
+
59
+ export function togggleTool(toolid){
60
+ const tool=availableTools.find(t=>t.id===toolid)
61
+
62
+ if(tool){
63
+ tool.enabled = !tool.enabled;
64
+ console.log(chalk.gray(`[DEBUG] Tool ${toolId} toggled to ${tool.enabled}`));
65
+ return tool.enabled;
66
+ }
67
+ console.log(chalk.red(`[DEBUG] Tool ${toolId} not found`));
68
+ return false;
69
+ }
70
+
71
+
72
+ export function enableTools(toolIds) {
73
+ console.log(chalk.gray('[DEBUG] enableTools called with:'), toolIds);
74
+
75
+ availableTools.forEach(tool => {
76
+ const wasEnabled = tool.enabled;
77
+ tool.enabled = toolIds.includes(tool.id);
78
+
79
+ if (tool.enabled !== wasEnabled) {
80
+ console.log(chalk.gray(`[DEBUG] ${tool.id}: ${wasEnabled} → ${tool.enabled}`));
81
+ }
82
+ });
83
+
84
+ const enabledCount = availableTools.filter(t => t.enabled).length;
85
+ console.log(chalk.gray(`[DEBUG] Total tools enabled: ${enabledCount}/${availableTools.length}`));
86
+ }
87
+
88
+ export function getEnabledToolNames() {
89
+ const names = availableTools.filter(t => t.enabled).map(t => t.name);
90
+ console.log(chalk.gray('[DEBUG] getEnabledToolNames returning:'), names);
91
+ return names;
92
+ }
93
+
94
+ export function resetTools() {
95
+ availableTools.forEach(tool => {
96
+ tool.enabled = false;
97
+ });
98
+ console.log(chalk.gray('[DEBUG] All tools have been reset (disabled)'));
99
+ }