create-qa-architect 5.0.0
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/.editorconfig +12 -0
- package/.github/CLAUDE_MD_AUTOMATION.md +248 -0
- package/.github/PROGRESSIVE_QUALITY_IMPLEMENTATION.md +408 -0
- package/.github/PROGRESSIVE_QUALITY_PROPOSAL.md +443 -0
- package/.github/RELEASE_CHECKLIST.md +100 -0
- package/.github/dependabot.yml +50 -0
- package/.github/git-sync.sh +48 -0
- package/.github/workflows/claude-md-validation.yml +82 -0
- package/.github/workflows/nightly-gitleaks-verification.yml +176 -0
- package/.github/workflows/pnpm-ci.yml.example +53 -0
- package/.github/workflows/python-ci.yml.example +69 -0
- package/.github/workflows/quality-legacy.yml.backup +165 -0
- package/.github/workflows/quality-progressive.yml.example +291 -0
- package/.github/workflows/quality.yml +436 -0
- package/.github/workflows/release.yml +53 -0
- package/.nvmrc +1 -0
- package/.prettierignore +14 -0
- package/.prettierrc +9 -0
- package/.stylelintrc.json +5 -0
- package/README.md +212 -0
- package/config/.lighthouserc.js +45 -0
- package/config/.pre-commit-config.yaml +66 -0
- package/config/constants.js +128 -0
- package/config/defaults.js +124 -0
- package/config/pyproject.toml +124 -0
- package/config/quality-config.schema.json +97 -0
- package/config/quality-python.yml +89 -0
- package/config/requirements-dev.txt +15 -0
- package/create-saas-monetization.js +1465 -0
- package/eslint.config.cjs +117 -0
- package/eslint.config.ts.cjs +99 -0
- package/legal/README.md +106 -0
- package/legal/copyright.md +76 -0
- package/legal/disclaimer.md +146 -0
- package/legal/privacy-policy.html +324 -0
- package/legal/privacy-policy.md +196 -0
- package/legal/terms-of-service.md +224 -0
- package/lib/billing-dashboard.html +645 -0
- package/lib/config-validator.js +163 -0
- package/lib/dependency-monitoring-basic.js +185 -0
- package/lib/dependency-monitoring-premium.js +1490 -0
- package/lib/error-reporter.js +444 -0
- package/lib/interactive/prompt.js +128 -0
- package/lib/interactive/questions.js +146 -0
- package/lib/license-validator.js +403 -0
- package/lib/licensing.js +989 -0
- package/lib/package-utils.js +187 -0
- package/lib/project-maturity.js +516 -0
- package/lib/security-enhancements.js +340 -0
- package/lib/setup-enhancements.js +317 -0
- package/lib/smart-strategy-generator.js +344 -0
- package/lib/telemetry.js +323 -0
- package/lib/template-loader.js +252 -0
- package/lib/typescript-config-generator.js +210 -0
- package/lib/ui-helpers.js +74 -0
- package/lib/validation/base-validator.js +174 -0
- package/lib/validation/cache-manager.js +158 -0
- package/lib/validation/config-security.js +741 -0
- package/lib/validation/documentation.js +326 -0
- package/lib/validation/index.js +186 -0
- package/lib/validation/validation-factory.js +153 -0
- package/lib/validation/workflow-validation.js +172 -0
- package/lib/yaml-utils.js +120 -0
- package/marketing/beta-user-email-campaign.md +372 -0
- package/marketing/landing-page.html +721 -0
- package/package.json +165 -0
- package/setup.js +2076 -0
package/lib/licensing.js
ADDED
|
@@ -0,0 +1,989 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Licensing System for create-qa-architect
|
|
3
|
+
* Handles free/pro/enterprise tier validation
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const fs = require('fs')
|
|
7
|
+
const path = require('path')
|
|
8
|
+
const os = require('os')
|
|
9
|
+
const crypto = require('crypto')
|
|
10
|
+
|
|
11
|
+
// License storage locations
|
|
12
|
+
// Support environment variable override for testing (like telemetry/error-reporter)
|
|
13
|
+
// Use getter functions to allow env override before module load
|
|
14
|
+
function getLicenseDir() {
|
|
15
|
+
return (
|
|
16
|
+
process.env.QAA_LICENSE_DIR ||
|
|
17
|
+
path.join(os.homedir(), '.create-qa-architect')
|
|
18
|
+
)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function getLicenseFile() {
|
|
22
|
+
return path.join(getLicenseDir(), 'license.json')
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Keep old constants for backward compatibility (but make them dynamic)
|
|
26
|
+
Object.defineProperty(exports, 'LICENSE_DIR', {
|
|
27
|
+
get: getLicenseDir,
|
|
28
|
+
})
|
|
29
|
+
Object.defineProperty(exports, 'LICENSE_FILE', {
|
|
30
|
+
get: getLicenseFile,
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* License tiers
|
|
35
|
+
*
|
|
36
|
+
* Standardized to use SCREAMING_SNAKE_CASE for both keys and values
|
|
37
|
+
* for consistency with ErrorCategory and other enums in the codebase.
|
|
38
|
+
*
|
|
39
|
+
* Pricing (effective Jan 15, 2026 - founder pricing retired):
|
|
40
|
+
* - FREE: $0 (Hobby/OSS - capped)
|
|
41
|
+
* - PRO: $59/mo or $590/yr (Solo Devs/Small Teams)
|
|
42
|
+
* - TEAM: $15/user/mo, 5-seat minimum (Organizations)
|
|
43
|
+
* - ENTERPRISE: $249/mo annual + $499 onboarding (Large Orgs)
|
|
44
|
+
*/
|
|
45
|
+
const LICENSE_TIERS = {
|
|
46
|
+
FREE: 'FREE',
|
|
47
|
+
PRO: 'PRO',
|
|
48
|
+
TEAM: 'TEAM',
|
|
49
|
+
ENTERPRISE: 'ENTERPRISE',
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Feature definitions by tier
|
|
54
|
+
*
|
|
55
|
+
* FREE: Hobby/OSS - capped usage, basic quality automation
|
|
56
|
+
* PRO: Solo devs/small teams - unlimited, full features
|
|
57
|
+
* TEAM: Organizations - per-seat, shared policies
|
|
58
|
+
* ENTERPRISE: Large orgs - SSO, compliance, SLA
|
|
59
|
+
*/
|
|
60
|
+
const FEATURES = {
|
|
61
|
+
[LICENSE_TIERS.FREE]: {
|
|
62
|
+
// Caps (enforced in setup.js and CLI)
|
|
63
|
+
maxPrivateRepos: 1,
|
|
64
|
+
maxDependencyPRsPerMonth: 10,
|
|
65
|
+
maxPrePushRunsPerMonth: 50,
|
|
66
|
+
// Features
|
|
67
|
+
dependencyMonitoring: 'basic',
|
|
68
|
+
languages: ['npm'], // JS/TS only
|
|
69
|
+
frameworkGrouping: false,
|
|
70
|
+
smartTestStrategy: false, // ā PRO feature
|
|
71
|
+
typescriptProtection: false, // ā PRO feature (moved from FREE)
|
|
72
|
+
securityScanning: false, // ā PRO feature - Gitleaks, ESLint security
|
|
73
|
+
projectTypeDetection: false,
|
|
74
|
+
customSchedules: false,
|
|
75
|
+
advancedWorkflows: false,
|
|
76
|
+
notifications: false,
|
|
77
|
+
multiRepo: false,
|
|
78
|
+
roadmap: [
|
|
79
|
+
'ā
ESLint, Prettier, Stylelint configuration',
|
|
80
|
+
'ā
Basic Husky pre-commit hooks',
|
|
81
|
+
'ā
Basic npm dependency monitoring (10 PRs/month)',
|
|
82
|
+
'ā ļø Limited: 1 private repo, JS/TS only',
|
|
83
|
+
'ā No security scanning (Gitleaks, ESLint security)',
|
|
84
|
+
'ā No Smart Test Strategy',
|
|
85
|
+
],
|
|
86
|
+
},
|
|
87
|
+
[LICENSE_TIERS.PRO]: {
|
|
88
|
+
// No caps - unlimited
|
|
89
|
+
maxPrivateRepos: Infinity,
|
|
90
|
+
maxDependencyPRsPerMonth: Infinity,
|
|
91
|
+
maxPrePushRunsPerMonth: Infinity,
|
|
92
|
+
// Features
|
|
93
|
+
dependencyMonitoring: 'premium',
|
|
94
|
+
languages: ['npm', 'python', 'rust', 'ruby'], // Multi-language
|
|
95
|
+
frameworkGrouping: true, // React, Vue, Angular, Svelte grouping
|
|
96
|
+
smartTestStrategy: true, // ā
KEY DIFFERENTIATOR: 70% faster pre-push
|
|
97
|
+
typescriptProtection: true, // ā
tests/tsconfig.json generation
|
|
98
|
+
securityScanning: true, // ā
Gitleaks + ESLint security rules
|
|
99
|
+
projectTypeDetection: true, // CLI, Web, SaaS, API, Library, Docs
|
|
100
|
+
advancedSecurity: true, // Rate limits, stricter audits
|
|
101
|
+
customSchedules: false,
|
|
102
|
+
advancedWorkflows: false,
|
|
103
|
+
notifications: false,
|
|
104
|
+
multiRepo: false,
|
|
105
|
+
roadmap: [
|
|
106
|
+
'ā
Unlimited repos and runs',
|
|
107
|
+
'ā
Smart Test Strategy (70% faster pre-push validation)',
|
|
108
|
+
'ā
Security scanning (Gitleaks + ESLint security rules)',
|
|
109
|
+
'ā
TypeScript production protection',
|
|
110
|
+
'ā
Multi-language (Python, Rust, Ruby)',
|
|
111
|
+
'ā
Framework-aware dependency grouping',
|
|
112
|
+
'ā
Email support (24-48h response)',
|
|
113
|
+
'š Performance budgets - Coming Q1 2026',
|
|
114
|
+
],
|
|
115
|
+
},
|
|
116
|
+
[LICENSE_TIERS.TEAM]: {
|
|
117
|
+
// Per-seat model (5-seat minimum)
|
|
118
|
+
minSeats: 5,
|
|
119
|
+
maxPrivateRepos: Infinity,
|
|
120
|
+
maxDependencyPRsPerMonth: Infinity,
|
|
121
|
+
maxPrePushRunsPerMonth: Infinity,
|
|
122
|
+
// All PRO features plus:
|
|
123
|
+
dependencyMonitoring: 'premium',
|
|
124
|
+
languages: ['npm', 'python', 'rust', 'ruby'],
|
|
125
|
+
frameworkGrouping: true,
|
|
126
|
+
smartTestStrategy: true,
|
|
127
|
+
typescriptProtection: true,
|
|
128
|
+
securityScanning: true,
|
|
129
|
+
projectTypeDetection: true,
|
|
130
|
+
advancedSecurity: true,
|
|
131
|
+
// Team-specific
|
|
132
|
+
perSeatLicensing: true,
|
|
133
|
+
sharedOrgQuota: true,
|
|
134
|
+
teamPolicies: true, // Team-wide config policies
|
|
135
|
+
roleBasedAccess: true,
|
|
136
|
+
teamAuditLog: true, // Local audit log
|
|
137
|
+
slackAlerts: true, // Slack/email alerts for failures
|
|
138
|
+
customSchedules: true,
|
|
139
|
+
advancedWorkflows: true,
|
|
140
|
+
notifications: true,
|
|
141
|
+
multiRepo: true,
|
|
142
|
+
roadmap: [
|
|
143
|
+
'ā
All PRO features included',
|
|
144
|
+
'ā
Per-seat licensing (5-seat minimum)',
|
|
145
|
+
'ā
Shared org quota & usage reporting',
|
|
146
|
+
'ā
Team-wide configuration policies',
|
|
147
|
+
'ā
Slack/email alerts for failures',
|
|
148
|
+
'ā
Team audit log',
|
|
149
|
+
'ā
Priority support (business hours)',
|
|
150
|
+
],
|
|
151
|
+
},
|
|
152
|
+
[LICENSE_TIERS.ENTERPRISE]: {
|
|
153
|
+
// Annual only + onboarding
|
|
154
|
+
annualOnly: true,
|
|
155
|
+
onboardingFee: 499,
|
|
156
|
+
maxPrivateRepos: Infinity,
|
|
157
|
+
maxDependencyPRsPerMonth: Infinity,
|
|
158
|
+
maxPrePushRunsPerMonth: Infinity,
|
|
159
|
+
// All TEAM features plus:
|
|
160
|
+
dependencyMonitoring: 'enterprise',
|
|
161
|
+
languages: ['npm', 'python', 'rust', 'ruby', 'go', 'java'],
|
|
162
|
+
frameworkGrouping: true,
|
|
163
|
+
smartTestStrategy: true,
|
|
164
|
+
typescriptProtection: true,
|
|
165
|
+
securityScanning: true,
|
|
166
|
+
projectTypeDetection: true,
|
|
167
|
+
advancedSecurity: true,
|
|
168
|
+
perSeatLicensing: true,
|
|
169
|
+
sharedOrgQuota: true,
|
|
170
|
+
teamPolicies: true,
|
|
171
|
+
roleBasedAccess: true,
|
|
172
|
+
teamAuditLog: true,
|
|
173
|
+
slackAlerts: true,
|
|
174
|
+
customSchedules: true,
|
|
175
|
+
advancedWorkflows: true,
|
|
176
|
+
notifications: true,
|
|
177
|
+
multiRepo: true,
|
|
178
|
+
// Enterprise-specific
|
|
179
|
+
ssoIntegration: true, // SSO/SAML
|
|
180
|
+
scimReady: true,
|
|
181
|
+
customRiskPatterns: true, // Custom Smart Test Strategy patterns
|
|
182
|
+
customDependencyPolicies: true, // Deny/allow lists, license classes
|
|
183
|
+
webhookBilling: true,
|
|
184
|
+
auditLogsExport: true,
|
|
185
|
+
dataRetentionControls: true,
|
|
186
|
+
compliancePack: true,
|
|
187
|
+
dedicatedTAM: true,
|
|
188
|
+
incidentSLA: true, // 24Ć5 support
|
|
189
|
+
onPremOption: true, // Optional on-prem license server
|
|
190
|
+
roadmap: [
|
|
191
|
+
'ā
All TEAM features included',
|
|
192
|
+
'ā
SSO/SAML integration',
|
|
193
|
+
'ā
Custom risk patterns for Smart Test Strategy',
|
|
194
|
+
'ā
Custom dependency policies (deny/allow, license classes)',
|
|
195
|
+
'ā
Audit logs export & data retention controls',
|
|
196
|
+
'ā
Compliance pack (SOC2, GDPR ready)',
|
|
197
|
+
'ā
Dedicated TAM + 24Ć5 support with SLA',
|
|
198
|
+
'ā
Optional on-prem license server',
|
|
199
|
+
],
|
|
200
|
+
},
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Check if developer/owner mode is enabled
|
|
205
|
+
* Allows the tool creator to use all features without a license
|
|
206
|
+
*/
|
|
207
|
+
function isDeveloperMode() {
|
|
208
|
+
// Check environment variable
|
|
209
|
+
if (process.env.QAA_DEVELOPER === 'true') {
|
|
210
|
+
return true
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Check for marker file in license directory
|
|
214
|
+
try {
|
|
215
|
+
const developerMarkerFile = path.join(getLicenseDir(), '.cqa-developer')
|
|
216
|
+
if (fs.existsSync(developerMarkerFile)) {
|
|
217
|
+
return true
|
|
218
|
+
}
|
|
219
|
+
} catch (_error) {
|
|
220
|
+
// Ignore errors checking for marker file
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
return false
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Check if user has a valid license file (USER-FACING - NO STRIPE DEPENDENCIES)
|
|
228
|
+
*/
|
|
229
|
+
function getLicenseInfo() {
|
|
230
|
+
try {
|
|
231
|
+
// Developer/owner bypass - full PRO access without license
|
|
232
|
+
if (isDeveloperMode()) {
|
|
233
|
+
return {
|
|
234
|
+
tier: LICENSE_TIERS.PRO,
|
|
235
|
+
valid: true,
|
|
236
|
+
email: 'developer@localhost',
|
|
237
|
+
isDeveloper: true,
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// Use pure license validator
|
|
242
|
+
const { LicenseValidator } = require('./license-validator')
|
|
243
|
+
const validator = new LicenseValidator()
|
|
244
|
+
|
|
245
|
+
const localLicense = validator.getLocalLicense()
|
|
246
|
+
|
|
247
|
+
if (!localLicense) {
|
|
248
|
+
return { tier: LICENSE_TIERS.FREE, valid: true }
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// Check if license is valid
|
|
252
|
+
if (!localLicense.valid) {
|
|
253
|
+
return {
|
|
254
|
+
tier: LICENSE_TIERS.FREE,
|
|
255
|
+
valid: true,
|
|
256
|
+
error:
|
|
257
|
+
'License signature verification failed - license may have been tampered with',
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// Check for required fields
|
|
262
|
+
const licenseKey = localLicense.licenseKey || localLicense.key
|
|
263
|
+
if (!licenseKey || !localLicense.email) {
|
|
264
|
+
return {
|
|
265
|
+
tier: LICENSE_TIERS.FREE,
|
|
266
|
+
valid: true,
|
|
267
|
+
error: 'Invalid license format',
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// Check expiration
|
|
272
|
+
if (localLicense.expires && new Date(localLicense.expires) < new Date()) {
|
|
273
|
+
return {
|
|
274
|
+
tier: LICENSE_TIERS.FREE,
|
|
275
|
+
valid: true,
|
|
276
|
+
error: 'License expired',
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// Validate license key format
|
|
281
|
+
if (!validateLicenseKey(licenseKey, localLicense.tier)) {
|
|
282
|
+
return {
|
|
283
|
+
tier: LICENSE_TIERS.FREE,
|
|
284
|
+
valid: true,
|
|
285
|
+
error: 'Invalid license key',
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// Return license info
|
|
290
|
+
return {
|
|
291
|
+
tier: localLicense.tier || LICENSE_TIERS.FREE,
|
|
292
|
+
valid: true,
|
|
293
|
+
email: localLicense.email,
|
|
294
|
+
expires: localLicense.expires,
|
|
295
|
+
isFounder: localLicense.isFounder || false,
|
|
296
|
+
customerId: localLicense.customerId,
|
|
297
|
+
}
|
|
298
|
+
} catch (error) {
|
|
299
|
+
return {
|
|
300
|
+
tier: LICENSE_TIERS.FREE,
|
|
301
|
+
valid: true,
|
|
302
|
+
error: `License read error: ${error.message}`,
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* License key validation with Stripe integration
|
|
309
|
+
* Supports both legacy format and new Stripe-generated keys
|
|
310
|
+
*/
|
|
311
|
+
function validateLicenseKey(key, tier) {
|
|
312
|
+
// New Stripe format: QAA-XXXX-XXXX-XXXX-XXXX
|
|
313
|
+
const stripeFormat = /^QAA-[A-F0-9]{4}-[A-F0-9]{4}-[A-F0-9]{4}-[A-F0-9]{4}$/
|
|
314
|
+
|
|
315
|
+
if (stripeFormat.test(key)) {
|
|
316
|
+
// Stripe-generated key - always valid if properly formatted
|
|
317
|
+
return true
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// Legacy format validation for backward compatibility
|
|
321
|
+
const expectedPrefix = `QAA-${tier.toUpperCase()}-`
|
|
322
|
+
return key.startsWith(expectedPrefix) && key.length > 20
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* Verify license signature using the same algorithm as StripeIntegration
|
|
327
|
+
*/
|
|
328
|
+
function verifyLicenseSignature(payload, signature) {
|
|
329
|
+
try {
|
|
330
|
+
const secret =
|
|
331
|
+
process.env.LICENSE_SIGNING_SECRET || 'cqa-dev-secret-change-in-prod'
|
|
332
|
+
const expectedSignature = crypto
|
|
333
|
+
.createHmac('sha256', secret)
|
|
334
|
+
.update(JSON.stringify(payload))
|
|
335
|
+
.digest('hex')
|
|
336
|
+
|
|
337
|
+
return crypto.timingSafeEqual(
|
|
338
|
+
Buffer.from(signature),
|
|
339
|
+
Buffer.from(expectedSignature)
|
|
340
|
+
)
|
|
341
|
+
} catch (_error) {
|
|
342
|
+
// If signature verification fails, treat as invalid
|
|
343
|
+
return false
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* Check if a specific feature is available for current license
|
|
349
|
+
*/
|
|
350
|
+
function hasFeature(featureName) {
|
|
351
|
+
const license = getLicenseInfo()
|
|
352
|
+
const tierFeatures = FEATURES[license.tier] || FEATURES[LICENSE_TIERS.FREE]
|
|
353
|
+
return tierFeatures[featureName] || false
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* Get the dependency monitoring level for current license
|
|
358
|
+
*/
|
|
359
|
+
function getDependencyMonitoringLevel() {
|
|
360
|
+
const license = getLicenseInfo()
|
|
361
|
+
const tierFeatures = FEATURES[license.tier] || FEATURES[LICENSE_TIERS.FREE]
|
|
362
|
+
return tierFeatures.dependencyMonitoring
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
/**
|
|
366
|
+
* Get supported languages for current license
|
|
367
|
+
*/
|
|
368
|
+
function getSupportedLanguages() {
|
|
369
|
+
const license = getLicenseInfo()
|
|
370
|
+
const tierFeatures = FEATURES[license.tier] || FEATURES[LICENSE_TIERS.FREE]
|
|
371
|
+
return tierFeatures.languages
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* Display upgrade message for premium features
|
|
376
|
+
*/
|
|
377
|
+
function showUpgradeMessage(feature) {
|
|
378
|
+
const license = getLicenseInfo()
|
|
379
|
+
const _tierFeatures = FEATURES[license.tier] || FEATURES[LICENSE_TIERS.FREE]
|
|
380
|
+
|
|
381
|
+
console.log(`\nš ${feature} is a premium feature`)
|
|
382
|
+
console.log(`š Current license: ${license.tier.toUpperCase()}`)
|
|
383
|
+
|
|
384
|
+
if (license.tier === LICENSE_TIERS.FREE) {
|
|
385
|
+
console.log('\nš Upgrade to PRO')
|
|
386
|
+
console.log('')
|
|
387
|
+
console.log(' š° $59/month or $590/year (save $118)')
|
|
388
|
+
console.log('')
|
|
389
|
+
console.log(' ā
Unlimited repos, LOC, and runs')
|
|
390
|
+
console.log(' ā
Smart Test Strategy (70% faster pre-push)')
|
|
391
|
+
console.log(' ā
Security scanning (Gitleaks + ESLint security)')
|
|
392
|
+
console.log(' ā
TypeScript production protection')
|
|
393
|
+
console.log(' ā
Multi-language (Python, Rust, Ruby)')
|
|
394
|
+
console.log(' ā
Framework-aware dependency grouping')
|
|
395
|
+
console.log(' ā
Email support (24-48h response)')
|
|
396
|
+
console.log('')
|
|
397
|
+
console.log(' š Start 14-day free trial - no credit card required')
|
|
398
|
+
console.log('')
|
|
399
|
+
console.log('š Upgrade: https://vibebuildlab.com/cqa')
|
|
400
|
+
console.log(
|
|
401
|
+
'š Activate: npx create-qa-architect@latest --activate-license'
|
|
402
|
+
)
|
|
403
|
+
} else if (license.tier === LICENSE_TIERS.PRO) {
|
|
404
|
+
console.log('\nš„ Upgrade to TEAM')
|
|
405
|
+
console.log('')
|
|
406
|
+
console.log(
|
|
407
|
+
' š° $15/user/month (5-seat min) or $150/user/year (save $30/user)'
|
|
408
|
+
)
|
|
409
|
+
console.log('')
|
|
410
|
+
console.log(' ā
All PRO features included')
|
|
411
|
+
console.log(' ā
Per-seat licensing for your org')
|
|
412
|
+
console.log(' ā
Shared quota & usage reporting')
|
|
413
|
+
console.log(' ā
Team-wide configuration policies')
|
|
414
|
+
console.log(' ā
Slack/email alerts for failures')
|
|
415
|
+
console.log(' ā
Priority support (business hours)')
|
|
416
|
+
console.log('')
|
|
417
|
+
console.log('š„ Upgrade: https://vibebuildlab.com/cqa/team')
|
|
418
|
+
} else if (license.tier === LICENSE_TIERS.TEAM) {
|
|
419
|
+
console.log('\nš¢ Upgrade to ENTERPRISE - $249/month (annual) + onboarding')
|
|
420
|
+
console.log('')
|
|
421
|
+
console.log(' ā
All TEAM features included')
|
|
422
|
+
console.log(' ā
SSO/SAML integration')
|
|
423
|
+
console.log(' ā
Custom risk patterns & dependency policies')
|
|
424
|
+
console.log(' ā
Audit logs export & compliance pack')
|
|
425
|
+
console.log(' ā
Dedicated TAM + 24Ć5 support with SLA')
|
|
426
|
+
console.log('')
|
|
427
|
+
console.log('š¢ Contact: enterprise@vibebuildlab.com')
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
/**
|
|
432
|
+
* Save license information (for testing or license activation)
|
|
433
|
+
*/
|
|
434
|
+
function saveLicense(tier, key, email, expires = null) {
|
|
435
|
+
try {
|
|
436
|
+
const licenseDir = getLicenseDir()
|
|
437
|
+
const licenseFile = getLicenseFile()
|
|
438
|
+
|
|
439
|
+
if (!fs.existsSync(licenseDir)) {
|
|
440
|
+
fs.mkdirSync(licenseDir, { recursive: true })
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
const licenseData = {
|
|
444
|
+
tier,
|
|
445
|
+
key,
|
|
446
|
+
email,
|
|
447
|
+
expires,
|
|
448
|
+
activated: new Date().toISOString(),
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
fs.writeFileSync(licenseFile, JSON.stringify(licenseData, null, 2))
|
|
452
|
+
return { success: true }
|
|
453
|
+
} catch (error) {
|
|
454
|
+
return { success: false, error: error.message }
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
/**
|
|
459
|
+
* Save license information with signature validation data
|
|
460
|
+
*/
|
|
461
|
+
function saveLicenseWithSignature(tier, key, email, validation) {
|
|
462
|
+
try {
|
|
463
|
+
const licenseDir = getLicenseDir()
|
|
464
|
+
const licenseFile = getLicenseFile()
|
|
465
|
+
|
|
466
|
+
if (!fs.existsSync(licenseDir)) {
|
|
467
|
+
fs.mkdirSync(licenseDir, { recursive: true })
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
const licenseData = {
|
|
471
|
+
tier,
|
|
472
|
+
licenseKey: key, // ā
Changed from 'key' to 'licenseKey' to match StripeIntegration
|
|
473
|
+
email,
|
|
474
|
+
expires: validation.expires,
|
|
475
|
+
activated: new Date().toISOString(),
|
|
476
|
+
customerId: validation.customerId,
|
|
477
|
+
isFounder: validation.isFounder,
|
|
478
|
+
// Include validation payload and signature for security
|
|
479
|
+
payload: validation.payload, // ā
Changed from 'validationPayload' to 'payload'
|
|
480
|
+
signature: validation.signature, // ā
Changed from 'validationSignature' to 'signature'
|
|
481
|
+
issued: validation.issued,
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
fs.writeFileSync(licenseFile, JSON.stringify(licenseData, null, 2))
|
|
485
|
+
return { success: true }
|
|
486
|
+
} catch (error) {
|
|
487
|
+
return { success: false, error: error.message }
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
/**
|
|
492
|
+
* Remove license (for testing)
|
|
493
|
+
*/
|
|
494
|
+
function removeLicense() {
|
|
495
|
+
try {
|
|
496
|
+
const licenseFile = getLicenseFile()
|
|
497
|
+
|
|
498
|
+
if (fs.existsSync(licenseFile)) {
|
|
499
|
+
fs.unlinkSync(licenseFile)
|
|
500
|
+
}
|
|
501
|
+
return { success: true }
|
|
502
|
+
} catch (error) {
|
|
503
|
+
return { success: false, error: error.message }
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
/**
|
|
508
|
+
* Activate license (USER-FACING - NO STRIPE DEPENDENCIES)
|
|
509
|
+
*/
|
|
510
|
+
async function activateLicense(licenseKey, email) {
|
|
511
|
+
try {
|
|
512
|
+
// Use pure license validator (no Stripe dependencies)
|
|
513
|
+
const { LicenseValidator } = require('./license-validator')
|
|
514
|
+
const validator = new LicenseValidator()
|
|
515
|
+
|
|
516
|
+
// Initialize license directory/database
|
|
517
|
+
validator.initialize()
|
|
518
|
+
|
|
519
|
+
// Activate license using local database validation only
|
|
520
|
+
return await validator.activateLicense(licenseKey, email)
|
|
521
|
+
} catch (error) {
|
|
522
|
+
return {
|
|
523
|
+
success: false,
|
|
524
|
+
error: `License activation failed: ${error.message}. Please contact support if the issue persists.`,
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
/**
|
|
530
|
+
* Add a legitimate license key (admin function - uses local database)
|
|
531
|
+
*/
|
|
532
|
+
async function addLegitimateKey(
|
|
533
|
+
licenseKey,
|
|
534
|
+
customerId,
|
|
535
|
+
tier,
|
|
536
|
+
isFounder = false,
|
|
537
|
+
purchaseEmail = null
|
|
538
|
+
) {
|
|
539
|
+
try {
|
|
540
|
+
// Use the same license directory as the CLI
|
|
541
|
+
const licenseDir =
|
|
542
|
+
process.env.QAA_LICENSE_DIR ||
|
|
543
|
+
path.join(os.homedir(), '.create-qa-architect')
|
|
544
|
+
const legitimateDBFile = path.join(licenseDir, 'legitimate-licenses.json')
|
|
545
|
+
|
|
546
|
+
// Ensure directory exists
|
|
547
|
+
if (!fs.existsSync(licenseDir)) {
|
|
548
|
+
fs.mkdirSync(licenseDir, { recursive: true })
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
// Load existing database
|
|
552
|
+
let database = {}
|
|
553
|
+
if (fs.existsSync(legitimateDBFile)) {
|
|
554
|
+
try {
|
|
555
|
+
database = JSON.parse(fs.readFileSync(legitimateDBFile, 'utf8'))
|
|
556
|
+
} catch (_error) {
|
|
557
|
+
console.error(
|
|
558
|
+
'Warning: Could not parse existing database, creating new one'
|
|
559
|
+
)
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
// Initialize metadata if needed
|
|
564
|
+
if (!database._metadata) {
|
|
565
|
+
database._metadata = {
|
|
566
|
+
version: '1.0',
|
|
567
|
+
created: new Date().toISOString(),
|
|
568
|
+
description: 'Legitimate license database - populated by admin/webhook',
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
// Add license
|
|
573
|
+
database[licenseKey] = {
|
|
574
|
+
customerId,
|
|
575
|
+
tier,
|
|
576
|
+
isFounder,
|
|
577
|
+
email: purchaseEmail,
|
|
578
|
+
addedDate: new Date().toISOString(),
|
|
579
|
+
addedBy: 'admin_tool',
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
// Update metadata
|
|
583
|
+
database._metadata.lastUpdate = new Date().toISOString()
|
|
584
|
+
database._metadata.totalLicenses = Object.keys(database).length - 1 // Exclude metadata
|
|
585
|
+
|
|
586
|
+
// Calculate SHA256 checksum for integrity verification (MANDATORY)
|
|
587
|
+
const { _metadata, ...licensesOnly } = database
|
|
588
|
+
const sha256 = crypto
|
|
589
|
+
.createHash('sha256')
|
|
590
|
+
.update(JSON.stringify(licensesOnly))
|
|
591
|
+
.digest('hex')
|
|
592
|
+
database._metadata.sha256 = sha256
|
|
593
|
+
|
|
594
|
+
// Save database
|
|
595
|
+
fs.writeFileSync(legitimateDBFile, JSON.stringify(database, null, 2))
|
|
596
|
+
|
|
597
|
+
console.log(`ā
Added legitimate license: ${licenseKey}`)
|
|
598
|
+
console.log(` Customer: ${customerId}`)
|
|
599
|
+
console.log(` Tier: ${tier}`)
|
|
600
|
+
console.log(` Founder: ${isFounder ? 'Yes' : 'No'}`)
|
|
601
|
+
if (purchaseEmail) {
|
|
602
|
+
console.log(` Purchase Email: ${purchaseEmail}`)
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
return { success: true }
|
|
606
|
+
} catch (error) {
|
|
607
|
+
return { success: false, error: error.message }
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
/**
|
|
612
|
+
* Interactive license activation prompt
|
|
613
|
+
*/
|
|
614
|
+
async function promptLicenseActivation() {
|
|
615
|
+
const readline = require('readline')
|
|
616
|
+
|
|
617
|
+
const rl = readline.createInterface({
|
|
618
|
+
input: process.stdin,
|
|
619
|
+
output: process.stdout,
|
|
620
|
+
})
|
|
621
|
+
|
|
622
|
+
return new Promise(resolve => {
|
|
623
|
+
console.log('\nš License Activation')
|
|
624
|
+
console.log(
|
|
625
|
+
'Enter your license key from the purchase confirmation email.\n'
|
|
626
|
+
)
|
|
627
|
+
|
|
628
|
+
rl.question('License key (QAA-XXXX-XXXX-XXXX-XXXX): ', licenseKey => {
|
|
629
|
+
if (!licenseKey.trim()) {
|
|
630
|
+
console.log('ā License key required')
|
|
631
|
+
rl.close()
|
|
632
|
+
resolve({ success: false })
|
|
633
|
+
return
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
rl.question('Email address: ', async email => {
|
|
637
|
+
if (!email.trim()) {
|
|
638
|
+
console.log('ā Email address required')
|
|
639
|
+
rl.close()
|
|
640
|
+
resolve({ success: false })
|
|
641
|
+
return
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
rl.close()
|
|
645
|
+
|
|
646
|
+
const result = await activateLicense(licenseKey.trim(), email.trim())
|
|
647
|
+
|
|
648
|
+
if (
|
|
649
|
+
!result.success &&
|
|
650
|
+
result.error &&
|
|
651
|
+
result.error.includes('not found')
|
|
652
|
+
) {
|
|
653
|
+
console.log('\nš License activation assistance:')
|
|
654
|
+
console.log(
|
|
655
|
+
' If you purchased this license, please contact support at:'
|
|
656
|
+
)
|
|
657
|
+
console.log(' Email: support@aibuilderlab.com')
|
|
658
|
+
console.log(
|
|
659
|
+
' Include your license key and purchase email for verification.'
|
|
660
|
+
)
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
resolve(result)
|
|
664
|
+
})
|
|
665
|
+
})
|
|
666
|
+
})
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
/**
|
|
670
|
+
* Enable developer mode by creating the marker file
|
|
671
|
+
*/
|
|
672
|
+
function enableDeveloperMode() {
|
|
673
|
+
try {
|
|
674
|
+
const licenseDir = getLicenseDir()
|
|
675
|
+
const developerMarkerFile = path.join(licenseDir, '.cqa-developer')
|
|
676
|
+
|
|
677
|
+
if (!fs.existsSync(licenseDir)) {
|
|
678
|
+
fs.mkdirSync(licenseDir, { recursive: true })
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
fs.writeFileSync(
|
|
682
|
+
developerMarkerFile,
|
|
683
|
+
`# QAA Developer Mode\n# Created: ${new Date().toISOString()}\n# This file grants full PRO access for development purposes.\n`
|
|
684
|
+
)
|
|
685
|
+
|
|
686
|
+
console.log('ā
Developer mode enabled')
|
|
687
|
+
console.log(` Marker file: ${developerMarkerFile}`)
|
|
688
|
+
return { success: true }
|
|
689
|
+
} catch (error) {
|
|
690
|
+
return { success: false, error: error.message }
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
/**
|
|
695
|
+
* Disable developer mode by removing the marker file
|
|
696
|
+
*/
|
|
697
|
+
function disableDeveloperMode() {
|
|
698
|
+
try {
|
|
699
|
+
const developerMarkerFile = path.join(getLicenseDir(), '.cqa-developer')
|
|
700
|
+
|
|
701
|
+
if (fs.existsSync(developerMarkerFile)) {
|
|
702
|
+
fs.unlinkSync(developerMarkerFile)
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
console.log('ā
Developer mode disabled')
|
|
706
|
+
return { success: true }
|
|
707
|
+
} catch (error) {
|
|
708
|
+
return { success: false, error: error.message }
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
// ============================================================================
|
|
713
|
+
// FREE TIER CAP ENFORCEMENT
|
|
714
|
+
// ============================================================================
|
|
715
|
+
|
|
716
|
+
/**
|
|
717
|
+
* Get the path to the usage tracking file
|
|
718
|
+
*/
|
|
719
|
+
function getUsageFile() {
|
|
720
|
+
return path.join(getLicenseDir(), 'usage.json')
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
/**
|
|
724
|
+
* Load current usage data
|
|
725
|
+
*/
|
|
726
|
+
function loadUsage() {
|
|
727
|
+
try {
|
|
728
|
+
const usageFile = getUsageFile()
|
|
729
|
+
if (fs.existsSync(usageFile)) {
|
|
730
|
+
const data = JSON.parse(fs.readFileSync(usageFile, 'utf8'))
|
|
731
|
+
|
|
732
|
+
// Check if we need to reset monthly counters
|
|
733
|
+
const now = new Date()
|
|
734
|
+
const currentMonth = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}`
|
|
735
|
+
|
|
736
|
+
if (data.month !== currentMonth) {
|
|
737
|
+
// New month - reset monthly counters
|
|
738
|
+
return {
|
|
739
|
+
month: currentMonth,
|
|
740
|
+
prePushRuns: 0,
|
|
741
|
+
dependencyPRs: 0,
|
|
742
|
+
repos: data.repos || [],
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
return data
|
|
747
|
+
}
|
|
748
|
+
} catch (_error) {
|
|
749
|
+
// Ignore errors reading usage file
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
// Default usage data
|
|
753
|
+
const now = new Date()
|
|
754
|
+
return {
|
|
755
|
+
month: `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}`,
|
|
756
|
+
prePushRuns: 0,
|
|
757
|
+
dependencyPRs: 0,
|
|
758
|
+
repos: [],
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
/**
|
|
763
|
+
* Save usage data
|
|
764
|
+
*/
|
|
765
|
+
function saveUsage(usage) {
|
|
766
|
+
try {
|
|
767
|
+
const licenseDir = getLicenseDir()
|
|
768
|
+
if (!fs.existsSync(licenseDir)) {
|
|
769
|
+
fs.mkdirSync(licenseDir, { recursive: true })
|
|
770
|
+
}
|
|
771
|
+
fs.writeFileSync(getUsageFile(), JSON.stringify(usage, null, 2))
|
|
772
|
+
return true
|
|
773
|
+
} catch (_error) {
|
|
774
|
+
return false
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
/**
|
|
779
|
+
* Check if usage is within FREE tier caps
|
|
780
|
+
* Returns { allowed: boolean, reason?: string, usage: object, caps: object }
|
|
781
|
+
*/
|
|
782
|
+
function checkUsageCaps(operation = 'general') {
|
|
783
|
+
const license = getLicenseInfo()
|
|
784
|
+
|
|
785
|
+
// Non-FREE tiers have no caps
|
|
786
|
+
if (license.tier !== LICENSE_TIERS.FREE) {
|
|
787
|
+
return { allowed: true, usage: {}, caps: {} }
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
const caps = FEATURES[LICENSE_TIERS.FREE]
|
|
791
|
+
const usage = loadUsage()
|
|
792
|
+
|
|
793
|
+
const result = {
|
|
794
|
+
allowed: true,
|
|
795
|
+
usage: {
|
|
796
|
+
prePushRuns: usage.prePushRuns,
|
|
797
|
+
dependencyPRs: usage.dependencyPRs,
|
|
798
|
+
repos: usage.repos.length,
|
|
799
|
+
},
|
|
800
|
+
caps: {
|
|
801
|
+
maxPrePushRunsPerMonth: caps.maxPrePushRunsPerMonth,
|
|
802
|
+
maxDependencyPRsPerMonth: caps.maxDependencyPRsPerMonth,
|
|
803
|
+
maxPrivateRepos: caps.maxPrivateRepos,
|
|
804
|
+
},
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
// Check specific cap based on operation
|
|
808
|
+
if (operation === 'pre-push') {
|
|
809
|
+
if (usage.prePushRuns >= caps.maxPrePushRunsPerMonth) {
|
|
810
|
+
result.allowed = false
|
|
811
|
+
result.reason = `FREE tier limit reached: ${usage.prePushRuns}/${caps.maxPrePushRunsPerMonth} pre-push runs this month`
|
|
812
|
+
}
|
|
813
|
+
} else if (operation === 'dependency-pr') {
|
|
814
|
+
if (usage.dependencyPRs >= caps.maxDependencyPRsPerMonth) {
|
|
815
|
+
result.allowed = false
|
|
816
|
+
result.reason = `FREE tier limit reached: ${usage.dependencyPRs}/${caps.maxDependencyPRsPerMonth} dependency PRs this month`
|
|
817
|
+
}
|
|
818
|
+
} else if (operation === 'repo') {
|
|
819
|
+
if (usage.repos.length >= caps.maxPrivateRepos) {
|
|
820
|
+
result.allowed = false
|
|
821
|
+
result.reason = `FREE tier limit reached: ${usage.repos.length}/${caps.maxPrivateRepos} private repos`
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
return result
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
/**
|
|
829
|
+
* Increment usage counter for an operation
|
|
830
|
+
*/
|
|
831
|
+
function incrementUsage(operation, amount = 1, repoId = null) {
|
|
832
|
+
const license = getLicenseInfo()
|
|
833
|
+
|
|
834
|
+
// Non-FREE tiers don't track usage
|
|
835
|
+
if (license.tier !== LICENSE_TIERS.FREE) {
|
|
836
|
+
return { success: true }
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
const usage = loadUsage()
|
|
840
|
+
|
|
841
|
+
if (operation === 'pre-push') {
|
|
842
|
+
usage.prePushRuns += amount
|
|
843
|
+
} else if (operation === 'dependency-pr') {
|
|
844
|
+
usage.dependencyPRs += amount
|
|
845
|
+
} else if (operation === 'repo' && repoId) {
|
|
846
|
+
if (!usage.repos.includes(repoId)) {
|
|
847
|
+
usage.repos.push(repoId)
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
saveUsage(usage)
|
|
852
|
+
return { success: true, usage }
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
/**
|
|
856
|
+
* Get usage summary for display
|
|
857
|
+
*/
|
|
858
|
+
function getUsageSummary() {
|
|
859
|
+
const license = getLicenseInfo()
|
|
860
|
+
const usage = loadUsage()
|
|
861
|
+
const caps = FEATURES[LICENSE_TIERS.FREE]
|
|
862
|
+
|
|
863
|
+
if (license.tier !== LICENSE_TIERS.FREE) {
|
|
864
|
+
return {
|
|
865
|
+
tier: license.tier,
|
|
866
|
+
unlimited: true,
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
return {
|
|
871
|
+
tier: license.tier,
|
|
872
|
+
unlimited: false,
|
|
873
|
+
month: usage.month,
|
|
874
|
+
prePushRuns: {
|
|
875
|
+
used: usage.prePushRuns,
|
|
876
|
+
limit: caps.maxPrePushRunsPerMonth,
|
|
877
|
+
remaining: Math.max(0, caps.maxPrePushRunsPerMonth - usage.prePushRuns),
|
|
878
|
+
},
|
|
879
|
+
dependencyPRs: {
|
|
880
|
+
used: usage.dependencyPRs,
|
|
881
|
+
limit: caps.maxDependencyPRsPerMonth,
|
|
882
|
+
remaining: Math.max(
|
|
883
|
+
0,
|
|
884
|
+
caps.maxDependencyPRsPerMonth - usage.dependencyPRs
|
|
885
|
+
),
|
|
886
|
+
},
|
|
887
|
+
repos: {
|
|
888
|
+
used: usage.repos.length,
|
|
889
|
+
limit: caps.maxPrivateRepos,
|
|
890
|
+
remaining: Math.max(0, caps.maxPrivateRepos - usage.repos.length),
|
|
891
|
+
},
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
/**
|
|
896
|
+
* Display current license status
|
|
897
|
+
*/
|
|
898
|
+
function showLicenseStatus() {
|
|
899
|
+
const license = getLicenseInfo()
|
|
900
|
+
|
|
901
|
+
console.log('\nš License Status:')
|
|
902
|
+
if (license.isDeveloper) {
|
|
903
|
+
console.log(' Mode: š ļø DEVELOPER (full PRO access)')
|
|
904
|
+
}
|
|
905
|
+
console.log(` Tier: ${license.tier.toUpperCase()}`)
|
|
906
|
+
|
|
907
|
+
if (license.email) {
|
|
908
|
+
console.log(` Email: ${license.email}`)
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
if (license.expires) {
|
|
912
|
+
console.log(` Expires: ${license.expires}`)
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
if (license.error) {
|
|
916
|
+
console.log(` ā ļø Issue: ${license.error}`)
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
console.log('\nšÆ Available Features:')
|
|
920
|
+
const features = FEATURES[license.tier] || FEATURES[LICENSE_TIERS.FREE]
|
|
921
|
+
|
|
922
|
+
// Show caps and current usage for FREE tier
|
|
923
|
+
if (license.tier === LICENSE_TIERS.FREE) {
|
|
924
|
+
const usage = getUsageSummary()
|
|
925
|
+
console.log('\nš Usage This Month:')
|
|
926
|
+
console.log(
|
|
927
|
+
` Pre-push Runs: ${usage.prePushRuns.used}/${usage.prePushRuns.limit} (${usage.prePushRuns.remaining} remaining)`
|
|
928
|
+
)
|
|
929
|
+
console.log(
|
|
930
|
+
` Dependency PRs: ${usage.dependencyPRs.used}/${usage.dependencyPRs.limit} (${usage.dependencyPRs.remaining} remaining)`
|
|
931
|
+
)
|
|
932
|
+
console.log(` Private Repos: ${usage.repos.used}/${usage.repos.limit}`)
|
|
933
|
+
} else {
|
|
934
|
+
console.log(` Repos/Runs: Unlimited`)
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
console.log(` Dependency Monitoring: ${features.dependencyMonitoring}`)
|
|
938
|
+
console.log(` Languages: ${features.languages.join(', ')}`)
|
|
939
|
+
console.log(
|
|
940
|
+
` Security Scanning: ${features.securityScanning ? 'ā
' : 'ā'}`
|
|
941
|
+
)
|
|
942
|
+
console.log(
|
|
943
|
+
` Smart Test Strategy: ${features.smartTestStrategy ? 'ā
' : 'ā'}`
|
|
944
|
+
)
|
|
945
|
+
console.log(
|
|
946
|
+
` Framework Grouping: ${features.frameworkGrouping ? 'ā
' : 'ā'}`
|
|
947
|
+
)
|
|
948
|
+
console.log(
|
|
949
|
+
` Advanced Workflows: ${features.advancedWorkflows ? 'ā
' : 'ā'}`
|
|
950
|
+
)
|
|
951
|
+
|
|
952
|
+
if (features.roadmap && features.roadmap.length) {
|
|
953
|
+
console.log('\nš¦ Your Plan Features:')
|
|
954
|
+
features.roadmap.forEach(item => console.log(` ${item}`))
|
|
955
|
+
}
|
|
956
|
+
|
|
957
|
+
// Show upgrade path
|
|
958
|
+
if (license.tier === LICENSE_TIERS.FREE) {
|
|
959
|
+
console.log('\nš” Upgrade to PRO for unlimited access + security scanning')
|
|
960
|
+
console.log(' ā https://vibebuildlab.com/cqa')
|
|
961
|
+
}
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
module.exports = {
|
|
965
|
+
LICENSE_TIERS,
|
|
966
|
+
FEATURES,
|
|
967
|
+
getLicenseInfo,
|
|
968
|
+
hasFeature,
|
|
969
|
+
getDependencyMonitoringLevel,
|
|
970
|
+
getSupportedLanguages,
|
|
971
|
+
showUpgradeMessage,
|
|
972
|
+
saveLicense,
|
|
973
|
+
saveLicenseWithSignature,
|
|
974
|
+
removeLicense,
|
|
975
|
+
showLicenseStatus,
|
|
976
|
+
activateLicense,
|
|
977
|
+
promptLicenseActivation,
|
|
978
|
+
verifyLicenseSignature,
|
|
979
|
+
LicenseValidator: require('./license-validator').LicenseValidator,
|
|
980
|
+
addLegitimateKey,
|
|
981
|
+
// Developer mode functions
|
|
982
|
+
isDeveloperMode,
|
|
983
|
+
enableDeveloperMode,
|
|
984
|
+
disableDeveloperMode,
|
|
985
|
+
// Usage tracking and cap enforcement (FREE tier)
|
|
986
|
+
checkUsageCaps,
|
|
987
|
+
incrementUsage,
|
|
988
|
+
getUsageSummary,
|
|
989
|
+
}
|