polydev-ai 1.9.11 → 1.9.13
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/mcp/stdio-wrapper.js +205 -45
- package/package.json +1 -1
package/mcp/stdio-wrapper.js
CHANGED
|
@@ -476,38 +476,79 @@ Token will be saved automatically after login.`
|
|
|
476
476
|
async handleLoginTool(params, id) {
|
|
477
477
|
const crypto = require('crypto');
|
|
478
478
|
|
|
479
|
-
// Check if already authenticated
|
|
479
|
+
// Check if already authenticated - verify with server first
|
|
480
480
|
if (this.isAuthenticated && this.userToken) {
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
481
|
+
console.error('[Polydev] Token exists locally, verifying with server...');
|
|
482
|
+
const isValid = await this.verifyTokenWithServer();
|
|
483
|
+
|
|
484
|
+
if (isValid) {
|
|
485
|
+
return {
|
|
486
|
+
jsonrpc: '2.0',
|
|
487
|
+
id,
|
|
488
|
+
result: {
|
|
489
|
+
content: [{
|
|
490
|
+
type: 'text',
|
|
491
|
+
text: `ALREADY AUTHENTICATED
|
|
488
492
|
=====================
|
|
489
493
|
|
|
490
|
-
Your token is configured and
|
|
494
|
+
Your token is configured and verified.
|
|
491
495
|
|
|
492
496
|
Available commands:
|
|
493
497
|
/polydev:ask Query multiple AI models
|
|
494
498
|
/polydev:auth Check status & credits
|
|
495
499
|
|
|
496
500
|
To re-login: npx polydev-ai`
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
501
|
+
}]
|
|
502
|
+
}
|
|
503
|
+
};
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
// Token is invalid/expired - clear state and proceed with login
|
|
507
|
+
console.error('[Polydev] Token invalid/expired, proceeding with re-authentication...');
|
|
508
|
+
this.isAuthenticated = false;
|
|
509
|
+
this.userToken = null;
|
|
500
510
|
}
|
|
501
511
|
|
|
502
512
|
// Generate unique session ID for polling-based auth
|
|
503
513
|
const sessionId = crypto.randomBytes(32).toString('hex');
|
|
504
514
|
|
|
505
515
|
try {
|
|
506
|
-
// Create session on server
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
516
|
+
// Create session on server (with retry for transient network issues)
|
|
517
|
+
let createResponse;
|
|
518
|
+
let lastError;
|
|
519
|
+
for (let attempt = 0; attempt < 3; attempt++) {
|
|
520
|
+
try {
|
|
521
|
+
createResponse = await fetch(`https://www.polydev.ai/api/auth/cli-session/${sessionId}`, {
|
|
522
|
+
method: 'POST',
|
|
523
|
+
headers: { 'Content-Type': 'application/json' }
|
|
524
|
+
});
|
|
525
|
+
lastError = null;
|
|
526
|
+
break; // Success, exit retry loop
|
|
527
|
+
} catch (fetchErr) {
|
|
528
|
+
lastError = fetchErr;
|
|
529
|
+
const cause = fetchErr.cause ? ` (${fetchErr.cause.code || fetchErr.cause.message || fetchErr.cause})` : '';
|
|
530
|
+
console.error(`[Polydev] Session creation attempt ${attempt + 1}/3 failed: ${fetchErr.message}${cause}`);
|
|
531
|
+
if (attempt < 2) {
|
|
532
|
+
await new Promise(r => setTimeout(r, 1000 * (attempt + 1))); // 1s, 2s backoff
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
if (lastError) {
|
|
538
|
+
const cause = lastError.cause ? ` (${lastError.cause.code || lastError.cause.message || lastError.cause})` : '';
|
|
539
|
+
console.error('[Polydev] All session creation attempts failed:', lastError.message, cause);
|
|
540
|
+
return {
|
|
541
|
+
jsonrpc: '2.0',
|
|
542
|
+
id,
|
|
543
|
+
result: {
|
|
544
|
+
content: [{
|
|
545
|
+
type: 'text',
|
|
546
|
+
text: `Login failed: Could not reach polydev.ai${cause}\n\nThis usually means a network connectivity issue.\n\nAlternative: Run in your terminal:\n npx polydev-ai`
|
|
547
|
+
}],
|
|
548
|
+
isError: true
|
|
549
|
+
}
|
|
550
|
+
};
|
|
551
|
+
}
|
|
511
552
|
|
|
512
553
|
if (!createResponse.ok) {
|
|
513
554
|
const error = await createResponse.text();
|
|
@@ -544,23 +585,26 @@ To re-login: npx polydev-ai`
|
|
|
544
585
|
// if this process dies, the new one can resume polling with same session)
|
|
545
586
|
this.startLoginPolling(sessionId);
|
|
546
587
|
|
|
547
|
-
// Return immediately
|
|
588
|
+
// Return immediately with accurate status
|
|
548
589
|
return {
|
|
549
590
|
jsonrpc: '2.0',
|
|
550
591
|
id,
|
|
551
592
|
result: {
|
|
552
593
|
content: [{
|
|
553
594
|
type: 'text',
|
|
554
|
-
text: `
|
|
555
|
-
|
|
595
|
+
text: `AUTHENTICATION STARTED
|
|
596
|
+
======================
|
|
556
597
|
|
|
557
|
-
|
|
598
|
+
A browser window is opening for you to sign in.
|
|
599
|
+
|
|
600
|
+
If it doesn't open, visit:
|
|
601
|
+
${authUrl}
|
|
602
|
+
|
|
603
|
+
Once you sign in, your token will be saved automatically to:
|
|
558
604
|
~/.polydev.env
|
|
559
605
|
~/.zshrc
|
|
560
606
|
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
After restart, you can:
|
|
607
|
+
After login completes, you can:
|
|
564
608
|
/polydev:ask Query multiple AI models
|
|
565
609
|
/polydev:auth Check status & credits
|
|
566
610
|
|
|
@@ -570,14 +614,124 @@ Dashboard: https://polydev.ai/dashboard`
|
|
|
570
614
|
};
|
|
571
615
|
|
|
572
616
|
} catch (error) {
|
|
573
|
-
|
|
617
|
+
const cause = error.cause ? ` (${error.cause.code || error.cause.message || error.cause})` : '';
|
|
618
|
+
console.error('[Polydev] Login error:', error.message, cause);
|
|
574
619
|
return {
|
|
575
620
|
jsonrpc: '2.0',
|
|
576
621
|
id,
|
|
577
622
|
result: {
|
|
578
623
|
content: [{
|
|
579
624
|
type: 'text',
|
|
580
|
-
text: `Login failed: ${error.message}\n\nPlease try: npx polydev-ai`
|
|
625
|
+
text: `Login failed: ${error.message}${cause}\n\nPlease try: npx polydev-ai`
|
|
626
|
+
}],
|
|
627
|
+
isError: true
|
|
628
|
+
}
|
|
629
|
+
};
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
/**
|
|
634
|
+
* Verify the current token is valid by checking with the server
|
|
635
|
+
* Returns true if valid, false if invalid/expired
|
|
636
|
+
* On network error, returns true (don't block for transient issues)
|
|
637
|
+
*/
|
|
638
|
+
async verifyTokenWithServer() {
|
|
639
|
+
if (!this.userToken) return false;
|
|
640
|
+
try {
|
|
641
|
+
const response = await fetch('https://www.polydev.ai/api/auth/status', {
|
|
642
|
+
method: 'POST',
|
|
643
|
+
headers: {
|
|
644
|
+
'Content-Type': 'application/json',
|
|
645
|
+
'Authorization': `Bearer ${this.userToken}`,
|
|
646
|
+
'User-Agent': 'polydev-stdio-wrapper/1.0.0'
|
|
647
|
+
}
|
|
648
|
+
});
|
|
649
|
+
if (!response.ok) {
|
|
650
|
+
console.error(`[Polydev] Token verification failed: ${response.status}`);
|
|
651
|
+
return false;
|
|
652
|
+
}
|
|
653
|
+
return true;
|
|
654
|
+
} catch (error) {
|
|
655
|
+
// Network error - assume valid to avoid blocking on transient issues
|
|
656
|
+
console.error('[Polydev] Token verification network error (assuming valid):', error.message);
|
|
657
|
+
return true;
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
/**
|
|
662
|
+
* Trigger re-authentication by opening browser and starting polling
|
|
663
|
+
* Used when token is detected as invalid/expired
|
|
664
|
+
* @param {*} id - JSON-RPC request id
|
|
665
|
+
* @param {string} reason - Human-readable reason for re-auth
|
|
666
|
+
* @returns {object} JSON-RPC response
|
|
667
|
+
*/
|
|
668
|
+
async triggerReAuth(id, reason = 'Token invalid or expired') {
|
|
669
|
+
const crypto = require('crypto');
|
|
670
|
+
const sessionId = crypto.randomBytes(32).toString('hex');
|
|
671
|
+
|
|
672
|
+
// Clear local auth state
|
|
673
|
+
this.isAuthenticated = false;
|
|
674
|
+
this.userToken = null;
|
|
675
|
+
|
|
676
|
+
try {
|
|
677
|
+
const createResponse = await fetch(`https://www.polydev.ai/api/auth/cli-session/${sessionId}`, {
|
|
678
|
+
method: 'POST',
|
|
679
|
+
headers: { 'Content-Type': 'application/json' }
|
|
680
|
+
});
|
|
681
|
+
|
|
682
|
+
if (!createResponse.ok) {
|
|
683
|
+
return {
|
|
684
|
+
jsonrpc: '2.0',
|
|
685
|
+
id,
|
|
686
|
+
result: {
|
|
687
|
+
content: [{
|
|
688
|
+
type: 'text',
|
|
689
|
+
text: `${reason}\n\nCould not initiate re-authentication. Please run:\n npx polydev-ai`
|
|
690
|
+
}],
|
|
691
|
+
isError: true
|
|
692
|
+
}
|
|
693
|
+
};
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
const authUrl = `https://polydev.ai/auth?session_id=${sessionId}&redirect=ide-plugin&auto=true`;
|
|
697
|
+
|
|
698
|
+
console.error(`[Polydev] Opening browser for re-authentication...`);
|
|
699
|
+
this.openBrowser(authUrl).catch(() => {
|
|
700
|
+
console.error('[Polydev] Could not open browser for re-authentication');
|
|
701
|
+
});
|
|
702
|
+
|
|
703
|
+
this.savePendingSession(sessionId);
|
|
704
|
+
this.startLoginPolling(sessionId);
|
|
705
|
+
|
|
706
|
+
return {
|
|
707
|
+
jsonrpc: '2.0',
|
|
708
|
+
id,
|
|
709
|
+
result: {
|
|
710
|
+
content: [{
|
|
711
|
+
type: 'text',
|
|
712
|
+
text: `RE-AUTHENTICATION REQUIRED
|
|
713
|
+
==========================
|
|
714
|
+
|
|
715
|
+
${reason}
|
|
716
|
+
|
|
717
|
+
A browser window is opening to re-authenticate.
|
|
718
|
+
|
|
719
|
+
If it doesn't open, visit:
|
|
720
|
+
${authUrl}
|
|
721
|
+
|
|
722
|
+
Once you sign in, your token will be updated automatically.`
|
|
723
|
+
}]
|
|
724
|
+
}
|
|
725
|
+
};
|
|
726
|
+
} catch (error) {
|
|
727
|
+
const cause = error.cause ? ` (${error.cause.code || error.cause.message || error.cause})` : '';
|
|
728
|
+
return {
|
|
729
|
+
jsonrpc: '2.0',
|
|
730
|
+
id,
|
|
731
|
+
result: {
|
|
732
|
+
content: [{
|
|
733
|
+
type: 'text',
|
|
734
|
+
text: `${reason}\n\nCould not reach server: ${error.message}${cause}\nPlease run: npx polydev-ai`
|
|
581
735
|
}],
|
|
582
736
|
isError: true
|
|
583
737
|
}
|
|
@@ -773,24 +927,9 @@ Configure: https://polydev.ai/dashboard/models`
|
|
|
773
927
|
}
|
|
774
928
|
};
|
|
775
929
|
} else {
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
result: {
|
|
780
|
-
content: [{
|
|
781
|
-
type: 'text',
|
|
782
|
-
text: `POLYDEV STATUS
|
|
783
|
-
==============
|
|
784
|
-
|
|
785
|
-
Token invalid or expired.
|
|
786
|
-
|
|
787
|
-
Please re-login:
|
|
788
|
-
1. Use the "login" tool
|
|
789
|
-
2. Or run: npx polydev-ai login`
|
|
790
|
-
}],
|
|
791
|
-
isError: true
|
|
792
|
-
}
|
|
793
|
-
};
|
|
930
|
+
// Token invalid/expired - auto-trigger re-authentication
|
|
931
|
+
console.error('[Polydev] Auth status: token invalid/expired, auto-triggering re-auth...');
|
|
932
|
+
return await this.triggerReAuth(id, 'Token invalid or expired.');
|
|
794
933
|
}
|
|
795
934
|
} catch (error) {
|
|
796
935
|
return {
|
|
@@ -1193,6 +1332,12 @@ Error: ${error.message}`
|
|
|
1193
1332
|
const errorText = await response.text();
|
|
1194
1333
|
console.error(`[Stdio Wrapper] Remote server error: ${response.status} - ${errorText}`);
|
|
1195
1334
|
|
|
1335
|
+
// Handle 401 specifically - token expired/invalid, trigger re-auth
|
|
1336
|
+
if (response.status === 401) {
|
|
1337
|
+
console.error('[Polydev] Remote API returned 401, auto-triggering re-authentication...');
|
|
1338
|
+
return await this.triggerReAuth(request.id, 'Authentication expired. Re-authenticating...');
|
|
1339
|
+
}
|
|
1340
|
+
|
|
1196
1341
|
return {
|
|
1197
1342
|
jsonrpc: '2.0',
|
|
1198
1343
|
id: request.id,
|
|
@@ -1247,6 +1392,13 @@ Error: ${error.message}`
|
|
|
1247
1392
|
// 3. Combines results
|
|
1248
1393
|
const result = await this.localSendCliPrompt(params.arguments);
|
|
1249
1394
|
|
|
1395
|
+
// Check if result indicates auth failure (from forwardToRemoteServer 401 handling)
|
|
1396
|
+
const resultText = result.content || this.formatCliResponse(result);
|
|
1397
|
+
if (result.result?.content?.[0]?.text?.includes('RE-AUTHENTICATION REQUIRED')) {
|
|
1398
|
+
// Auth failure already handled with re-auth response - pass through
|
|
1399
|
+
return result;
|
|
1400
|
+
}
|
|
1401
|
+
|
|
1250
1402
|
return {
|
|
1251
1403
|
jsonrpc: '2.0',
|
|
1252
1404
|
id,
|
|
@@ -1254,7 +1406,7 @@ Error: ${error.message}`
|
|
|
1254
1406
|
content: [
|
|
1255
1407
|
{
|
|
1256
1408
|
type: 'text',
|
|
1257
|
-
text:
|
|
1409
|
+
text: resultText
|
|
1258
1410
|
}
|
|
1259
1411
|
]
|
|
1260
1412
|
}
|
|
@@ -1262,6 +1414,14 @@ Error: ${error.message}`
|
|
|
1262
1414
|
|
|
1263
1415
|
} catch (error) {
|
|
1264
1416
|
console.error(`[Stdio Wrapper] get_perspectives error:`, error);
|
|
1417
|
+
|
|
1418
|
+
// Check if error is auth-related
|
|
1419
|
+
const errMsg = (error.message || '').toLowerCase();
|
|
1420
|
+
if (errMsg.includes('401') || errMsg.includes('unauthorized') || errMsg.includes('auth') || errMsg.includes('token')) {
|
|
1421
|
+
console.error('[Polydev] Perspectives auth error, auto-triggering re-authentication...');
|
|
1422
|
+
return await this.triggerReAuth(id, 'Authentication expired. Re-authenticating...');
|
|
1423
|
+
}
|
|
1424
|
+
|
|
1265
1425
|
return {
|
|
1266
1426
|
jsonrpc: '2.0',
|
|
1267
1427
|
id,
|