polydev-ai 1.2.3 → 1.2.5
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/README.md +1 -1
- package/lib/cliManager.ts +144 -2
- package/lib/smartCliCache.ts +189 -0
- package/lib/universalMemoryExtractor.js +607 -0
- package/lib/zeroKnowledgeEncryption.js +289 -0
- package/mcp/README.md +165 -0
- package/mcp/manifest.json +8 -8
- package/mcp/package.json +2 -2
- package/mcp/server.js +43 -20
- package/mcp/stdio-wrapper.js +203 -2
- package/package.json +5 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Polydev AI Website
|
|
2
2
|
|
|
3
|
-
Advanced Model Context Protocol platform with comprehensive multi-LLM
|
|
3
|
+
Advanced Model Context Protocol platform with comprehensive multi-LLM integrations, subscription-based CLI access, OAuth bridges, and advanced tooling for AI development.
|
|
4
4
|
|
|
5
5
|
## Features
|
|
6
6
|
|
package/lib/cliManager.ts
CHANGED
|
@@ -34,6 +34,9 @@ export interface CLIStatus {
|
|
|
34
34
|
path?: string
|
|
35
35
|
lastChecked: Date
|
|
36
36
|
error?: string
|
|
37
|
+
default_model?: string
|
|
38
|
+
available_models?: string[]
|
|
39
|
+
model_detection_method?: 'interactive' | 'fallback'
|
|
37
40
|
}
|
|
38
41
|
|
|
39
42
|
export interface CLIResponse {
|
|
@@ -280,12 +283,30 @@ export class CLIManager {
|
|
|
280
283
|
authenticated = true
|
|
281
284
|
}
|
|
282
285
|
|
|
286
|
+
// Detect available models for this CLI tool
|
|
287
|
+
let default_model: string | undefined
|
|
288
|
+
let available_models: string[] | undefined
|
|
289
|
+
let model_detection_method: 'interactive' | 'fallback' | undefined
|
|
290
|
+
|
|
291
|
+
try {
|
|
292
|
+
const modelDetection = await this.detectDefaultModel(provider.id);
|
|
293
|
+
default_model = modelDetection.defaultModel;
|
|
294
|
+
available_models = modelDetection.availableModels;
|
|
295
|
+
model_detection_method = modelDetection.detectionMethod;
|
|
296
|
+
} catch (error) {
|
|
297
|
+
console.error(`[CLI Manager] Model detection failed for ${provider.name}:`, error);
|
|
298
|
+
// Continue without model info - fallback will be used
|
|
299
|
+
}
|
|
300
|
+
|
|
283
301
|
return {
|
|
284
302
|
available: true,
|
|
285
303
|
authenticated,
|
|
286
304
|
version,
|
|
287
305
|
path: executablePath,
|
|
288
|
-
lastChecked: new Date()
|
|
306
|
+
lastChecked: new Date(),
|
|
307
|
+
default_model,
|
|
308
|
+
available_models,
|
|
309
|
+
model_detection_method
|
|
289
310
|
}
|
|
290
311
|
}
|
|
291
312
|
|
|
@@ -423,6 +444,127 @@ export class CLIManager {
|
|
|
423
444
|
getProvider(providerId: string): CLIProvider | undefined {
|
|
424
445
|
return this.providers.get(providerId)
|
|
425
446
|
}
|
|
447
|
+
|
|
448
|
+
/**
|
|
449
|
+
* Detect available models for a CLI provider using interactive commands
|
|
450
|
+
*/
|
|
451
|
+
async detectDefaultModel(providerId: string): Promise<{
|
|
452
|
+
defaultModel: string;
|
|
453
|
+
availableModels: string[];
|
|
454
|
+
detectionMethod: 'interactive' | 'fallback';
|
|
455
|
+
}> {
|
|
456
|
+
try {
|
|
457
|
+
// Try interactive detection using CLI commands
|
|
458
|
+
let command = '';
|
|
459
|
+
switch (providerId) {
|
|
460
|
+
case 'claude_code':
|
|
461
|
+
command = 'models'; // Claude Code model listing command
|
|
462
|
+
break;
|
|
463
|
+
case 'codex_cli':
|
|
464
|
+
command = 'list-models'; // Codex CLI model listing command
|
|
465
|
+
break;
|
|
466
|
+
case 'gemini_cli':
|
|
467
|
+
command = 'models'; // Gemini CLI model listing command
|
|
468
|
+
break;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
if (!command) {
|
|
472
|
+
throw new Error(`No model detection command for ${providerId}`);
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
const result = await this.sendCliPrompt(providerId, command, 'args', 10000);
|
|
476
|
+
|
|
477
|
+
if (result.success && result.content) {
|
|
478
|
+
const models = this.parseModelsFromOutput(providerId, result.content);
|
|
479
|
+
if (models.length > 0) {
|
|
480
|
+
return {
|
|
481
|
+
defaultModel: this.extractDefaultModel(providerId, models),
|
|
482
|
+
availableModels: models,
|
|
483
|
+
detectionMethod: 'interactive'
|
|
484
|
+
};
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
} catch (error) {
|
|
488
|
+
console.error(`Interactive model detection failed for ${providerId}:`, error);
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
// Fallback to known defaults if interactive detection fails
|
|
492
|
+
return {
|
|
493
|
+
defaultModel: this.getDefaultModelFallback(providerId),
|
|
494
|
+
availableModels: [this.getDefaultModelFallback(providerId)],
|
|
495
|
+
detectionMethod: 'fallback'
|
|
496
|
+
};
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
/**
|
|
500
|
+
* Parse model names from CLI output
|
|
501
|
+
*/
|
|
502
|
+
private parseModelsFromOutput(providerId: string, output: string): string[] {
|
|
503
|
+
const models: string[] = [];
|
|
504
|
+
const lines = output.split('\n');
|
|
505
|
+
|
|
506
|
+
switch (providerId) {
|
|
507
|
+
case 'claude_code':
|
|
508
|
+
// Parse Claude Code output format
|
|
509
|
+
lines.forEach(line => {
|
|
510
|
+
const matches = line.match(/claude-[\w\-.]+/gi);
|
|
511
|
+
if (matches) models.push(...matches);
|
|
512
|
+
});
|
|
513
|
+
break;
|
|
514
|
+
case 'codex_cli':
|
|
515
|
+
// Parse Codex CLI output format
|
|
516
|
+
lines.forEach(line => {
|
|
517
|
+
const matches = line.match(/gpt-[\w\-.]+|o1-[\w\-.]+/gi);
|
|
518
|
+
if (matches) models.push(...matches);
|
|
519
|
+
});
|
|
520
|
+
break;
|
|
521
|
+
case 'gemini_cli':
|
|
522
|
+
// Parse Gemini CLI output format
|
|
523
|
+
lines.forEach(line => {
|
|
524
|
+
const matches = line.match(/gemini-[\w\-.]+/gi);
|
|
525
|
+
if (matches) models.push(...matches);
|
|
526
|
+
});
|
|
527
|
+
break;
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
return [...new Set(models)]; // Remove duplicates
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
/**
|
|
534
|
+
* Extract the default model from available models
|
|
535
|
+
*/
|
|
536
|
+
private extractDefaultModel(providerId: string, models: string[]): string {
|
|
537
|
+
if (models.length === 0) return this.getDefaultModelFallback(providerId);
|
|
538
|
+
|
|
539
|
+
switch (providerId) {
|
|
540
|
+
case 'claude_code':
|
|
541
|
+
// Prefer Claude 3.5 Sonnet, then Claude 3 Sonnet
|
|
542
|
+
return models.find(m => m.includes('claude-3-5-sonnet')) ||
|
|
543
|
+
models.find(m => m.includes('claude-3-sonnet')) ||
|
|
544
|
+
models[0];
|
|
545
|
+
case 'codex_cli':
|
|
546
|
+
// Prefer GPT-4, then GPT-3.5
|
|
547
|
+
return models.find(m => m.includes('gpt-4')) || models[0];
|
|
548
|
+
case 'gemini_cli':
|
|
549
|
+
// Prefer Gemini Pro, then Gemini Flash
|
|
550
|
+
return models.find(m => m.includes('gemini-1.5-pro')) ||
|
|
551
|
+
models.find(m => m.includes('gemini-pro')) ||
|
|
552
|
+
models[0];
|
|
553
|
+
}
|
|
554
|
+
return models[0];
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
/**
|
|
558
|
+
* Get fallback default model for a provider
|
|
559
|
+
*/
|
|
560
|
+
private getDefaultModelFallback(providerId: string): string {
|
|
561
|
+
const fallbacks = {
|
|
562
|
+
'claude_code': 'claude-3-sonnet',
|
|
563
|
+
'codex_cli': 'gpt-4',
|
|
564
|
+
'gemini_cli': 'gemini-pro'
|
|
565
|
+
};
|
|
566
|
+
return fallbacks[providerId as keyof typeof fallbacks] || 'unknown';
|
|
567
|
+
}
|
|
426
568
|
}
|
|
427
569
|
|
|
428
|
-
export default CLIManager
|
|
570
|
+
export default CLIManager
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Smart Cache for CLI Status Management
|
|
3
|
+
* Provides intelligent caching based on CLI reliability and status
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export class SmartCliCache {
|
|
7
|
+
private supabase: any;
|
|
8
|
+
|
|
9
|
+
constructor(supabase: any) {
|
|
10
|
+
this.supabase = supabase;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Get smart timeout based on CLI configuration
|
|
15
|
+
* Returns timeout in minutes
|
|
16
|
+
*/
|
|
17
|
+
getSmartTimeout(cliConfig: any): number {
|
|
18
|
+
if (!cliConfig.available) {
|
|
19
|
+
return 2; // 2 minutes - check frequently for new installs
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (!cliConfig.authenticated) {
|
|
23
|
+
return 3; // 3 minutes - check for authentication
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (cliConfig.model_detection_method === 'fallback') {
|
|
27
|
+
return 5; // 5 minutes - retry interactive detection
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return 10; // 10 minutes - stable, working CLI
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Check if CLI configuration is stale
|
|
35
|
+
*/
|
|
36
|
+
isStale(cliConfig: any): boolean {
|
|
37
|
+
if (!cliConfig.last_checked_at) return true;
|
|
38
|
+
|
|
39
|
+
const now = new Date();
|
|
40
|
+
const lastChecked = new Date(cliConfig.last_checked_at);
|
|
41
|
+
const minutesOld = (now.getTime() - lastChecked.getTime()) / (1000 * 60);
|
|
42
|
+
const timeout = this.getSmartTimeout(cliConfig);
|
|
43
|
+
|
|
44
|
+
return minutesOld > timeout;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Get CLI status with smart caching
|
|
49
|
+
* Checks if data is stale and triggers refresh if needed
|
|
50
|
+
*/
|
|
51
|
+
async getCliStatusWithCache(userId: string): Promise<any[]> {
|
|
52
|
+
// 1. Get current CLI configurations from database
|
|
53
|
+
const { data: cliConfigs, error } = await this.supabase
|
|
54
|
+
.from('cli_provider_configurations')
|
|
55
|
+
.select('*')
|
|
56
|
+
.eq('user_id', userId);
|
|
57
|
+
|
|
58
|
+
if (error || !cliConfigs) {
|
|
59
|
+
console.error('[Smart Cache] Failed to get CLI configs:', error);
|
|
60
|
+
return [];
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (cliConfigs.length === 0) {
|
|
64
|
+
return [];
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// 2. Check which configurations are stale
|
|
68
|
+
const staleConfigs = cliConfigs.filter((config: any) => this.isStale(config));
|
|
69
|
+
|
|
70
|
+
// 3. If any are stale, trigger refresh (async, don't wait)
|
|
71
|
+
if (staleConfigs.length > 0) {
|
|
72
|
+
console.log(`[Smart Cache] ${staleConfigs.length} CLI configs are stale, triggering refresh for user ${userId}`);
|
|
73
|
+
this.refreshStaleConfigs(userId, staleConfigs).catch((error: any) => {
|
|
74
|
+
console.error('[Smart Cache] Refresh failed:', error);
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// 4. Return current data (will be fresh after refresh)
|
|
79
|
+
return cliConfigs;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Refresh stale CLI configurations (called asynchronously)
|
|
84
|
+
*/
|
|
85
|
+
private async refreshStaleConfigs(userId: string, staleConfigs: any[]): Promise<void> {
|
|
86
|
+
console.log(`[Smart Cache] Starting refresh for ${staleConfigs.length} stale CLI configs`);
|
|
87
|
+
|
|
88
|
+
try {
|
|
89
|
+
// Trigger forced CLI detection via stdio-wrapper
|
|
90
|
+
for (const config of staleConfigs) {
|
|
91
|
+
await this.updateCliStatus(userId, config.provider);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
console.log('[Smart Cache] Successfully triggered refresh for stale CLI configs');
|
|
95
|
+
} catch (error) {
|
|
96
|
+
console.error('[Smart Cache] Failed to trigger refresh:', error);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Update CLI status for a specific provider
|
|
102
|
+
*/
|
|
103
|
+
private async updateCliStatus(userId: string, provider: string): Promise<void> {
|
|
104
|
+
try {
|
|
105
|
+
const response = await fetch('/api/cli-status/refresh', {
|
|
106
|
+
method: 'POST',
|
|
107
|
+
headers: {
|
|
108
|
+
'Content-Type': 'application/json',
|
|
109
|
+
'Authorization': `Bearer ${this.getUserToken()}`
|
|
110
|
+
},
|
|
111
|
+
body: JSON.stringify({
|
|
112
|
+
user_id: userId,
|
|
113
|
+
provider: provider
|
|
114
|
+
})
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
if (!response.ok) {
|
|
118
|
+
console.error(`[Smart Cache] Failed to refresh ${provider}:`, response.status);
|
|
119
|
+
}
|
|
120
|
+
} catch (error) {
|
|
121
|
+
console.error(`[Smart Cache] Network error refreshing ${provider}:`, error);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Get current user token (helper method)
|
|
127
|
+
*/
|
|
128
|
+
private getUserToken(): string {
|
|
129
|
+
// Try to get from environment or context
|
|
130
|
+
return process.env.POLYDEV_USER_TOKEN || '';
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Format last checked time for display
|
|
135
|
+
*/
|
|
136
|
+
formatLastCheckedTime(lastChecked: string): string {
|
|
137
|
+
if (!lastChecked) return 'Unknown';
|
|
138
|
+
|
|
139
|
+
const lastCheckedDate = new Date(lastChecked);
|
|
140
|
+
const now = new Date();
|
|
141
|
+
const minutes = Math.floor((now.getTime() - lastCheckedDate.getTime()) / (1000 * 60));
|
|
142
|
+
|
|
143
|
+
if (minutes < 1) return 'Just now';
|
|
144
|
+
if (minutes < 60) return `${minutes} min ago`;
|
|
145
|
+
if (minutes < 1440) return `${Math.floor(minutes / 60)} hours ago`;
|
|
146
|
+
return `${Math.floor(minutes / 1440)} days ago`;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Check if CLI should use local tools based on database status
|
|
151
|
+
*/
|
|
152
|
+
shouldUseLocalCli(model: string, cliConfigs: any[]): boolean {
|
|
153
|
+
// Find CLI that supports this model
|
|
154
|
+
const cliMatch = cliConfigs.find((cli: any) =>
|
|
155
|
+
cli.status === 'available' &&
|
|
156
|
+
cli.authenticated &&
|
|
157
|
+
(cli.available_models?.includes(model) || cli.default_model === model)
|
|
158
|
+
);
|
|
159
|
+
|
|
160
|
+
return !!cliMatch;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Get summary statistics for dashboard
|
|
165
|
+
*/
|
|
166
|
+
getClimiStatusSummary(cliConfigs: any[]): {
|
|
167
|
+
total: number;
|
|
168
|
+
available: number;
|
|
169
|
+
authenticated: number;
|
|
170
|
+
stale: number;
|
|
171
|
+
} {
|
|
172
|
+
const now = new Date();
|
|
173
|
+
let staleCount = 0;
|
|
174
|
+
|
|
175
|
+
// Count stale configs
|
|
176
|
+
cliConfigs.forEach(config => {
|
|
177
|
+
if (this.isStale(config)) {
|
|
178
|
+
staleCount++;
|
|
179
|
+
}
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
return {
|
|
183
|
+
total: cliConfigs.length,
|
|
184
|
+
available: cliConfigs.filter(cli => cli.status === 'available').length,
|
|
185
|
+
authenticated: cliConfigs.filter(cli => cli.authenticated).length,
|
|
186
|
+
stale: staleCount
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
}
|