mirror of
https://github.com/tcsenpai/vvk.git
synced 2025-06-10 04:58:06 +00:00
release
This commit is contained in:
parent
13ffdff9a6
commit
19e3e38571
4
.gitignore
vendored
4
.gitignore
vendored
@ -1,3 +1,7 @@
|
|||||||
node_modules
|
node_modules
|
||||||
.env
|
.env
|
||||||
dist
|
dist
|
||||||
|
*.app
|
||||||
|
*.exe
|
||||||
|
*.pkg
|
||||||
|
*.blob
|
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "vvk",
|
"name": "vvk",
|
||||||
"version": "1.0.0",
|
"version": "1.1.0",
|
||||||
"description": "",
|
"description": "A command-line interface tool that converts natural language instructions into shell commands using OpenAI's GPT-4.",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "rimraf dist && webpack"
|
"build": "rimraf dist && webpack"
|
||||||
@ -22,6 +22,7 @@
|
|||||||
"webpack-cli": "^6.0.1"
|
"webpack-cli": "^6.0.1"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"axios": "^1.8.1",
|
||||||
"child_process": "^1.0.2",
|
"child_process": "^1.0.2",
|
||||||
"openai": "^4.85.4",
|
"openai": "^4.85.4",
|
||||||
"readline": "^1.3.0"
|
"readline": "^1.3.0"
|
||||||
|
30
pnpm-lock.yaml
generated
30
pnpm-lock.yaml
generated
@ -8,6 +8,9 @@ importers:
|
|||||||
|
|
||||||
.:
|
.:
|
||||||
dependencies:
|
dependencies:
|
||||||
|
axios:
|
||||||
|
specifier: ^1.8.1
|
||||||
|
version: 1.8.1
|
||||||
child_process:
|
child_process:
|
||||||
specifier: ^1.0.2
|
specifier: ^1.0.2
|
||||||
version: 1.0.2
|
version: 1.0.2
|
||||||
@ -242,6 +245,9 @@ packages:
|
|||||||
asynckit@0.4.0:
|
asynckit@0.4.0:
|
||||||
resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
|
resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
|
||||||
|
|
||||||
|
axios@1.8.1:
|
||||||
|
resolution: {integrity: sha512-NN+fvwH/kV01dYUQ3PTOZns4LWtWhOFCAhQ/pHb88WQ1hNe5V/dvFwc4VJcDL11LT9xSX0QtsR8sWUuyOuOq7g==}
|
||||||
|
|
||||||
balanced-match@1.0.2:
|
balanced-match@1.0.2:
|
||||||
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
|
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
|
||||||
|
|
||||||
@ -412,6 +418,15 @@ packages:
|
|||||||
resolution: {integrity: sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==}
|
resolution: {integrity: sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
|
follow-redirects@1.15.9:
|
||||||
|
resolution: {integrity: sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==}
|
||||||
|
engines: {node: '>=4.0'}
|
||||||
|
peerDependencies:
|
||||||
|
debug: '*'
|
||||||
|
peerDependenciesMeta:
|
||||||
|
debug:
|
||||||
|
optional: true
|
||||||
|
|
||||||
foreground-child@3.3.1:
|
foreground-child@3.3.1:
|
||||||
resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==}
|
resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==}
|
||||||
engines: {node: '>=14'}
|
engines: {node: '>=14'}
|
||||||
@ -639,6 +654,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==}
|
resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
|
proxy-from-env@1.1.0:
|
||||||
|
resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==}
|
||||||
|
|
||||||
randombytes@2.1.0:
|
randombytes@2.1.0:
|
||||||
resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==}
|
resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==}
|
||||||
|
|
||||||
@ -1098,6 +1116,14 @@ snapshots:
|
|||||||
|
|
||||||
asynckit@0.4.0: {}
|
asynckit@0.4.0: {}
|
||||||
|
|
||||||
|
axios@1.8.1:
|
||||||
|
dependencies:
|
||||||
|
follow-redirects: 1.15.9
|
||||||
|
form-data: 4.0.2
|
||||||
|
proxy-from-env: 1.1.0
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- debug
|
||||||
|
|
||||||
balanced-match@1.0.2: {}
|
balanced-match@1.0.2: {}
|
||||||
|
|
||||||
brace-expansion@2.0.1:
|
brace-expansion@2.0.1:
|
||||||
@ -1241,6 +1267,8 @@ snapshots:
|
|||||||
|
|
||||||
flat@5.0.2: {}
|
flat@5.0.2: {}
|
||||||
|
|
||||||
|
follow-redirects@1.15.9: {}
|
||||||
|
|
||||||
foreground-child@3.3.1:
|
foreground-child@3.3.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
cross-spawn: 7.0.6
|
cross-spawn: 7.0.6
|
||||||
@ -1436,6 +1464,8 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
find-up: 4.1.0
|
find-up: 4.1.0
|
||||||
|
|
||||||
|
proxy-from-env@1.1.0: {}
|
||||||
|
|
||||||
randombytes@2.1.0:
|
randombytes@2.1.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
safe-buffer: 5.2.1
|
safe-buffer: 5.2.1
|
||||||
|
@ -17,14 +17,14 @@ export function configCommand(args: string[]) {
|
|||||||
}
|
}
|
||||||
const updated = updateConfig({ [key]: parsedValue });
|
const updated = updateConfig({ [key]: parsedValue });
|
||||||
console.log('Configuration updated:', updated);
|
console.log('Configuration updated:', updated);
|
||||||
} else if (command === 'get') {
|
} else if (command === 'get' || command === 'list') {
|
||||||
const config = loadConfig();
|
let config = loadConfig();
|
||||||
console.log('Current configuration:', config);
|
if (config.key && config.key.length > 0) {
|
||||||
} else if (command === 'list') {
|
config.key = '********';
|
||||||
const config = loadConfig();
|
}
|
||||||
console.log('Current configuration:', config);
|
console.log('Current configuration:', config);
|
||||||
} else {
|
} else {
|
||||||
console.error('Unknown config command. Use "set" or "get".');
|
console.error('Unknown config command. Use "set" or "list".');
|
||||||
}
|
}
|
||||||
process.exit(0);
|
process.exit(0);
|
||||||
}
|
}
|
||||||
|
@ -4,12 +4,16 @@ import os from 'os';
|
|||||||
|
|
||||||
export interface Config {
|
export interface Config {
|
||||||
openaiApiKey: string;
|
openaiApiKey: string;
|
||||||
|
userId: string;
|
||||||
|
key: string;
|
||||||
confirmCommand: boolean;
|
confirmCommand: boolean;
|
||||||
defaultConfirmation: 'y' | 'n';
|
defaultConfirmation: 'y' | 'n';
|
||||||
}
|
}
|
||||||
|
|
||||||
const DEFAULT_CONFIG: Config = {
|
const DEFAULT_CONFIG: Config = {
|
||||||
openaiApiKey: '',
|
openaiApiKey: '',
|
||||||
|
userId: '',
|
||||||
|
key: '',
|
||||||
confirmCommand: true,
|
confirmCommand: true,
|
||||||
defaultConfirmation: 'y',
|
defaultConfirmation: 'y',
|
||||||
};
|
};
|
||||||
|
@ -1,14 +1,13 @@
|
|||||||
import OpenAI from 'openai';
|
import OpenAI from 'openai';
|
||||||
import { loadConfig } from './config';
|
import { loadConfig } from './config';
|
||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
export async function generateCommand(input: string) {
|
export async function generateCommand(input: string) {
|
||||||
const { openaiApiKey } = loadConfig();
|
const { openaiApiKey, key, userId } = loadConfig();
|
||||||
|
|
||||||
if (!openaiApiKey) {
|
let command;
|
||||||
console.log("Couldn't generage command: Set your OpenAI API Key with vvk config set openaiApiKey <your_api_key>");
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (openaiApiKey && openaiApiKey.length > 1) {
|
||||||
const openai = new OpenAI({ apiKey: openaiApiKey });
|
const openai = new OpenAI({ apiKey: openaiApiKey });
|
||||||
|
|
||||||
const response = await openai.chat.completions.create({
|
const response = await openai.chat.completions.create({
|
||||||
@ -23,8 +22,21 @@ export async function generateCommand(input: string) {
|
|||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
const command = response.choices[0]?.message?.content?.trim();
|
command = response.choices[0]?.message?.content?.trim();
|
||||||
|
} else if (key?.length > 1 && userId?.length > 1) {
|
||||||
|
const response = await axios.post('https://vvk.ai/api/command', {
|
||||||
|
input,
|
||||||
|
key,
|
||||||
|
userId,
|
||||||
|
});
|
||||||
|
|
||||||
|
command = response.data.command;
|
||||||
|
} else {
|
||||||
|
console.log(
|
||||||
|
"Couldn't generage command: Set your OpenAI API Key with vvk config set openaiApiKey <your_api_key> or log in with vvk login"
|
||||||
|
);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
if (!command) {
|
if (!command) {
|
||||||
console.log("Couldn't generate a command.");
|
console.log("Couldn't generate a command.");
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
|
30
src/index.ts
30
src/index.ts
@ -1,10 +1,14 @@
|
|||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
// remove warnings in console
|
||||||
|
process.removeAllListeners('warning');
|
||||||
|
|
||||||
import { exec } from 'child_process';
|
import { exec } from 'child_process';
|
||||||
import readline from 'readline';
|
import readline from 'readline';
|
||||||
import { generateCommand } from './generate-command';
|
import { generateCommand } from './generate-command';
|
||||||
import { loadConfig } from './config';
|
import { loadConfig } from './config';
|
||||||
import { configCommand } from './config-command';
|
import { configCommand } from './config-command';
|
||||||
|
import { login, logout } from './login';
|
||||||
|
|
||||||
const config = loadConfig();
|
const config = loadConfig();
|
||||||
|
|
||||||
@ -14,17 +18,31 @@ const userInput = args.join(' ');
|
|||||||
|
|
||||||
if (args[0] === 'config') {
|
if (args[0] === 'config') {
|
||||||
configCommand(args);
|
configCommand(args);
|
||||||
}
|
} else if (args[0] === 'help' && args.length === 1) {
|
||||||
|
console.log(`
|
||||||
if ((args[0] === '--version' || args[0] === '-v') && args.length === 1) {
|
Available commands:
|
||||||
console.log('1.0.0');
|
vvk <command> : Generate and execute a command
|
||||||
|
vvk config set ... : Set configuration options
|
||||||
|
vvk login : Log in to your account
|
||||||
|
vvk logout : Log out of your account
|
||||||
|
vvk --version : Show version
|
||||||
|
`);
|
||||||
process.exit(0);
|
process.exit(0);
|
||||||
}
|
} else if ((args[0] === '--version' || args[0] === '-v') && args.length === 1) {
|
||||||
|
console.log('1.0.0');
|
||||||
|
} else if (args[0] === 'login' && args.length === 1) {
|
||||||
|
login();
|
||||||
|
} else if (args[0] === 'logout' && args.length === 1) {
|
||||||
|
logout();
|
||||||
|
} else {
|
||||||
if (!userInput) {
|
if (!userInput) {
|
||||||
console.log('Usage: vvk <your natural language command>');
|
console.log('Usage: vvk <your natural language command>');
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
processCommand(userInput);
|
||||||
|
}
|
||||||
|
|
||||||
// Function to prompt for confirmation
|
// Function to prompt for confirmation
|
||||||
function confirmExecution(command: string) {
|
function confirmExecution(command: string) {
|
||||||
// If confirmation is disabled, use the default confirmation setting
|
// If confirmation is disabled, use the default confirmation setting
|
||||||
@ -87,5 +105,3 @@ async function processCommand(input: string) {
|
|||||||
|
|
||||||
confirmExecution(command);
|
confirmExecution(command);
|
||||||
}
|
}
|
||||||
|
|
||||||
processCommand(userInput);
|
|
||||||
|
66
src/login.ts
Normal file
66
src/login.ts
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
import { randomUUID } from 'crypto';
|
||||||
|
import axios from 'axios';
|
||||||
|
import { updateConfig } from './config';
|
||||||
|
import { spawn } from 'child_process';
|
||||||
|
import os from 'os';
|
||||||
|
|
||||||
|
async function openUrl(url: string) {
|
||||||
|
const platform = os.platform();
|
||||||
|
|
||||||
|
if (platform === 'win32') {
|
||||||
|
spawn('cmd', ['/c', 'start', url], { detached: true, stdio: 'ignore' });
|
||||||
|
} else if (platform === 'darwin') {
|
||||||
|
spawn('open', [url], { detached: true, stdio: 'ignore' });
|
||||||
|
} else {
|
||||||
|
spawn('xdg-open', [url], { detached: true, stdio: 'ignore' });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function login() {
|
||||||
|
// const { default: open } = await import('open');
|
||||||
|
const loginCode = randomUUID();
|
||||||
|
const loginUrl = `http://vvk.ai/api/cli-login?c=${loginCode}`;
|
||||||
|
await openUrl(loginUrl);
|
||||||
|
|
||||||
|
console.log('🌐 Please log in via the browser. Waiting for authentication...');
|
||||||
|
|
||||||
|
let key, userId;
|
||||||
|
const startTime = Date.now();
|
||||||
|
const timeoutMs = 60 * 1000; // 60 seconds timeout
|
||||||
|
|
||||||
|
while (!key) {
|
||||||
|
// Check if timeout has been reached
|
||||||
|
if (Date.now() - startTime > timeoutMs) {
|
||||||
|
console.log('❌ Login timed out after 60 seconds. Please try again.');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
await new Promise((res) => setTimeout(res, 3000)); // Wait 3 seconds between polls
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { data } = await axios.get(`https://vvk.ai/api/cli-auth?c=${loginCode}`);
|
||||||
|
if (data.key && data.userId) {
|
||||||
|
key = data.key;
|
||||||
|
userId = data.userId;
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
// Ignore errors while waiting
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateConfig({ key, userId });
|
||||||
|
console.log('✅ Logged in successfully!');
|
||||||
|
process.exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function logout() {
|
||||||
|
try {
|
||||||
|
// Clear user credentials from config
|
||||||
|
updateConfig({ key: '', userId: '' });
|
||||||
|
console.log('✅ Logged out successfully!');
|
||||||
|
process.exit(0);
|
||||||
|
} catch (err: any) {
|
||||||
|
console.error('❌ Error during logout:', err.message);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user