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.
Files changed (2) hide show
  1. package/mcp/stdio-wrapper.js +205 -45
  2. package/package.json +1 -1
@@ -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
- return {
482
- jsonrpc: '2.0',
483
- id,
484
- result: {
485
- content: [{
486
- type: 'text',
487
- text: `ALREADY AUTHENTICATED
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 ready.
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
- const createResponse = await fetch(`https://www.polydev.ai/api/auth/cli-session/${sessionId}`, {
508
- method: 'POST',
509
- headers: { 'Content-Type': 'application/json' }
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: `LOGIN SUCCESSFUL
555
- ================
595
+ text: `AUTHENTICATION STARTED
596
+ ======================
556
597
 
557
- Token saved to:
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
- IMPORTANT: Restart your IDE to activate.
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
- console.error('[Polydev] Login error:', error);
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
- return {
777
- jsonrpc: '2.0',
778
- id,
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: result.content || this.formatCliResponse(result)
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,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "polydev-ai",
3
- "version": "1.9.11",
3
+ "version": "1.9.13",
4
4
  "engines": {
5
5
  "node": ">=20.x <=22.x"
6
6
  },