polydev-ai 1.9.26 → 1.9.28

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 +170 -263
  2. package/package.json +1 -1
@@ -155,9 +155,6 @@ if (writableTmp) {
155
155
  * Strips: provider info, approval, sandbox, reasoning, session id, MCP errors, etc.
156
156
  *
157
157
  * Codex CLI output structure:
158
- * [metadata] → user → [echoed prompt] → thinking → [status] → codex → [RESPONSE] → tokens used → [count]
159
- *
160
- * Claude Code output structure:
161
158
  * [may include JSON or plain text response]
162
159
  */
163
160
  function cleanCliResponse(content) {
@@ -1046,9 +1043,28 @@ To authenticate, pass your token directly:
1046
1043
  };
1047
1044
  }
1048
1045
 
1049
- // Not sandboxed — auto-trigger browser login
1050
- console.error('[Polydev] No token found, auto-triggering login flow...');
1051
- return await this.triggerReAuth(id, 'Not authenticated. Opening browser for login...');
1046
+ // Not sandboxed — tell user to use login tool (don't auto-open browser)
1047
+ console.error('[Polydev] No token found, prompting user to login');
1048
+ return {
1049
+ jsonrpc: '2.0',
1050
+ id,
1051
+ result: {
1052
+ content: [{
1053
+ type: 'text',
1054
+ text: `POLYDEV STATUS
1055
+ ==============
1056
+
1057
+ Authentication: Not connected
1058
+
1059
+ To login:
1060
+ 1. Use the "login" tool (opens browser)
1061
+ 2. Or run: npx polydev-ai
1062
+
1063
+ Token will be saved automatically after login.`
1064
+ }],
1065
+ isError: true
1066
+ }
1067
+ };
1052
1068
  }
1053
1069
 
1054
1070
  try {
@@ -1138,9 +1154,26 @@ Configure: https://polydev.ai/dashboard/models`
1138
1154
  }
1139
1155
  };
1140
1156
  } else {
1141
- // Token invalid/expired - auto-trigger re-authentication
1142
- console.error('[Polydev] Auth status: token invalid/expired, auto-triggering re-auth...');
1143
- return await this.triggerReAuth(id, 'Token invalid or expired.');
1157
+ // Token invalid/expired - tell user to re-login (don't auto-open browser)
1158
+ console.error('[Polydev] Auth status: token invalid/expired');
1159
+ return {
1160
+ jsonrpc: '2.0',
1161
+ id,
1162
+ result: {
1163
+ content: [{
1164
+ type: 'text',
1165
+ text: `POLYDEV STATUS
1166
+ ==============
1167
+
1168
+ Authentication: Token invalid or expired
1169
+
1170
+ To re-authenticate:
1171
+ 1. Use the "login" tool (opens browser)
1172
+ 2. Or run: npx polydev-ai`
1173
+ }],
1174
+ isError: true
1175
+ }
1176
+ };
1144
1177
  }
1145
1178
  } catch (error) {
1146
1179
  // Categorize the error for clearer user messaging
@@ -1566,76 +1599,64 @@ To re-login: /polydev:login`
1566
1599
  </html>`;
1567
1600
  }
1568
1601
 
