@taj-special/dravix-code 1.1.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,100 @@
1
+ import { execFileSync } from 'child_process';
2
+ import { buildContext } from '../services/context.js';
3
+ import chalk from 'chalk';
4
+ import { banner, printHelp, printInfo, colors } from '../utils/display.js';
5
+ import { logout, getSavedUser, checkPlan } from '../services/auth.js';
6
+ export async function handleCommand(cmd, cwd, clearHistory, addFileToContext, getLastAI, retryLast) {
7
+ const parts = cmd.trim().split(/\s+/);
8
+ const name = parts[0].toLowerCase();
9
+ if (name === '/exit' || name === '/quit') {
10
+ console.log(colors.muted('\n Goodbye!\n'));
11
+ process.exit(0);
12
+ }
13
+ if (name === '/logout') {
14
+ logout();
15
+ console.log(colors.muted('\n Signed out. Run dravix again to authenticate.\n'));
16
+ process.exit(0);
17
+ }
18
+ if (name === '/whoami') {
19
+ const { name: n, email } = getSavedUser();
20
+ if (!n) {
21
+ printInfo('Not signed in.');
22
+ return true;
23
+ }
24
+ const plan = email ? await checkPlan(email) : 'Free';
25
+ const planBadge = plan === 'Plus'
26
+ ? chalk.hex('#fcd34d').bold('Plus')
27
+ : chalk.hex('#4b5563')('Free');
28
+ const W = chalk.hex('#e2e8f0');
29
+ process.stdout.write('\n ' + colors.success('✓') + ' ' + W(n) +
30
+ chalk.hex('#4b5563')(' · ') + colors.muted(email ?? '') +
31
+ '\n ' + colors.muted(' Plan ') + planBadge + '\n\n');
32
+ return true;
33
+ }
34
+ if (name === '/help') {
35
+ printHelp();
36
+ return true;
37
+ }
38
+ if (name === '/clear' || name === '/new') {
39
+ clearHistory();
40
+ console.clear();
41
+ banner();
42
+ const { name: userName } = getSavedUser();
43
+ const C = chalk.hex('#818cf8');
44
+ const DIM = chalk.hex('#4b5563');
45
+ const W = chalk.hex('#e2e8f0');
46
+ console.log(DIM(' ─────────────────────────────────────────────'));
47
+ console.log(colors.success(' ✓') + W(' New session started — your chat history is saved'));
48
+ console.log(colors.muted(' ') + colors.muted('Use ') + C('/resume') + colors.muted(' to return to any previous conversation'));
49
+ console.log(DIM(' ─────────────────────────────────────────────'));
50
+ console.log(colors.muted(' ') + C('Tab') + colors.muted(' path complete ') + C('@') + colors.muted(' file picker ') + C('Ctrl+C') + colors.muted(' cancel ') + C('/help') + colors.muted(' commands'));
51
+ process.stdout.write('\n');
52
+ return true;
53
+ }
54
+ if (name === '/files') {
55
+ const ctx = buildContext(cwd);
56
+ console.log('\n' + colors.muted(ctx) + '\n');
57
+ return true;
58
+ }
59
+ if (name === '/retry') {
60
+ if (!retryLast) {
61
+ printInfo('Retry not available.');
62
+ return true;
63
+ }
64
+ retryLast();
65
+ return true;
66
+ }
67
+ if (name === '/copy') {
68
+ const text = getLastAI?.() ?? null;
69
+ if (!text) {
70
+ printInfo('No AI response to copy.');
71
+ return true;
72
+ }
73
+ try {
74
+ const buf = Buffer.from(text, 'utf-8');
75
+ if (process.platform === 'win32') {
76
+ execFileSync('clip', [], { input: buf, stdio: ['pipe', 'ignore', 'ignore'] });
77
+ }
78
+ else if (process.platform === 'darwin') {
79
+ execFileSync('pbcopy', [], { input: buf, stdio: ['pipe', 'ignore', 'ignore'] });
80
+ }
81
+ else {
82
+ execFileSync('xclip', ['-selection', 'clipboard'], { input: buf, stdio: ['pipe', 'ignore', 'ignore'] });
83
+ }
84
+ printInfo('Last response copied to clipboard.');
85
+ }
86
+ catch {
87
+ printInfo('Copy failed — clipboard tool not available.');
88
+ }
89
+ return true;
90
+ }
91
+ if (name === '/model') {
92
+ process.stdout.write('\n ' + colors.primary('Dravix 3 Titanium') +
93
+ colors.muted(' + ') +
94
+ colors.primary('Dravix 3.5 Quantum') +
95
+ colors.muted(' · by Taj Special\n\n'));
96
+ return true;
97
+ }
98
+ printInfo(`Unknown command: ${cmd} (type /help for list)`);
99
+ return true;
100
+ }
@@ -0,0 +1,280 @@
1
+ #!/usr/bin/env node
2
+ import * as path from 'path';
3
+ import { randomBytes } from 'crypto';
4
+ import { execSync } from 'child_process';
5
+ import chalk from 'chalk';
6
+ import { banner, printHelp, printError, colors, VERSION } from '../utils/display.js';
7
+ import { startRepl } from './repl.js';
8
+ import { isLoggedIn, logout, getSavedUser, saveConfig, checkPlan, AUTH_URL } from '../services/auth.js';
9
+ import { checkUsage, usageBar, fmtNum, formatResetTime } from '../services/usage.js';
10
+ const POLL_URL = 'https://dravix.app/cli-auth';
11
+ const SPINNER = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
12
+ // OSC 8 terminal hyperlink — clickable in modern terminals
13
+ function link(url) {
14
+ return `\x1b]8;;${url}\x1b\\${url}\x1b]8;;\x1b\\`;
15
+ }
16
+ function openBrowser(url) {
17
+ try {
18
+ if (process.platform === 'win32')
19
+ execSync(`start "" "${url}"`, { stdio: 'ignore' });
20
+ else if (process.platform === 'darwin')
21
+ execSync(`open "${url}"`, { stdio: 'ignore' });
22
+ else
23
+ execSync(`xdg-open "${url}"`, { stdio: 'ignore' });
24
+ }
25
+ catch { /* ignore — user sees the URL */ }
26
+ }
27
+ async function pollSession(sessionId) {
28
+ try {
29
+ const res = await fetch(`${POLL_URL}?action=poll&session=${sessionId}`);
30
+ const data = await res.json();
31
+ if (data.authenticated && data.token) {
32
+ return { token: data.token, name: data.name ?? '', email: data.email ?? '' };
33
+ }
34
+ }
35
+ catch { /* network error — keep polling */ }
36
+ return null;
37
+ }
38
+ async function showWelcome() {
39
+ const cols = process.stdout.columns ?? 80;
40
+ const W = Math.min(cols, 64);
41
+ const P = chalk.hex('#818cf8');
42
+ const PB = chalk.hex('#6366f1').bold;
43
+ const DIM = chalk.hex('#4b5563');
44
+ const G = chalk.hex('#34d399');
45
+ const White = chalk.hex('#e2e8f0');
46
+ const Muted = chalk.hex('#6b7280');
47
+ const line = () => DIM(' ' + '─'.repeat(W - 4));
48
+ const pad = (s, w) => s + ' '.repeat(Math.max(w - s.length, 0));
49
+ console.clear();
50
+ // ── Logo ──────────────────────────────────────────────────────
51
+ console.log(chalk.hex('#6366f1').bold(`
52
+ ██████╗ ██████╗ █████╗ ██╗ ██╗██╗██╗ ██╗
53
+ ██╔══██╗██╔══██╗██╔══██╗██║ ██║██║╚██╗██╔╝
54
+ ██║ ██║██████╔╝███████║██║ ██║██║ ╚███╔╝
55
+ ██║ ██║██╔══██╗██╔══██║╚██╗ ██╔╝██║ ██╔██╗
56
+ ██████╔╝██║ ██║██║ ██║ ╚████╔╝ ██║██╔╝ ██╗
57
+ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═══╝ ╚═╝╚═╝ ╚═╝`));
58
+ console.log(PB(' Code') +
59
+ DIM(` v${VERSION}`) +
60
+ DIM(' ·') +
61
+ P(' AI-powered coding assistant') +
62
+ DIM(' ·') +
63
+ Muted(' by Taj Special'));
64
+ console.log(line() + '\n');
65
+ // ── Welcome ───────────────────────────────────────────────────
66
+ console.log(' ' + White.bold('Welcome to Dravix Code') + Muted(' — your AI coding partner'));
67
+ console.log(' ' + Muted('Works like Claude Code, Cursor, Copilot — but in your terminal.') + '\n');
68
+ // ── Features box ──────────────────────────────────────────────
69
+ const boxW = Math.min(W - 4, 58);
70
+ const inner = boxW - 2;
71
+ const BC = DIM;
72
+ const row = (icon, text) => {
73
+ const content = ` ${icon} ${text}`;
74
+ const padded = pad(content, inner);
75
+ return ' ' + BC('│') + White(padded) + BC('│');
76
+ };
77
+ console.log(' ' + BC('╭' + '─'.repeat(boxW - 2) + '╮'));
78
+ console.log(' ' + BC('│') + P(pad(' Capabilities', inner)) + BC('│'));
79
+ console.log(' ' + BC('├' + '─'.repeat(boxW - 2) + '┤'));
80
+ console.log(row(G('✓'), 'Write, edit and create code with AI'));
81
+ console.log(row(G('✓'), 'Search and understand any codebase'));
82
+ console.log(row(G('✓'), 'Run commands and fix errors automatically'));
83
+ console.log(row(G('✓'), 'Read files, folders and project structure'));
84
+ console.log(row(G('✓'), 'Works with TypeScript, Python, Go and more'));
85
+ console.log(' ' + BC('╰' + '─'.repeat(boxW - 2) + '╯') + '\n');
86
+ // ── Models ────────────────────────────────────────────────────
87
+ console.log(' ' + Muted('Powered by ') +
88
+ P.bold('Dravix 3 Titanium') +
89
+ Muted(' + ') +
90
+ P.bold('Dravix 3.5 Quantum'));
91
+ console.log(line() + '\n');
92
+ // ── CTA ───────────────────────────────────────────────────────
93
+ console.log(' ' + Muted('A free account is required to get started.') + '\n');
94
+ const enterKey = chalk.bgHex('#312e81').hex('#c7d2fe').bold(' Enter ');
95
+ const escKey = chalk.hex('#374151').bold(' Esc ');
96
+ console.log(' ' + enterKey + Muted(' Create account and continue'));
97
+ console.log(' ' + escKey + Muted(' Cancel') + '\n');
98
+ console.log(line() + '\n');
99
+ return new Promise((resolve) => {
100
+ process.stdin.setRawMode(true);
101
+ process.stdin.resume();
102
+ process.stdin.setEncoding('utf8');
103
+ function onKey(data) {
104
+ if (data === '\r' || data === '\n') {
105
+ cleanup();
106
+ resolve(true);
107
+ }
108
+ else if (data === '\x1b' || data === '\x03' || data === 'n' || data === 'N') {
109
+ cleanup();
110
+ resolve(false);
111
+ }
112
+ }
113
+ function cleanup() {
114
+ process.stdin.removeListener('data', onKey);
115
+ process.stdin.setRawMode(false);
116
+ process.stdin.pause();
117
+ }
118
+ process.stdin.on('data', onKey);
119
+ });
120
+ }
121
+ async function browserAuth() {
122
+ const sessionId = randomBytes(16).toString('hex');
123
+ const authUrl = `${AUTH_URL}?session=${sessionId}`;
124
+ const DIM = chalk.hex('#4b5563');
125
+ const W = chalk.hex('#e2e8f0');
126
+ const P = chalk.hex('#818cf8');
127
+ const cols = process.stdout.columns ?? 80;
128
+ const line = DIM(' ' + '─'.repeat(Math.min(cols - 4, 51)));
129
+ console.clear();
130
+ console.log(chalk.hex('#6366f1').bold(`
131
+ ██████╗ ██████╗ █████╗ ██╗ ██╗██╗██╗ ██╗
132
+ ██╔══██╗██╔══██╗██╔══██╗██║ ██║██║╚██╗██╔╝
133
+ ██║ ██║██████╔╝███████║██║ ██║██║ ╚███╔╝
134
+ ██║ ██║██╔══██╗██╔══██║╚██╗ ██╔╝██║ ██╔██╗
135
+ ██████╔╝██║ ██║██║ ██║ ╚████╔╝ ██║██╔╝ ██╗
136
+ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═══╝ ╚═╝╚═╝ ╚═╝`));
137
+ console.log(chalk.hex('#6366f1').bold(' Code') + DIM(` v${VERSION} · `) + P('AI-powered coding assistant'));
138
+ console.log(line + '\n');
139
+ console.log(' ' + colors.warn('⚠') + ' ' + W.bold('You do not have an account yet'));
140
+ console.log(' ' + DIM('To use Dravix Code you need to register a free account.') + '\n');
141
+ console.log(line);
142
+ console.log('\n ' + DIM('Step 1 — ') + W('Open this link in your browser:') + '\n');
143
+ console.log(' ' + colors.primary(link(authUrl)) + '\n');
144
+ console.log(' ' + DIM('Step 2 — ') + W('Create your account or sign in'));
145
+ console.log(' ' + DIM('Step 3 — ') + W('Return here — it connects automatically') + '\n');
146
+ console.log(line + '\n');
147
+ let frameIdx = 0;
148
+ await new Promise((resolve, reject) => {
149
+ let done = false;
150
+ // Spinner runs fast (80ms)
151
+ const spinnerInterval = setInterval(() => {
152
+ if (done)
153
+ return;
154
+ process.stdout.write(`\r ${colors.muted(SPINNER[frameIdx % SPINNER.length])} ${colors.muted('Waiting for authentication...')}`);
155
+ frameIdx++;
156
+ }, 80);
157
+ // Polling runs every 2s
158
+ const poll = async () => {
159
+ if (done)
160
+ return;
161
+ const result = await pollSession(sessionId);
162
+ if (result) {
163
+ done = true;
164
+ clearInterval(spinnerInterval);
165
+ process.stdout.write('\r\x1b[K');
166
+ saveConfig({ token: result.token, name: result.name, email: result.email });
167
+ const DIM2 = chalk.hex('#4b5563');
168
+ const W2 = chalk.hex('#e2e8f0');
169
+ console.log(' ' + colors.success('✓') + ' ' + W2('Signed in as ') + colors.primary.bold(result.name) + DIM2(' · ') + colors.muted(result.email));
170
+ console.log(DIM2('\n ' + '─'.repeat(47)) + '\n');
171
+ resolve();
172
+ return;
173
+ }
174
+ setTimeout(poll, 2000);
175
+ };
176
+ poll();
177
+ setTimeout(() => {
178
+ if (!done) {
179
+ done = true;
180
+ clearInterval(spinnerInterval);
181
+ process.stdout.write('\r\x1b[K');
182
+ reject(new Error('Authentication timed out after 5 minutes.'));
183
+ }
184
+ }, 300_000);
185
+ });
186
+ }
187
+ async function checkVersion() {
188
+ try {
189
+ const res = await fetch('https://registry.npmjs.org/@taj-special/dravix-code/latest', {
190
+ signal: AbortSignal.timeout(5000),
191
+ });
192
+ const data = await res.json();
193
+ const latest = data.version ?? '';
194
+ const current = VERSION;
195
+ if (latest && latest !== current) {
196
+ const DIM = chalk.hex('#4b5563');
197
+ const W = chalk.hex('#e2e8f0');
198
+ const Y = chalk.hex('#fcd34d');
199
+ console.log('\n ' + Y('⚠') + ' ' + W.bold('New version available: ' + latest));
200
+ console.log(' ' + DIM(' Run: npm install -g @taj-special/dravix-code') + '\n');
201
+ }
202
+ }
203
+ catch { /* ignore network errors */ }
204
+ }
205
+ async function main() {
206
+ // Ensure UTF-8 output on Windows
207
+ if (process.platform === 'win32') {
208
+ try {
209
+ execSync('chcp 65001', { stdio: 'ignore' });
210
+ }
211
+ catch { }
212
+ }
213
+ const arg = process.argv[2];
214
+ // Check for updates in background
215
+ checkVersion().catch(() => { });
216
+ if (arg === '--help' || arg === '-h') {
217
+ banner();
218
+ printHelp();
219
+ process.exit(0);
220
+ }
221
+ if (arg === '--logout') {
222
+ logout();
223
+ console.log(colors.muted('\n Signed out.\n'));
224
+ process.exit(0);
225
+ }
226
+ if (arg === '--login') {
227
+ if (isLoggedIn()) {
228
+ // Already logged in — skip login, go straight to main menu
229
+ banner();
230
+ }
231
+ else {
232
+ await browserAuth();
233
+ }
234
+ }
235
+ else if (!isLoggedIn()) {
236
+ await browserAuth();
237
+ }
238
+ else {
239
+ banner();
240
+ }
241
+ const { name, email } = getSavedUser();
242
+ const cwd = (arg && !arg.startsWith('--')) ? path.resolve(arg) : process.cwd();
243
+ // Fetch plan + usage in parallel
244
+ const token = (await import('../services/auth.js')).getToken() ?? '';
245
+ const [plan, usage] = await Promise.all([
246
+ email ? checkPlan(email) : Promise.resolve('Free'),
247
+ token ? checkUsage(token) : Promise.resolve(null),
248
+ ]);
249
+ const C = chalk.hex('#818cf8');
250
+ const DIM = chalk.hex('#4b5563');
251
+ const W = chalk.hex('#e2e8f0');
252
+ const planBadge = plan === 'Plus'
253
+ ? chalk.hex('#fcd34d').bold('Plus')
254
+ : chalk.hex('#4b5563')('Free');
255
+ console.log(DIM(' ' + '─'.repeat(45)));
256
+ console.log(colors.muted(' User ') + W(name ?? 'Unknown') + DIM(' · ') + colors.muted(email ?? ''));
257
+ console.log(colors.muted(' Plan ') + planBadge);
258
+ if (usage) {
259
+ const { bar, pct } = usageBar(usage.tokens_used, usage.limit);
260
+ const barColor = pct >= 80 ? chalk.hex('#f87171')
261
+ : pct >= 50 ? chalk.hex('#fbbf24')
262
+ : chalk.hex('#34d399');
263
+ const resetStr = formatResetTime(usage);
264
+ console.log(colors.muted(' Tokens ') +
265
+ barColor(bar) + ' ' +
266
+ chalk.hex('#94a3b8')(fmtNum(usage.tokens_used) + ' / ' + fmtNum(usage.limit)) +
267
+ DIM(' · resets in ' + resetStr));
268
+ }
269
+ console.log(colors.muted(' Project ') + W(path.basename(cwd)));
270
+ console.log(colors.muted(' Path ') + DIM(cwd));
271
+ console.log(DIM(' ' + '─'.repeat(45)));
272
+ console.log(colors.muted(' ') + C('Tab') + colors.muted(' path complete ') + C('@') + colors.muted(' file picker ') + C('Ctrl+C') + colors.muted(' cancel ') + C('/help') + colors.muted(' commands'));
273
+ process.stdout.write('\n');
274
+ await startRepl(cwd);
275
+ }
276
+ main().catch((err) => {
277
+ const msg = err instanceof Error ? err.message : String(err);
278
+ printError(msg);
279
+ process.exit(1);
280
+ });