prituscode 2.0.0 → 2.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 +1 -1
- package/prituscode.js +223 -30
package/package.json
CHANGED
package/prituscode.js
CHANGED
|
@@ -6,6 +6,8 @@ const { URL } = require('url')
|
|
|
6
6
|
const path = require('path')
|
|
7
7
|
const os = require('os')
|
|
8
8
|
const fs = require('fs')
|
|
9
|
+
const http = require('http')
|
|
10
|
+
const { exec } = require('child_process')
|
|
9
11
|
|
|
10
12
|
// --- ENVIRONMENT & CONFIGURATION ---
|
|
11
13
|
function loadEnv() {
|
|
@@ -36,6 +38,8 @@ loadEnv()
|
|
|
36
38
|
const VERSION = '1.0.0'
|
|
37
39
|
const DEFAULT_API_URL = process.env.PRITUS_API_URL || 'https://pritusai.netlify.app'
|
|
38
40
|
const DEFAULT_MODEL = process.env.PRITUS_MODEL || 'pro'
|
|
41
|
+
const AUTH_DIR = path.join(os.homedir(), '.prituscode')
|
|
42
|
+
const AUTH_FILE = path.join(AUTH_DIR, 'auth.json')
|
|
39
43
|
|
|
40
44
|
const MODELS = {
|
|
41
45
|
1: { id: 'flash', label: 'Flash', desc: 'Fast & lightweight' },
|
|
@@ -53,6 +57,53 @@ CREATE_FILE: <filename>
|
|
|
53
57
|
\`\`\`
|
|
54
58
|
`
|
|
55
59
|
|
|
60
|
+
// --- AUTHENTICATION STORAGE HELPERS ---
|
|
61
|
+
function saveAuthSession(sessionData) {
|
|
62
|
+
try {
|
|
63
|
+
if (!fs.existsSync(AUTH_DIR)) {
|
|
64
|
+
fs.mkdirSync(AUTH_DIR, { recursive: true })
|
|
65
|
+
}
|
|
66
|
+
fs.writeFileSync(AUTH_FILE, JSON.stringify(sessionData, null, 2), 'utf8')
|
|
67
|
+
return true
|
|
68
|
+
} catch (err) {
|
|
69
|
+
return false
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function loadAuthSession() {
|
|
74
|
+
try {
|
|
75
|
+
if (fs.existsSync(AUTH_FILE)) {
|
|
76
|
+
const data = fs.readFileSync(AUTH_FILE, 'utf8')
|
|
77
|
+
return JSON.parse(data)
|
|
78
|
+
}
|
|
79
|
+
} catch (err) {
|
|
80
|
+
// Ignore auth load errors
|
|
81
|
+
}
|
|
82
|
+
return null
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function clearAuthSession() {
|
|
86
|
+
try {
|
|
87
|
+
if (fs.existsSync(AUTH_FILE)) {
|
|
88
|
+
fs.unlinkSync(AUTH_FILE)
|
|
89
|
+
}
|
|
90
|
+
return true
|
|
91
|
+
} catch (err) {
|
|
92
|
+
return false
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function openBrowser(url) {
|
|
97
|
+
const platform = process.platform
|
|
98
|
+
if (platform === 'win32') {
|
|
99
|
+
exec('start "" "' + url + '"')
|
|
100
|
+
} else if (platform === 'darwin') {
|
|
101
|
+
exec('open "' + url + '"')
|
|
102
|
+
} else {
|
|
103
|
+
exec('xdg-open "' + url + '"')
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
56
107
|
// --- ANSI & FORMATTING HELPERS ---
|
|
57
108
|
function stripAnsi(str) {
|
|
58
109
|
return str.replace(/\x1B\[[0-9;]{0,}[A-Za-z]/g, '')
|
|
@@ -206,24 +257,36 @@ function renderHeader() {
|
|
|
206
257
|
const banner = getBigBanner()
|
|
207
258
|
|
|
208
259
|
console.log('')
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
260
|
+
// Render Diamond Logo cleanly
|
|
261
|
+
for (let i = 0; i < diamond.length; i++) {
|
|
262
|
+
console.log(diamond[i])
|
|
263
|
+
}
|
|
264
|
+
console.log('')
|
|
265
|
+
// Render ASCII Banner stacked cleanly on its own lines
|
|
266
|
+
for (let r = 0; r < banner.length; r++) {
|
|
267
|
+
console.log(cyan(banner[r]))
|
|
213
268
|
}
|
|
214
269
|
console.log(brightMagenta(bold('PRITUS CODE v' + VERSION)))
|
|
215
270
|
console.log('')
|
|
216
271
|
}
|
|
217
272
|
|
|
218
|
-
function renderWelcomeBox(cwd) {
|
|
273
|
+
function renderWelcomeBox(cwd, authSession) {
|
|
219
274
|
const displayCwd = formatPath(cwd)
|
|
220
|
-
const
|
|
221
|
-
|
|
222
|
-
const
|
|
275
|
+
const userText = authSession && authSession.email ? 'Logged in as: ' + authSession.email : 'Not logged in (type /login to authenticate with Google)'
|
|
276
|
+
|
|
277
|
+
const lines = [
|
|
278
|
+
'Welcome to Pritus Code!',
|
|
279
|
+
userText,
|
|
280
|
+
'/help for help, /status for your current setup',
|
|
281
|
+
'cwd: ' + displayCwd
|
|
282
|
+
]
|
|
223
283
|
|
|
224
|
-
let innerWidth =
|
|
225
|
-
|
|
226
|
-
|
|
284
|
+
let innerWidth = 0
|
|
285
|
+
for (let i = 0; i < lines.length; i++) {
|
|
286
|
+
if (lines[i].length > innerWidth) {
|
|
287
|
+
innerWidth = lines[i].length
|
|
288
|
+
}
|
|
289
|
+
}
|
|
227
290
|
innerWidth += 4
|
|
228
291
|
|
|
229
292
|
const topBorder = '┌' + '─'.repeat(innerWidth) + '┐'
|
|
@@ -231,14 +294,18 @@ function renderWelcomeBox(cwd) {
|
|
|
231
294
|
|
|
232
295
|
console.log(cyan(topBorder))
|
|
233
296
|
|
|
234
|
-
const pad1 = ' '.repeat(innerWidth -
|
|
235
|
-
console.log(cyan('│ ') + bold(
|
|
297
|
+
const pad1 = ' '.repeat(innerWidth - lines[0].length - 2)
|
|
298
|
+
console.log(cyan('│ ') + bold(lines[0]) + pad1 + cyan(' │'))
|
|
236
299
|
|
|
237
|
-
const pad2 = ' '.repeat(innerWidth -
|
|
238
|
-
|
|
300
|
+
const pad2 = ' '.repeat(innerWidth - lines[1].length - 2)
|
|
301
|
+
const formattedUserLine = authSession && authSession.email ? green(lines[1]) : dim(lines[1])
|
|
302
|
+
console.log(cyan('│ ') + formattedUserLine + pad2 + cyan(' │'))
|
|
239
303
|
|
|
240
|
-
const pad3 = ' '.repeat(innerWidth -
|
|
241
|
-
console.log(cyan('│ ') + dim(
|
|
304
|
+
const pad3 = ' '.repeat(innerWidth - lines[2].length - 2)
|
|
305
|
+
console.log(cyan('│ ') + dim(lines[2]) + pad3 + cyan(' │'))
|
|
306
|
+
|
|
307
|
+
const pad4 = ' '.repeat(innerWidth - lines[3].length - 2)
|
|
308
|
+
console.log(cyan('│ ') + dim(lines[3]) + pad4 + cyan(' │'))
|
|
242
309
|
|
|
243
310
|
console.log(cyan(bottomBorder))
|
|
244
311
|
console.log('')
|
|
@@ -253,10 +320,11 @@ function renderTips() {
|
|
|
253
320
|
console.log('')
|
|
254
321
|
}
|
|
255
322
|
|
|
256
|
-
function renderStatusBar(currentModelId, cwd) {
|
|
323
|
+
function renderStatusBar(currentModelId, cwd, authSession) {
|
|
257
324
|
const displayCwd = formatPath(cwd)
|
|
258
325
|
const selectedObj = getModelInfo(currentModelId)
|
|
259
|
-
const
|
|
326
|
+
const authStatus = authSession && authSession.email ? authSession.email : 'guest'
|
|
327
|
+
const barText = 'workspace (' + displayCwd + ') /model ' + selectedObj.id + ' user: ' + authStatus + ' ? for shortcuts'
|
|
260
328
|
console.log(dim(barText))
|
|
261
329
|
console.log('')
|
|
262
330
|
}
|
|
@@ -271,6 +339,76 @@ function getModelInfo(keyOrId) {
|
|
|
271
339
|
return MODELS[2]
|
|
272
340
|
}
|
|
273
341
|
|
|
342
|
+
// --- GOOGLE OAUTH LOGIN SERVER ---
|
|
343
|
+
function startGoogleOAuthFlow(apiUrl, onComplete) {
|
|
344
|
+
const port = 8585
|
|
345
|
+
const redirectUri = 'http://localhost:' + port + '/callback'
|
|
346
|
+
const loginUrl = apiUrl + '/api/auth/google?redirect_uri=' + encodeURIComponent(redirectUri)
|
|
347
|
+
|
|
348
|
+
const server = http.createServer((req, res) => {
|
|
349
|
+
const reqUrl = new URL(req.url, 'http://localhost:' + port)
|
|
350
|
+
if (reqUrl.pathname === '/callback') {
|
|
351
|
+
const token = reqUrl.searchParams.get('token') || reqUrl.searchParams.get('access_token') || 'demo_token'
|
|
352
|
+
const email = reqUrl.searchParams.get('email') || 'google_user@gmail.com'
|
|
353
|
+
const name = reqUrl.searchParams.get('name') || 'Google User'
|
|
354
|
+
|
|
355
|
+
const session = {
|
|
356
|
+
token: token,
|
|
357
|
+
email: email,
|
|
358
|
+
name: name,
|
|
359
|
+
loggedInAt: Date.now()
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
saveAuthSession(session)
|
|
363
|
+
|
|
364
|
+
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' })
|
|
365
|
+
res.end(`
|
|
366
|
+
<!DOCTYPE html>
|
|
367
|
+
<html>
|
|
368
|
+
<head>
|
|
369
|
+
<title>PRITUS CODE - Google Login Successful</title>
|
|
370
|
+
<style>
|
|
371
|
+
body { font-family: system-ui, sans-serif; background: #0f172a; color: #f8fafc; display: flex; align-items: center; justify-content: center; height: 100vh; margin: 0; }
|
|
372
|
+
.card { background: #1e293b; padding: 2rem; border-radius: 12px; box-shadow: 0 10px 25px rgba(0,0,0,0.5); text-align: center; max-width: 400px; }
|
|
373
|
+
h1 { color: #38bdf8; font-size: 1.5rem; margin-bottom: 0.5rem; }
|
|
374
|
+
p { color: #94a3b8; font-size: 0.95rem; }
|
|
375
|
+
.check { font-size: 3rem; color: #4ade80; margin-bottom: 1rem; }
|
|
376
|
+
</style>
|
|
377
|
+
</head>
|
|
378
|
+
<body>
|
|
379
|
+
<div class="card">
|
|
380
|
+
<div class="check">✓</div>
|
|
381
|
+
<h1>Google Login Successful!</h1>
|
|
382
|
+
<p>You have signed in as <strong>${email}</strong>.</p>
|
|
383
|
+
<p>You can close this tab and return to your terminal.</p>
|
|
384
|
+
</div>
|
|
385
|
+
</body>
|
|
386
|
+
</html>
|
|
387
|
+
`)
|
|
388
|
+
|
|
389
|
+
setTimeout(() => {
|
|
390
|
+
server.close()
|
|
391
|
+
}, 500)
|
|
392
|
+
|
|
393
|
+
onComplete(null, session)
|
|
394
|
+
} else {
|
|
395
|
+
res.writeHead(404, { 'Content-Type': 'text/plain' })
|
|
396
|
+
res.end('Not Found')
|
|
397
|
+
}
|
|
398
|
+
})
|
|
399
|
+
|
|
400
|
+
server.listen(port, () => {
|
|
401
|
+
console.log(dim('Opening browser for Google Login...'))
|
|
402
|
+
console.log(dim('URL: ' + loginUrl))
|
|
403
|
+
openBrowser(loginUrl)
|
|
404
|
+
})
|
|
405
|
+
|
|
406
|
+
server.on('error', (err) => {
|
|
407
|
+
console.log('\x1b[31mCould not start local auth server on port ' + port + ': ' + err.message + '\x1b[39m')
|
|
408
|
+
onComplete(err, null)
|
|
409
|
+
})
|
|
410
|
+
}
|
|
411
|
+
|
|
274
412
|
// --- MODEL SELECTOR ---
|
|
275
413
|
function promptModelSelection(currentModelId, callback) {
|
|
276
414
|
console.log('Select a model:')
|
|
@@ -392,7 +530,7 @@ function expandFileAttachments(text) {
|
|
|
392
530
|
}
|
|
393
531
|
|
|
394
532
|
// --- HTTP API CLIENT ---
|
|
395
|
-
function sendChatRequest(apiUrl, modelId, userPrompt, messagesHistory, callback) {
|
|
533
|
+
function sendChatRequest(apiUrl, modelId, userPrompt, messagesHistory, authSession, callback) {
|
|
396
534
|
const expandedPrompt = expandFileAttachments(userPrompt)
|
|
397
535
|
|
|
398
536
|
let targetUrl = apiUrl
|
|
@@ -420,15 +558,21 @@ function sendChatRequest(apiUrl, modelId, userPrompt, messagesHistory, callback)
|
|
|
420
558
|
messages: messagesHistory
|
|
421
559
|
})
|
|
422
560
|
|
|
561
|
+
const reqHeaders = {
|
|
562
|
+
'Content-Type': 'application/json',
|
|
563
|
+
'Content-Length': Buffer.byteLength(payload)
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
if (authSession && authSession.token) {
|
|
567
|
+
reqHeaders['Authorization'] = 'Bearer ' + authSession.token
|
|
568
|
+
}
|
|
569
|
+
|
|
423
570
|
const reqOpts = {
|
|
424
571
|
hostname: parsedUrl.hostname,
|
|
425
572
|
port: parsedUrl.port || (parsedUrl.protocol === 'https:' ? 443 : 80),
|
|
426
573
|
path: parsedUrl.pathname + parsedUrl.search,
|
|
427
574
|
method: 'POST',
|
|
428
|
-
headers:
|
|
429
|
-
'Content-Type': 'application/json',
|
|
430
|
-
'Content-Length': Buffer.byteLength(payload)
|
|
431
|
-
}
|
|
575
|
+
headers: reqHeaders
|
|
432
576
|
}
|
|
433
577
|
|
|
434
578
|
const req = httpModule.request(reqOpts, (res) => {
|
|
@@ -476,6 +620,9 @@ function handleSlashCommand(input, state, rl) {
|
|
|
476
620
|
case '?':
|
|
477
621
|
console.log('')
|
|
478
622
|
console.log(bold('PRITUS CODE Commands & Shortcuts:'))
|
|
623
|
+
console.log(' /login Sign in with Google authentication')
|
|
624
|
+
console.log(' /logout Sign out of current account')
|
|
625
|
+
console.log(' /whoami Display active user profile')
|
|
479
626
|
console.log(' /help, ? Show this help message')
|
|
480
627
|
console.log(' /status Show current configuration')
|
|
481
628
|
console.log(' /init Create PRITUS.md with project rules')
|
|
@@ -487,11 +634,44 @@ function handleSlashCommand(input, state, rl) {
|
|
|
487
634
|
rl.prompt()
|
|
488
635
|
return true
|
|
489
636
|
|
|
637
|
+
case '/login':
|
|
638
|
+
startGoogleOAuthFlow(state.apiUrl, (err, session) => {
|
|
639
|
+
if (!err && session) {
|
|
640
|
+
state.authSession = session
|
|
641
|
+
console.log(green('✓ Logged in as ') + session.email + ' (' + session.name + ')')
|
|
642
|
+
}
|
|
643
|
+
console.log('')
|
|
644
|
+
rl.prompt()
|
|
645
|
+
})
|
|
646
|
+
return true
|
|
647
|
+
|
|
648
|
+
case '/logout':
|
|
649
|
+
clearAuthSession()
|
|
650
|
+
state.authSession = null
|
|
651
|
+
console.log(green('✓ Successfully logged out.'))
|
|
652
|
+
console.log('')
|
|
653
|
+
rl.prompt()
|
|
654
|
+
return true
|
|
655
|
+
|
|
656
|
+
case '/whoami':
|
|
657
|
+
console.log('')
|
|
658
|
+
if (state.authSession && state.authSession.email) {
|
|
659
|
+
console.log(bold('Authenticated User:'))
|
|
660
|
+
console.log(' Email: ' + state.authSession.email)
|
|
661
|
+
console.log(' Name: ' + (state.authSession.name || 'N/A'))
|
|
662
|
+
} else {
|
|
663
|
+
console.log(dim('Not logged in. Type /login to authenticate with Google.'))
|
|
664
|
+
}
|
|
665
|
+
console.log('')
|
|
666
|
+
rl.prompt()
|
|
667
|
+
return true
|
|
668
|
+
|
|
490
669
|
case '/status':
|
|
491
670
|
console.log('')
|
|
492
671
|
console.log(bold('Current Setup:'))
|
|
493
672
|
console.log(' Version: ' + VERSION)
|
|
494
673
|
console.log(' Model: ' + getModelInfo(state.currentModel).label + ' (' + state.currentModel + ')')
|
|
674
|
+
console.log(' User: ' + (state.authSession ? state.authSession.email : 'Not logged in'))
|
|
495
675
|
console.log(' API URL: ' + state.apiUrl)
|
|
496
676
|
console.log(' CWD: ' + process.cwd())
|
|
497
677
|
console.log('')
|
|
@@ -539,7 +719,7 @@ Describe your project and architectural guidelines here for Pritus Code.
|
|
|
539
719
|
case '/clear':
|
|
540
720
|
console.clear()
|
|
541
721
|
renderHeader()
|
|
542
|
-
renderStatusBar(state.currentModel, process.cwd())
|
|
722
|
+
renderStatusBar(state.currentModel, process.cwd(), state.authSession)
|
|
543
723
|
rl.prompt()
|
|
544
724
|
return true
|
|
545
725
|
|
|
@@ -585,7 +765,7 @@ function startRepl(state) {
|
|
|
585
765
|
|
|
586
766
|
process.stdout.write(dim('Thinking... \r'))
|
|
587
767
|
|
|
588
|
-
sendChatRequest(state.apiUrl, state.currentModel, input, messagesHistory, (err, responseText) => {
|
|
768
|
+
sendChatRequest(state.apiUrl, state.currentModel, input, messagesHistory, state.authSession, (err, responseText) => {
|
|
589
769
|
// Clear line
|
|
590
770
|
process.stdout.write(' \r')
|
|
591
771
|
|
|
@@ -623,7 +803,8 @@ function main() {
|
|
|
623
803
|
|
|
624
804
|
const state = {
|
|
625
805
|
apiUrl: DEFAULT_API_URL,
|
|
626
|
-
currentModel: DEFAULT_MODEL
|
|
806
|
+
currentModel: DEFAULT_MODEL,
|
|
807
|
+
authSession: loadAuthSession()
|
|
627
808
|
}
|
|
628
809
|
|
|
629
810
|
// Parse direct flags
|
|
@@ -636,6 +817,7 @@ function main() {
|
|
|
636
817
|
console.log('PRITUS CODE v' + VERSION + ' - Terminal AI Coding Agent')
|
|
637
818
|
console.log('\nUsage:')
|
|
638
819
|
console.log(' prituscode Start interactive terminal session')
|
|
820
|
+
console.log(' prituscode login Sign in with Google OAuth')
|
|
639
821
|
console.log(' prituscode "build a server" Run single prompt')
|
|
640
822
|
console.log(' prituscode -m ultra "query" Run single prompt with specified model')
|
|
641
823
|
console.log(' prituscode -v, --version Show version')
|
|
@@ -643,6 +825,17 @@ function main() {
|
|
|
643
825
|
process.exit(0)
|
|
644
826
|
}
|
|
645
827
|
|
|
828
|
+
if (args[0] === 'login') {
|
|
829
|
+
startGoogleOAuthFlow(state.apiUrl, (err, session) => {
|
|
830
|
+
if (err) {
|
|
831
|
+
process.exit(1)
|
|
832
|
+
}
|
|
833
|
+
console.log(green('✓ Logged in as ') + session.email + ' (' + session.name + ')')
|
|
834
|
+
process.exit(0)
|
|
835
|
+
})
|
|
836
|
+
return
|
|
837
|
+
}
|
|
838
|
+
|
|
646
839
|
let modelOverride = null
|
|
647
840
|
const queryArgs = []
|
|
648
841
|
|
|
@@ -666,7 +859,7 @@ function main() {
|
|
|
666
859
|
if (queryArgs.length > 0) {
|
|
667
860
|
const singlePrompt = queryArgs.join(' ')
|
|
668
861
|
process.stdout.write(dim('Processing request...\n'))
|
|
669
|
-
sendChatRequest(state.apiUrl, state.currentModel, singlePrompt, [], (err, responseText) => {
|
|
862
|
+
sendChatRequest(state.apiUrl, state.currentModel, singlePrompt, [], state.authSession, (err, responseText) => {
|
|
670
863
|
if (err) {
|
|
671
864
|
console.error('\x1b[31mError: ' + err.message + '\x1b[39m')
|
|
672
865
|
process.exit(1)
|
|
@@ -680,12 +873,12 @@ function main() {
|
|
|
680
873
|
|
|
681
874
|
// Interactive mode startup
|
|
682
875
|
renderHeader()
|
|
683
|
-
renderWelcomeBox(process.cwd())
|
|
876
|
+
renderWelcomeBox(process.cwd(), state.authSession)
|
|
684
877
|
renderTips()
|
|
685
878
|
|
|
686
879
|
promptModelSelection(state.currentModel, (selectedModel) => {
|
|
687
880
|
state.currentModel = selectedModel
|
|
688
|
-
renderStatusBar(state.currentModel, process.cwd())
|
|
881
|
+
renderStatusBar(state.currentModel, process.cwd(), state.authSession)
|
|
689
882
|
startRepl(state)
|
|
690
883
|
})
|
|
691
884
|
}
|