1569
- async forwardToRemoteServer(request) {
1570
- console.error(`[Stdio Wrapper] Forwarding request to remote server`);
1571
-
1572
- try {
1573
- // Use AbortController for timeout if available, otherwise rely on fetch timeout
1574
- const controller = typeof AbortController !== 'undefined' ? new AbortController() : null;
1575
- const timeoutId = controller ? setTimeout(() => controller.abort(), 400000) : null; // 400s timeout
1576
-
1577
- const response = await fetch('https://www.polydev.ai/api/mcp', {
1578
- method: 'POST',
1579
- headers: {
1580
- 'Content-Type': 'application/json',
1581
- 'Authorization': `Bearer ${this.userToken}`,
1582
- 'User-Agent': 'polydev-stdio-wrapper/1.0.0'
1583
- },
1584
- body: JSON.stringify(request),
1585
- ...(controller ? { signal: controller.signal } : {})
1586
- });
1587
-
1588
- if (timeoutId) clearTimeout(timeoutId);
1589
-
1590
- if (!response.ok) {
1591
- const errorText = await response.text();
1592
- console.error(`[Stdio Wrapper] Remote server error: ${response.status} - ${errorText}`);
1593
-
1594
- // Handle 401 specifically - token expired/invalid, trigger re-auth
1595
- if (response.status === 401) {
1596
- console.error('[Polydev] Remote API returned 401, auto-triggering re-authentication...');
1597
- return await this.triggerReAuth(request.id, 'Authentication expired. Re-authenticating...');
1598
- }
1599
-
1600
- return {
1601
- jsonrpc: '2.0',
1602
- id: request.id,
1603
- error: {
1604
- code: -32603,
1605
- message: `Remote server error: ${response.status} - ${errorText}`
1606
- }
1607
- };
1602
+ /**
1603
+ * Send an MCP progress notification to the client
1604
+ * This is a JSON-RPC notification (no id) that compatible clients use to:
1605
+ * 1. Display progress UI (status bar, spinners)
1606
+ * 2. Reset their tool call timeout (if resetTimeoutOnProgress is enabled)
1607
+ * @param {string|number} progressToken - Token from request's _meta.progressToken
1608
+ * @param {number} progress - Current progress value (must increase monotonically)
1609
+ * @param {number} total - Total expected progress value
1610
+ * @param {string} message - Human-readable status message
1611
+ */
1612
+ sendProgressNotification(progressToken, progress, total, message) {
1613
+ if (!progressToken) return; // Client didn't request progress
1614
+
1615
+ const notification = {
1616
+ jsonrpc: '2.0',
1617
+ method: 'notifications/progress',
1618
+ params: {
1619
+ progressToken,
1620
+ progress,
1621
+ total,
1622
+ message
1608
1623
  }
1609
-
1610
- const result = await response.json();
1611
- console.error(`[Stdio Wrapper] Got response from remote server`);
1612
- return result;
1613
- } catch (error) {
1614
- console.error(`[Stdio Wrapper] Network error:`, error.message);
1615
- return {
1616
- jsonrpc: '2.0',
1617
- id: request.id,
1618
- error: {
1619
- code: -32603,
1620
- message: `Network error: ${error.message}`
1621
- }
1622
- };
1624
+ };
1625
+
1626
+ try {
1627
+ process.stdout.write(JSON.stringify(notification) + '\n');
1628
+ console.error(`[Polydev] Progress: ${progress}/${total} - ${message}`);
1629
+ } catch (err) {
1630
+ console.error(`[Polydev] Failed to send progress notification:`, err.message);
1623
1631
  }
1624
1632
  }
1625
1633
 
1626
1634
  /**
1627
- * Check if a tool is a CLI tool that should be handled locally
1635
+ * Start a periodic progress heartbeat to prevent client-side timeouts.
1636
+ * Sends a progress notification every intervalMs to signal the server is still working.
1637
+ * Compatible MCP clients will reset their timeout clock on each notification.
1638
+ * @param {string|number} progressToken - Token from request's _meta.progressToken
1639
+ * @param {number} intervalMs - Heartbeat interval in milliseconds (default: 10s)
1640
+ * @returns {{ stop: Function, tick: Function }} Controller to stop heartbeat and manually tick progress
1628
1641
  */
1629
- isCliTool(toolName) {
1630
- const cliTools = [
1631
- 'force_cli_detection',
1632
- 'get_cli_status',
1633
- 'send_cli_prompt',
1634
- 'polydev.force_cli_detection',
1635
- 'polydev.get_cli_status',
1636
- 'polydev.send_cli_prompt'
1637
- ];
1638
- return cliTools.includes(toolName);
1642
+ startProgressHeartbeat(progressToken, intervalMs = 10000) {
1643
+ if (!progressToken) return { stop: () => {}, tick: () => {} };
1644
+
1645
+ let currentProgress = 0;
1646
+ const total = 100; // Use percentage-style progress
1647
+
1648
+ const interval = setInterval(() => {
1649
+ currentProgress = Math.min(currentProgress + 5, 95); // Never reach 100 until done
1650
+ this.sendProgressNotification(progressToken, currentProgress, total, 'Processing perspectives...');
1651
+ }, intervalMs);
1652
+
1653
+ return {
1654
+ stop: () => clearInterval(interval),
1655
+ tick: (progress, message) => {
1656
+ currentProgress = progress;
1657
+ this.sendProgressNotification(progressToken, progress, total, message);
1658
+ }
1659
+ };
1639
1660
  }
1640
1661
 
1641
1662
  /**
@@ -1644,12 +1665,30 @@ To re-login: /polydev:login`
1644
1665
  async handleGetPerspectivesWithCLIs(params, id) {
1645
1666
  console.error(`[Stdio Wrapper] Handling get_perspectives with local CLIs + remote`);
1646
1667
 
1668
+ // Extract progress token from request metadata (MCP spec)
1669
+ // Clients that want progress updates include this in _meta.progressToken
1670
+ const progressToken = params?._meta?.progressToken;
1671
+ if (progressToken) {
1672
+ console.error(`[Stdio Wrapper] Client requested progress updates (token: ${progressToken})`);
1673
+ }
1674
+
1675
+ // Start periodic heartbeat to prevent client-side timeouts
1676
+ // Sends progress notification every 10s to keep the connection alive
1677
+ const heartbeat = this.startProgressHeartbeat(progressToken);
1678
+
1647
1679
  try {
1680
+ // Send initial progress
1681
+ heartbeat.tick(5, 'Initializing perspectives engine...');
1682
+
1648
1683
  // Use existing localSendCliPrompt logic which already:
1649
1684
  // 1. Checks all local CLIs
1650
1685
  // 2. Calls remote perspectives
1651
1686
  // 3. Combines results
1652
- const result = await this.localSendCliPrompt(params.arguments);
1687
+ const result = await this.localSendCliPrompt(params.arguments, progressToken);
1688
+
1689
+ // Stop heartbeat before sending final response
1690
+ heartbeat.stop();
1691
+ heartbeat.tick(100, 'Complete');
1653
1692
 
1654
1693
  // Check if result indicates auth failure (from forwardToRemoteServer 401 handling)
1655
1694
  if (result?.result?.content?.[0]?.text?.includes('RE-AUTHENTICATION REQUIRED')) {
@@ -1678,13 +1717,27 @@ To re-login: /polydev:login`
1678
1717
  };
1679
1718
 
1680
1719
  } catch (error) {
1720
+ heartbeat.stop(); // Always clean up heartbeat
1681
1721
  console.error(`[Stdio Wrapper] get_perspectives error:`, error);
1682
1722
 
1683
- // Check if error is auth-related
1723
+ // Check if error is specifically an HTTP 401 unauthorized
1724
+ // IMPORTANT: Don't match generic 'token' or 'auth' strings — they appear in
1725
+ // unrelated errors like "Unexpected token" (JSON parse) or "authentication" (CLI output)
1684
1726
  const errMsg = (error.message || '').toLowerCase();
1685
- if (errMsg.includes('401') || errMsg.includes('unauthorized') || errMsg.includes('auth') || errMsg.includes('token')) {
1686
- console.error('[Polydev] Perspectives auth error, auto-triggering re-authentication...');
1687
- return await this.triggerReAuth(id, 'Authentication expired. Re-authenticating...');
1727
+ const isHttp401 = errMsg.includes('401') || errMsg.includes('unauthorized');
1728
+ if (isHttp401) {
1729
+ console.error('[Polydev] Perspectives auth error (401), suggesting re-login');
1730
+ return {
1731
+ jsonrpc: '2.0',
1732
+ id,
1733
+ result: {
1734
+ content: [{
1735
+ type: 'text',
1736
+ text: `Authentication error: ${error.message}\n\nPlease re-authenticate using the "login" tool or run: npx polydev-ai`
1737
+ }],
1738
+ isError: true
1739
+ }
1740
+ };
1688
1741
  }
1689
1742
 
1690
1743
  return {
@@ -1704,9 +1757,13 @@ To re-login: /polydev:login`
1704
1757
  async handleLocalCliTool(request) {
1705
1758
  const { method, params, id } = request;
1706
1759
  const { name: toolName, arguments: args } = params;
1760
+ const progressToken = params?._meta?.progressToken;
1707
1761
 
1708
1762
  console.error(`[Stdio Wrapper] Handling local CLI tool: ${toolName}`);
1709
1763
 
1764
+ // Start heartbeat for long-running CLI operations
1765
+ const heartbeat = this.startProgressHeartbeat(progressToken);
1766
+
1710
1767
  try {
1711
1768
  let result;
1712
1769
 
@@ -1723,13 +1780,14 @@ To re-login: /polydev:login`
1723
1780
 
1724
1781
  case 'send_cli_prompt':
1725
1782
  case 'polydev.send_cli_prompt':
1726
- result = await this.localSendCliPrompt(args);
1783
+ result = await this.localSendCliPrompt(args, progressToken);
1727
1784
  break;
1728
1785
 
1729
1786
  default:
1730
1787
  throw new Error(`Unknown CLI tool: ${toolName}`);
1731
1788
  }
1732
1789
 
1790
+ heartbeat.stop();
1733
1791
  return {
1734
1792
  jsonrpc: '2.0',
1735
1793
  id,
@@ -1745,6 +1803,7 @@ To re-login: /polydev:login`
1745
1803
 
1746
1804
  } catch (error) {
1747
1805
  console.error(`[Stdio Wrapper] CLI tool error:`, error);
1806
+ heartbeat.stop();
1748
1807
  return {
1749
1808
  jsonrpc: '2.0',
1750
1809
  id,
@@ -1885,7 +1944,7 @@ To re-login: /polydev:login`
1885
1944
  * Respects user's perspectives_per_message setting for total perspectives
1886
1945
  * Uses allProviders from dashboard - tries CLI first, falls back to API
1887
1946
  */
1888
- async localSendCliPrompt(args) {
1947
+ async localSendCliPrompt(args, progressToken) {
1889
1948
  console.error(`[Stdio Wrapper] Local CLI prompt sending with perspectives`);
1890
1949
 
1891
1950
  try {
@@ -1912,6 +1971,8 @@ To re-login: /polydev:login`
1912
1971
  } catch (prefError) {
1913
1972
  console.error('[Stdio Wrapper] Model preferences fetch failed (will use CLI defaults):', prefError.message);
1914
1973
  }
1974
+
1975
+ this.sendProgressNotification(progressToken, 15, 100, 'Loaded model preferences');
1915
1976
 
1916
1977
  // Get the user's perspectives_per_message setting (default 2)
1917
1978
  const maxPerspectives = this.perspectivesPerMessage || 2;
@@ -1920,14 +1981,11 @@ To re-login: /polydev:login`
1920
1981
  let localResults = [];
1921
1982
 
1922
1983
  if (provider_id) {
1923
- // Specific provider requested - use only that one
1984
+ // Single provider requested - use only that one
1924
1985
  console.error(`[Stdio Wrapper] Using specific provider: ${provider_id}`);
1925
1986
  const model = modelPreferences[provider_id] || null;
1926
- if (model) {
1927
- console.error(`[Stdio Wrapper] Using user's preferred model for ${provider_id}: ${model}`);
1928
- } else {
1929
- console.error(`[Stdio Wrapper] No model preference for ${provider_id}, using CLI default`);
1930
- }
1987
+ console.error(`[Stdio Wrapper] Using user's preferred model for ${provider_id}: ${model}`);
1988
+ this.sendProgressNotification(progressToken, 20, 100, `Querying ${provider_id}...`);
1931
1989
  const result = await this.cliManager.sendCliPrompt(provider_id, prompt, mode, gracefulTimeout, model);
1932
1990
  localResults = [{ provider_id, ...result }];
1933
1991
  } else {
@@ -1939,6 +1997,7 @@ To re-login: /polydev:login`
1939
1997
  // Get available CLIs for checking
1940
1998
  const { available: availableClis } = await this.getAllAvailableProviders();
1941
1999
  console.error(`[Stdio Wrapper] Available CLIs: ${availableClis.join(', ') || 'none'}`);
2000
+ this.sendProgressNotification(progressToken, 25, 100, `Found ${availableClis.length} CLIs: ${availableClis.join(', ') || 'none'}`);
1942
2001
 
1943
2002
  // Get all providers from dashboard (user's configured providers + CLI-only)
1944
2003
  const allProviders = this.allProviders || [];
@@ -1983,7 +2042,7 @@ To re-login: /polydev:login`
1983
2042
  for (const cliId of cliPriorityOrder) {
1984
2043
  if (!availableClis.includes(cliId)) continue;
1985
2044
  if (cliId === excludedCli) {
1986
- console.error(`[Stdio Wrapper] [CLI-FIRST] Skipping ${cliId} (same as current IDE — would cause recursive call)`);
2045
+ console.error(`[Stdio Wrapper] [CLI-FIRST] Skipping ${excludedCli} (same as current IDE — would cause recursive call)`);
1987
2046
  continue;
1988
2047
  }
1989
2048
 
@@ -2041,6 +2100,7 @@ To re-login: /polydev:login`
2041
2100
  // Run ALL CLI prompts concurrently with fast-collect pattern
2042
2101
  // Resolves once we have maxPerspectives successes OR all complete
2043
2102
  if (cliProviderEntries.length > 0) {
2103
+ this.sendProgressNotification(progressToken, 30, 100, `Querying ${cliProviderEntries.length} CLIs in parallel...`);
2044
2104
  const cliPromises = cliProviderEntries.map(async (providerEntry) => {
2045
2105
  try {
2046
2106
  // ONLY use the model from providerEntry (which is filtered to user's own API keys, not credits)
@@ -2094,6 +2154,7 @@ To re-login: /polydev:login`
2094
2154
  // Fast-collect: resolve early once we have maxPerspectives successes OR all complete
2095
2155
  localResults = await this.collectFirstNSuccesses(cliPromises, maxPerspectives);
2096
2156
  console.error(`[Stdio Wrapper] Fast-collect: got ${localResults.filter(r => r.success).length} successful, ${localResults.filter(r => !r.success).length} failed out of ${cliPromises.length} CLIs`);
2157
+ this.sendProgressNotification(progressToken, 55, 100, `Got ${localResults.filter(r => r.success).length} CLI responses`);
2097
2158
  }
2098
2159
 
2099
2160
  // Store API-only providers info for remote API call
@@ -2116,6 +2177,7 @@ To re-login: /polydev:login`
2116
2177
  // Get remote perspectives for API-only providers and failed CLIs
2117
2178
  let perspectivesResult;
2118
2179
  if (remainingPerspectives > 0) {
2180
+ this.sendProgressNotification(progressToken, 65, 100, `Fetching ${remainingPerspectives} more perspectives from API...`);
2119
2181
  console.error(`[Stdio Wrapper] Calling remote API for ${remainingPerspectives} more perspectives (have ${successfulLocalCount}/${maxPerspectives})`);
2120
2182
 
2121
2183
  // Pass API-only provider info to help remote API choose correct models
@@ -2155,6 +2217,7 @@ To re-login: /polydev:login`
2155
2217
  }
2156
2218
 
2157
2219
  // Combine all results
2220
+ this.sendProgressNotification(progressToken, 90, 100, 'Combining all perspectives...');
2158
2221
  return this.combineAllCliAndPerspectives(localResults, perspectivesResult, args);
2159
2222
 
2160
2223
  } catch (error) {
@@ -2462,7 +2525,7 @@ To re-login: /polydev:login`
2462
2525
  user_token: this.userToken,
2463
2526
  // Exclude providers that succeeded locally
2464
2527
  exclude_providers: excludeProviders,
2465
- // NEW: Specific providers to request (from API-only list)
2528
+ // NEW: Specific providers to request (from API-only providers)
2466
2529
  request_providers: requestProviders.length > 0 ? requestProviders : undefined,
2467
2530
  // Pass CLI responses for dashboard logging
2468
2531
  cli_responses: cliResponses,
@@ -2659,8 +2722,8 @@ To re-login: /polydev:login`
2659
2722
  const statusFile = path.join(polydevevDir, 'cli-status.json');
2660
2723
 
2661
2724
  // Ensure directory exists
2662
- if (!fs.existsSync(polydevevDir)) {
2663
- fs.mkdirSync(polydevevDir, { recursive: true });
2725
+ if (!fs.existsSync(polydeveevDir)) {
2726
+ fs.mkdirSync(polydeveevDir, { recursive: true });
2664
2727
  }
2665
2728
 
2666
2729
  // Save status with timestamp
@@ -2849,11 +2912,11 @@ To re-login: /polydev:login`
2849
2912
  try {
2850
2913
  const homeDir = require('os').homedir();
2851
2914
  const polydevevDir = path.join(homeDir, '.polydev');
2852
- const usageFile = path.join(polydevevDir, 'cli-usage.json');
2915
+ const usageFile = path.join(polydeveevDir, 'cli-usage.json');
2853
2916
 
2854
2917
  // Ensure directory exists
2855
- if (!fs.existsSync(polydevevDir)) {
2856
- fs.mkdirSync(polydevevDir, { recursive: true });
2918
+ if (!fs.existsSync(polydeveevDir)) {
2919
+ fs.mkdirSync(polydeveevDir, { recursive: true });
2857
2920
  }
2858
2921
 
2859
2922
  // Add new usage record
@@ -2963,7 +3026,7 @@ To re-login: /polydev:login`
2963
3026
  this.userModelPreferences = result.modelPreferences;
2964
3027
  this.modelPreferencesCacheTime = Date.now();
2965
3028
 
2966
- // Also cache perspectives_per_message setting (default 2)
3029
+ // Also cache perspectivesPerMessage setting (default 2)
2967
3030
  this.perspectivesPerMessage = result.perspectivesPerMessage || 2;
2968
3031
 
2969
3032
  // Cache provider order from user's dashboard (respects display_order)
@@ -3124,27 +3187,24 @@ To re-login: /polydev:login`
3124
3187
  }
3125
3188
 
3126
3189
  /**
3127
- * Run startup flow - auto-login if no token, verify if has token
3190
+ * Run startup flow - verify token if present, show instructions if not
3191
+ * NEVER auto-opens browser — only the explicit "login" tool should do that
3128
3192
  */
3129
3193
  async runStartupFlow() {
3194
+ // Last-chance token reload before deciding
3130
3195
  if (!this.userToken) {
3131
- // No token - auto-open browser for login (NON-BLOCKING)
3196
+ this.reloadTokenFromFiles();
3197
+ }
3198
+
3199
+ if (!this.userToken) {
3200
+ // No token found — just log instructions, don't open browser
3132
3201
  console.error('─'.repeat(50));
3133
- console.error('Polydev - First Time Setup');
3202
+ console.error('Polydev - Authentication Required');
3134
3203
  console.error('─'.repeat(50));
3135
- console.error('Opening browser for authentication...\n');
3136
-
3137
- // Run auto-login in background (don't block MCP server startup)
3138
- this.autoLoginOnStartup().then(() => {
3139
- console.error('[Polydev] Login completed, running CLI detection...');
3140
- // After login, run CLI detection
3141
- return this.localForceCliDetection({});
3142
- }).then(() => {
3143
- this._cliDetectionComplete = true;
3144
- return this.displayCliStatus();
3145
- }).catch((error) => {
3146
- console.error('[Polydev] Auto-login flow error:', error.message);
3147
- });
3204
+ console.error('No token found. To authenticate:');
3205
+ console.error(' 1. Use the "login" tool');
3206
+ console.error(' 2. Or run: npx polydev-ai');
3207
+ console.error('─'.repeat(50) + '\n');
3148
3208
  } else {
3149
3209
  // Has token - verify it (also non-blocking)
3150
3210
  this.verifyAndDisplayAuth().catch((error) => {
@@ -3153,159 +3213,6 @@ To re-login: /polydev:login`
3153
3213
  }
3154
3214
  }
3155
3215
 
3156
- /**
3157
- * Auto-login on startup (opens browser automatically)
3158
- */
3159
- async autoLoginOnStartup() {
3160
- const http = require('http');
3161
- const { spawn } = require('child_process');
3162
-
3163
- return new Promise((resolve) => {
3164
- const server = http.createServer((req, res) => {
3165
- const url = new URL(req.url, `http://localhost`);
3166
-
3167
- if (req.method === 'OPTIONS') {
3168
- res.writeHead(204, {
3169
- 'Access-Control-Allow-Origin': '*',
3170
- 'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
3171
- 'Access-Control-Allow-Headers': 'Content-Type'
3172
- });
3173
- res.end();
3174
- return;
3175
- }
3176
-
3177
- if (url.pathname === '/callback') {
3178
- const token = url.searchParams.get('token');
3179
-
3180
- if (token && token.startsWith('pd_')) {
3181
- this.saveTokenToFiles(token);
3182
- this.userToken = token;
3183
- this.isAuthenticated = true;
3184
- this._freshLogin = true; // Flag for opening models page after CLI detection
3185
-
3186
- res.writeHead(200, {
3187
- 'Content-Type': 'text/html; charset=utf-8',
3188
- 'Access-Control-Allow-Origin': '*'
3189
- });
3190
- res.end(this.getLoginSuccessHTML());
3191
-
3192
- console.error('[Polydev] Login successful, token saved');
3193
-
3194
- // Wait 7 seconds before closing server (gives time for 5s auto-close countdown + buffer)
3195
- setTimeout(() => {
3196
- server.close();
3197
- resolve({
3198
- jsonrpc: '2.0',
3199
- id,
3200
- result: {
3201
- content: [{
3202
- type: 'text',
3203
- text: `LOGIN SUCCESSFUL
3204
- ================
3205
-
3206
- Token saved to:
3207
- ~/.polydev.env
3208
- ~/.zshrc
3209
-
3210
- IMPORTANT: Restart your IDE to activate.
3211
-
3212
- After restart, you can:
3213
- /polydev:ask Query multiple AI models
3214
- /polydev:auth Check status & credits
3215
-
3216
- Dashboard: https://polydev.ai/dashboard`
3217
- }]
3218
- }
3219
- });
3220
- }, 7000);
3221
- } else {
3222
- res.writeHead(400, { 'Content-Type': 'text/plain' });
3223
- res.end('Invalid or missing token');
3224
- }
3225
- } else {
3226
- res.writeHead(404);
3227
- res.end('Not found');
3228
- }
3229
- });
3230
-
3231
- server.listen(0, 'localhost', () => {
3232
- const port = server.address().port;
3233
- const callbackUrl = `http://localhost:${port}/callback`;
3234
- const authUrl = `https://polydev.ai/auth?callback=${encodeURIComponent(callbackUrl)}&redirect=ide-plugin&auto=true`;
3235
-
3236
- console.error('If browser does not open, visit:');
3237
- console.error(authUrl);
3238
- console.error('');
3239
-
3240
- // Best-in-class browser opening using 'open' package (cross-platform)
3241
- // Falls back to platform-specific commands if package fails
3242
- this.openBrowser(authUrl).catch(() => {
3243
- console.error('[Polydev] All browser open methods failed');
3244
- console.error('[Polydev] Please open this URL manually:', authUrl);
3245
- });
3246
-
3247
- // Don't block forever - resolve after timeout
3248
- setTimeout(() => {
3249
- if (!this.isAuthenticated) {
3250
- console.error('[!] Login timeout - use "login" tool to try again');
3251
- console.error('─'.repeat(50) + '\n');
3252
- }
3253
- server.close();
3254
- resolve(false);
3255
- }, 2 * 60 * 1000); // 2 minute timeout for startup
3256
- });
3257
-
3258
- server.on('error', (err) => {
3259
- console.error('[!] Could not start login server:', err.message);
3260
- console.error(' Use "login" tool or run: npx polydev-ai login');
3261
- console.error('─'.repeat(50) + '\n');
3262
- resolve(false);
3263
- });
3264
- });
3265
- }
3266
-
3267
- /**
3268
- * Verify token and display auth status
3269
- */
3270
- async verifyAndDisplayAuth() {
3271
- try {
3272
- const response = await fetch('https://www.polydev.ai/api/mcp', {
3273
- method: 'POST',
3274
- headers: {
3275
- 'Content-Type': 'application/json',
3276
- 'Authorization': `Bearer ${this.userToken}`,
3277
- 'User-Agent': 'polydev-mcp/1.0.0'
3278
- },
3279
- body: JSON.stringify({ action: 'check_status' })
3280
- });
3281
-
3282
- if (response.ok) {
3283
- const data = await response.json();
3284
- const credits = data.credits_remaining?.toLocaleString() || 0;
3285
- const tier = data.subscription_tier || 'Free';
3286
-
3287
- console.error('─'.repeat(50));
3288
- console.error('Polydev - Authenticated');
3289
- console.error('─'.repeat(50));
3290
- console.error(`Account: ${data.email || 'Connected'}`);
3291
- console.error(`Credits: ${credits} | Tier: ${tier}`);
3292
- console.error('─'.repeat(50) + '\n');
3293
- } else {
3294
- // Don't clear isAuthenticated here — handleGetAuthStatus has its own
3295
- // verification and triggerReAuth logic. Clearing here causes a race condition
3296
- // where startup verification can invalidate auth before the user checks status.
3297
- console.error('─'.repeat(50));
3298
- console.error('Polydev - Token may be invalid');
3299
- console.error('─'.repeat(50));
3300
- console.error('Server returned non-OK. Will re-verify on next auth check.');
3301
- console.error('Use the "login" tool or run: npx polydev-ai login');
3302
- console.error('─'.repeat(50) + '\n');
3303
- }
3304
- } catch (error) {
3305
- console.error('[Polydev] Could not verify auth (offline?):', error.message || 'unknown');
3306
- }
3307
- }
3308
-
3309
3216
  /**
3310
3217
  * Display CLI tools status after detection
3311
3218
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "polydev-ai",
3
- "version": "1.9.26",
3
+ "version": "1.9.28",
4
4
  "engines": {
5
5
  "node": ">=20.x <=22.x"
6
6
  },