aios-core 4.2.13 → 4.2.15
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/.aios-core/core/code-intel/helpers/dev-helper.js +206 -0
- package/.aios-core/core/registry/registry-schema.json +166 -166
- package/.aios-core/core/synapse/diagnostics/collectors/hook-collector.js +3 -3
- package/.aios-core/data/entity-registry.yaml +27 -0
- package/.aios-core/development/scripts/approval-workflow.js +642 -642
- package/.aios-core/development/scripts/backup-manager.js +606 -606
- package/.aios-core/development/scripts/branch-manager.js +389 -389
- package/.aios-core/development/scripts/code-quality-improver.js +1311 -1311
- package/.aios-core/development/scripts/commit-message-generator.js +849 -849
- package/.aios-core/development/scripts/conflict-resolver.js +674 -674
- package/.aios-core/development/scripts/dependency-analyzer.js +637 -637
- package/.aios-core/development/scripts/diff-generator.js +351 -351
- package/.aios-core/development/scripts/elicitation-engine.js +384 -384
- package/.aios-core/development/scripts/elicitation-session-manager.js +299 -299
- package/.aios-core/development/scripts/git-wrapper.js +461 -461
- package/.aios-core/development/scripts/manifest-preview.js +244 -244
- package/.aios-core/development/scripts/metrics-tracker.js +775 -775
- package/.aios-core/development/scripts/modification-validator.js +554 -554
- package/.aios-core/development/scripts/pattern-learner.js +1224 -1224
- package/.aios-core/development/scripts/performance-analyzer.js +757 -757
- package/.aios-core/development/scripts/refactoring-suggester.js +1138 -1138
- package/.aios-core/development/scripts/rollback-handler.js +530 -530
- package/.aios-core/development/scripts/security-checker.js +358 -358
- package/.aios-core/development/scripts/template-engine.js +239 -239
- package/.aios-core/development/scripts/template-validator.js +278 -278
- package/.aios-core/development/scripts/test-generator.js +843 -843
- package/.aios-core/development/scripts/transaction-manager.js +589 -589
- package/.aios-core/development/scripts/usage-tracker.js +673 -673
- package/.aios-core/development/scripts/validate-filenames.js +226 -226
- package/.aios-core/development/scripts/version-tracker.js +526 -526
- package/.aios-core/development/scripts/yaml-validator.js +396 -396
- package/.aios-core/development/tasks/build-autonomous.md +10 -4
- package/.aios-core/development/tasks/create-service.md +23 -0
- package/.aios-core/development/tasks/dev-develop-story.md +12 -6
- package/.aios-core/development/tasks/dev-suggest-refactoring.md +7 -1
- package/.aios-core/development/tasks/publish-npm.md +3 -3
- package/.aios-core/hooks/unified/README.md +1 -1
- package/.aios-core/install-manifest.yaml +65 -61
- package/.aios-core/manifests/schema/manifest-schema.json +190 -190
- package/.aios-core/product/templates/component-react-tmpl.tsx +98 -98
- package/.aios-core/product/templates/engine/schemas/adr.schema.json +102 -102
- package/.aios-core/product/templates/engine/schemas/dbdr.schema.json +205 -205
- package/.aios-core/product/templates/engine/schemas/epic.schema.json +175 -175
- package/.aios-core/product/templates/engine/schemas/pmdr.schema.json +175 -175
- package/.aios-core/product/templates/engine/schemas/prd-v2.schema.json +300 -300
- package/.aios-core/product/templates/engine/schemas/prd.schema.json +152 -152
- package/.aios-core/product/templates/engine/schemas/story.schema.json +222 -222
- package/.aios-core/product/templates/engine/schemas/task.schema.json +154 -154
- package/.aios-core/product/templates/eslintrc-security.json +32 -32
- package/.aios-core/product/templates/github-actions-cd.yml +212 -212
- package/.aios-core/product/templates/github-actions-ci.yml +172 -172
- package/.aios-core/product/templates/shock-report-tmpl.html +502 -502
- package/.aios-core/product/templates/token-exports-css-tmpl.css +240 -240
- package/.aios-core/quality/schemas/quality-metrics.schema.json +233 -233
- package/.aios-core/scripts/migrate-framework-docs.sh +300 -300
- package/README.en.md +747 -0
- package/README.md +4 -2
- package/bin/aios.js +7 -4
- package/package.json +1 -1
- package/packages/aios-pro-cli/src/recover.js +1 -1
- package/packages/installer/src/wizard/ide-config-generator.js +6 -6
- package/packages/installer/src/wizard/pro-setup.js +3 -3
- package/pro/license/degradation.js +220 -220
- package/pro/license/errors.js +450 -450
- package/pro/license/feature-gate.js +354 -354
- package/pro/license/index.js +181 -181
- package/pro/license/license-cache.js +523 -523
- package/pro/license/license-crypto.js +303 -303
- package/scripts/package-synapse.js +5 -5
- package/scripts/validate-package-completeness.js +3 -3
- package/.aios-core/.session/current-session.json +0 -14
- package/.aios-core/data/registry-update-log.jsonl +0 -191
- package/.aios-core/docs/SHARD-TRANSLATION-GUIDE.md +0 -335
- package/.aios-core/docs/component-creation-guide.md +0 -458
- package/.aios-core/docs/session-update-pattern.md +0 -307
- package/.aios-core/docs/standards/AIOS-FRAMEWORK-MASTER.md +0 -1963
- package/.aios-core/docs/standards/AIOS-LIVRO-DE-OURO-V2.1-SUMMARY.md +0 -1190
- package/.aios-core/docs/standards/AIOS-LIVRO-DE-OURO-V2.1.md +0 -439
- package/.aios-core/docs/standards/AIOS-LIVRO-DE-OURO.md +0 -5398
- package/.aios-core/docs/standards/V3-ARCHITECTURAL-DECISIONS.md +0 -523
- package/.aios-core/docs/template-syntax.md +0 -267
- package/.aios-core/docs/troubleshooting-guide.md +0 -625
- package/.aios-core/infrastructure/tests/utilities-audit-results.json +0 -501
- package/.aios-core/manifests/agents.csv +0 -29
- package/.aios-core/manifests/tasks.csv +0 -198
- package/.aios-core/manifests/workers.csv +0 -204
- package/.claude/rules/agent-authority.md +0 -105
- package/.claude/rules/coderabbit-integration.md +0 -93
- package/.claude/rules/ids-principles.md +0 -112
- package/.claude/rules/story-lifecycle.md +0 -139
- package/.claude/rules/workflow-execution.md +0 -150
- package/scripts/glue/README.md +0 -355
- package/scripts/glue/compose-agent-prompt.cjs +0 -362
- /package/.claude/hooks/{precompact-session-digest.js → precompact-session-digest.cjs} +0 -0
- /package/.claude/hooks/{synapse-engine.js → synapse-engine.cjs} +0 -0
package/README.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# Synkra AIOS: Framework Universal de Agentes IA 🚀
|
|
2
2
|
|
|
3
|
+
> 🌍 [English](README.en.md) | **[Português](README.md)**
|
|
4
|
+
|
|
3
5
|
[](https://www.npmjs.com/package/aios-core)
|
|
4
6
|
[](LICENSE)
|
|
5
7
|
[](https://nodejs.org/)
|
|
@@ -479,7 +481,7 @@ O Synkra AIOS vem com 11 agentes especializados:
|
|
|
479
481
|
### Guias Essenciais
|
|
480
482
|
|
|
481
483
|
- 📖 **[Guia do Usuário](docs/guides/user-guide.md)** - Passo a passo completo desde a concepção até a conclusão do projeto
|
|
482
|
-
- 🏗️ **[Arquitetura Principal](docs/architecture/
|
|
484
|
+
- 🏗️ **[Arquitetura Principal](docs/architecture/AIOS-VISUAL-OVERVIEW.md)** - Mergulho técnico profundo e design do sistema
|
|
483
485
|
- 🚀 **[Guia de Squads](docs/guides/squads-guide.md)** - Estenda o AIOS para qualquer domínio além do desenvolvimento de software
|
|
484
486
|
|
|
485
487
|
### Documentação Adicional
|
|
@@ -488,7 +490,7 @@ O Synkra AIOS vem com 11 agentes especializados:
|
|
|
488
490
|
- 📋 **[Primeiros Passos](docs/getting-started.md)** - Tutorial passo a passo para iniciantes
|
|
489
491
|
- 🔧 **[Solução de Problemas](docs/troubleshooting.md)** - Soluções para problemas comuns
|
|
490
492
|
- 🎯 **[Princípios Orientadores](docs/GUIDING-PRINCIPLES.md)** - Filosofia e melhores práticas do AIOS
|
|
491
|
-
- 🏛️ **[Visão Geral da Arquitetura](docs/architecture/
|
|
493
|
+
- 🏛️ **[Visão Geral da Arquitetura](docs/architecture/AIOS-VISUAL-OVERVIEW.md)** - Visão detalhada da arquitetura do sistema
|
|
492
494
|
- ⚙️ **[Guia de Ajuste de Performance](docs/performance-tuning-guide.md)** - Otimize seu fluxo de trabalho AIOS
|
|
493
495
|
- 🔒 **[Melhores Práticas de Segurança](docs/security-best-practices.md)** - Segurança e proteção de dados
|
|
494
496
|
- 🔄 **[Guia de Migração](docs/migration-guide.md)** - Migração de versões anteriores
|
package/bin/aios.js
CHANGED
|
@@ -199,10 +199,13 @@ function showInfo() {
|
|
|
199
199
|
}
|
|
200
200
|
};
|
|
201
201
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
console.log(` -
|
|
202
|
+
const devDir = path.join(aiosCoreDir, 'development');
|
|
203
|
+
const componentBase = fs.existsSync(devDir) ? devDir : aiosCoreDir;
|
|
204
|
+
|
|
205
|
+
console.log(` - Agents: ${countFiles(path.join(componentBase, 'agents'))}`);
|
|
206
|
+
console.log(` - Tasks: ${countFiles(path.join(componentBase, 'tasks'))}`);
|
|
207
|
+
console.log(` - Templates: ${countFiles(path.join(componentBase, 'templates'))}`);
|
|
208
|
+
console.log(` - Workflows: ${countFiles(path.join(componentBase, 'workflows'))}`);
|
|
206
209
|
} else {
|
|
207
210
|
console.log('\n⚠️ AIOS Core not found');
|
|
208
211
|
}
|
package/package.json
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
|
|
15
15
|
const readline = require('readline');
|
|
16
16
|
|
|
17
|
-
const RECOVERY_URL = 'https://
|
|
17
|
+
const RECOVERY_URL = 'https://aios-license-server.vercel.app/reset-password';
|
|
18
18
|
|
|
19
19
|
const RECOVERY_MESSAGE =
|
|
20
20
|
'Se este email estiver associado a uma licenca, voce recebera instrucoes de recuperacao.';
|
|
@@ -659,8 +659,8 @@ async function copyClaudeHooksFolder(projectRoot) {
|
|
|
659
659
|
|
|
660
660
|
// Only copy JS hooks that work standalone (no Python/shell deps)
|
|
661
661
|
const HOOKS_TO_COPY = [
|
|
662
|
-
'synapse-engine.
|
|
663
|
-
'precompact-session-digest.
|
|
662
|
+
'synapse-engine.cjs',
|
|
663
|
+
'precompact-session-digest.cjs',
|
|
664
664
|
'README.md',
|
|
665
665
|
];
|
|
666
666
|
|
|
@@ -692,7 +692,7 @@ async function copyClaudeHooksFolder(projectRoot) {
|
|
|
692
692
|
*/
|
|
693
693
|
async function createClaudeSettingsLocal(projectRoot) {
|
|
694
694
|
const settingsPath = path.join(projectRoot, '.claude', 'settings.local.json');
|
|
695
|
-
const hookFile = path.join(projectRoot, '.claude', 'hooks', 'synapse-engine.
|
|
695
|
+
const hookFile = path.join(projectRoot, '.claude', 'hooks', 'synapse-engine.cjs');
|
|
696
696
|
|
|
697
697
|
// Only create if the hook file was actually copied
|
|
698
698
|
if (!await fs.pathExists(hookFile)) {
|
|
@@ -705,7 +705,7 @@ async function createClaudeSettingsLocal(projectRoot) {
|
|
|
705
705
|
hooks: [
|
|
706
706
|
{
|
|
707
707
|
type: 'command',
|
|
708
|
-
command: 'node ".claude/hooks/synapse-engine.
|
|
708
|
+
command: 'node ".claude/hooks/synapse-engine.cjs"',
|
|
709
709
|
},
|
|
710
710
|
],
|
|
711
711
|
};
|
|
@@ -736,10 +736,10 @@ async function createClaudeSettingsLocal(projectRoot) {
|
|
|
736
736
|
const alreadyRegistered = settings.hooks.UserPromptSubmit.some(entry => {
|
|
737
737
|
// Nested format: entry.hooks[].command
|
|
738
738
|
if (Array.isArray(entry.hooks)) {
|
|
739
|
-
return entry.hooks.some(h => h.command && h.command.includes('synapse-engine
|
|
739
|
+
return entry.hooks.some(h => h.command && h.command.includes('synapse-engine'));
|
|
740
740
|
}
|
|
741
741
|
// Flat format (legacy): entry.command
|
|
742
|
-
return entry.command && entry.command.includes('synapse-engine
|
|
742
|
+
return entry.command && entry.command.includes('synapse-engine');
|
|
743
743
|
});
|
|
744
744
|
|
|
745
745
|
if (!alreadyRegistered) {
|
|
@@ -426,11 +426,11 @@ async function loginWithRetry(client, email) {
|
|
|
426
426
|
const remaining = MAX_RETRIES - attempt;
|
|
427
427
|
if (remaining > 0) {
|
|
428
428
|
spinner.fail(`Incorrect password. ${remaining} attempt${remaining > 1 ? 's' : ''} remaining.`);
|
|
429
|
-
showInfo('Forgot your password? Visit https://
|
|
429
|
+
showInfo('Forgot your password? Visit https://aios-license-server.vercel.app/reset-password');
|
|
430
430
|
} else {
|
|
431
431
|
spinner.fail('Maximum login attempts reached.');
|
|
432
|
-
showInfo('Forgot your password? Visit https://
|
|
433
|
-
showInfo('Or
|
|
432
|
+
showInfo('Forgot your password? Visit https://aios-license-server.vercel.app/reset-password');
|
|
433
|
+
showInfo('Or open an issue: https://github.com/SynkraAI/aios-core/issues');
|
|
434
434
|
return { success: false, error: 'Maximum login attempts reached.' };
|
|
435
435
|
}
|
|
436
436
|
} else if (loginError.code === 'AUTH_RATE_LIMITED') {
|
|
@@ -1,220 +1,220 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Graceful Degradation Module
|
|
3
|
-
*
|
|
4
|
-
* Utilities for handling pro feature unavailability with user-friendly
|
|
5
|
-
* messages that preserve data and provide clear next steps.
|
|
6
|
-
*
|
|
7
|
-
* Per ADR-PRO-003 and AC-8:
|
|
8
|
-
* - Never show upgrade prompts on core features
|
|
9
|
-
* - Never delete or corrupt user data
|
|
10
|
-
* - Always provide actionable next steps
|
|
11
|
-
* - Keep messages non-intrusive
|
|
12
|
-
*
|
|
13
|
-
* @module pro/license/degradation
|
|
14
|
-
* @see ADR-PRO-003 - Feature Gating & Licensing
|
|
15
|
-
* @see Story PRO-6 - License Key & Feature Gating System
|
|
16
|
-
*/
|
|
17
|
-
|
|
18
|
-
'use strict';
|
|
19
|
-
|
|
20
|
-
const { featureGate } = require('./feature-gate');
|
|
21
|
-
const { ProFeatureError } = require('./errors');
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Default degradation message template.
|
|
25
|
-
* @private Reserved for future customization support.
|
|
26
|
-
*/
|
|
27
|
-
const _DEFAULT_MESSAGE_TEMPLATE = `
|
|
28
|
-
{featureName} requires an active AIOS Pro license.
|
|
29
|
-
|
|
30
|
-
Your data and configurations are preserved.
|
|
31
|
-
|
|
32
|
-
Activate: aios pro activate --key <KEY>
|
|
33
|
-
Purchase: https://synkra.ai/pro
|
|
34
|
-
`;
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Handle pro feature gracefully - return fallback instead of throwing.
|
|
38
|
-
*
|
|
39
|
-
* Use this when you want to provide a degraded experience instead
|
|
40
|
-
* of completely blocking the feature.
|
|
41
|
-
*
|
|
42
|
-
* @param {string} featureId - Feature ID to check
|
|
43
|
-
* @param {Function} proAction - Function to execute if feature is available
|
|
44
|
-
* @param {Function} [fallbackAction] - Function to execute if not available
|
|
45
|
-
* @param {object} [options] - Options
|
|
46
|
-
* @param {boolean} [options.silent=false] - Don't log degradation message
|
|
47
|
-
* @returns {*} Result of proAction or fallbackAction
|
|
48
|
-
*/
|
|
49
|
-
function withGracefulDegradation(featureId, proAction, fallbackAction, options = {}) {
|
|
50
|
-
if (featureGate.isAvailable(featureId)) {
|
|
51
|
-
return proAction();
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
if (!options.silent) {
|
|
55
|
-
const featureName = getFeatureFriendlyName(featureId);
|
|
56
|
-
logDegradationMessage(featureName, featureId);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
if (typeof fallbackAction === 'function') {
|
|
60
|
-
return fallbackAction();
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
return null;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* Execute action only if pro feature is available.
|
|
68
|
-
*
|
|
69
|
-
* Unlike featureGate.require(), this doesn't throw - it just
|
|
70
|
-
* returns undefined if the feature isn't available.
|
|
71
|
-
*
|
|
72
|
-
* @param {string} featureId - Feature ID to check
|
|
73
|
-
* @param {Function} action - Action to execute if available
|
|
74
|
-
* @returns {*|undefined} Result of action or undefined
|
|
75
|
-
*/
|
|
76
|
-
function ifProAvailable(featureId, action) {
|
|
77
|
-
if (featureGate.isAvailable(featureId)) {
|
|
78
|
-
return action();
|
|
79
|
-
}
|
|
80
|
-
return undefined;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
* Get a user-friendly name for a feature.
|
|
85
|
-
*
|
|
86
|
-
* @param {string} featureId - Feature ID
|
|
87
|
-
* @returns {string} Friendly name
|
|
88
|
-
*/
|
|
89
|
-
function getFeatureFriendlyName(featureId) {
|
|
90
|
-
const all = featureGate.listAll();
|
|
91
|
-
const feature = all.find((f) => f.id === featureId);
|
|
92
|
-
return feature ? feature.name : featureId;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* Log a degradation message to the console.
|
|
97
|
-
*
|
|
98
|
-
* This is used internally when a pro feature is accessed without
|
|
99
|
-
* a license. Messages are non-intrusive and informative.
|
|
100
|
-
*
|
|
101
|
-
* @param {string} featureName - Human-friendly feature name
|
|
102
|
-
* @param {string} _featureId - Feature ID for reference (reserved for logging)
|
|
103
|
-
*/
|
|
104
|
-
function logDegradationMessage(featureName, _featureId) {
|
|
105
|
-
console.log('');
|
|
106
|
-
console.log(` ${featureName} requires an active AIOS Pro license.`);
|
|
107
|
-
console.log('');
|
|
108
|
-
console.log(' Your data and configurations are preserved.');
|
|
109
|
-
console.log('');
|
|
110
|
-
console.log(' Activate: aios pro activate --key <KEY>');
|
|
111
|
-
console.log(' Purchase: https://synkra.ai/pro');
|
|
112
|
-
console.log('');
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* Create a degradation-aware wrapper for a pro module.
|
|
117
|
-
*
|
|
118
|
-
* This creates a proxy that catches ProFeatureError and provides
|
|
119
|
-
* a graceful degradation experience.
|
|
120
|
-
*
|
|
121
|
-
* @param {object} proModule - Pro module with methods
|
|
122
|
-
* @param {object} fallbacks - Map of method names to fallback functions
|
|
123
|
-
* @returns {Proxy} Wrapped module
|
|
124
|
-
*
|
|
125
|
-
* @example
|
|
126
|
-
* const safePremiumSquads = createDegradationWrapper(
|
|
127
|
-
* PremiumSquads,
|
|
128
|
-
* {
|
|
129
|
-
* listTemplates: () => ['basic'], // Fallback to basic templates
|
|
130
|
-
* exportSquad: () => null, // No export without license
|
|
131
|
-
* }
|
|
132
|
-
* );
|
|
133
|
-
*/
|
|
134
|
-
function createDegradationWrapper(proModule, fallbacks = {}) {
|
|
135
|
-
return new Proxy(proModule, {
|
|
136
|
-
get(target, prop) {
|
|
137
|
-
const original = target[prop];
|
|
138
|
-
|
|
139
|
-
if (typeof original !== 'function') {
|
|
140
|
-
return original;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
return function (...args) {
|
|
144
|
-
try {
|
|
145
|
-
return original.apply(target, args);
|
|
146
|
-
} catch (error) {
|
|
147
|
-
if (error instanceof ProFeatureError) {
|
|
148
|
-
if (fallbacks[prop]) {
|
|
149
|
-
logDegradationMessage(error.friendlyName, error.featureId);
|
|
150
|
-
return fallbacks[prop](...args);
|
|
151
|
-
}
|
|
152
|
-
// Re-throw if no fallback
|
|
153
|
-
throw error;
|
|
154
|
-
}
|
|
155
|
-
// Re-throw non-license errors
|
|
156
|
-
throw error;
|
|
157
|
-
}
|
|
158
|
-
};
|
|
159
|
-
},
|
|
160
|
-
});
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
/**
|
|
164
|
-
* Check if the system is in degraded mode (expired/no license).
|
|
165
|
-
*
|
|
166
|
-
* @returns {boolean} true if pro features are unavailable
|
|
167
|
-
*/
|
|
168
|
-
function isInDegradedMode() {
|
|
169
|
-
const state = featureGate.getLicenseState();
|
|
170
|
-
return state === 'Expired' || state === 'Not Activated';
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
/**
|
|
174
|
-
* Get degradation status summary.
|
|
175
|
-
*
|
|
176
|
-
* @returns {{ degraded: boolean, reason: string, action: string }}
|
|
177
|
-
*/
|
|
178
|
-
function getDegradationStatus() {
|
|
179
|
-
const state = featureGate.getLicenseState();
|
|
180
|
-
|
|
181
|
-
if (state === 'Active') {
|
|
182
|
-
return {
|
|
183
|
-
degraded: false,
|
|
184
|
-
reason: 'License is active',
|
|
185
|
-
action: null,
|
|
186
|
-
};
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
if (state === 'Grace') {
|
|
190
|
-
return {
|
|
191
|
-
degraded: false,
|
|
192
|
-
reason: 'License in grace period - revalidate soon',
|
|
193
|
-
action: 'aios pro validate',
|
|
194
|
-
};
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
if (state === 'Expired') {
|
|
198
|
-
return {
|
|
199
|
-
degraded: true,
|
|
200
|
-
reason: 'License has expired',
|
|
201
|
-
action: 'aios pro activate --key <KEY>',
|
|
202
|
-
};
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
return {
|
|
206
|
-
degraded: true,
|
|
207
|
-
reason: 'No license activated',
|
|
208
|
-
action: 'aios pro activate --key <KEY>',
|
|
209
|
-
};
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
module.exports = {
|
|
213
|
-
withGracefulDegradation,
|
|
214
|
-
ifProAvailable,
|
|
215
|
-
getFeatureFriendlyName,
|
|
216
|
-
logDegradationMessage,
|
|
217
|
-
createDegradationWrapper,
|
|
218
|
-
isInDegradedMode,
|
|
219
|
-
getDegradationStatus,
|
|
220
|
-
};
|
|
1
|
+
/**
|
|
2
|
+
* Graceful Degradation Module
|
|
3
|
+
*
|
|
4
|
+
* Utilities for handling pro feature unavailability with user-friendly
|
|
5
|
+
* messages that preserve data and provide clear next steps.
|
|
6
|
+
*
|
|
7
|
+
* Per ADR-PRO-003 and AC-8:
|
|
8
|
+
* - Never show upgrade prompts on core features
|
|
9
|
+
* - Never delete or corrupt user data
|
|
10
|
+
* - Always provide actionable next steps
|
|
11
|
+
* - Keep messages non-intrusive
|
|
12
|
+
*
|
|
13
|
+
* @module pro/license/degradation
|
|
14
|
+
* @see ADR-PRO-003 - Feature Gating & Licensing
|
|
15
|
+
* @see Story PRO-6 - License Key & Feature Gating System
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
'use strict';
|
|
19
|
+
|
|
20
|
+
const { featureGate } = require('./feature-gate');
|
|
21
|
+
const { ProFeatureError } = require('./errors');
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Default degradation message template.
|
|
25
|
+
* @private Reserved for future customization support.
|
|
26
|
+
*/
|
|
27
|
+
const _DEFAULT_MESSAGE_TEMPLATE = `
|
|
28
|
+
{featureName} requires an active AIOS Pro license.
|
|
29
|
+
|
|
30
|
+
Your data and configurations are preserved.
|
|
31
|
+
|
|
32
|
+
Activate: aios pro activate --key <KEY>
|
|
33
|
+
Purchase: https://synkra.ai/pro
|
|
34
|
+
`;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Handle pro feature gracefully - return fallback instead of throwing.
|
|
38
|
+
*
|
|
39
|
+
* Use this when you want to provide a degraded experience instead
|
|
40
|
+
* of completely blocking the feature.
|
|
41
|
+
*
|
|
42
|
+
* @param {string} featureId - Feature ID to check
|
|
43
|
+
* @param {Function} proAction - Function to execute if feature is available
|
|
44
|
+
* @param {Function} [fallbackAction] - Function to execute if not available
|
|
45
|
+
* @param {object} [options] - Options
|
|
46
|
+
* @param {boolean} [options.silent=false] - Don't log degradation message
|
|
47
|
+
* @returns {*} Result of proAction or fallbackAction
|
|
48
|
+
*/
|
|
49
|
+
function withGracefulDegradation(featureId, proAction, fallbackAction, options = {}) {
|
|
50
|
+
if (featureGate.isAvailable(featureId)) {
|
|
51
|
+
return proAction();
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (!options.silent) {
|
|
55
|
+
const featureName = getFeatureFriendlyName(featureId);
|
|
56
|
+
logDegradationMessage(featureName, featureId);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (typeof fallbackAction === 'function') {
|
|
60
|
+
return fallbackAction();
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Execute action only if pro feature is available.
|
|
68
|
+
*
|
|
69
|
+
* Unlike featureGate.require(), this doesn't throw - it just
|
|
70
|
+
* returns undefined if the feature isn't available.
|
|
71
|
+
*
|
|
72
|
+
* @param {string} featureId - Feature ID to check
|
|
73
|
+
* @param {Function} action - Action to execute if available
|
|
74
|
+
* @returns {*|undefined} Result of action or undefined
|
|
75
|
+
*/
|
|
76
|
+
function ifProAvailable(featureId, action) {
|
|
77
|
+
if (featureGate.isAvailable(featureId)) {
|
|
78
|
+
return action();
|
|
79
|
+
}
|
|
80
|
+
return undefined;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Get a user-friendly name for a feature.
|
|
85
|
+
*
|
|
86
|
+
* @param {string} featureId - Feature ID
|
|
87
|
+
* @returns {string} Friendly name
|
|
88
|
+
*/
|
|
89
|
+
function getFeatureFriendlyName(featureId) {
|
|
90
|
+
const all = featureGate.listAll();
|
|
91
|
+
const feature = all.find((f) => f.id === featureId);
|
|
92
|
+
return feature ? feature.name : featureId;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Log a degradation message to the console.
|
|
97
|
+
*
|
|
98
|
+
* This is used internally when a pro feature is accessed without
|
|
99
|
+
* a license. Messages are non-intrusive and informative.
|
|
100
|
+
*
|
|
101
|
+
* @param {string} featureName - Human-friendly feature name
|
|
102
|
+
* @param {string} _featureId - Feature ID for reference (reserved for logging)
|
|
103
|
+
*/
|
|
104
|
+
function logDegradationMessage(featureName, _featureId) {
|
|
105
|
+
console.log('');
|
|
106
|
+
console.log(` ${featureName} requires an active AIOS Pro license.`);
|
|
107
|
+
console.log('');
|
|
108
|
+
console.log(' Your data and configurations are preserved.');
|
|
109
|
+
console.log('');
|
|
110
|
+
console.log(' Activate: aios pro activate --key <KEY>');
|
|
111
|
+
console.log(' Purchase: https://synkra.ai/pro');
|
|
112
|
+
console.log('');
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Create a degradation-aware wrapper for a pro module.
|
|
117
|
+
*
|
|
118
|
+
* This creates a proxy that catches ProFeatureError and provides
|
|
119
|
+
* a graceful degradation experience.
|
|
120
|
+
*
|
|
121
|
+
* @param {object} proModule - Pro module with methods
|
|
122
|
+
* @param {object} fallbacks - Map of method names to fallback functions
|
|
123
|
+
* @returns {Proxy} Wrapped module
|
|
124
|
+
*
|
|
125
|
+
* @example
|
|
126
|
+
* const safePremiumSquads = createDegradationWrapper(
|
|
127
|
+
* PremiumSquads,
|
|
128
|
+
* {
|
|
129
|
+
* listTemplates: () => ['basic'], // Fallback to basic templates
|
|
130
|
+
* exportSquad: () => null, // No export without license
|
|
131
|
+
* }
|
|
132
|
+
* );
|
|
133
|
+
*/
|
|
134
|
+
function createDegradationWrapper(proModule, fallbacks = {}) {
|
|
135
|
+
return new Proxy(proModule, {
|
|
136
|
+
get(target, prop) {
|
|
137
|
+
const original = target[prop];
|
|
138
|
+
|
|
139
|
+
if (typeof original !== 'function') {
|
|
140
|
+
return original;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return function (...args) {
|
|
144
|
+
try {
|
|
145
|
+
return original.apply(target, args);
|
|
146
|
+
} catch (error) {
|
|
147
|
+
if (error instanceof ProFeatureError) {
|
|
148
|
+
if (fallbacks[prop]) {
|
|
149
|
+
logDegradationMessage(error.friendlyName, error.featureId);
|
|
150
|
+
return fallbacks[prop](...args);
|
|
151
|
+
}
|
|
152
|
+
// Re-throw if no fallback
|
|
153
|
+
throw error;
|
|
154
|
+
}
|
|
155
|
+
// Re-throw non-license errors
|
|
156
|
+
throw error;
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
},
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Check if the system is in degraded mode (expired/no license).
|
|
165
|
+
*
|
|
166
|
+
* @returns {boolean} true if pro features are unavailable
|
|
167
|
+
*/
|
|
168
|
+
function isInDegradedMode() {
|
|
169
|
+
const state = featureGate.getLicenseState();
|
|
170
|
+
return state === 'Expired' || state === 'Not Activated';
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Get degradation status summary.
|
|
175
|
+
*
|
|
176
|
+
* @returns {{ degraded: boolean, reason: string, action: string }}
|
|
177
|
+
*/
|
|
178
|
+
function getDegradationStatus() {
|
|
179
|
+
const state = featureGate.getLicenseState();
|
|
180
|
+
|
|
181
|
+
if (state === 'Active') {
|
|
182
|
+
return {
|
|
183
|
+
degraded: false,
|
|
184
|
+
reason: 'License is active',
|
|
185
|
+
action: null,
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
if (state === 'Grace') {
|
|
190
|
+
return {
|
|
191
|
+
degraded: false,
|
|
192
|
+
reason: 'License in grace period - revalidate soon',
|
|
193
|
+
action: 'aios pro validate',
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
if (state === 'Expired') {
|
|
198
|
+
return {
|
|
199
|
+
degraded: true,
|
|
200
|
+
reason: 'License has expired',
|
|
201
|
+
action: 'aios pro activate --key <KEY>',
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
return {
|
|
206
|
+
degraded: true,
|
|
207
|
+
reason: 'No license activated',
|
|
208
|
+
action: 'aios pro activate --key <KEY>',
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
module.exports = {
|
|
213
|
+
withGracefulDegradation,
|
|
214
|
+
ifProAvailable,
|
|
215
|
+
getFeatureFriendlyName,
|
|
216
|
+
logDegradationMessage,
|
|
217
|
+
createDegradationWrapper,
|
|
218
|
+
isInDegradedMode,
|
|
219
|
+
getDegradationStatus,
|
|
220
|
+
};
|