polydev-ai 1.9.13 → 1.9.15
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 +168 -70
- package/package.json +1 -1
package/mcp/stdio-wrapper.js
CHANGED
|
@@ -585,7 +585,16 @@ To re-login: npx polydev-ai`
|
|
|
585
585
|
// if this process dies, the new one can resume polling with same session)
|
|
586
586
|
this.startLoginPolling(sessionId);
|
|
587
587
|
|
|
588
|
-
//
|
|
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
|
|
589
598
|
return {
|
|
590
599
|
jsonrpc: '2.0',
|
|
591
600
|
id,
|
|
@@ -595,20 +604,12 @@ To re-login: npx polydev-ai`
|
|
|
595
604
|
text: `AUTHENTICATION STARTED
|
|
596
605
|
======================
|
|
597
606
|
|
|
598
|
-
A browser window
|
|
607
|
+
A browser window was opened for you to sign in.
|
|
599
608
|
|
|
600
|
-
If it
|
|
609
|
+
If it didn't open, visit:
|
|
601
610
|
${authUrl}
|
|
602
611
|
|
|
603
|
-
|
|
604
|
-
~/.polydev.env
|
|
605
|
-
~/.zshrc
|
|
606
|
-
|
|
607
|
-
After login completes, you can:
|
|
608
|
-
/polydev:ask Query multiple AI models
|
|
609
|
-
/polydev:auth Check status & credits
|
|
610
|
-
|
|
611
|
-
Dashboard: https://polydev.ai/dashboard`
|
|
612
|
+
Still waiting for login. Use /polydev:auth to check status after signing in.`
|
|
612
613
|
}]
|
|
613
614
|
}
|
|
614
615
|
};
|
|
@@ -658,8 +659,102 @@ Dashboard: https://polydev.ai/dashboard`
|
|
|
658
659
|
}
|
|
659
660
|
}
|
|
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}
|
|
719
|
+
|
|
720
|
+
You can now use:
|
|
721
|
+
/polydev:ask Query multiple AI models
|
|
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
|
|
748
|
+
|
|
749
|
+
Dashboard: https://polydev.ai/dashboard`
|
|
750
|
+
}]
|
|
751
|
+
}
|
|
752
|
+
};
|
|
753
|
+
}
|
|
754
|
+
|
|
661
755
|
/**
|
|
662
756
|
* Trigger re-authentication by opening browser and starting polling
|
|
757
|
+
* Waits for auth to complete inline (up to 120s) then returns full status
|
|
663
758
|
* Used when token is detected as invalid/expired
|
|
664
759
|
* @param {*} id - JSON-RPC request id
|
|
665
760
|
* @param {string} reason - Human-readable reason for re-auth
|
|
@@ -703,23 +798,33 @@ Dashboard: https://polydev.ai/dashboard`
|
|
|
703
798
|
this.savePendingSession(sessionId);
|
|
704
799
|
this.startLoginPolling(sessionId);
|
|
705
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
|
|
706
811
|
return {
|
|
707
812
|
jsonrpc: '2.0',
|
|
708
813
|
id,
|
|
709
814
|
result: {
|
|
710
815
|
content: [{
|
|
711
816
|
type: 'text',
|
|
712
|
-
text: `RE-AUTHENTICATION
|
|
713
|
-
|
|
817
|
+
text: `RE-AUTHENTICATION STARTED
|
|
818
|
+
=========================
|
|
714
819
|
|
|
715
820
|
${reason}
|
|
716
821
|
|
|
717
|
-
A browser window
|
|
822
|
+
A browser window was opened for re-authentication.
|
|
718
823
|
|
|
719
|
-
If it
|
|
824
|
+
If it didn't open, visit:
|
|
720
825
|
${authUrl}
|
|
721
826
|
|
|
722
|
-
|
|
827
|
+
Still waiting for login. Use /polydev:auth to check status after signing in.`
|
|
723
828
|
}]
|
|
724
829
|
}
|
|
725
830
|
};
|
|
@@ -1664,9 +1769,10 @@ Error: ${error.message}`
|
|
|
1664
1769
|
const finalProviders = [];
|
|
1665
1770
|
const usedProviderNames = new Set();
|
|
1666
1771
|
|
|
1667
|
-
// STEP 1: Add ALL available CLIs
|
|
1772
|
+
// STEP 1: Add ALL available CLIs (in priority order) - they're FREE
|
|
1773
|
+
// Don't limit to maxPerspectives here — we run all CLIs in parallel
|
|
1774
|
+
// and take the first maxPerspectives successes (fast-collect pattern)
|
|
1668
1775
|
for (const cliId of cliPriorityOrder) {
|
|
1669
|
-
if (finalProviders.length >= maxPerspectives) break;
|
|
1670
1776
|
if (!availableClis.includes(cliId)) continue;
|
|
1671
1777
|
|
|
1672
1778
|
const providerName = cliToProviderMap[cliId];
|
|
@@ -1712,7 +1818,7 @@ Error: ${error.message}`
|
|
|
1712
1818
|
console.error(`[Stdio Wrapper] [CLI-FIRST] Added API/credits: ${normalizedProvider} (${p.model})${p.tier ? ` [${p.tier}]` : ''}`);
|
|
1713
1819
|
}
|
|
1714
1820
|
|
|
1715
|
-
console.error(`[Stdio Wrapper] Final provider list (${finalProviders.length}
|
|
1821
|
+
console.error(`[Stdio Wrapper] Final provider list (${finalProviders.length}, need ${maxPerspectives}): ${finalProviders.map(p => `${p.provider}[${p.source}]`).join(', ')}`);
|
|
1716
1822
|
|
|
1717
1823
|
// Separate into CLI entries (local execution) vs API entries (remote execution)
|
|
1718
1824
|
const cliProviderEntries = finalProviders.filter(p => p.source === 'cli');
|
|
@@ -1720,7 +1826,8 @@ Error: ${error.message}`
|
|
|
1720
1826
|
|
|
1721
1827
|
console.error(`[Stdio Wrapper] Provider breakdown: CLI=${cliProviderEntries.map(p => p.cliId).join(', ') || 'none'}, API-only=${apiOnlyProviders.map(p => p.provider).join(', ') || 'none'}`);
|
|
1722
1828
|
|
|
1723
|
-
// Run
|
|
1829
|
+
// Run ALL CLI prompts concurrently with fast-collect pattern
|
|
1830
|
+
// Resolves once we have maxPerspectives successes (don't wait for slow CLIs)
|
|
1724
1831
|
if (cliProviderEntries.length > 0) {
|
|
1725
1832
|
const cliPromises = cliProviderEntries.map(async (providerEntry) => {
|
|
1726
1833
|
try {
|
|
@@ -1761,7 +1868,10 @@ Error: ${error.message}`
|
|
|
1761
1868
|
};
|
|
1762
1869
|
}
|
|
1763
1870
|
});
|
|
1764
|
-
|
|
1871
|
+
|
|
1872
|
+
// Fast-collect: resolve once we have maxPerspectives successes OR all complete
|
|
1873
|
+
localResults = await this.collectFirstNSuccesses(cliPromises, maxPerspectives);
|
|
1874
|
+
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`);
|
|
1765
1875
|
}
|
|
1766
1876
|
|
|
1767
1877
|
// Store API-only providers info for remote API call
|
|
@@ -1836,6 +1946,42 @@ Error: ${error.message}`
|
|
|
1836
1946
|
}
|
|
1837
1947
|
}
|
|
1838
1948
|
|
|
1949
|
+
/**
|
|
1950
|
+
* Collect results from parallel promises, resolving early once we have N successes
|
|
1951
|
+
* This avoids waiting for slow/timed-out CLIs when we already have enough results
|
|
1952
|
+
* @param {Promise[]} promises - Array of promises to collect from
|
|
1953
|
+
* @param {number} needed - Number of successful results needed
|
|
1954
|
+
* @returns {Promise<Array>} Array of results (may include failures if not enough successes)
|
|
1955
|
+
*/
|
|
1956
|
+
collectFirstNSuccesses(promises, needed) {
|
|
1957
|
+
return new Promise((resolve) => {
|
|
1958
|
+
const results = [];
|
|
1959
|
+
let successCount = 0;
|
|
1960
|
+
let completedCount = 0;
|
|
1961
|
+
let resolved = false;
|
|
1962
|
+
|
|
1963
|
+
if (promises.length === 0) {
|
|
1964
|
+
resolve([]);
|
|
1965
|
+
return;
|
|
1966
|
+
}
|
|
1967
|
+
|
|
1968
|
+
for (const promise of promises) {
|
|
1969
|
+
promise.then(result => {
|
|
1970
|
+
if (resolved) return;
|
|
1971
|
+
results.push(result);
|
|
1972
|
+
if (result.success) successCount++;
|
|
1973
|
+
completedCount++;
|
|
1974
|
+
|
|
1975
|
+
// Resolve early if we have enough successes OR all promises done
|
|
1976
|
+
if (successCount >= needed || completedCount >= promises.length) {
|
|
1977
|
+
resolved = true;
|
|
1978
|
+
resolve([...results]); // Copy to prevent mutation from late arrivals
|
|
1979
|
+
}
|
|
1980
|
+
});
|
|
1981
|
+
}
|
|
1982
|
+
});
|
|
1983
|
+
}
|
|
1984
|
+
|
|
1839
1985
|
/**
|
|
1840
1986
|
* Get all available and authenticated CLI providers
|
|
1841
1987
|
* Uses user's provider order from dashboard (display_order) instead of hardcoded order
|
|
@@ -2173,54 +2319,6 @@ Error: ${error.message}`
|
|
|
2173
2319
|
combineAllCliAndPerspectives(localResults, perspectivesResult, args) {
|
|
2174
2320
|
// Ensure perspectivesResult is always an object to prevent undefined errors
|
|
2175
2321
|
const safePersp = perspectivesResult || { success: false, error: 'No response from perspectives server' };
|
|
2176
|
-
|
|
2177
|
-
const combinedResult = {
|
|
2178
|
-
success: true,
|
|
2179
|
-
timestamp: new Date().toISOString(),
|
|
2180
|
-
mode: args.mode,
|
|
2181
|
-
local_cli_count: localResults.length,
|
|
2182
|
-
sections: {
|
|
2183
|
-
local: localResults,
|
|
2184
|
-
remote: safePersp
|
|
2185
|
-
}
|
|
2186
|
-
};
|
|
2187
|
-
|
|
2188
|
-
// Check if any local CLIs succeeded
|
|
2189
|
-
const successfulClis = localResults.filter(result => result.success);
|
|
2190
|
-
const hasSomeLocalSuccess = successfulClis.length > 0;
|
|
2191
|
-
|
|
2192
|
-
// Determine overall success and content
|
|
2193
|
-
if (hasSomeLocalSuccess && safePersp.success) {
|
|
2194
|
-
combinedResult.content = this.formatMultipleCliResponse(localResults, safePersp, false);
|
|
2195
|
-
combinedResult.tokens_used = successfulClis.reduce((total, cli) => total + (cli.tokens_used || 0), 0);
|
|
2196
|
-
combinedResult.latency_ms = Math.max(...successfulClis.map(cli => cli.latency_ms || 0));
|
|
2197
|
-
} else if (!hasSomeLocalSuccess && safePersp.success) {
|
|
2198
|
-
// Complete fallback case - no local CLIs worked
|
|
2199
|
-
combinedResult.content = this.formatMultipleCliResponse(localResults, safePersp, true);
|
|
2200
|
-
combinedResult.fallback_used = true;
|
|
2201
|
-
combinedResult.tokens_used = 0; // No local tokens used
|
|
2202
|
-
} else if (hasSomeLocalSuccess && !safePersp.success) {
|
|
2203
|
-
// Local CLIs succeeded, remote failed
|
|
2204
|
-
combinedResult.content = this.formatMultipleCliResponse(localResults, safePersp, false);
|
|
2205
|
-
combinedResult.tokens_used = successfulClis.reduce((total, cli) => total + (cli.tokens_used || 0), 0);
|
|
2206
|
-
combinedResult.latency_ms = Math.max(...successfulClis.map(cli => cli.latency_ms || 0));
|
|
2207
|
-
} else {
|
|
2208
|
-
// Both failed
|
|
2209
|
-
combinedResult.success = false;
|
|
2210
|
-
const cliErrors = localResults.map(cli => `${cli.provider_id}: ${cli.error || 'Unknown error'}`).join('; ');
|
|
2211
|
-
const perspectivesError = safePersp.error || 'Unknown remote error';
|
|
2212
|
-
combinedResult.error = `All local CLIs failed: ${cliErrors}; Perspectives also failed: ${perspectivesError}`;
|
|
2213
|
-
}
|
|
2214
|
-
|
|
2215
|
-
return combinedResult;
|
|
2216
|
-
}
|
|
2217
|
-
|
|
2218
|
-
/**
|
|
2219
|
-
* Format multiple CLI responses with remote perspectives
|
|
2220
|
-
*/
|
|
2221
|
-
formatMultipleCliResponse(localResults, perspectivesResult, isFallback) {
|
|
2222
|
-
// Safety check - ensure perspectivesResult is always an object
|
|
2223
|
-
const safePersp = perspectivesResult || { success: false, error: 'No perspectives data' };
|
|
2224
2322
|
let formatted = '';
|
|
2225
2323
|
|
|
2226
2324
|
// Show all local CLI responses
|