polydev-ai 1.10.1 → 1.10.3
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/manifest.json +1 -1
- package/mcp/server.js +5 -5
- package/mcp/stdio-wrapper.js +176 -24
- package/package.json +2 -1
package/mcp/manifest.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "polydev-perspectives",
|
|
3
|
-
"version": "1.10.
|
|
3
|
+
"version": "1.10.2",
|
|
4
4
|
"description": "Multi-model AI perspectives - query GPT 5.2, Claude Opus 4.5, Gemini 3, and Grok 4.1 simultaneously. Get diverse perspectives when stuck or need enhanced reasoning. Achieved 74.6% on SWE-bench Verified.",
|
|
5
5
|
"author": "Polydev AI",
|
|
6
6
|
"license": "MIT",
|
package/mcp/server.js
CHANGED
|
@@ -1803,7 +1803,7 @@ class MCPServer {
|
|
|
1803
1803
|
);
|
|
1804
1804
|
}
|
|
1805
1805
|
|
|
1806
|
-
const serverUrl = 'https://
|
|
1806
|
+
const serverUrl = 'https://polydev.ai/api/x-search';
|
|
1807
1807
|
|
|
1808
1808
|
try {
|
|
1809
1809
|
const response = await fetch(serverUrl, {
|
|
@@ -1863,7 +1863,7 @@ class MCPServer {
|
|
|
1863
1863
|
);
|
|
1864
1864
|
}
|
|
1865
1865
|
|
|
1866
|
-
const serverUrl = 'https://
|
|
1866
|
+
const serverUrl = 'https://polydev.ai/api/generate-image';
|
|
1867
1867
|
|
|
1868
1868
|
try {
|
|
1869
1869
|
const response = await fetch(serverUrl, {
|
|
@@ -1876,7 +1876,7 @@ class MCPServer {
|
|
|
1876
1876
|
body: JSON.stringify({
|
|
1877
1877
|
prompt: args.prompt,
|
|
1878
1878
|
user_token: userToken,
|
|
1879
|
-
provider: args.provider || '
|
|
1879
|
+
provider: args.provider || 'gemini',
|
|
1880
1880
|
model: args.model,
|
|
1881
1881
|
size: args.size || '1024x1024',
|
|
1882
1882
|
quality: args.quality || 'auto'
|
|
@@ -1974,8 +1974,8 @@ class MCPServer {
|
|
|
1974
1974
|
formatImageResponse(result) {
|
|
1975
1975
|
let formatted = `# Image Generated\n\n`;
|
|
1976
1976
|
|
|
1977
|
-
formatted += `**Provider**: ${result.provider || '
|
|
1978
|
-
formatted += `**Model**: ${result.model || '
|
|
1977
|
+
formatted += `**Provider**: ${result.provider || 'gemini'}\n`;
|
|
1978
|
+
formatted += `**Model**: ${result.model || 'gemini-3.1-flash-image-preview'}\n`;
|
|
1979
1979
|
|
|
1980
1980
|
if (result.size) {
|
|
1981
1981
|
formatted += `**Size**: ${result.size}\n`;
|
package/mcp/stdio-wrapper.js
CHANGED
|
@@ -507,6 +507,10 @@ Token will be saved automatically after login.`
|
|
|
507
507
|
// Handle CLI tools locally (support both prefixed and unprefixed names)
|
|
508
508
|
if (this.isCliTool(toolName)) {
|
|
509
509
|
return await this.handleLocalCliTool(request);
|
|
510
|
+
} else if (toolName === 'search_x' || toolName === 'polydev.search_x') {
|
|
511
|
+
return await this.handleSearchX(params, id);
|
|
512
|
+
} else if (toolName === 'generate_image' || toolName === 'polydev.generate_image') {
|
|
513
|
+
return await this.handleGenerateImage(params, id);
|
|
510
514
|
} else {
|
|
511
515
|
// Forward non-CLI tools to remote server
|
|
512
516
|
const result = await this.forwardToRemoteServer(request);
|
|
@@ -1730,6 +1734,156 @@ To re-login: /polydev:login`
|
|
|
1730
1734
|
}
|
|
1731
1735
|
}
|
|
1732
1736
|
|
|
1737
|
+
/**
|
|
1738
|
+
* Handle X search tool locally — calls polydev.ai API
|
|
1739
|
+
*/
|
|
1740
|
+
async handleSearchX(params, id) {
|
|
1741
|
+
const args = params.arguments || {};
|
|
1742
|
+
console.error('[Stdio Wrapper] Handling search_x locally');
|
|
1743
|
+
|
|
1744
|
+
if (!args.query || typeof args.query !== 'string') {
|
|
1745
|
+
return { jsonrpc: '2.0', id, result: { content: [{ type: 'text', text: 'Error: query is required and must be a string' }], isError: true } };
|
|
1746
|
+
}
|
|
1747
|
+
|
|
1748
|
+
const userToken = args.user_token || this.userToken;
|
|
1749
|
+
if (!userToken) {
|
|
1750
|
+
return { jsonrpc: '2.0', id, result: { content: [{ type: 'text', text: 'Authentication required for X search.\nUse the "login" tool first, or pass user_token parameter.' }], isError: true } };
|
|
1751
|
+
}
|
|
1752
|
+
|
|
1753
|
+
try {
|
|
1754
|
+
const response = await fetch('https://polydev.ai/api/x-search', {
|
|
1755
|
+
method: 'POST',
|
|
1756
|
+
headers: {
|
|
1757
|
+
'Content-Type': 'application/json',
|
|
1758
|
+
'Authorization': `Bearer ${userToken}`,
|
|
1759
|
+
'User-Agent': 'polydev-stdio-wrapper/1.0.0'
|
|
1760
|
+
},
|
|
1761
|
+
body: JSON.stringify({
|
|
1762
|
+
query: args.query,
|
|
1763
|
+
user_token: userToken,
|
|
1764
|
+
model: args.model || 'grok-4-1-fast-reasoning'
|
|
1765
|
+
})
|
|
1766
|
+
});
|
|
1767
|
+
|
|
1768
|
+
if (!response.ok) {
|
|
1769
|
+
const errorData = await response.json().catch(() => ({}));
|
|
1770
|
+
if (response.status === 429) {
|
|
1771
|
+
return { jsonrpc: '2.0', id, result: { content: [{ type: 'text', text: 'Free X search limit reached (50 searches).\n\nTo continue:\n1. Get an xAI API key at https://console.x.ai\n2. Add it at https://polydev.ai/dashboard/models (select "X-AI" provider)\n\nWith your own key, you get unlimited X searches.' }], isError: true } };
|
|
1772
|
+
}
|
|
1773
|
+
throw new Error(errorData.error || `X search failed: HTTP ${response.status}`);
|
|
1774
|
+
}
|
|
1775
|
+
|
|
1776
|
+
const result = await response.json();
|
|
1777
|
+
|
|
1778
|
+
// Format response
|
|
1779
|
+
let formatted = '# X Search Results\n\n';
|
|
1780
|
+
if (result.answer) {
|
|
1781
|
+
formatted += `## Summary\n${result.answer}\n\n`;
|
|
1782
|
+
}
|
|
1783
|
+
if (result.search_results && result.search_results.length > 0) {
|
|
1784
|
+
formatted += '## Posts Found\n\n';
|
|
1785
|
+
result.search_results.forEach((post, i) => {
|
|
1786
|
+
formatted += `### ${i + 1}. ${post.author || 'Unknown'}\n`;
|
|
1787
|
+
if (post.snippet) formatted += `${post.snippet}\n`;
|
|
1788
|
+
if (post.url) formatted += `[View post](${post.url})\n`;
|
|
1789
|
+
if (post.date) formatted += `*${post.date}*\n`;
|
|
1790
|
+
formatted += '\n';
|
|
1791
|
+
});
|
|
1792
|
+
}
|
|
1793
|
+
if (result.using_free_tier) {
|
|
1794
|
+
formatted += `---\n*Free tier: ${result.free_searches_remaining} searches remaining of 50*\n`;
|
|
1795
|
+
}
|
|
1796
|
+
if (result.model) {
|
|
1797
|
+
formatted += `\n*Model: ${result.model} | Latency: ${result.latency_ms}ms*\n`;
|
|
1798
|
+
}
|
|
1799
|
+
|
|
1800
|
+
return { jsonrpc: '2.0', id, result: { content: [{ type: 'text', text: formatted }] } };
|
|
1801
|
+
} catch (error) {
|
|
1802
|
+
console.error('[Stdio Wrapper] X search error:', error.message);
|
|
1803
|
+
return { jsonrpc: '2.0', id, result: { content: [{ type: 'text', text: `X search failed: ${error.message}` }], isError: true } };
|
|
1804
|
+
}
|
|
1805
|
+
}
|
|
1806
|
+
|
|
1807
|
+
/**
|
|
1808
|
+
* Handle image generation tool locally — calls polydev.ai API
|
|
1809
|
+
*/
|
|
1810
|
+
async handleGenerateImage(params, id) {
|
|
1811
|
+
const args = params.arguments || {};
|
|
1812
|
+
console.error('[Stdio Wrapper] Handling generate_image locally');
|
|
1813
|
+
|
|
1814
|
+
if (!args.prompt || typeof args.prompt !== 'string') {
|
|
1815
|
+
return { jsonrpc: '2.0', id, result: { content: [{ type: 'text', text: 'Error: prompt is required and must be a string' }], isError: true } };
|
|
1816
|
+
}
|
|
1817
|
+
|
|
1818
|
+
const userToken = args.user_token || this.userToken;
|
|
1819
|
+
if (!userToken) {
|
|
1820
|
+
return { jsonrpc: '2.0', id, result: { content: [{ type: 'text', text: 'Authentication required for image generation.\nUse the "login" tool first, or pass user_token parameter.' }], isError: true } };
|
|
1821
|
+
}
|
|
1822
|
+
|
|
1823
|
+
try {
|
|
1824
|
+
const response = await fetch('https://polydev.ai/api/generate-image', {
|
|
1825
|
+
method: 'POST',
|
|
1826
|
+
headers: {
|
|
1827
|
+
'Content-Type': 'application/json',
|
|
1828
|
+
'Authorization': `Bearer ${userToken}`,
|
|
1829
|
+
'User-Agent': 'polydev-stdio-wrapper/1.0.0'
|
|
1830
|
+
},
|
|
1831
|
+
body: JSON.stringify({
|
|
1832
|
+
prompt: args.prompt,
|
|
1833
|
+
user_token: userToken,
|
|
1834
|
+
provider: args.provider || 'gemini',
|
|
1835
|
+
model: args.model,
|
|
1836
|
+
size: args.size || '1024x1024',
|
|
1837
|
+
quality: args.quality || 'auto'
|
|
1838
|
+
})
|
|
1839
|
+
});
|
|
1840
|
+
|
|
1841
|
+
if (!response.ok) {
|
|
1842
|
+
const errorData = await response.json().catch(() => ({}));
|
|
1843
|
+
if (response.status === 400 && errorData.setup_url) {
|
|
1844
|
+
return { jsonrpc: '2.0', id, result: { content: [{ type: 'text', text: `No ${errorData.provider || 'API'} key found for image generation.\n\nImage generation requires your own API key:\n1. Get an API key from OpenAI (https://platform.openai.com) or Google AI (https://aistudio.google.com)\n2. Add it at https://polydev.ai/dashboard/models\n\nSupported providers: OpenAI (gpt-image-1.5) and Google Gemini` }], isError: true } };
|
|
1845
|
+
}
|
|
1846
|
+
throw new Error(errorData.error || `Image generation failed: HTTP ${response.status}`);
|
|
1847
|
+
}
|
|
1848
|
+
|
|
1849
|
+
const result = await response.json();
|
|
1850
|
+
|
|
1851
|
+
// Save image to file if we have base64 data
|
|
1852
|
+
if (result.image_base64) {
|
|
1853
|
+
try {
|
|
1854
|
+
const timestamp = Date.now();
|
|
1855
|
+
const filename = `polydev-image-${timestamp}.png`;
|
|
1856
|
+
const outputDir = path.join(os.homedir(), '.polydev', 'images');
|
|
1857
|
+
if (!fs.existsSync(outputDir)) {
|
|
1858
|
+
fs.mkdirSync(outputDir, { recursive: true });
|
|
1859
|
+
}
|
|
1860
|
+
const outputPath = path.join(outputDir, filename);
|
|
1861
|
+
fs.writeFileSync(outputPath, Buffer.from(result.image_base64, 'base64'));
|
|
1862
|
+
result.saved_to = outputPath;
|
|
1863
|
+
console.error(`[Stdio Wrapper] Image saved to: ${outputPath}`);
|
|
1864
|
+
} catch (saveError) {
|
|
1865
|
+
console.error('[Stdio Wrapper] Failed to save image:', saveError.message);
|
|
1866
|
+
}
|
|
1867
|
+
}
|
|
1868
|
+
|
|
1869
|
+
// Format response
|
|
1870
|
+
let formatted = '# Image Generated\n\n';
|
|
1871
|
+
formatted += `**Provider**: ${result.provider || 'gemini'}\n`;
|
|
1872
|
+
formatted += `**Model**: ${result.model || 'gemini-3.1-flash-image-preview'}\n`;
|
|
1873
|
+
if (result.size) formatted += `**Size**: ${result.size}\n`;
|
|
1874
|
+
if (result.revised_prompt) formatted += `**Revised prompt**: ${result.revised_prompt}\n`;
|
|
1875
|
+
if (result.text_response) formatted += `**Description**: ${result.text_response}\n`;
|
|
1876
|
+
if (result.saved_to) formatted += `\n**Saved to**: \`${result.saved_to}\`\n`;
|
|
1877
|
+
if (result.image_base64) formatted += `\nImage data: ${(result.image_base64.length / 1024).toFixed(0)}KB base64 PNG\n`;
|
|
1878
|
+
if (result.latency_ms) formatted += `\n*Latency: ${result.latency_ms}ms*\n`;
|
|
1879
|
+
|
|
1880
|
+
return { jsonrpc: '2.0', id, result: { content: [{ type: 'text', text: formatted }] } };
|
|
1881
|
+
} catch (error) {
|
|
1882
|
+
console.error('[Stdio Wrapper] Image generation error:', error.message);
|
|
1883
|
+
return { jsonrpc: '2.0', id, result: { content: [{ type: 'text', text: `Image generation failed: ${error.message}` }], isError: true } };
|
|
1884
|
+
}
|
|
1885
|
+
}
|
|
1886
|
+
|
|
1733
1887
|
/**
|
|
1734
1888
|
* Check if a tool is a CLI tool that should be handled locally
|
|
1735
1889
|
*/
|
|
@@ -2262,34 +2416,36 @@ To re-login: /polydev:login`
|
|
|
2262
2416
|
console.error(`[Stdio Wrapper] Perspectives breakdown: ${successfulLocalCount} successful CLI, ${failedCliCount} failed CLI, ${apiOnlyCount} API-only, need ${remainingPerspectives} more from remote (target: ${maxPerspectives})`);
|
|
2263
2417
|
|
|
2264
2418
|
// Get remote perspectives for API-only providers and failed CLIs
|
|
2419
|
+
// ALWAYS call the server when CLIs succeeded, even if remainingPerspectives=0.
|
|
2420
|
+
// The server has the authoritative perspectives_per_message from the database.
|
|
2421
|
+
// If fetchUserModelPreferences failed (e.g., timeout), maxPerspectives defaults to 2,
|
|
2422
|
+
// which can cause remainingPerspectives=0 with 2+ CLIs. The server will correct this
|
|
2423
|
+
// using the user's actual DB setting.
|
|
2265
2424
|
let perspectivesResult;
|
|
2266
|
-
if (remainingPerspectives > 0) {
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2425
|
+
if (remainingPerspectives > 0 || successfulLocalCount > 0) {
|
|
2426
|
+
const fetchCount = Math.max(remainingPerspectives, 0);
|
|
2427
|
+
this.sendProgressNotification(progressToken, 65, 100, fetchCount > 0
|
|
2428
|
+
? `Fetching ${fetchCount} more perspectives from API...`
|
|
2429
|
+
: 'Checking for supplemental credits perspectives...');
|
|
2430
|
+
console.error(`[Stdio Wrapper] Calling remote API for ${fetchCount} more perspectives (have ${successfulLocalCount}/${maxPerspectives})`);
|
|
2431
|
+
|
|
2270
2432
|
// Pass API-only provider info to help remote API choose correct models
|
|
2271
2433
|
const apiProvidersInfo = (this._apiOnlyProviders || []).map(p => ({
|
|
2272
2434
|
provider: p.provider,
|
|
2273
2435
|
model: p.model
|
|
2274
2436
|
}));
|
|
2275
|
-
|
|
2437
|
+
|
|
2276
2438
|
// Note: Remote API will handle logging of CLI + API results together
|
|
2277
2439
|
// We do NOT call reportCliResultsToServer() here to avoid duplicate logs
|
|
2278
|
-
perspectivesResult = await this.callPerspectivesForCli(args, localResults,
|
|
2440
|
+
perspectivesResult = await this.callPerspectivesForCli(args, localResults, fetchCount, apiProvidersInfo);
|
|
2279
2441
|
} else {
|
|
2280
|
-
console.error(`[Stdio Wrapper]
|
|
2281
|
-
|
|
2282
|
-
// ONLY report CLI results when NOT calling remote API
|
|
2283
|
-
// (When we call remote API, it handles logging CLI + API results together)
|
|
2284
|
-
this.reportCliResultsToServer(prompt, localResults, args).catch(err => {
|
|
2285
|
-
console.error('[Stdio Wrapper] CLI results reporting failed (non-critical):', err.message);
|
|
2286
|
-
});
|
|
2287
|
-
|
|
2442
|
+
console.error(`[Stdio Wrapper] No CLIs and no remaining perspectives — skipping remote call`);
|
|
2443
|
+
|
|
2288
2444
|
perspectivesResult = {
|
|
2289
2445
|
success: true,
|
|
2290
2446
|
content: '',
|
|
2291
2447
|
skipped: true,
|
|
2292
|
-
reason: `
|
|
2448
|
+
reason: `No CLIs available and no remaining perspectives needed`,
|
|
2293
2449
|
timestamp: new Date().toISOString()
|
|
2294
2450
|
};
|
|
2295
2451
|
}
|
|
@@ -2572,16 +2728,12 @@ To re-login: /polydev:login`
|
|
|
2572
2728
|
})
|
|
2573
2729
|
.filter(Boolean);
|
|
2574
2730
|
|
|
2575
|
-
//
|
|
2731
|
+
// NOTE: We no longer early-return when maxPerspectives=0.
|
|
2732
|
+
// The server has the authoritative perspectives_per_message from the DB.
|
|
2733
|
+
// When maxPerspectives=0, we send max_perspectives=0 (or omit it) and let
|
|
2734
|
+
// the server compute the correct value from user settings minus CLI count.
|
|
2576
2735
|
if (maxPerspectives <= 0) {
|
|
2577
|
-
console.error(`[Stdio Wrapper]
|
|
2578
|
-
return {
|
|
2579
|
-
success: true,
|
|
2580
|
-
content: '',
|
|
2581
|
-
skipped: true,
|
|
2582
|
-
reason: 'No perspectives needed',
|
|
2583
|
-
timestamp: new Date().toISOString()
|
|
2584
|
-
};
|
|
2736
|
+
console.error(`[Stdio Wrapper] Client-side maxPerspectives is ${maxPerspectives}, but still calling server — server has authoritative DB setting`);
|
|
2585
2737
|
}
|
|
2586
2738
|
|
|
2587
2739
|
// Build list of specific providers to request (from API-only providers + failed CLI fallbacks)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "polydev-ai",
|
|
3
|
-
"version": "1.10.
|
|
3
|
+
"version": "1.10.3",
|
|
4
4
|
"engines": {
|
|
5
5
|
"node": ">=20.x <=22.x"
|
|
6
6
|
},
|
|
@@ -87,6 +87,7 @@
|
|
|
87
87
|
"react": "^18.3.1",
|
|
88
88
|
"react-dom": "^18.3.1",
|
|
89
89
|
"resend": "^6.0.2",
|
|
90
|
+
"sharp": "^0.34.5",
|
|
90
91
|
"sonner": "^2.0.7",
|
|
91
92
|
"stripe": "^20.3.0",
|
|
92
93
|
"tailwind-merge": "^3.3.1",
|