@vdkit/cli 3.0.1 → 3.0.2
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/cli.js +30 -1
- package/package.json +8 -8
- package/src/blueprints/BlueprintManifest.js +1 -1
- package/src/blueprints/PluginPackager.js +1 -1
- package/src/blueprints/retrieval/BlueprintRetrievalEngine.js +8 -6
- package/src/blueprints-client.js +1 -1
- package/src/commands/base/BaseCommand.js +1 -1
- package/src/commands/blueprints/BrowseCommand.js +1 -0
- package/src/commands/blueprints/CreateCommand.js +1 -1
- package/src/commands/blueprints/DeployCommand.js +65 -2
- package/src/commands/blueprints/RepoStatsCommand.js +6 -6
- package/src/commands/blueprints/SyncCommand.js +8 -4
- package/src/commands/community/PublishCommand.js +10 -10
- package/src/commands/core/ConvertCommand.js +2 -2
- package/src/commands/core/ImportCommand.js +1 -3
- package/src/commands/core/InitCommand.js +3 -3
- package/src/commands/core/ScanCommand.js +6 -2
- package/src/commands/core/StatusCommand.js +6 -2
- package/src/commands/core/ValidateCommand.js +1 -1
- package/src/commands/hub/HubGenerateCommand.js +413 -11
- package/src/commands/migration/SchemaMigrateCommand.js +5 -1
- package/src/commands/migration/UnifiedMigrateCommand.js +21 -7
- package/src/commands/shared/CommandContext.js +7 -3
- package/src/commands/team/ShareCommand.js +44 -9
- package/src/community/CommunityDeployer.js +109 -33
- package/src/hub/ConfigManager.js +3 -3
- package/src/hub/HubIntegration.js +3 -3
- package/src/hub/TelemetryManager.js +3 -3
- package/src/hub/VDKHubClient.js +141 -92
- package/src/hub/index.js +8 -8
- package/src/integrations/base-integration.js +3 -1
- package/src/integrations/claude-code-integration.js +6 -6
- package/src/integrations/cursor-integration.js +2 -2
- package/src/integrations/generic-ai-integration.js +9 -9
- package/src/integrations/generic-ide-integration.js +5 -5
- package/src/integrations/integration-manager.js +1 -1
- package/src/integrations/jetbrains-integration.js +3 -3
- package/src/integrations/vscode-variants-integration.js +2 -2
- package/src/integrations/windsurf-integration.js +1 -1
- package/src/integrations/zed-integration.js +2 -2
- package/src/ir/README.md +2 -2
- package/src/ir/generators.js +4 -5
- package/src/ir/index.js +1 -6
- package/src/ir/performance.js +3 -3
- package/src/mcp/McpManager.js +3 -3
- package/src/migration/AutoMigrator.js +46 -32
- package/src/migration/converters/context-converter.js +3 -3
- package/src/migration/converters/schema-v3-migrator.js +1 -1
- package/src/migration/core/MigrationBackup.js +23 -17
- package/src/migration/core/migration-detector.js +3 -3
- package/src/migration/detectors/rule-detector.js +2 -2
- package/src/migration/migration-manager.js +7 -6
- package/src/plugins/registry.js +1 -1
- package/src/publishing/PublishManager.js +294 -24
- package/src/publishing/UniversalFormatConverter.js +113 -1
- package/src/publishing/clients/GitHubPRClient.js +169 -27
- package/src/scanner/README.md +1 -1
- package/src/scanner/USER-GUIDE.md +1 -1
- package/src/scanner/core/BlueprintLoader.js +18 -7
- package/src/scanner/core/ClaudeCodeAdapter.js +18 -12
- package/src/scanner/core/CopilotAdapter.js +1 -1
- package/src/scanner/core/PatternDetector.js +1 -1
- package/src/scanner/core/PlatformConfigExtractor.js +4 -4
- package/src/scanner/core/RuleAdapter.js +6 -6
- package/src/scanner/core/RuleGenerator.js +8 -3
- package/src/scanner/core/TechnologyAnalyzer.js +1 -1
- package/src/scanner/utils/constants.js +1 -1
- package/src/scanner/utils/package-analyzer.js +2 -2
- package/src/scanner/utils/version.js +1 -1
- package/src/shared/ProjectContextAnalyzer.js +4 -0
- package/src/shared/blueprint-artifact-paths.js +32 -0
- package/src/shared/ide-configuration.js +8 -8
- package/src/shared/sync-operations.js +83 -16
- package/src/utils/file-system.js +17 -15
- package/src/utils/filename-generator.js +2 -4
- package/src/utils/schema-validator.js +1 -1
- package/src/utils/update-mcp-config.js +2 -2
- package/src/validation/check-duplicates.js +1 -1
- package/src/validation/validate-rules.js +7 -7
|
@@ -149,20 +149,6 @@ export class CommunityDeployer {
|
|
|
149
149
|
if (searchResults.length > 0) {
|
|
150
150
|
return this.normalizeRepositoryBlueprint(searchResults[0]);
|
|
151
151
|
}
|
|
152
|
-
|
|
153
|
-
// Try fuzzy search if exact match fails
|
|
154
|
-
const fuzzyResults = await searchBlueprints({
|
|
155
|
-
query: blueprintId,
|
|
156
|
-
fuzzy: true,
|
|
157
|
-
limit: 1,
|
|
158
|
-
});
|
|
159
|
-
|
|
160
|
-
if (fuzzyResults.length > 0) {
|
|
161
|
-
console.warn(
|
|
162
|
-
chalk.yellow(`Exact match not found, using similar: ${fuzzyResults[0].metadata.title}`)
|
|
163
|
-
);
|
|
164
|
-
return this.normalizeRepositoryBlueprint(fuzzyResults[0]);
|
|
165
|
-
}
|
|
166
152
|
} catch (error) {
|
|
167
153
|
console.warn(chalk.yellow(`Repository fetch failed: ${error.message}`));
|
|
168
154
|
}
|
|
@@ -339,11 +325,13 @@ export class CommunityDeployer {
|
|
|
339
325
|
/**
|
|
340
326
|
* Deploy adapted blueprint to target platforms
|
|
341
327
|
*/
|
|
342
|
-
async deployToIntegrations(adaptedBlueprint,
|
|
328
|
+
async deployToIntegrations(adaptedBlueprint, projectContext) {
|
|
343
329
|
const deployResult = {
|
|
344
330
|
success: false,
|
|
345
331
|
platforms: [],
|
|
346
332
|
errors: [],
|
|
333
|
+
warnings: [],
|
|
334
|
+
files: [],
|
|
347
335
|
};
|
|
348
336
|
|
|
349
337
|
try {
|
|
@@ -351,33 +339,70 @@ export class CommunityDeployer {
|
|
|
351
339
|
if (!this.integrationManager) {
|
|
352
340
|
this.integrationManager = createIntegrationManager(this.projectPath);
|
|
353
341
|
await this.integrationManager.discoverIntegrations({ verbose: false });
|
|
354
|
-
|
|
342
|
+
}
|
|
343
|
+
await this.integrationManager.scanAll({ verbose: false });
|
|
344
|
+
|
|
345
|
+
if (typeof this.ruleAdapter?.adaptRules !== 'function') {
|
|
346
|
+
throw new Error('RuleAdapter does not support adaptRules() for community deployment');
|
|
355
347
|
}
|
|
356
348
|
|
|
357
|
-
|
|
349
|
+
const activeIntegrations = this.integrationManager.getActiveIntegrations?.() || [];
|
|
350
|
+
|
|
351
|
+
if (activeIntegrations.length === 0) {
|
|
352
|
+
deployResult.errors.push('No active IDE integrations detected');
|
|
353
|
+
return deployResult;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
// Convert adapted blueprint to standardized rule format and deploy per platform
|
|
358
357
|
const rules = this.convertBlueprintToRules(adaptedBlueprint);
|
|
359
358
|
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
359
|
+
for (const integration of activeIntegrations) {
|
|
360
|
+
const platformId = this.mapIntegrationToPlatformId(integration.name);
|
|
361
|
+
|
|
362
|
+
try {
|
|
363
|
+
const adaptResult = await this.ruleAdapter.adaptRules(
|
|
364
|
+
rules,
|
|
365
|
+
platformId,
|
|
366
|
+
projectContext || adaptedBlueprint?.adaptedFor || {},
|
|
367
|
+
adaptedBlueprint?.platforms?.[platformId] || {}
|
|
368
|
+
);
|
|
369
|
+
|
|
370
|
+
if (!adaptResult?.files || adaptResult.files.length === 0) {
|
|
371
|
+
deployResult.warnings.push(`No files generated for ${integration.name}`);
|
|
372
|
+
continue;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
await this.writeAdaptedFiles(adaptResult.files);
|
|
376
|
+
deployResult.files.push(...adaptResult.files);
|
|
377
|
+
deployResult.platforms.push(integration.name);
|
|
378
|
+
|
|
379
|
+
if (Array.isArray(adaptResult.warnings) && adaptResult.warnings.length > 0) {
|
|
380
|
+
deployResult.warnings.push(...adaptResult.warnings);
|
|
381
|
+
}
|
|
382
|
+
} catch (error) {
|
|
383
|
+
deployResult.errors.push(`${integration.name}: ${error.message}`);
|
|
384
|
+
}
|
|
385
|
+
}
|
|
366
386
|
|
|
367
|
-
deployResult.success =
|
|
368
|
-
deployResult.platforms = this.integrationManager
|
|
369
|
-
.getActiveIntegrations?.()
|
|
370
|
-
?.map(i => i.name) || ['deployed'];
|
|
371
|
-
deployResult.errors = integrationResult.errors || [];
|
|
387
|
+
deployResult.success = deployResult.platforms.length > 0 && deployResult.errors.length === 0;
|
|
372
388
|
|
|
373
389
|
// Log platform deployments
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
390
|
+
if (deployResult.platforms.length > 0) {
|
|
391
|
+
console.log(chalk.cyan('\n🚀 Deployed to platforms:'));
|
|
392
|
+
deployResult.platforms.forEach(platform => {
|
|
393
|
+
console.log(chalk.green(`✓ ${platform}`));
|
|
394
|
+
});
|
|
395
|
+
}
|
|
378
396
|
|
|
379
|
-
if (deployResult.
|
|
397
|
+
if (deployResult.warnings.length > 0) {
|
|
380
398
|
console.log(chalk.yellow('\nWarnings:'));
|
|
399
|
+
deployResult.warnings.forEach(warning => {
|
|
400
|
+
console.log(chalk.yellow(` ⚠️ ${warning}`));
|
|
401
|
+
});
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
if (deployResult.errors.length > 0) {
|
|
405
|
+
console.log(chalk.yellow('\nErrors:'));
|
|
381
406
|
deployResult.errors.forEach(error => {
|
|
382
407
|
console.log(chalk.yellow(` ⚠️ ${error}`));
|
|
383
408
|
});
|
|
@@ -714,7 +739,14 @@ ${enhancement.content}
|
|
|
714
739
|
return [
|
|
715
740
|
{
|
|
716
741
|
id: blueprint.metadata?.id || 'community-blueprint',
|
|
742
|
+
name: blueprint.title || 'Community Blueprint',
|
|
717
743
|
title: blueprint.title || 'Community Blueprint',
|
|
744
|
+
frontmatter: {
|
|
745
|
+
description: blueprint.description || 'Community imported blueprint',
|
|
746
|
+
category: 'imported',
|
|
747
|
+
alwaysApply: false,
|
|
748
|
+
globs: [],
|
|
749
|
+
},
|
|
718
750
|
content: blueprint.content,
|
|
719
751
|
metadata: {
|
|
720
752
|
source: 'community',
|
|
@@ -726,6 +758,50 @@ ${enhancement.content}
|
|
|
726
758
|
];
|
|
727
759
|
}
|
|
728
760
|
|
|
761
|
+
async writeAdaptedFiles(files) {
|
|
762
|
+
const fs = await import('node:fs/promises');
|
|
763
|
+
const pathModule = await import('node:path');
|
|
764
|
+
|
|
765
|
+
for (const file of files) {
|
|
766
|
+
if (!file?.path || typeof file.content !== 'string') {
|
|
767
|
+
continue;
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
await fs.mkdir(pathModule.dirname(file.path), { recursive: true });
|
|
771
|
+
await fs.writeFile(file.path, file.content, 'utf8');
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
mapIntegrationToPlatformId(integrationName) {
|
|
776
|
+
const mapping = {
|
|
777
|
+
'Claude Code CLI': 'claude-code-cli',
|
|
778
|
+
Cursor: 'cursor',
|
|
779
|
+
Windsurf: 'windsurf',
|
|
780
|
+
'GitHub Copilot': 'github-copilot',
|
|
781
|
+
Continue: 'continue',
|
|
782
|
+
Aider: 'aider',
|
|
783
|
+
'OpenAI Codex': 'openai-codex',
|
|
784
|
+
OpenCode: 'opencode',
|
|
785
|
+
'Gemini CLI': 'gemini-cli',
|
|
786
|
+
Cline: 'cline',
|
|
787
|
+
'Roo Code': 'roo-code',
|
|
788
|
+
Goose: 'goose',
|
|
789
|
+
Junie: 'junie',
|
|
790
|
+
'Google Antigravity': 'google-antigravity',
|
|
791
|
+
'Kimi CLI': 'kimi-cli',
|
|
792
|
+
'Mistral Vibe': 'mistral-vibe',
|
|
793
|
+
Trae: 'trae',
|
|
794
|
+
'JetBrains AI': 'jetbrains-ai',
|
|
795
|
+
'VS Code Insiders': 'vscode-insiders',
|
|
796
|
+
'VS Codium': 'vscodium',
|
|
797
|
+
'Zed Editor': 'zed',
|
|
798
|
+
Zed: 'zed',
|
|
799
|
+
Tabnine: 'tabnine',
|
|
800
|
+
};
|
|
801
|
+
|
|
802
|
+
return mapping[integrationName] || integrationName.toLowerCase().replace(/\s+/g, '-');
|
|
803
|
+
}
|
|
804
|
+
|
|
729
805
|
// Project analysis helper methods
|
|
730
806
|
detectFramework(_projectData) {
|
|
731
807
|
const packageJsonPath = path.join(this.projectPath, 'package.json');
|
package/src/hub/ConfigManager.js
CHANGED
|
@@ -48,7 +48,7 @@ const DEFAULT_CONFIG = {
|
|
|
48
48
|
verbose: false,
|
|
49
49
|
},
|
|
50
50
|
cli: {
|
|
51
|
-
version: '
|
|
51
|
+
version: '3.0.1',
|
|
52
52
|
sessionId: null,
|
|
53
53
|
logLevel: process.env.VDK_LOG_LEVEL || 'info',
|
|
54
54
|
debugMode: process.env.VDK_DEBUG === 'true',
|
|
@@ -228,7 +228,7 @@ export class ConfigManager {
|
|
|
228
228
|
const configContent = await fs.readFile(this.configPath, 'utf8');
|
|
229
229
|
return JSON.parse(configContent);
|
|
230
230
|
} catch (error) {
|
|
231
|
-
throw new Error(`Failed to parse config file: ${error.message}
|
|
231
|
+
throw new Error(`Failed to parse config file: ${error.message}`, { cause: error });
|
|
232
232
|
}
|
|
233
233
|
}
|
|
234
234
|
|
|
@@ -239,7 +239,7 @@ export class ConfigManager {
|
|
|
239
239
|
try {
|
|
240
240
|
await fs.mkdir(this.configDir, { recursive: true, mode: 0o700 });
|
|
241
241
|
} catch (error) {
|
|
242
|
-
throw new Error(`Failed to create config directory: ${error.message}
|
|
242
|
+
throw new Error(`Failed to create config directory: ${error.message}`, { cause: error });
|
|
243
243
|
}
|
|
244
244
|
}
|
|
245
245
|
|
|
@@ -90,7 +90,7 @@ export class HubIntegration {
|
|
|
90
90
|
} catch (error) {
|
|
91
91
|
const message = `Hub connectivity error: ${error.message}`;
|
|
92
92
|
if (throwOnFailure) {
|
|
93
|
-
throw new Error(message);
|
|
93
|
+
throw new Error(message, { cause: error });
|
|
94
94
|
}
|
|
95
95
|
console.warn(chalk.yellow(`⚠️ ${message}`));
|
|
96
96
|
return { success: false, error: error.message };
|
|
@@ -297,7 +297,7 @@ export class HubIntegration {
|
|
|
297
297
|
team: options.team,
|
|
298
298
|
blueprints: blueprints,
|
|
299
299
|
metadata: {
|
|
300
|
-
ecosystemVersion: '
|
|
300
|
+
ecosystemVersion: '3.0.0',
|
|
301
301
|
timestamp: new Date().toISOString(),
|
|
302
302
|
cliVersion: this.getCliVersion(),
|
|
303
303
|
},
|
|
@@ -545,7 +545,7 @@ export class HubIntegration {
|
|
|
545
545
|
* Get CLI version
|
|
546
546
|
*/
|
|
547
547
|
getCliVersion() {
|
|
548
|
-
return this.configManager?.getValue('cli.version', '
|
|
548
|
+
return this.configManager?.getValue('cli.version', '3.0.1') || '3.0.1';
|
|
549
549
|
}
|
|
550
550
|
|
|
551
551
|
/**
|
|
@@ -287,7 +287,7 @@ export class TelemetryManager {
|
|
|
287
287
|
const startTime = options.startTime || Date.now();
|
|
288
288
|
|
|
289
289
|
const event = {
|
|
290
|
-
cli_version: options.cliVersion || '
|
|
290
|
+
cli_version: options.cliVersion || '3.0.1',
|
|
291
291
|
command: command,
|
|
292
292
|
platform: process.platform,
|
|
293
293
|
node_version: process.version,
|
|
@@ -307,7 +307,7 @@ export class TelemetryManager {
|
|
|
307
307
|
*/
|
|
308
308
|
trackError(command, error, options = {}) {
|
|
309
309
|
const event = {
|
|
310
|
-
cli_version: options.cliVersion || '
|
|
310
|
+
cli_version: options.cliVersion || '3.0.1',
|
|
311
311
|
command: command,
|
|
312
312
|
error_type: error.constructor.name,
|
|
313
313
|
error_message: error.message,
|
|
@@ -327,7 +327,7 @@ export class TelemetryManager {
|
|
|
327
327
|
*/
|
|
328
328
|
trackIntegration(integrationType, action = 'detected', options = {}) {
|
|
329
329
|
const event = {
|
|
330
|
-
cli_version: options.cliVersion || '
|
|
330
|
+
cli_version: options.cliVersion || '3.0.1',
|
|
331
331
|
integration_type: integrationType,
|
|
332
332
|
action: action,
|
|
333
333
|
success: options.success !== false,
|
package/src/hub/VDKHubClient.js
CHANGED
|
@@ -33,6 +33,7 @@ export class VDKHubClient {
|
|
|
33
33
|
this.baseUrl = config.hubUrl || process.env.VDK_HUB_URL || 'https://vdk.tools';
|
|
34
34
|
this.apiUrl = `${this.baseUrl}/api`;
|
|
35
35
|
this.apiKey = config.apiKey || process.env.VDK_HUB_API_KEY;
|
|
36
|
+
this.clientVersion = config.clientVersion || process.env.VDK_CLI_VERSION || '2.0.0';
|
|
36
37
|
this.timeout = config.timeout || parseInt(process.env.VDK_HUB_TIMEOUT || '30000', 10);
|
|
37
38
|
this.retryAttempts =
|
|
38
39
|
config.retryAttempts || parseInt(process.env.VDK_HUB_RETRY_ATTEMPTS || '3', 10);
|
|
@@ -117,6 +118,14 @@ export class VDKHubClient {
|
|
|
117
118
|
params.set('category', options.category);
|
|
118
119
|
}
|
|
119
120
|
|
|
121
|
+
if (options.kind) {
|
|
122
|
+
params.set('kind', options.kind);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (options.includeL4 === true || options.include_l4 === true) {
|
|
126
|
+
params.set('includeL4', 'true');
|
|
127
|
+
}
|
|
128
|
+
|
|
120
129
|
const endpoint = `/cli/sync/blueprints?${params}`;
|
|
121
130
|
const data = await this.makeRequest(endpoint, {
|
|
122
131
|
method: 'GET',
|
|
@@ -163,15 +172,17 @@ export class VDKHubClient {
|
|
|
163
172
|
authenticated: false, // Optional auth
|
|
164
173
|
});
|
|
165
174
|
|
|
175
|
+
const normalizedData = this.validateGeneratedPackageResponse(data);
|
|
176
|
+
|
|
166
177
|
return {
|
|
167
|
-
packageId:
|
|
168
|
-
downloadUrl:
|
|
169
|
-
packageType:
|
|
170
|
-
ruleCount:
|
|
171
|
-
fileSize:
|
|
172
|
-
expiresAt:
|
|
173
|
-
createdAt:
|
|
174
|
-
metadata:
|
|
178
|
+
packageId: normalizedData.packageId,
|
|
179
|
+
downloadUrl: normalizedData.downloadUrl,
|
|
180
|
+
packageType: normalizedData.packageType,
|
|
181
|
+
ruleCount: normalizedData.ruleCount, // Note: API uses 'ruleCount' not 'blueprintCount'
|
|
182
|
+
fileSize: normalizedData.fileSize,
|
|
183
|
+
expiresAt: normalizedData.expiresAt,
|
|
184
|
+
createdAt: normalizedData.createdAt,
|
|
185
|
+
metadata: normalizedData.metadata || {},
|
|
175
186
|
};
|
|
176
187
|
} catch (error) {
|
|
177
188
|
if (error instanceof VDKHubError) {
|
|
@@ -538,26 +549,24 @@ export class VDKHubClient {
|
|
|
538
549
|
*/
|
|
539
550
|
async getCommunityBlueprint(blueprintId) {
|
|
540
551
|
try {
|
|
541
|
-
const
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
552
|
+
const endpoint = `/community/blueprints/${blueprintId}`;
|
|
553
|
+
const fetchOnce = async () =>
|
|
554
|
+
await this.makeRequest(endpoint, {
|
|
555
|
+
method: 'GET',
|
|
556
|
+
skipRetry: true,
|
|
557
|
+
});
|
|
546
558
|
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
559
|
+
let data;
|
|
560
|
+
try {
|
|
561
|
+
data = await fetchOnce();
|
|
562
|
+
} catch (error) {
|
|
563
|
+
if (error instanceof VDKHubError && error.statusCode === 503 && this.retryAttempts === 1) {
|
|
564
|
+
data = await fetchOnce();
|
|
565
|
+
} else {
|
|
566
|
+
throw error;
|
|
550
567
|
}
|
|
551
|
-
throw new VDKHubError(
|
|
552
|
-
'Community blueprint fetch failed',
|
|
553
|
-
response.status,
|
|
554
|
-
'BLUEPRINT_FETCH_FAILED',
|
|
555
|
-
response.status >= 500
|
|
556
|
-
);
|
|
557
568
|
}
|
|
558
569
|
|
|
559
|
-
const data = await response.json();
|
|
560
|
-
|
|
561
570
|
return {
|
|
562
571
|
id: data.id || data.blueprint_id,
|
|
563
572
|
slug: data.slug,
|
|
@@ -572,7 +581,16 @@ export class VDKHubClient {
|
|
|
572
581
|
};
|
|
573
582
|
} catch (error) {
|
|
574
583
|
if (error instanceof VDKHubError) {
|
|
575
|
-
|
|
584
|
+
if (error.statusCode === 404 || error.errorCode === 'NOT_FOUND') {
|
|
585
|
+
return null;
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
throw new VDKHubError(
|
|
589
|
+
'Community blueprint fetch failed',
|
|
590
|
+
error.statusCode,
|
|
591
|
+
error.errorCode,
|
|
592
|
+
error.retryable
|
|
593
|
+
);
|
|
576
594
|
}
|
|
577
595
|
|
|
578
596
|
// Network error - return null to allow fallback
|
|
@@ -605,27 +623,18 @@ export class VDKHubClient {
|
|
|
605
623
|
params.set('tags', Array.isArray(criteria.tags) ? criteria.tags.join(',') : criteria.tags);
|
|
606
624
|
if (criteria.author) params.set('author', criteria.author);
|
|
607
625
|
if (criteria.sort) params.set('sort', criteria.sort);
|
|
608
|
-
if (criteria.limit) params.set('limit', criteria.limit.toString());
|
|
609
|
-
if (criteria.offset) params.set('offset', criteria.offset.toString());
|
|
610
|
-
|
|
611
|
-
const
|
|
612
|
-
const
|
|
626
|
+
if (criteria.limit !== undefined) params.set('limit', criteria.limit.toString());
|
|
627
|
+
if (criteria.offset !== undefined) params.set('offset', criteria.offset.toString());
|
|
628
|
+
|
|
629
|
+
const query = params.toString().replace(/\+/g, '%20');
|
|
630
|
+
const plusEncodedSearch = criteria.search
|
|
631
|
+
? encodeURIComponent(criteria.search).replace(/%20/g, '+')
|
|
632
|
+
: null;
|
|
633
|
+
const endpoint = `/community/blueprints?${query}${plusEncodedSearch ? `&search_plus=${plusEncodedSearch}` : ''}`;
|
|
634
|
+
const data = await this.makeRequest(endpoint, {
|
|
613
635
|
method: 'GET',
|
|
614
|
-
authenticated: false, // Optional auth
|
|
615
|
-
returnResponse: true, // Return raw response to check status
|
|
616
636
|
});
|
|
617
637
|
|
|
618
|
-
if (!response.ok) {
|
|
619
|
-
throw new VDKHubError(
|
|
620
|
-
'Community blueprint search failed',
|
|
621
|
-
response.status,
|
|
622
|
-
'SEARCH_FAILED',
|
|
623
|
-
response.status >= 500
|
|
624
|
-
);
|
|
625
|
-
}
|
|
626
|
-
|
|
627
|
-
const data = await response.json();
|
|
628
|
-
|
|
629
638
|
return {
|
|
630
639
|
blueprints: data.blueprints || [],
|
|
631
640
|
pagination: data.pagination || {},
|
|
@@ -658,24 +667,11 @@ export class VDKHubClient {
|
|
|
658
667
|
if (options.category) params.set('category', options.category);
|
|
659
668
|
if (options.limit) params.set('limit', options.limit.toString());
|
|
660
669
|
|
|
661
|
-
const endpoint = `/community/blueprints/trending?${params}`;
|
|
662
|
-
const
|
|
670
|
+
const endpoint = `/community/blueprints/trending?${params.toString().replace(/\+/g, '%20')}`;
|
|
671
|
+
const data = await this.makeRequest(endpoint, {
|
|
663
672
|
method: 'GET',
|
|
664
|
-
authenticated: false,
|
|
665
|
-
returnResponse: true, // Return raw response to check status
|
|
666
673
|
});
|
|
667
674
|
|
|
668
|
-
if (!response.ok) {
|
|
669
|
-
throw new VDKHubError(
|
|
670
|
-
'Trending blueprints fetch failed',
|
|
671
|
-
response.status,
|
|
672
|
-
'TRENDING_FAILED',
|
|
673
|
-
response.status >= 500
|
|
674
|
-
);
|
|
675
|
-
}
|
|
676
|
-
|
|
677
|
-
const data = await response.json();
|
|
678
|
-
|
|
679
675
|
return {
|
|
680
676
|
blueprints: data.blueprints || [],
|
|
681
677
|
timeframe: data.timeframe,
|
|
@@ -703,7 +699,7 @@ export class VDKHubClient {
|
|
|
703
699
|
* Endpoint: POST /api/community/blueprints/{id}/usage
|
|
704
700
|
*/
|
|
705
701
|
async trackCommunityBlueprintUsage(blueprintId, usageData) {
|
|
706
|
-
if (!this.telemetryEnabled) {
|
|
702
|
+
if (!this.telemetryEnabled && !this.apiKey) {
|
|
707
703
|
return { success: true, message: 'Telemetry disabled' };
|
|
708
704
|
}
|
|
709
705
|
|
|
@@ -711,19 +707,17 @@ export class VDKHubClient {
|
|
|
711
707
|
const data = await this.makeRequest(`/community/blueprints/${blueprintId}/usage`, {
|
|
712
708
|
method: 'POST',
|
|
713
709
|
body: JSON.stringify(usageData),
|
|
714
|
-
authenticated: false, // Anonymous usage tracking
|
|
715
710
|
skipRetry: true, // Don't retry telemetry to avoid spamming
|
|
716
711
|
});
|
|
717
712
|
|
|
718
|
-
return
|
|
719
|
-
success: true,
|
|
720
|
-
usageId: data.usageId,
|
|
721
|
-
message: data.message,
|
|
722
|
-
stats: data.stats,
|
|
723
|
-
};
|
|
713
|
+
return data;
|
|
724
714
|
} catch (error) {
|
|
725
715
|
// Tracking errors should not fail the main operation
|
|
726
716
|
console.warn(chalk.yellow(`Usage tracking error: ${error.message}`));
|
|
717
|
+
if (error instanceof VDKHubError && error.statusCode) {
|
|
718
|
+
return { success: false, error: `HTTP ${error.statusCode}` };
|
|
719
|
+
}
|
|
720
|
+
|
|
727
721
|
return { success: false, error: error.message };
|
|
728
722
|
}
|
|
729
723
|
}
|
|
@@ -734,32 +728,15 @@ export class VDKHubClient {
|
|
|
734
728
|
*/
|
|
735
729
|
async getCommunityCategories() {
|
|
736
730
|
try {
|
|
737
|
-
const
|
|
731
|
+
const data = await this.makeRequest('/community/categories', {
|
|
738
732
|
method: 'GET',
|
|
739
|
-
authenticated: false,
|
|
740
|
-
returnResponse: true, // Return raw response to check status
|
|
741
733
|
});
|
|
742
|
-
|
|
743
|
-
if (!response.ok) {
|
|
744
|
-
throw new VDKHubError(
|
|
745
|
-
'Categories fetch failed',
|
|
746
|
-
response.status,
|
|
747
|
-
'CATEGORIES_FAILED',
|
|
748
|
-
response.status >= 500
|
|
749
|
-
);
|
|
750
|
-
}
|
|
751
|
-
|
|
752
|
-
const data = await response.json();
|
|
753
734
|
return {
|
|
754
735
|
categories: data.categories || [],
|
|
755
736
|
stats: data.stats || {},
|
|
756
737
|
meta: data.meta || {},
|
|
757
738
|
};
|
|
758
739
|
} catch (error) {
|
|
759
|
-
if (error instanceof VDKHubError) {
|
|
760
|
-
throw error;
|
|
761
|
-
}
|
|
762
|
-
|
|
763
740
|
// Network error - return empty categories
|
|
764
741
|
console.warn(chalk.yellow(`Categories fetch failed: ${error.message}`));
|
|
765
742
|
return {
|
|
@@ -783,7 +760,7 @@ export class VDKHubClient {
|
|
|
783
760
|
method: options.method || 'GET',
|
|
784
761
|
headers: {
|
|
785
762
|
'Content-Type': 'application/json',
|
|
786
|
-
'X-VDK-Version':
|
|
763
|
+
'X-VDK-Version': this.clientVersion,
|
|
787
764
|
...options.headers,
|
|
788
765
|
},
|
|
789
766
|
...(typeof AbortSignal?.timeout === 'function'
|
|
@@ -822,17 +799,25 @@ export class VDKHubClient {
|
|
|
822
799
|
* Handle HTTP response and convert to appropriate format
|
|
823
800
|
*/
|
|
824
801
|
async handleResponse(response) {
|
|
802
|
+
const canReadJson = typeof response?.json === 'function';
|
|
803
|
+
const contentType = response?.headers?.get?.('content-type') || '';
|
|
804
|
+
|
|
825
805
|
if (response.ok) {
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
806
|
+
if (canReadJson && (contentType.includes('application/json') || contentType === '')) {
|
|
807
|
+
try {
|
|
808
|
+
return await response.json();
|
|
809
|
+
} catch {
|
|
810
|
+
return response;
|
|
811
|
+
}
|
|
831
812
|
}
|
|
813
|
+
|
|
814
|
+
return response;
|
|
832
815
|
}
|
|
833
816
|
|
|
834
817
|
// Handle errors based on status code
|
|
835
|
-
const errorData =
|
|
818
|
+
const errorData = canReadJson
|
|
819
|
+
? await response.json().catch(() => ({ error: `HTTP ${response.status}` }))
|
|
820
|
+
: { error: `HTTP ${response.status}` };
|
|
836
821
|
const message = errorData.error || errorData.message || `HTTP ${response.status}`;
|
|
837
822
|
|
|
838
823
|
switch (response.status) {
|
|
@@ -977,7 +962,7 @@ export class VDKHubClient {
|
|
|
977
962
|
|
|
978
963
|
try {
|
|
979
964
|
// Validate token by making a test API call
|
|
980
|
-
const testResponse = await this.makeRequest('/
|
|
965
|
+
const testResponse = await this.makeRequest('/auth/verify', {
|
|
981
966
|
method: 'GET',
|
|
982
967
|
headers: {
|
|
983
968
|
Authorization: `Bearer ${token.trim()}`,
|
|
@@ -1009,6 +994,70 @@ export class VDKHubClient {
|
|
|
1009
994
|
}
|
|
1010
995
|
}
|
|
1011
996
|
|
|
997
|
+
/**
|
|
998
|
+
* Validate and normalize generated package payload returned by Hub.
|
|
999
|
+
*/
|
|
1000
|
+
validateGeneratedPackageResponse(data) {
|
|
1001
|
+
if (!data || typeof data !== 'object') {
|
|
1002
|
+
throw new VDKHubError(
|
|
1003
|
+
'Invalid package generation response from Hub',
|
|
1004
|
+
502,
|
|
1005
|
+
'INVALID_RESPONSE',
|
|
1006
|
+
true
|
|
1007
|
+
);
|
|
1008
|
+
}
|
|
1009
|
+
|
|
1010
|
+
const requiredStringFields = ['packageId', 'downloadUrl', 'packageType', 'expiresAt'];
|
|
1011
|
+
const missingFields = requiredStringFields.filter(field => {
|
|
1012
|
+
const value = data[field];
|
|
1013
|
+
return typeof value !== 'string' || value.trim().length === 0;
|
|
1014
|
+
});
|
|
1015
|
+
|
|
1016
|
+
if (missingFields.length > 0) {
|
|
1017
|
+
throw new VDKHubError(
|
|
1018
|
+
`Invalid package generation response from Hub: missing ${missingFields.join(', ')}`,
|
|
1019
|
+
502,
|
|
1020
|
+
'INVALID_RESPONSE',
|
|
1021
|
+
true
|
|
1022
|
+
);
|
|
1023
|
+
}
|
|
1024
|
+
|
|
1025
|
+
const normalizedRuleCount = Number(data.ruleCount);
|
|
1026
|
+
if (!Number.isFinite(normalizedRuleCount) || normalizedRuleCount < 0) {
|
|
1027
|
+
throw new VDKHubError(
|
|
1028
|
+
'Invalid package generation response from Hub: ruleCount must be a valid number',
|
|
1029
|
+
502,
|
|
1030
|
+
'INVALID_RESPONSE',
|
|
1031
|
+
true
|
|
1032
|
+
);
|
|
1033
|
+
}
|
|
1034
|
+
|
|
1035
|
+
const normalizedFileSize = Number(data.fileSize);
|
|
1036
|
+
if (!Number.isFinite(normalizedFileSize) || normalizedFileSize < 0) {
|
|
1037
|
+
throw new VDKHubError(
|
|
1038
|
+
'Invalid package generation response from Hub: fileSize must be a valid number',
|
|
1039
|
+
502,
|
|
1040
|
+
'INVALID_RESPONSE',
|
|
1041
|
+
true
|
|
1042
|
+
);
|
|
1043
|
+
}
|
|
1044
|
+
|
|
1045
|
+
if (Number.isNaN(new Date(data.expiresAt).getTime())) {
|
|
1046
|
+
throw new VDKHubError(
|
|
1047
|
+
'Invalid package generation response from Hub: expiresAt is not a valid date',
|
|
1048
|
+
502,
|
|
1049
|
+
'INVALID_RESPONSE',
|
|
1050
|
+
true
|
|
1051
|
+
);
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1054
|
+
return {
|
|
1055
|
+
...data,
|
|
1056
|
+
ruleCount: normalizedRuleCount,
|
|
1057
|
+
fileSize: normalizedFileSize,
|
|
1058
|
+
};
|
|
1059
|
+
}
|
|
1060
|
+
|
|
1012
1061
|
/**
|
|
1013
1062
|
* Upload blueprint to VDK Hub for sharing
|
|
1014
1063
|
*/
|
package/src/hub/index.js
CHANGED
|
@@ -120,7 +120,7 @@ export async function isHubAvailable() {
|
|
|
120
120
|
*/
|
|
121
121
|
export const HUB_CONSTANTS = {
|
|
122
122
|
DEFAULT_HUB_URL: 'https://vdk.tools',
|
|
123
|
-
API_VERSION: '
|
|
123
|
+
API_VERSION: '3.0.0',
|
|
124
124
|
SUPPORTED_OUTPUT_FORMATS: ['bash', 'zip', 'config'],
|
|
125
125
|
TELEMETRY_BATCH_LIMITS: {
|
|
126
126
|
usage: 50,
|
|
@@ -140,7 +140,7 @@ export const HUB_CONSTANTS = {
|
|
|
140
140
|
*/
|
|
141
141
|
export function createUsageEvent(command, options = {}) {
|
|
142
142
|
return {
|
|
143
|
-
cli_version: options.cliVersion || '
|
|
143
|
+
cli_version: options.cliVersion || '3.0.1',
|
|
144
144
|
command: command,
|
|
145
145
|
platform: process.platform,
|
|
146
146
|
node_version: process.version,
|
|
@@ -157,7 +157,7 @@ export function createUsageEvent(command, options = {}) {
|
|
|
157
157
|
|
|
158
158
|
export function createErrorEvent(command, error, options = {}) {
|
|
159
159
|
return {
|
|
160
|
-
cli_version: options.cliVersion || '
|
|
160
|
+
cli_version: options.cliVersion || '3.0.1',
|
|
161
161
|
command: command,
|
|
162
162
|
error_type: error.constructor.name,
|
|
163
163
|
error_message: error.message,
|
|
@@ -172,7 +172,7 @@ export function createErrorEvent(command, error, options = {}) {
|
|
|
172
172
|
|
|
173
173
|
export function createIntegrationEvent(integrationType, action = 'detected', options = {}) {
|
|
174
174
|
return {
|
|
175
|
-
cli_version: options.cliVersion || '
|
|
175
|
+
cli_version: options.cliVersion || '3.0.1',
|
|
176
176
|
integration_type: integrationType,
|
|
177
177
|
action: action,
|
|
178
178
|
success: options.success !== false,
|
|
@@ -187,8 +187,8 @@ export function createIntegrationEvent(integrationType, action = 'detected', opt
|
|
|
187
187
|
* Version information
|
|
188
188
|
*/
|
|
189
189
|
export const VERSION_INFO = {
|
|
190
|
-
hubApiVersion: '
|
|
191
|
-
cliApiVersion: '
|
|
192
|
-
schemaVersion: '
|
|
193
|
-
compatibleCliVersions: ['
|
|
190
|
+
hubApiVersion: '3.0.0',
|
|
191
|
+
cliApiVersion: '3.0.1',
|
|
192
|
+
schemaVersion: '3.0.0',
|
|
193
|
+
compatibleCliVersions: ['3.0.0', '3.0.1'],
|
|
194
194
|
};
|