aiox-core 5.0.0 → 5.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/.aiox-core/data/entity-registry.yaml +5297 -1814
- package/.aiox-core/data/registry-update-log.jsonl +2 -0
- package/.aiox-core/development/templates/service-template/README.md.hbs +158 -158
- package/.aiox-core/development/templates/service-template/__tests__/index.test.ts.hbs +237 -237
- package/.aiox-core/development/templates/service-template/client.ts.hbs +403 -403
- package/.aiox-core/development/templates/service-template/errors.ts.hbs +182 -182
- package/.aiox-core/development/templates/service-template/index.ts.hbs +120 -120
- package/.aiox-core/development/templates/service-template/package.json.hbs +87 -87
- package/.aiox-core/development/templates/service-template/types.ts.hbs +145 -145
- package/.aiox-core/development/templates/squad-template/LICENSE +21 -21
- package/.aiox-core/infrastructure/scripts/tool-resolver.js +4 -4
- package/.aiox-core/infrastructure/templates/aiox-sync.yaml.template +182 -182
- package/.aiox-core/infrastructure/templates/coderabbit.yaml.template +279 -279
- package/.aiox-core/infrastructure/templates/github-workflows/ci.yml.template +169 -169
- package/.aiox-core/infrastructure/templates/github-workflows/pr-automation.yml.template +330 -330
- package/.aiox-core/infrastructure/templates/github-workflows/release.yml.template +196 -196
- package/.aiox-core/infrastructure/templates/gitignore/gitignore-aiox-base.tmpl +63 -63
- package/.aiox-core/infrastructure/templates/gitignore/gitignore-brownfield-merge.tmpl +18 -18
- package/.aiox-core/infrastructure/templates/gitignore/gitignore-node.tmpl +85 -85
- package/.aiox-core/infrastructure/templates/gitignore/gitignore-python.tmpl +145 -145
- package/.aiox-core/install-manifest.yaml +58 -58
- package/.aiox-core/local-config.yaml.template +71 -71
- package/.aiox-core/monitor/hooks/lib/__init__.py +1 -1
- package/.aiox-core/monitor/hooks/lib/enrich.py +58 -58
- package/.aiox-core/monitor/hooks/lib/send_event.py +47 -47
- package/.aiox-core/monitor/hooks/notification.py +29 -29
- package/.aiox-core/monitor/hooks/post_tool_use.py +45 -45
- package/.aiox-core/monitor/hooks/pre_compact.py +29 -29
- package/.aiox-core/monitor/hooks/pre_tool_use.py +40 -40
- package/.aiox-core/monitor/hooks/stop.py +29 -29
- package/.aiox-core/monitor/hooks/subagent_stop.py +29 -29
- package/.aiox-core/monitor/hooks/user_prompt_submit.py +38 -38
- package/.aiox-core/product/templates/adr.hbs +125 -125
- package/.aiox-core/product/templates/dbdr.hbs +241 -241
- package/.aiox-core/product/templates/engine/elicitation.js +2 -3
- package/.aiox-core/product/templates/epic.hbs +212 -212
- package/.aiox-core/product/templates/pmdr.hbs +186 -186
- package/.aiox-core/product/templates/prd-v2.0.hbs +216 -216
- package/.aiox-core/product/templates/prd.hbs +201 -201
- package/.aiox-core/product/templates/story.hbs +263 -263
- package/.aiox-core/product/templates/task.hbs +170 -170
- package/.aiox-core/product/templates/tmpl-comment-on-examples.sql +158 -158
- package/.aiox-core/product/templates/tmpl-migration-script.sql +91 -91
- package/.aiox-core/product/templates/tmpl-rls-granular-policies.sql +104 -104
- package/.aiox-core/product/templates/tmpl-rls-kiss-policy.sql +10 -10
- package/.aiox-core/product/templates/tmpl-rls-roles.sql +135 -135
- package/.aiox-core/product/templates/tmpl-rls-simple.sql +77 -77
- package/.aiox-core/product/templates/tmpl-rls-tenant.sql +152 -152
- package/.aiox-core/product/templates/tmpl-rollback-script.sql +77 -77
- package/.aiox-core/product/templates/tmpl-seed-data.sql +140 -140
- package/.aiox-core/product/templates/tmpl-smoke-test.sql +16 -16
- package/.aiox-core/product/templates/tmpl-staging-copy-merge.sql +139 -139
- package/.aiox-core/product/templates/tmpl-stored-proc.sql +140 -140
- package/.aiox-core/product/templates/tmpl-trigger.sql +152 -152
- package/.aiox-core/product/templates/tmpl-view-materialized.sql +133 -133
- package/.aiox-core/product/templates/tmpl-view.sql +177 -177
- package/.aiox-core/scripts/pm.sh +0 -0
- package/.claude/hooks/code-intel-pretool.cjs +107 -0
- package/.claude/hooks/enforce-architecture-first.py +196 -196
- package/.claude/hooks/mind-clone-governance.py +192 -192
- package/.claude/hooks/read-protection.py +151 -151
- package/.claude/hooks/slug-validation.py +176 -176
- package/.claude/hooks/sql-governance.py +182 -182
- package/.claude/hooks/write-path-validation.py +194 -194
- package/LICENSE +33 -33
- package/bin/aiox-graph.js +0 -0
- package/bin/aiox-minimal.js +0 -0
- package/bin/aiox.js +0 -0
- package/docs/guides/aios-workflows/README.md +247 -0
- package/docs/guides/aios-workflows/bob-orchestrator-workflow.md +1536 -0
- package/package.json +1 -1
- package/packages/aiox-install/bin/aiox-install.js +0 -0
- package/packages/aiox-install/bin/edmcp.js +0 -0
- package/packages/aiox-pro-cli/bin/aiox-pro.js +0 -0
- package/packages/installer/src/wizard/pro-setup.js +210 -123
- package/pro/README.md +66 -0
- package/pro/license/degradation.js +220 -0
- package/pro/license/errors.js +450 -0
- package/pro/license/feature-gate.js +354 -0
- package/pro/license/index.js +181 -0
- package/pro/license/license-api.js +679 -0
- package/pro/license/license-cache.js +523 -0
- package/pro/license/license-crypto.js +303 -0
- package/scripts/check-markdown-links.py +352 -352
- package/scripts/dashboard-parallel-dev.sh +0 -0
- package/scripts/dashboard-parallel-phase3.sh +0 -0
- package/scripts/dashboard-parallel-phase4.sh +0 -0
- package/scripts/glue/README.md +355 -0
- package/scripts/glue/compose-agent-prompt.cjs +362 -0
- package/scripts/install-monitor-hooks.sh +0 -0
- package/.aiox-core/lib/build.json +0 -1
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Feature Gate Module
|
|
3
|
+
*
|
|
4
|
+
* Singleton class that validates feature availability based on license cache.
|
|
5
|
+
* Supports exact feature ID matching and wildcard patterns.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* const { featureGate } = require('./feature-gate');
|
|
9
|
+
*
|
|
10
|
+
* // Check availability
|
|
11
|
+
* if (featureGate.isAvailable('pro.squads.premium')) {
|
|
12
|
+
* // Feature is licensed
|
|
13
|
+
* }
|
|
14
|
+
*
|
|
15
|
+
* // Require feature (throws if not available)
|
|
16
|
+
* featureGate.require('pro.squads.premium', 'Premium Squads');
|
|
17
|
+
*
|
|
18
|
+
* @module pro/license/feature-gate
|
|
19
|
+
* @see ADR-PRO-003 - Feature Gating & Licensing
|
|
20
|
+
* @see Story PRO-6 - License Key & Feature Gating System
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
'use strict';
|
|
24
|
+
|
|
25
|
+
const fs = require('fs');
|
|
26
|
+
const path = require('path');
|
|
27
|
+
const yaml = require('yaml');
|
|
28
|
+
const { readLicenseCache, isExpired, isInGracePeriod, getLicenseState } = require('./license-cache');
|
|
29
|
+
const { ProFeatureError } = require('./errors');
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Path to the feature registry YAML file.
|
|
33
|
+
*/
|
|
34
|
+
const REGISTRY_PATH = path.join(__dirname, '..', 'feature-registry.yaml');
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* FeatureGate class - validates feature availability.
|
|
38
|
+
*
|
|
39
|
+
* Singleton pattern ensures consistent state across the application.
|
|
40
|
+
*/
|
|
41
|
+
class FeatureGate {
|
|
42
|
+
constructor() {
|
|
43
|
+
this._cache = null;
|
|
44
|
+
this._licensedFeatures = new Set();
|
|
45
|
+
this._registeredFeatures = new Map();
|
|
46
|
+
this._lastLoadTime = null;
|
|
47
|
+
this._cacheLoaded = false;
|
|
48
|
+
this._registryLoaded = false;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Load the feature registry from YAML.
|
|
53
|
+
*
|
|
54
|
+
* @private
|
|
55
|
+
*/
|
|
56
|
+
_loadRegistry() {
|
|
57
|
+
if (this._registryLoaded) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
try {
|
|
62
|
+
if (fs.existsSync(REGISTRY_PATH)) {
|
|
63
|
+
const content = fs.readFileSync(REGISTRY_PATH, 'utf8');
|
|
64
|
+
const registry = yaml.parse(content);
|
|
65
|
+
|
|
66
|
+
// Extract all features from modules
|
|
67
|
+
const modules = ['squads', 'memory', 'metrics', 'integrations', 'agents', 'cli', 'config'];
|
|
68
|
+
|
|
69
|
+
for (const module of modules) {
|
|
70
|
+
if (registry[module] && Array.isArray(registry[module])) {
|
|
71
|
+
for (const feature of registry[module]) {
|
|
72
|
+
this._registeredFeatures.set(feature.id, {
|
|
73
|
+
id: feature.id,
|
|
74
|
+
name: feature.name,
|
|
75
|
+
description: feature.description,
|
|
76
|
+
module: feature.module,
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
this._registryLoaded = true;
|
|
83
|
+
}
|
|
84
|
+
} catch {
|
|
85
|
+
// Registry load failed, features will still work via license patterns
|
|
86
|
+
this._registryLoaded = true;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Load the license cache and populate licensed features.
|
|
92
|
+
*
|
|
93
|
+
* @private
|
|
94
|
+
* @param {boolean} [force=false] - Force reload even if already loaded
|
|
95
|
+
*/
|
|
96
|
+
_loadCache(force = false) {
|
|
97
|
+
if (this._cacheLoaded && !force) {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Load registry if not loaded
|
|
102
|
+
this._loadRegistry();
|
|
103
|
+
|
|
104
|
+
// Read license cache
|
|
105
|
+
this._cache = readLicenseCache();
|
|
106
|
+
this._licensedFeatures.clear();
|
|
107
|
+
this._lastLoadTime = Date.now();
|
|
108
|
+
|
|
109
|
+
if (!this._cache) {
|
|
110
|
+
this._cacheLoaded = true;
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Check if cache is valid (not expired, or in grace period)
|
|
115
|
+
const state = getLicenseState(this._cache);
|
|
116
|
+
|
|
117
|
+
if (state === 'Expired') {
|
|
118
|
+
// Expired and past grace period - no features available
|
|
119
|
+
// Keep cache reference so getLicenseState can report 'Expired'
|
|
120
|
+
this._cacheLoaded = true;
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Populate licensed features from cache
|
|
125
|
+
if (this._cache.features && Array.isArray(this._cache.features)) {
|
|
126
|
+
for (const featurePattern of this._cache.features) {
|
|
127
|
+
this._licensedFeatures.add(featurePattern);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
this._cacheLoaded = true;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Check if a feature pattern matches a feature ID using wildcard matching.
|
|
136
|
+
*
|
|
137
|
+
* Supports:
|
|
138
|
+
* - Exact match: "pro.squads.premium" matches "pro.squads.premium"
|
|
139
|
+
* - Wildcard: "pro.squads.*" matches "pro.squads.premium", "pro.squads.custom"
|
|
140
|
+
* - Module wildcard: "pro.*" matches any pro feature
|
|
141
|
+
*
|
|
142
|
+
* @private
|
|
143
|
+
* @param {string} featureId - Feature ID to check
|
|
144
|
+
* @param {string} pattern - Pattern from license
|
|
145
|
+
* @returns {boolean} true if pattern matches feature ID
|
|
146
|
+
*/
|
|
147
|
+
_matchWildcard(featureId, pattern) {
|
|
148
|
+
// Exact match
|
|
149
|
+
if (pattern === featureId) {
|
|
150
|
+
return true;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Wildcard match: "pro.*" matches "pro.squads.premium"
|
|
154
|
+
if (pattern.endsWith('.*')) {
|
|
155
|
+
const prefix = pattern.slice(0, -1); // Remove trailing * → "pro."
|
|
156
|
+
return featureId.startsWith(prefix);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// BUG-7 fix (INS-1): Bare module pattern without dots (e.g., "pro")
|
|
160
|
+
// should match all features under that module (equivalent to "pro.*")
|
|
161
|
+
if (!pattern.includes('.') && featureId.startsWith(pattern + '.')) {
|
|
162
|
+
return true;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return false;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Check if a feature is available (licensed).
|
|
170
|
+
*
|
|
171
|
+
* Performance: < 5ms (cache read only, per AC-13)
|
|
172
|
+
*
|
|
173
|
+
* @param {string} featureId - Feature ID to check (e.g., "pro.squads.premium")
|
|
174
|
+
* @returns {boolean} true if feature is licensed and available
|
|
175
|
+
*/
|
|
176
|
+
isAvailable(featureId) {
|
|
177
|
+
this._loadCache();
|
|
178
|
+
|
|
179
|
+
if (this._licensedFeatures.size === 0) {
|
|
180
|
+
return false;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Check direct match first (O(1))
|
|
184
|
+
if (this._licensedFeatures.has(featureId)) {
|
|
185
|
+
return true;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Check wildcard patterns
|
|
189
|
+
for (const pattern of this._licensedFeatures) {
|
|
190
|
+
if (this._matchWildcard(featureId, pattern)) {
|
|
191
|
+
return true;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
return false;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Require a feature to be available, throw if not.
|
|
200
|
+
*
|
|
201
|
+
* Use this to gate premium functionality:
|
|
202
|
+
*
|
|
203
|
+
* featureGate.require('pro.squads.premium', 'Premium Squads');
|
|
204
|
+
*
|
|
205
|
+
* @param {string} featureId - Feature ID to require
|
|
206
|
+
* @param {string} [friendlyName] - Human-friendly feature name for error message
|
|
207
|
+
* @throws {ProFeatureError} If feature is not licensed
|
|
208
|
+
*/
|
|
209
|
+
require(featureId, friendlyName) {
|
|
210
|
+
if (!this.isAvailable(featureId)) {
|
|
211
|
+
const name = friendlyName || this._getFeatureName(featureId) || featureId;
|
|
212
|
+
throw new ProFeatureError(featureId, name);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Get the friendly name of a feature from registry.
|
|
218
|
+
*
|
|
219
|
+
* @private
|
|
220
|
+
* @param {string} featureId - Feature ID
|
|
221
|
+
* @returns {string|null} Feature name or null
|
|
222
|
+
*/
|
|
223
|
+
_getFeatureName(featureId) {
|
|
224
|
+
this._loadRegistry();
|
|
225
|
+
const feature = this._registeredFeatures.get(featureId);
|
|
226
|
+
return feature ? feature.name : null;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* List all available (licensed) feature IDs.
|
|
231
|
+
*
|
|
232
|
+
* Returns the resolved list of feature IDs, expanding wildcards
|
|
233
|
+
* against the registry.
|
|
234
|
+
*
|
|
235
|
+
* @returns {string[]} Array of available feature IDs
|
|
236
|
+
*/
|
|
237
|
+
listAvailable() {
|
|
238
|
+
this._loadCache();
|
|
239
|
+
this._loadRegistry();
|
|
240
|
+
|
|
241
|
+
const available = new Set();
|
|
242
|
+
|
|
243
|
+
// Check each registered feature against licensed patterns
|
|
244
|
+
for (const [featureId] of this._registeredFeatures) {
|
|
245
|
+
if (this.isAvailable(featureId)) {
|
|
246
|
+
available.add(featureId);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
return Array.from(available).sort();
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* List all registered features with their availability status.
|
|
255
|
+
*
|
|
256
|
+
* @returns {Array<{ id: string, name: string, description: string, module: string, available: boolean }>}
|
|
257
|
+
*/
|
|
258
|
+
listAll() {
|
|
259
|
+
this._loadCache();
|
|
260
|
+
this._loadRegistry();
|
|
261
|
+
|
|
262
|
+
const features = [];
|
|
263
|
+
|
|
264
|
+
for (const [featureId, feature] of this._registeredFeatures) {
|
|
265
|
+
features.push({
|
|
266
|
+
...feature,
|
|
267
|
+
available: this.isAvailable(featureId),
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
return features.sort((a, b) => a.id.localeCompare(b.id));
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* List features grouped by module.
|
|
276
|
+
*
|
|
277
|
+
* @returns {Object<string, Array<{ id: string, name: string, available: boolean }>>}
|
|
278
|
+
*/
|
|
279
|
+
listByModule() {
|
|
280
|
+
const all = this.listAll();
|
|
281
|
+
const grouped = {};
|
|
282
|
+
|
|
283
|
+
for (const feature of all) {
|
|
284
|
+
if (!grouped[feature.module]) {
|
|
285
|
+
grouped[feature.module] = [];
|
|
286
|
+
}
|
|
287
|
+
grouped[feature.module].push(feature);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
return grouped;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* Force reload of the license cache.
|
|
295
|
+
*
|
|
296
|
+
* Call this after activation to pick up new features.
|
|
297
|
+
*/
|
|
298
|
+
reload() {
|
|
299
|
+
this._cacheLoaded = false;
|
|
300
|
+
this._loadCache(true);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* Get the current license state.
|
|
305
|
+
*
|
|
306
|
+
* @returns {'Active'|'Expired'|'Grace'|'Not Activated'} License state
|
|
307
|
+
*/
|
|
308
|
+
getLicenseState() {
|
|
309
|
+
this._loadCache();
|
|
310
|
+
return getLicenseState(this._cache);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* Get license information for display.
|
|
315
|
+
*
|
|
316
|
+
* @returns {{ state: string, features: string[], inGrace: boolean } | null}
|
|
317
|
+
*/
|
|
318
|
+
getLicenseInfo() {
|
|
319
|
+
this._loadCache();
|
|
320
|
+
|
|
321
|
+
if (!this._cache) {
|
|
322
|
+
return null;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
return {
|
|
326
|
+
state: getLicenseState(this._cache),
|
|
327
|
+
features: Array.from(this._licensedFeatures),
|
|
328
|
+
inGrace: isInGracePeriod(this._cache),
|
|
329
|
+
isExpired: isExpired(this._cache),
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* Clear internal state (for testing).
|
|
335
|
+
*
|
|
336
|
+
* @private
|
|
337
|
+
*/
|
|
338
|
+
_reset() {
|
|
339
|
+
this._cache = null;
|
|
340
|
+
this._licensedFeatures.clear();
|
|
341
|
+
this._registeredFeatures.clear();
|
|
342
|
+
this._lastLoadTime = null;
|
|
343
|
+
this._cacheLoaded = false;
|
|
344
|
+
this._registryLoaded = false;
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// Singleton instance
|
|
349
|
+
const featureGate = new FeatureGate();
|
|
350
|
+
|
|
351
|
+
module.exports = {
|
|
352
|
+
FeatureGate,
|
|
353
|
+
featureGate,
|
|
354
|
+
};
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* License Module - Public API
|
|
3
|
+
*
|
|
4
|
+
* This module provides the public interface for license management
|
|
5
|
+
* and feature gating in AIOS Pro.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* const { featureGate, licenseApi } = require('@aios-fullstack/pro/license');
|
|
9
|
+
*
|
|
10
|
+
* // Check feature availability
|
|
11
|
+
* if (featureGate.isAvailable('pro.squads.premium')) {
|
|
12
|
+
* // Feature is licensed
|
|
13
|
+
* }
|
|
14
|
+
*
|
|
15
|
+
* // Require feature (throws if not available)
|
|
16
|
+
* featureGate.require('pro.squads.premium', 'Premium Squads');
|
|
17
|
+
*
|
|
18
|
+
* // Activate license
|
|
19
|
+
* await licenseApi.activate(key, machineId, version);
|
|
20
|
+
*
|
|
21
|
+
* @module pro/license
|
|
22
|
+
* @see ADR-PRO-003 - Feature Gating & Licensing
|
|
23
|
+
* @see Story PRO-6 - License Key & Feature Gating System
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
'use strict';
|
|
27
|
+
|
|
28
|
+
// Feature gating
|
|
29
|
+
const { FeatureGate, featureGate } = require('./feature-gate');
|
|
30
|
+
|
|
31
|
+
// License cache operations
|
|
32
|
+
const {
|
|
33
|
+
writeLicenseCache,
|
|
34
|
+
readLicenseCache,
|
|
35
|
+
deleteLicenseCache,
|
|
36
|
+
isExpired,
|
|
37
|
+
isInGracePeriod,
|
|
38
|
+
getDaysRemaining,
|
|
39
|
+
getExpiryDate,
|
|
40
|
+
getLicenseState,
|
|
41
|
+
setPendingDeactivation,
|
|
42
|
+
hasPendingDeactivation,
|
|
43
|
+
markPendingDeactivationSynced,
|
|
44
|
+
clearPendingDeactivation,
|
|
45
|
+
} = require('./license-cache');
|
|
46
|
+
|
|
47
|
+
// License API client
|
|
48
|
+
const { LicenseApiClient, licenseApi } = require('./license-api');
|
|
49
|
+
|
|
50
|
+
// Crypto utilities
|
|
51
|
+
const {
|
|
52
|
+
generateMachineId,
|
|
53
|
+
maskKey,
|
|
54
|
+
validateKeyFormat,
|
|
55
|
+
} = require('./license-crypto');
|
|
56
|
+
|
|
57
|
+
// Error classes
|
|
58
|
+
const {
|
|
59
|
+
ProFeatureError,
|
|
60
|
+
LicenseActivationError,
|
|
61
|
+
LicenseValidationError,
|
|
62
|
+
} = require('./errors');
|
|
63
|
+
|
|
64
|
+
// Graceful degradation utilities
|
|
65
|
+
const {
|
|
66
|
+
withGracefulDegradation,
|
|
67
|
+
ifProAvailable,
|
|
68
|
+
getFeatureFriendlyName,
|
|
69
|
+
logDegradationMessage,
|
|
70
|
+
createDegradationWrapper,
|
|
71
|
+
isInDegradedMode,
|
|
72
|
+
getDegradationStatus,
|
|
73
|
+
} = require('./degradation');
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Convenience function to check if a feature is available.
|
|
77
|
+
*
|
|
78
|
+
* @param {string} featureId - Feature ID to check
|
|
79
|
+
* @returns {boolean} true if feature is licensed
|
|
80
|
+
*/
|
|
81
|
+
function isFeatureAvailable(featureId) {
|
|
82
|
+
return featureGate.isAvailable(featureId);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Convenience function to require a feature.
|
|
87
|
+
*
|
|
88
|
+
* @param {string} featureId - Feature ID to require
|
|
89
|
+
* @param {string} [friendlyName] - Human-friendly name for error message
|
|
90
|
+
* @throws {ProFeatureError} If feature is not licensed
|
|
91
|
+
*/
|
|
92
|
+
function requireFeature(featureId, friendlyName) {
|
|
93
|
+
featureGate.require(featureId, friendlyName);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Convenience function to get the current license state.
|
|
98
|
+
*
|
|
99
|
+
* @returns {'Active'|'Expired'|'Grace'|'Not Activated'} License state
|
|
100
|
+
*/
|
|
101
|
+
function getCurrentLicenseState() {
|
|
102
|
+
return featureGate.getLicenseState();
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Convenience function to get license info.
|
|
107
|
+
*
|
|
108
|
+
* @returns {{ state: string, features: string[], inGrace: boolean } | null}
|
|
109
|
+
*/
|
|
110
|
+
function getLicenseInfo() {
|
|
111
|
+
return featureGate.getLicenseInfo();
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Convenience function to list available features.
|
|
116
|
+
*
|
|
117
|
+
* @returns {string[]} Array of available feature IDs
|
|
118
|
+
*/
|
|
119
|
+
function listAvailableFeatures() {
|
|
120
|
+
return featureGate.listAvailable();
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Reload license cache (after activation/deactivation).
|
|
125
|
+
*/
|
|
126
|
+
function reloadLicense() {
|
|
127
|
+
featureGate.reload();
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
module.exports = {
|
|
131
|
+
// Primary exports - Feature Gate
|
|
132
|
+
FeatureGate,
|
|
133
|
+
featureGate,
|
|
134
|
+
|
|
135
|
+
// Primary exports - API Client
|
|
136
|
+
LicenseApiClient,
|
|
137
|
+
licenseApi,
|
|
138
|
+
|
|
139
|
+
// Error classes
|
|
140
|
+
ProFeatureError,
|
|
141
|
+
LicenseActivationError,
|
|
142
|
+
LicenseValidationError,
|
|
143
|
+
|
|
144
|
+
// Cache operations
|
|
145
|
+
writeLicenseCache,
|
|
146
|
+
readLicenseCache,
|
|
147
|
+
deleteLicenseCache,
|
|
148
|
+
isExpired,
|
|
149
|
+
isInGracePeriod,
|
|
150
|
+
getDaysRemaining,
|
|
151
|
+
getExpiryDate,
|
|
152
|
+
getLicenseState,
|
|
153
|
+
|
|
154
|
+
// Pending deactivation
|
|
155
|
+
setPendingDeactivation,
|
|
156
|
+
hasPendingDeactivation,
|
|
157
|
+
markPendingDeactivationSynced,
|
|
158
|
+
clearPendingDeactivation,
|
|
159
|
+
|
|
160
|
+
// Crypto utilities
|
|
161
|
+
generateMachineId,
|
|
162
|
+
maskKey,
|
|
163
|
+
validateKeyFormat,
|
|
164
|
+
|
|
165
|
+
// Convenience functions
|
|
166
|
+
isFeatureAvailable,
|
|
167
|
+
requireFeature,
|
|
168
|
+
getCurrentLicenseState,
|
|
169
|
+
getLicenseInfo,
|
|
170
|
+
listAvailableFeatures,
|
|
171
|
+
reloadLicense,
|
|
172
|
+
|
|
173
|
+
// Graceful degradation (AC-8)
|
|
174
|
+
withGracefulDegradation,
|
|
175
|
+
ifProAvailable,
|
|
176
|
+
getFeatureFriendlyName,
|
|
177
|
+
logDegradationMessage,
|
|
178
|
+
createDegradationWrapper,
|
|
179
|
+
isInDegradedMode,
|
|
180
|
+
getDegradationStatus,
|
|
181
|
+
};
|