polydev-ai 1.9.12 → 1.9.14
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 +272 -42
- package/package.json +1 -1
package/mcp/stdio-wrapper.js
CHANGED
|
@@ -476,27 +476,37 @@ 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
|
|
@@ -575,7 +585,16 @@ To re-login: npx polydev-ai`
|
|
|
575
585
|
// if this process dies, the new one can resume polling with same session)
|
|
576
586
|
this.startLoginPolling(sessionId);
|
|
577
587
|
|
|
578
|
-
//
|
|
588
|
+
// Wait for authentication to complete (blocks up to 120s)
|
|
589
|
+
console.error('[Polydev] Waiting for browser authentication to complete...');
|
|
590
|
+
const authenticated = await this.waitForAuthCompletion();
|
|
591
|
+
|
|
592
|
+
if (authenticated) {
|
|
593
|
+
console.error('[Polydev] Login successful, returning status...');
|
|
594
|
+
return await this.fetchAuthStatusResponse(id);
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
// Timeout - return fallback message
|
|
579
598
|
return {
|
|
580
599
|
jsonrpc: '2.0',
|
|
581
600
|
id,
|
|
@@ -585,34 +604,239 @@ To re-login: npx polydev-ai`
|
|
|
585
604
|
text: `AUTHENTICATION STARTED
|
|
586
605
|
======================
|
|
587
606
|
|
|
588
|
-
A browser window
|
|
607
|
+
A browser window was opened for you to sign in.
|
|
589
608
|
|
|
590
|
-
If it
|
|
609
|
+
If it didn't open, visit:
|
|
591
610
|
${authUrl}
|
|
592
611
|
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
612
|
+
Still waiting for login. Use /polydev:auth to check status after signing in.`
|
|
613
|
+
}]
|
|
614
|
+
}
|
|
615
|
+
};
|
|
616
|
+
|
|
617
|
+
} catch (error) {
|
|
618
|
+
const cause = error.cause ? ` (${error.cause.code || error.cause.message || error.cause})` : '';
|
|
619
|
+
console.error('[Polydev] Login error:', error.message, cause);
|
|
620
|
+
return {
|
|
621
|
+
jsonrpc: '2.0',
|
|
622
|
+
id,
|
|
623
|
+
result: {
|
|
624
|
+
content: [{
|
|
625
|
+
type: 'text',
|
|
626
|
+
text: `Login failed: ${error.message}${cause}\n\nPlease try: npx polydev-ai`
|
|
627
|
+
}],
|
|
628
|
+
isError: true
|
|
629
|
+
}
|
|
630
|
+
};
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
/**
|
|
635
|
+
* Verify the current token is valid by checking with the server
|
|
636
|
+
* Returns true if valid, false if invalid/expired
|
|
637
|
+
* On network error, returns true (don't block for transient issues)
|
|
638
|
+
*/
|
|
639
|
+
async verifyTokenWithServer() {
|
|
640
|
+
if (!this.userToken) return false;
|
|
641
|
+
try {
|
|
642
|
+
const response = await fetch('https://www.polydev.ai/api/auth/status', {
|
|
643
|
+
method: 'POST',
|
|
644
|
+
headers: {
|
|
645
|
+
'Content-Type': 'application/json',
|
|
646
|
+
'Authorization': `Bearer ${this.userToken}`,
|
|
647
|
+
'User-Agent': 'polydev-stdio-wrapper/1.0.0'
|
|
648
|
+
}
|
|
649
|
+
});
|
|
650
|
+
if (!response.ok) {
|
|
651
|
+
console.error(`[Polydev] Token verification failed: ${response.status}`);
|
|
652
|
+
return false;
|
|
653
|
+
}
|
|
654
|
+
return true;
|
|
655
|
+
} catch (error) {
|
|
656
|
+
// Network error - assume valid to avoid blocking on transient issues
|
|
657
|
+
console.error('[Polydev] Token verification network error (assuming valid):', error.message);
|
|
658
|
+
return true;
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
/**
|
|
663
|
+
* Wait for authentication to complete by polling this.isAuthenticated
|
|
664
|
+
* Used to block tool calls until login is detected by startLoginPolling
|
|
665
|
+
* @param {number} timeoutMs - Max time to wait (default 120s)
|
|
666
|
+
* @returns {boolean} true if authenticated, false if timed out
|
|
667
|
+
*/
|
|
668
|
+
async waitForAuthCompletion(timeoutMs = 120000) {
|
|
669
|
+
const pollInterval = 3000; // Check every 3 seconds
|
|
670
|
+
const maxPolls = Math.floor(timeoutMs / pollInterval);
|
|
671
|
+
|
|
672
|
+
for (let i = 0; i < maxPolls; i++) {
|
|
673
|
+
await new Promise(r => setTimeout(r, pollInterval));
|
|
674
|
+
if (this.isAuthenticated && this.userToken) {
|
|
675
|
+
return true;
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
return false;
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
/**
|
|
682
|
+
* Fetch account status and return formatted response
|
|
683
|
+
* Standalone helper that doesn't trigger re-auth (avoids circular calls)
|
|
684
|
+
* @param {*} id - JSON-RPC request id
|
|
685
|
+
* @returns {object} JSON-RPC response with account status
|
|
686
|
+
*/
|
|
687
|
+
async fetchAuthStatusResponse(id) {
|
|
688
|
+
try {
|
|
689
|
+
const accountResponse = await fetch('https://www.polydev.ai/api/auth/status', {
|
|
690
|
+
method: 'POST',
|
|
691
|
+
headers: {
|
|
692
|
+
'Content-Type': 'application/json',
|
|
693
|
+
'Authorization': `Bearer ${this.userToken}`,
|
|
694
|
+
'User-Agent': 'polydev-stdio-wrapper/1.0.0'
|
|
695
|
+
}
|
|
696
|
+
});
|
|
697
|
+
|
|
698
|
+
if (accountResponse.ok) {
|
|
699
|
+
const data = await accountResponse.json();
|
|
700
|
+
const credits = data.credits_remaining?.toLocaleString() || 0;
|
|
701
|
+
const messages = data.messages_remaining?.toLocaleString() || 0;
|
|
702
|
+
const tier = data.subscription_tier || 'Free';
|
|
703
|
+
const email = data.email || 'Connected';
|
|
704
|
+
|
|
705
|
+
return {
|
|
706
|
+
jsonrpc: '2.0',
|
|
707
|
+
id,
|
|
708
|
+
result: {
|
|
709
|
+
content: [{
|
|
710
|
+
type: 'text',
|
|
711
|
+
text: `LOGIN SUCCESSFUL
|
|
712
|
+
================
|
|
713
|
+
|
|
714
|
+
Authentication: Connected
|
|
715
|
+
Account: ${email}
|
|
716
|
+
Credits: ${credits}
|
|
717
|
+
Messages: ${messages}
|
|
718
|
+
Tier: ${tier}
|
|
596
719
|
|
|
597
|
-
|
|
720
|
+
You can now use:
|
|
598
721
|
/polydev:ask Query multiple AI models
|
|
599
|
-
/polydev:auth Check status & credits
|
|
722
|
+
/polydev:auth Check full status & credits
|
|
723
|
+
|
|
724
|
+
Dashboard: https://polydev.ai/dashboard`
|
|
725
|
+
}]
|
|
726
|
+
}
|
|
727
|
+
};
|
|
728
|
+
}
|
|
729
|
+
} catch (e) {
|
|
730
|
+
console.error('[Polydev] Post-login status fetch failed:', e.message);
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
// Fallback if status fetch fails
|
|
734
|
+
return {
|
|
735
|
+
jsonrpc: '2.0',
|
|
736
|
+
id,
|
|
737
|
+
result: {
|
|
738
|
+
content: [{
|
|
739
|
+
type: 'text',
|
|
740
|
+
text: `LOGIN SUCCESSFUL
|
|
741
|
+
================
|
|
742
|
+
|
|
743
|
+
Your token has been saved and is active.
|
|
744
|
+
|
|
745
|
+
You can now use:
|
|
746
|
+
/polydev:ask Query multiple AI models
|
|
747
|
+
/polydev:auth Check full status & credits
|
|
600
748
|
|
|
601
749
|
Dashboard: https://polydev.ai/dashboard`
|
|
750
|
+
}]
|
|
751
|
+
}
|
|
752
|
+
};
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
/**
|
|
756
|
+
* Trigger re-authentication by opening browser and starting polling
|
|
757
|
+
* Waits for auth to complete inline (up to 120s) then returns full status
|
|
758
|
+
* Used when token is detected as invalid/expired
|
|
759
|
+
* @param {*} id - JSON-RPC request id
|
|
760
|
+
* @param {string} reason - Human-readable reason for re-auth
|
|
761
|
+
* @returns {object} JSON-RPC response
|
|
762
|
+
*/
|
|
763
|
+
async triggerReAuth(id, reason = 'Token invalid or expired') {
|
|
764
|
+
const crypto = require('crypto');
|
|
765
|
+
const sessionId = crypto.randomBytes(32).toString('hex');
|
|
766
|
+
|
|
767
|
+
// Clear local auth state
|
|
768
|
+
this.isAuthenticated = false;
|
|
769
|
+
this.userToken = null;
|
|
770
|
+
|
|
771
|
+
try {
|
|
772
|
+
const createResponse = await fetch(`https://www.polydev.ai/api/auth/cli-session/${sessionId}`, {
|
|
773
|
+
method: 'POST',
|
|
774
|
+
headers: { 'Content-Type': 'application/json' }
|
|
775
|
+
});
|
|
776
|
+
|
|
777
|
+
if (!createResponse.ok) {
|
|
778
|
+
return {
|
|
779
|
+
jsonrpc: '2.0',
|
|
780
|
+
id,
|
|
781
|
+
result: {
|
|
782
|
+
content: [{
|
|
783
|
+
type: 'text',
|
|
784
|
+
text: `${reason}\n\nCould not initiate re-authentication. Please run:\n npx polydev-ai`
|
|
785
|
+
}],
|
|
786
|
+
isError: true
|
|
787
|
+
}
|
|
788
|
+
};
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
const authUrl = `https://polydev.ai/auth?session_id=${sessionId}&redirect=ide-plugin&auto=true`;
|
|
792
|
+
|
|
793
|
+
console.error(`[Polydev] Opening browser for re-authentication...`);
|
|
794
|
+
this.openBrowser(authUrl).catch(() => {
|
|
795
|
+
console.error('[Polydev] Could not open browser for re-authentication');
|
|
796
|
+
});
|
|
797
|
+
|
|
798
|
+
this.savePendingSession(sessionId);
|
|
799
|
+
this.startLoginPolling(sessionId);
|
|
800
|
+
|
|
801
|
+
// Wait for authentication to complete (blocks up to 120s)
|
|
802
|
+
console.error('[Polydev] Waiting for browser authentication to complete...');
|
|
803
|
+
const authenticated = await this.waitForAuthCompletion();
|
|
804
|
+
|
|
805
|
+
if (authenticated) {
|
|
806
|
+
console.error('[Polydev] Re-authentication successful, returning status...');
|
|
807
|
+
return await this.fetchAuthStatusResponse(id);
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
// Timeout - return fallback message
|
|
811
|
+
return {
|
|
812
|
+
jsonrpc: '2.0',
|
|
813
|
+
id,
|
|
814
|
+
result: {
|
|
815
|
+
content: [{
|
|
816
|
+
type: 'text',
|
|
817
|
+
text: `RE-AUTHENTICATION STARTED
|
|
818
|
+
=========================
|
|
819
|
+
|
|
820
|
+
${reason}
|
|
821
|
+
|
|
822
|
+
A browser window was opened for re-authentication.
|
|
823
|
+
|
|
824
|
+
If it didn't open, visit:
|
|
825
|
+
${authUrl}
|
|
826
|
+
|
|
827
|
+
Still waiting for login. Use /polydev:auth to check status after signing in.`
|
|
602
828
|
}]
|
|
603
829
|
}
|
|
604
830
|
};
|
|
605
|
-
|
|
606
831
|
} catch (error) {
|
|
607
832
|
const cause = error.cause ? ` (${error.cause.code || error.cause.message || error.cause})` : '';
|
|
608
|
-
console.error('[Polydev] Login error:', error.message, cause);
|
|
609
833
|
return {
|
|
610
834
|
jsonrpc: '2.0',
|
|
611
835
|
id,
|
|
612
836
|
result: {
|
|
613
837
|
content: [{
|
|
614
838
|
type: 'text',
|
|
615
|
-
text:
|
|
839
|
+
text: `${reason}\n\nCould not reach server: ${error.message}${cause}\nPlease run: npx polydev-ai`
|
|
616
840
|
}],
|
|
617
841
|
isError: true
|
|
618
842
|
}
|
|
@@ -808,24 +1032,9 @@ Configure: https://polydev.ai/dashboard/models`
|
|
|
808
1032
|
}
|
|
809
1033
|
};
|
|
810
1034
|
} else {
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
result: {
|
|
815
|
-
content: [{
|
|
816
|
-
type: 'text',
|
|
817
|
-
text: `POLYDEV STATUS
|
|
818
|
-
==============
|
|
819
|
-
|
|
820
|
-
Token invalid or expired.
|
|
821
|
-
|
|
822
|
-
Please re-login:
|
|
823
|
-
1. Use the "login" tool
|
|
824
|
-
2. Or run: npx polydev-ai login`
|
|
825
|
-
}],
|
|
826
|
-
isError: true
|
|
827
|
-
}
|
|
828
|
-
};
|
|
1035
|
+
// Token invalid/expired - auto-trigger re-authentication
|
|
1036
|
+
console.error('[Polydev] Auth status: token invalid/expired, auto-triggering re-auth...');
|
|
1037
|
+
return await this.triggerReAuth(id, 'Token invalid or expired.');
|
|
829
1038
|
}
|
|
830
1039
|
} catch (error) {
|
|
831
1040
|
return {
|
|
@@ -1228,6 +1437,12 @@ Error: ${error.message}`
|
|
|
1228
1437
|
const errorText = await response.text();
|
|
1229
1438
|
console.error(`[Stdio Wrapper] Remote server error: ${response.status} - ${errorText}`);
|
|
1230
1439
|
|
|
1440
|
+
// Handle 401 specifically - token expired/invalid, trigger re-auth
|
|
1441
|
+
if (response.status === 401) {
|
|
1442
|
+
console.error('[Polydev] Remote API returned 401, auto-triggering re-authentication...');
|
|
1443
|
+
return await this.triggerReAuth(request.id, 'Authentication expired. Re-authenticating...');
|
|
1444
|
+
}
|
|
1445
|
+
|
|
1231
1446
|
return {
|
|
1232
1447
|
jsonrpc: '2.0',
|
|
1233
1448
|
id: request.id,
|
|
@@ -1282,6 +1497,13 @@ Error: ${error.message}`
|
|
|
1282
1497
|
// 3. Combines results
|
|
1283
1498
|
const result = await this.localSendCliPrompt(params.arguments);
|
|
1284
1499
|
|
|
1500
|
+
// Check if result indicates auth failure (from forwardToRemoteServer 401 handling)
|
|
1501
|
+
const resultText = result.content || this.formatCliResponse(result);
|
|
1502
|
+
if (result.result?.content?.[0]?.text?.includes('RE-AUTHENTICATION REQUIRED')) {
|
|
1503
|
+
// Auth failure already handled with re-auth response - pass through
|
|
1504
|
+
return result;
|
|
1505
|
+
}
|
|
1506
|
+
|
|
1285
1507
|
return {
|
|
1286
1508
|
jsonrpc: '2.0',
|
|
1287
1509
|
id,
|
|
@@ -1289,7 +1511,7 @@ Error: ${error.message}`
|
|
|
1289
1511
|
content: [
|
|
1290
1512
|
{
|
|
1291
1513
|
type: 'text',
|
|
1292
|
-
text:
|
|
1514
|
+
text: resultText
|
|
1293
1515
|
}
|
|
1294
1516
|
]
|
|
1295
1517
|
}
|
|
@@ -1297,6 +1519,14 @@ Error: ${error.message}`
|
|
|
1297
1519
|
|
|
1298
1520
|
} catch (error) {
|
|
1299
1521
|
console.error(`[Stdio Wrapper] get_perspectives error:`, error);
|
|
1522
|
+
|
|
1523
|
+
// Check if error is auth-related
|
|
1524
|
+
const errMsg = (error.message || '').toLowerCase();
|
|
1525
|
+
if (errMsg.includes('401') || errMsg.includes('unauthorized') || errMsg.includes('auth') || errMsg.includes('token')) {
|
|
1526
|
+
console.error('[Polydev] Perspectives auth error, auto-triggering re-authentication...');
|
|
1527
|
+
return await this.triggerReAuth(id, 'Authentication expired. Re-authenticating...');
|
|
1528
|
+
}
|
|
1529
|
+
|
|
1300
1530
|
return {
|
|
1301
1531
|
jsonrpc: '2.0',
|
|
1302
1532
|
id,
|