vynthen-vcode 1.0.0 → 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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/vcode.js +222 -164
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vynthen-vcode",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "Vynthen AI Local Code Agent CLI",
5
5
  "main": "vcode.js",
6
6
  "bin": {
package/vcode.js CHANGED
@@ -1,75 +1,136 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- /**
4
- * V Code CLI — Local Bridge for Vynthen AI
5
- *
6
- * This CLI connects your local file system to V Code.
7
- * When running, V Code can read, edit, and create files on your machine.
8
- */
9
-
10
3
  const { WebSocket } = require('ws');
11
4
  const fs = require('fs');
12
5
  const path = require('path');
13
6
  const os = require('os');
14
7
  const { execSync } = require('child_process');
15
8
 
16
- const VYNTHEN_BASE_URL = process.env.VYNTHEN_API_URL || 'http://localhost:3000';
17
- const VYNTHEN_WS_URL = process.env.VYNTHEN_WS_URL || 'ws://localhost:3000/api/cli-bridge';
9
+ const VYNTHEN_BASE_URL = process.env.VYNTHEN_API_URL || 'https://vynthen.ai';
10
+ const VYNTHEN_WS_URL = process.env.VYNTHEN_WS_URL || 'wss://vynthen.ai/api/cli-bridge';
18
11
  const TOKEN_PATH = path.join(os.homedir(), '.vcode_token');
19
12
 
13
+ // ── ANSI Colors ───────────────────────────────────────────────────────────
14
+ const C = {
15
+ reset: '\x1b[0m',
16
+ bold: '\x1b[1m',
17
+ dim: '\x1b[2m',
18
+ cyan: '\x1b[38;2;60;210;255m',
19
+ violet: '\x1b[38;2;167;139;250m',
20
+ orange: '\x1b[38;2;251;146;60m',
21
+ green: '\x1b[38;2;52;211;153m',
22
+ red: '\x1b[38;2;248;113;113m'
23
+ };
24
+
25
+ const ANIMATION_FRAMES = [
26
+ [`╔══════════╗`, `║ / \\ ║`, `║ / ◠◠ \\ ║`, `║ / ● ● \\║`, `║ △──────△ ║`, `╚══════════╝`],
27
+ [`╔══════════╗`, `║ / \\ ║`, `║ / ◠◠ \\ ║`, `║ / ⊙ ⊙ \\║`, `║ △──────△ ║`, `╚══════════╝`],
28
+ [`╔══════════╗`, `║ / \\ ║`, `║ / ◠◠ \\ ║`, `║ / - - \\║`, `║ △──────△ ║`, `╚══════════╝`]
29
+ ];
30
+
31
+ function drawAvatar(frameIdx, text = '') {
32
+ const f = ANIMATION_FRAMES[frameIdx % ANIMATION_FRAMES.length];
33
+ console.clear();
34
+ console.log(`\n${C.violet}${C.bold} V CODE COMPANION${C.reset}`);
35
+ console.log(`${C.cyan}${f[0]}`);
36
+ console.log(`${f[1]}`);
37
+ console.log(`${f[2]} ${C.orange}${text}${C.cyan}`);
38
+ console.log(`${f[3]}`);
39
+ console.log(`${f[4]}`);
40
+ console.log(`${f[5]}${C.reset}\n`);
41
+ }
42
+
43
+ async function playIntro(text) {
44
+ return new Promise(resolve => {
45
+ let tick = 0;
46
+ const interval = setInterval(() => {
47
+ drawAvatar(tick, text);
48
+ tick++;
49
+ if (tick > 5) {
50
+ clearInterval(interval);
51
+ resolve();
52
+ }
53
+ }, 200);
54
+ });
55
+ }
56
+
20
57
  // ── Login Command ────────────────────────────────────────────────────────
21
58
  if (process.argv[2] === 'login') {
22
59
  const readline = require('readline');
23
- const rl = readline.createInterface({
24
- input: process.stdin,
25
- output: process.stdout
60
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
61
+
62
+ const askEmail = () => new Promise(r => {
63
+ console.log(`${C.cyan}?${C.reset} ${C.bold}Email:${C.reset}`);
64
+ rl.question(' > ', ans => r(ans));
26
65
  });
27
66
 
28
- rl.question('Email: ', (email) => {
29
- // Simple prompt for password, masking with *
30
- const rlPass = readline.createInterface({
31
- input: process.stdin,
32
- output: process.stdout
33
- });
67
+ const askPass = () => new Promise(r => {
68
+ console.log(`\n${C.cyan}?${C.reset} ${C.bold}Password:${C.reset} ${C.dim}(Invisible securely)${C.reset}`);
69
+ process.stdout.write(' > ');
70
+ let password = '';
34
71
 
35
- rlPass.stdoutMuted = true;
36
- rlPass._writeToOutput = function _writeToOutput(stringToWrite) {
37
- if (rlPass.stdoutMuted) {
38
- rlPass.output.write(stringToWrite === '\\r\\n' || stringToWrite === '\\n' ? '\\n' : '*');
39
- } else {
40
- rlPass.output.write(stringToWrite);
72
+ process.stdin.setRawMode(true);
73
+ process.stdin.resume();
74
+ process.stdin.setEncoding('utf8');
75
+
76
+ const listener = (chunk) => {
77
+ chunk = String(chunk);
78
+ switch (chunk) {
79
+ case '\n':
80
+ case '\r':
81
+ case '\u0004':
82
+ process.stdin.removeListener('data', listener);
83
+ process.stdin.setRawMode(false);
84
+ process.stdin.pause();
85
+ console.log('');
86
+ r(password);
87
+ break;
88
+ case '\u0003': // Ctrl+C
89
+ process.exit();
90
+ break;
91
+ case '\x7f':
92
+ case '\b':
93
+ password = password.slice(0, -1);
94
+ break;
95
+ default:
96
+ password += chunk;
97
+ break;
41
98
  }
42
99
  };
100
+ process.stdin.on('data', listener);
101
+ });
43
102
 
44
- rlPass.question('Password: ', async (password) => {
45
- rlPass.close();
46
- rl.close();
47
- console.log('\\nLogging in...');
48
-
49
- try {
50
- const fetch = globalThis.fetch || require('node-fetch');
51
- const res = await fetch(`${VYNTHEN_BASE_URL}/api/auth/cli-login`, {
52
- method: 'POST',
53
- headers: { 'Content-Type': 'application/json' },
54
- body: JSON.stringify({ email: email.trim(), password })
55
- });
56
-
57
- if (!res.ok) {
58
- const err = await res.json().catch(()=>({}));
59
- console.error('\\x1b[31m%s\\x1b[0m', `Login failed: ${err.error || res.statusText}`);
60
- process.exit(1);
61
- }
103
+ (async () => {
104
+ await playIntro("Initializing Secure Link...");
105
+ const email = await askEmail();
106
+ const password = await askPass();
107
+ rl.close();
62
108
 
63
- const data = await res.json();
64
- fs.writeFileSync(TOKEN_PATH, data.token, { mode: 0o600 });
65
- console.log('\\x1b[32m%s\\x1b[0m', '✓ Successfully logged in. Token saved locally.');
66
- process.exit(0);
67
- } catch (err) {
68
- console.error('\\x1b[31m%s\\x1b[0m', `Connection error: ${err.message}`);
109
+ console.log(`${C.violet}\n⟳ Authenticating with Vynthen...${C.reset}`);
110
+
111
+ try {
112
+ const fetch = globalThis.fetch || require('node-fetch');
113
+ const res = await fetch(`${VYNTHEN_BASE_URL}/api/auth/cli-login`, {
114
+ method: 'POST',
115
+ headers: { 'Content-Type': 'application/json' },
116
+ body: JSON.stringify({ email: email.trim(), password })
117
+ });
118
+
119
+ if (!res.ok) {
120
+ const err = await res.json().catch(()=>({}));
121
+ console.error(`\n${C.red}✖ Login failed:${C.reset} ${err.error || res.statusText}`);
69
122
  process.exit(1);
70
123
  }
71
- });
72
- });
124
+
125
+ const data = await res.json();
126
+ fs.writeFileSync(TOKEN_PATH, data.token, { mode: 0o600 });
127
+ console.log(`\n${C.green}✔ Successfully logged in. Secure Token saved.${C.reset}`);
128
+ process.exit(0);
129
+ } catch (err) {
130
+ console.error(`\n${C.red}✖ Connection error:${C.reset} ${err.message}`);
131
+ process.exit(1);
132
+ }
133
+ })();
73
134
  return;
74
135
  }
75
136
 
@@ -79,136 +140,133 @@ let token = process.env.VYNTHEN_CLI_TOKEN;
79
140
  if (!token) {
80
141
  try {
81
142
  token = fs.readFileSync(TOKEN_PATH, 'utf8').trim();
82
- } catch (err) {
83
- // file not found or unreadable
84
- }
143
+ } catch (err) {}
85
144
  }
86
145
 
87
146
  if (!token && !process.argv.includes('--anonymous')) {
88
- console.error('\\x1b[31m%s\\x1b[0m', 'Error: Not logged in.');
89
- console.log('Run `vcode login` or export VYNTHEN_CLI_TOKEN.');
147
+ console.error(`${C.red}✖ Error: Not logged in.${C.reset}`);
148
+ console.log(`Run \`${C.cyan}Vcode login${C.reset}\` or export VYNTHEN_CLI_TOKEN.`);
90
149
  process.exit(1);
91
150
  }
92
151
 
93
- console.log('\\x1b[36m%s\\x1b[0m', 'Initializing V Code CLI Bridge...');
94
- const ws = new WebSocket(`${VYNTHEN_WS_URL}?token=${token || 'anonymous'}`);
152
+ (async () => {
153
+ if (process.argv[2] !== '--silent') {
154
+ await playIntro("Awaiting AI Orders...");
155
+ }
156
+ console.log(`${C.violet}≈ Establishing secure bridge to Vynthen...${C.reset}`);
157
+ const ws = new WebSocket(`${VYNTHEN_WS_URL}?token=${token || 'anonymous'}`);
95
158
 
96
- // ── Handlers ─────────────────────────────────────────────────────────────
159
+ // ── Handlers ───────────────────────────────────────────────
160
+ const handleFileRead = (filepath) => {
161
+ try {
162
+ const absolutePath = path.resolve(process.cwd(), filepath);
163
+ const content = fs.readFileSync(absolutePath, 'utf8');
164
+ return { success: true, content };
165
+ } catch (err) {
166
+ return { success: false, error: err.message };
167
+ }
168
+ };
97
169
 
98
- const handleFileRead = (filepath) => {
99
- try {
100
- const absolutePath = path.resolve(process.cwd(), filepath);
101
- const content = fs.readFileSync(absolutePath, 'utf8');
102
- return { success: true, content };
103
- } catch (err) {
104
- return { success: false, error: err.message };
105
- }
106
- };
170
+ const handleFileWrite = (filepath, content) => {
171
+ try {
172
+ const absolutePath = path.resolve(process.cwd(), filepath);
173
+ fs.mkdirSync(path.dirname(absolutePath), { recursive: true });
174
+ fs.writeFileSync(absolutePath, content, 'utf8');
175
+ return { success: true };
176
+ } catch (err) {
177
+ return { success: false, error: err.message };
178
+ }
179
+ };
107
180
 
108
- const handleFileWrite = (filepath, content) => {
109
- try {
110
- const absolutePath = path.resolve(process.cwd(), filepath);
111
- fs.mkdirSync(path.dirname(absolutePath), { recursive: true });
112
- fs.writeFileSync(absolutePath, content, 'utf8');
113
- return { success: true };
114
- } catch (err) {
115
- return { success: false, error: err.message };
116
- }
117
- };
181
+ const handleCommand = (command) => {
182
+ try {
183
+ const output = execSync(command, { encoding: 'utf8', cwd: process.cwd(), timeout: 10000 });
184
+ return { success: true, output };
185
+ } catch (err) {
186
+ return { success: false, error: err.message, output: err.stdout ? err.stdout.toString() : '' };
187
+ }
188
+ };
118
189
 
119
- const handleCommand = (command) => {
120
- try {
121
- const output = execSync(command, { encoding: 'utf8', cwd: process.cwd(), timeout: 10000 });
122
- return { success: true, output };
123
- } catch (err) {
124
- return { success: false, error: err.message, output: err.stdout ? err.stdout.toString() : '' };
125
- }
126
- };
190
+ ws.on('open', () => {
191
+ console.log(`${C.green}✔ Connected to V Code AI.${C.reset}`);
192
+ console.log(`${C.dim}Listening in: ${process.cwd()}${C.reset}`);
193
+
194
+ ws.send(JSON.stringify({
195
+ type: 'init',
196
+ hostname: os.hostname(),
197
+ cwd: process.cwd()
198
+ }));
199
+ });
127
200
 
128
- // ── WebSocket Logic ──────────────────────────────────────────────────────
129
-
130
- ws.on('open', () => {
131
- console.log('\x1b[32m%s\x1b[0m', '✓ Connected to V Code.');
132
- console.log(`Listening in: ${process.cwd()}`);
133
-
134
- ws.send(JSON.stringify({
135
- type: 'init',
136
- hostname: require('os').hostname(),
137
- cwd: process.cwd()
138
- }));
139
- });
140
-
141
- const readline = require('readline');
142
- const rl = readline.createInterface({
143
- input: process.stdin,
144
- output: process.stdout
145
- });
146
-
147
- const askApproval = (question) => {
148
- return new Promise(resolve => {
149
- rl.question(`\x1b[33m[Authorization Required]\x1b[0m ${question} (y/N): `, answer => {
150
- resolve(answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes');
201
+ const readline = require('readline');
202
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
203
+
204
+ const askApproval = (question) => {
205
+ return new Promise(resolve => {
206
+ rl.question(`\n${C.orange}[Authorization]${C.reset} ${question} (y/N): `, answer => {
207
+ resolve(answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes');
208
+ });
151
209
  });
152
- });
153
- };
210
+ };
154
211
 
155
- ws.on('message', async (data) => {
156
- const req = JSON.parse(data);
157
- let response = { id: req.id, type: req.type };
158
-
159
- switch (req.type) {
160
- case 'read_file':
161
- console.log(`\x1b[34m[Agent]\x1b[0m Reading ${req.filepath}`);
162
- response.result = handleFileRead(req.filepath);
163
- break;
164
-
165
- case 'write_file':
166
- console.log(`\n\x1b[34m[Agent Request]\x1b[0m Write to \x1b[36m${req.filepath}\x1b[0m (${req.content.length} bytes)`);
167
- const writeApproved = await askApproval(`Allow Agent to modify ${req.filepath}?`);
168
- if (writeApproved) {
169
- response.result = handleFileWrite(req.filepath, req.content);
170
- console.log('\x1b[32m✓ Allowed\x1b[0m');
171
- } else {
172
- response.result = { success: false, error: 'User denied file write permission.' };
173
- console.log('\x1b[31m✕ Denied\x1b[0m');
174
- }
175
- break;
176
-
177
- case 'run_command':
178
- console.log(`\n\x1b[34m[Agent Request]\x1b[0m Execute command: \x1b[36m${req.command}\x1b[0m`);
179
- const cmdApproved = await askApproval(`Allow Agent to run this command?`);
180
- if (cmdApproved) {
181
- response.result = handleCommand(req.command);
182
- console.log('\x1b[32m✓ Allowed\x1b[0m');
183
- } else {
184
- response.result = { success: false, error: 'User denied command execution permission.' };
185
- console.log('\x1b[31m✕ Denied\x1b[0m');
186
- }
187
- break;
212
+ ws.on('message', async (data) => {
213
+ const req = JSON.parse(data);
214
+ let response = { id: req.id, type: req.type };
188
215
 
189
- case 'ping':
190
- response.result = { success: true };
191
- break;
216
+ switch (req.type) {
217
+ case 'read_file':
218
+ console.log(`${C.cyan}[Agent]${C.reset} Reading ${req.filepath}`);
219
+ response.result = handleFileRead(req.filepath);
220
+ break;
192
221
 
193
- default:
194
- console.error('\x1b[31m%s\x1b[0m', `Unknown command type: ${req.type}`);
195
- response.result = { success: false, error: 'Unknown command type' };
196
- }
222
+ case 'write_file':
223
+ console.log(`\n${C.violet}[Agent]${C.reset} Write to ${C.bold}${req.filepath}${C.reset} (${req.content.length} bytes)`);
224
+ const writeApproved = await askApproval(`Allow Agent to modify ${req.filepath}?`);
225
+ if (writeApproved) {
226
+ response.result = handleFileWrite(req.filepath, req.content);
227
+ console.log(`${C.green}✔ Allowed${C.reset}`);
228
+ } else {
229
+ response.result = { success: false, error: 'User denied permission.' };
230
+ console.log(`${C.red}✖ Denied${C.reset}`);
231
+ }
232
+ break;
233
+
234
+ case 'run_command':
235
+ console.log(`\n${C.violet}[Agent]${C.reset} Execute command: ${C.bold}${req.command}${C.reset}`);
236
+ const cmdApproved = await askApproval(`Allow Agent to run this command?`);
237
+ if (cmdApproved) {
238
+ response.result = handleCommand(req.command);
239
+ console.log(`${C.green}✔ Allowed${C.reset}`);
240
+ } else {
241
+ response.result = { success: false, error: 'User denied permission.' };
242
+ console.log(`${C.red}✖ Denied${C.reset}`);
243
+ }
244
+ break;
197
245
 
198
- ws.send(JSON.stringify(response));
199
- });
246
+ case 'ping':
247
+ response.result = { success: true };
248
+ break;
200
249
 
201
- ws.on('close', () => {
202
- console.log('\x1b[33m%s\x1b[0m', 'Connection closed. V Code CLI is exiting.');
203
- process.exit(0);
204
- });
250
+ default:
251
+ console.error(`${C.red}Unknown command type: ${req.type}${C.reset}`);
252
+ response.result = { success: false, error: 'Unknown command type' };
253
+ }
205
254
 
206
- ws.on('error', (err) => {
207
- console.error('\x1b[31m%s\x1b[0m', `WebSocket Error: ${err.message}`);
208
- });
255
+ ws.send(JSON.stringify(response));
256
+ });
257
+
258
+ ws.on('close', () => {
259
+ console.log(`${C.orange}\nConnection closed.${C.reset} V Code exiting.`);
260
+ process.exit(0);
261
+ });
209
262
 
210
- process.on('SIGINT', () => {
211
- console.log('\nGracefully shutting down...');
212
- ws.close();
213
- process.exit(0);
214
- });
263
+ ws.on('error', (err) => {
264
+ console.error(`${C.red}WebSocket Error:${C.reset} ${err.message}`);
265
+ });
266
+
267
+ process.on('SIGINT', () => {
268
+ console.log(`\n${C.dim}Gracefully shutting down...${C.reset}`);
269
+ ws.close();
270
+ process.exit(0);
271
+ });
272
+ })();