aios-core 4.0.2 → 4.1.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/.aios-core/cli/commands/migrate/analyze.js +6 -6
- package/.aios-core/cli/commands/migrate/backup.js +2 -2
- package/.aios-core/cli/commands/migrate/execute.js +4 -4
- package/.aios-core/cli/commands/migrate/index.js +5 -5
- package/.aios-core/cli/commands/migrate/rollback.js +6 -6
- package/.aios-core/cli/commands/migrate/update-imports.js +2 -2
- package/.aios-core/cli/commands/migrate/validate.js +2 -2
- package/.aios-core/cli/commands/pro/index.js +52 -0
- package/.aios-core/cli/index.js +1 -1
- package/.aios-core/core/ids/registry-updater.js +29 -3
- package/.aios-core/core/migration/migration-config.yaml +2 -2
- package/.aios-core/core/migration/module-mapping.yaml +2 -2
- package/.aios-core/core/registry/README.md +2 -2
- package/.aios-core/core/synapse/context/context-builder.js +34 -0
- package/.aios-core/core/synapse/diagnostics/collectors/consistency-collector.js +168 -0
- package/.aios-core/core/synapse/diagnostics/collectors/hook-collector.js +129 -0
- package/.aios-core/core/synapse/diagnostics/collectors/manifest-collector.js +82 -0
- package/.aios-core/core/synapse/diagnostics/collectors/output-analyzer.js +134 -0
- package/.aios-core/core/synapse/diagnostics/collectors/pipeline-collector.js +75 -0
- package/.aios-core/core/synapse/diagnostics/collectors/quality-collector.js +252 -0
- package/.aios-core/core/synapse/diagnostics/collectors/relevance-matrix.js +174 -0
- package/.aios-core/core/synapse/diagnostics/collectors/safe-read-json.js +31 -0
- package/.aios-core/core/synapse/diagnostics/collectors/session-collector.js +102 -0
- package/.aios-core/core/synapse/diagnostics/collectors/timing-collector.js +126 -0
- package/.aios-core/core/synapse/diagnostics/collectors/uap-collector.js +83 -0
- package/.aios-core/core/synapse/diagnostics/report-formatter.js +484 -0
- package/.aios-core/core/synapse/diagnostics/synapse-diagnostics.js +95 -0
- package/.aios-core/core/synapse/engine.js +73 -20
- package/.aios-core/core/synapse/runtime/hook-runtime.js +60 -0
- package/.aios-core/core-config.yaml +6 -0
- package/.aios-core/data/agent-config-requirements.yaml +2 -2
- package/.aios-core/data/aios-kb.md +4 -4
- package/.aios-core/data/entity-registry.yaml +5 -5
- package/.aios-core/development/agents/architect.md +10 -10
- package/.aios-core/development/agents/devops.md +93 -50
- package/.aios-core/development/agents/qa.md +94 -40
- package/.aios-core/development/agents/ux-design-expert.md +25 -25
- package/.aios-core/development/scripts/activation-runtime.js +63 -0
- package/.aios-core/development/scripts/generate-greeting.js +9 -8
- package/.aios-core/development/scripts/unified-activation-pipeline.js +102 -2
- package/.aios-core/development/tasks/{db-expansion-pack-integration.md → db-squad-integration.md} +5 -5
- package/.aios-core/development/tasks/{integrate-expansion-pack.md → integrate-squad.md} +2 -2
- package/.aios-core/development/tasks/next.md +3 -3
- package/.aios-core/development/tasks/pr-automation.md +2 -2
- package/.aios-core/development/tasks/publish-npm.md +257 -0
- package/.aios-core/development/tasks/release-management.md +4 -4
- package/.aios-core/development/tasks/setup-github.md +1 -1
- package/.aios-core/development/tasks/squad-creator-migrate.md +1 -1
- package/.aios-core/development/tasks/squad-creator-sync-ide-command.md +14 -14
- package/.aios-core/development/tasks/update-aios.md +1 -1
- package/.aios-core/docs/standards/AIOS-COLOR-PALETTE-QUICK-REFERENCE.md +1 -1
- package/.aios-core/docs/standards/AIOS-COLOR-PALETTE-V2.1.md +5 -5
- package/.aios-core/docs/standards/AIOS-LIVRO-DE-OURO-V2.1-COMPLETE.md +21 -21
- package/.aios-core/docs/standards/AIOS-LIVRO-DE-OURO-V2.2-SUMMARY.md +25 -25
- package/.aios-core/docs/standards/OPEN-SOURCE-VS-SERVICE-DIFFERENCES.md +4 -4
- package/.aios-core/docs/standards/QUALITY-GATES-SPECIFICATION.md +3 -3
- package/.aios-core/docs/standards/STANDARDS-INDEX.md +13 -13
- package/.aios-core/docs/standards/STORY-TEMPLATE-V2-SPECIFICATION.md +1 -1
- package/.aios-core/framework-config.yaml +4 -0
- package/.aios-core/infrastructure/scripts/codex-skills-sync/index.js +182 -0
- package/.aios-core/infrastructure/scripts/codex-skills-sync/validate.js +172 -0
- package/.aios-core/infrastructure/scripts/ide-sync/README.md +14 -0
- package/.aios-core/infrastructure/scripts/ide-sync/index.js +6 -0
- package/.aios-core/infrastructure/scripts/tool-resolver.js +4 -4
- package/.aios-core/infrastructure/scripts/validate-paths.js +142 -0
- package/.aios-core/infrastructure/templates/aios-sync.yaml.template +11 -11
- package/.aios-core/infrastructure/templates/github-workflows/README.md +1 -1
- package/.aios-core/install-manifest.yaml +190 -106
- package/.aios-core/local-config.yaml.template +2 -0
- package/.aios-core/product/README.md +2 -2
- package/.aios-core/product/data/integration-patterns.md +1 -1
- package/.aios-core/product/templates/ide-rules/cline-rules.md +1 -1
- package/.aios-core/product/templates/ide-rules/codex-rules.md +65 -0
- package/.aios-core/product/templates/ide-rules/copilot-rules.md +1 -1
- package/.aios-core/product/templates/ide-rules/roo-rules.md +1 -1
- package/.aios-core/user-guide.md +15 -14
- package/.aios-core/workflow-intelligence/engine/output-formatter.js +1 -1
- package/.claude/hooks/enforce-architecture-first.py +196 -0
- package/.claude/hooks/install-hooks.sh +41 -0
- package/.claude/hooks/mind-clone-governance.py +192 -0
- package/.claude/hooks/pre-commit-mmos-guard.sh +99 -0
- package/.claude/hooks/pre-commit-version-check.sh +156 -0
- package/.claude/hooks/read-protection.py +151 -0
- package/.claude/hooks/slug-validation.py +176 -0
- package/.claude/hooks/sql-governance.py +182 -0
- package/.claude/hooks/synapse-engine.js +9 -20
- package/.claude/hooks/write-path-validation.py +194 -0
- package/README.md +44 -14
- package/bin/aios-init.js +255 -184
- package/bin/aios-minimal.js +2 -2
- package/bin/aios.js +19 -19
- package/package.json +7 -4
- package/packages/aios-pro-cli/bin/aios-pro.js +75 -2
- package/packages/aios-pro-cli/package.json +5 -1
- package/packages/aios-pro-cli/src/recover.js +100 -0
- package/packages/installer/src/__tests__/performance-benchmark.js +382 -0
- package/packages/installer/src/config/ide-configs.js +12 -1
- package/packages/installer/src/config/templates/core-config-template.js +2 -2
- package/packages/installer/src/installer/aios-core-installer.js +2 -2
- package/packages/installer/src/installer/file-hasher.js +97 -0
- package/packages/installer/src/installer/post-install-validator.js +41 -1
- package/packages/installer/src/pro/pro-scaffolder.js +335 -0
- package/packages/installer/src/utils/aios-colors.js +2 -2
- package/packages/installer/src/wizard/feedback.js +1 -1
- package/packages/installer/src/wizard/ide-config-generator.js +2 -2
- package/packages/installer/src/wizard/index.js +58 -19
- package/packages/installer/src/wizard/pro-setup.js +547 -0
- package/packages/installer/src/wizard/questions.js +20 -14
- package/packages/installer/src/wizard/validators.js +1 -1
- package/scripts/package-synapse.js +323 -0
- package/scripts/validate-package-completeness.js +317 -0
|
@@ -0,0 +1,547 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pro Installation Wizard with License Gate
|
|
3
|
+
*
|
|
4
|
+
* 3-step wizard: (1) License Gate, (2) Install/Scaffold, (3) Verify
|
|
5
|
+
* Supports interactive mode, CI mode (AIOS_PRO_KEY env var), and lazy import.
|
|
6
|
+
*
|
|
7
|
+
* @module wizard/pro-setup
|
|
8
|
+
* @story INS-3.2 — Implement Pro Installation Wizard with License Gate
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
'use strict';
|
|
12
|
+
|
|
13
|
+
const { createSpinner, showSuccess, showError, showWarning, showInfo } = require('./feedback');
|
|
14
|
+
const { colors, headings, status } = require('../utils/aios-colors');
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Gold color for Pro branding.
|
|
18
|
+
* Falls back gracefully if chalk hex is unavailable.
|
|
19
|
+
*/
|
|
20
|
+
let gold;
|
|
21
|
+
try {
|
|
22
|
+
const chalk = require('chalk');
|
|
23
|
+
gold = chalk.hex('#FFD700').bold;
|
|
24
|
+
} catch {
|
|
25
|
+
gold = (text) => text;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* License key format: PRO-XXXX-XXXX-XXXX-XXXX
|
|
30
|
+
*/
|
|
31
|
+
const LICENSE_KEY_PATTERN = /^PRO-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}$/;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Maximum retry attempts for license validation.
|
|
35
|
+
*/
|
|
36
|
+
const MAX_RETRIES = 3;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Detect CI environment.
|
|
40
|
+
*
|
|
41
|
+
* @returns {boolean} true if running in CI or non-interactive terminal
|
|
42
|
+
*/
|
|
43
|
+
function isCIEnvironment() {
|
|
44
|
+
return process.env.CI === 'true' || !process.stdout.isTTY;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Mask a license key for safe display.
|
|
49
|
+
* Shows first and last segments, masks middle two.
|
|
50
|
+
* Example: PRO-ABCD-****-****-WXYZ
|
|
51
|
+
*
|
|
52
|
+
* @param {string} key - License key
|
|
53
|
+
* @returns {string} Masked key
|
|
54
|
+
*/
|
|
55
|
+
function maskLicenseKey(key) {
|
|
56
|
+
if (!key || typeof key !== 'string') {
|
|
57
|
+
return '****';
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const trimmed = key.trim().toUpperCase();
|
|
61
|
+
|
|
62
|
+
if (!LICENSE_KEY_PATTERN.test(trimmed)) {
|
|
63
|
+
return '****';
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const parts = trimmed.split('-');
|
|
67
|
+
return `${parts[0]}-${parts[1]}-****-****-${parts[4]}`;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Validate license key format before sending to API.
|
|
72
|
+
*
|
|
73
|
+
* @param {string} key - License key
|
|
74
|
+
* @returns {boolean} true if format is valid
|
|
75
|
+
*/
|
|
76
|
+
function validateKeyFormat(key) {
|
|
77
|
+
if (!key || typeof key !== 'string') {
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
return LICENSE_KEY_PATTERN.test(key.trim().toUpperCase());
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Show the Pro branding header.
|
|
85
|
+
*/
|
|
86
|
+
function showProHeader() {
|
|
87
|
+
console.log('');
|
|
88
|
+
console.log(gold(' ╔══════════════════════════════════════════════╗'));
|
|
89
|
+
console.log(gold(' ║ AIOS Pro Installation Wizard ║'));
|
|
90
|
+
console.log(gold(' ║ Premium Content & Features ║'));
|
|
91
|
+
console.log(gold(' ╚══════════════════════════════════════════════╝'));
|
|
92
|
+
console.log('');
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Show step indicator.
|
|
97
|
+
*
|
|
98
|
+
* @param {number} current - Current step (1-based)
|
|
99
|
+
* @param {number} total - Total steps
|
|
100
|
+
* @param {string} label - Step label
|
|
101
|
+
*/
|
|
102
|
+
function showStep(current, total, label) {
|
|
103
|
+
const progress = `[${current}/${total}]`;
|
|
104
|
+
console.log(gold(`\n ${progress} ${label}`));
|
|
105
|
+
console.log(colors.dim(' ' + '─'.repeat(44)));
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Try to load the license API client via lazy import.
|
|
110
|
+
*
|
|
111
|
+
* @returns {{ LicenseApiClient: Function, licenseApi: Object }|null} License API or null
|
|
112
|
+
*/
|
|
113
|
+
function loadLicenseApi() {
|
|
114
|
+
try {
|
|
115
|
+
return require('../../../../pro/license/license-api');
|
|
116
|
+
} catch {
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Try to load the feature gate via lazy import.
|
|
123
|
+
*
|
|
124
|
+
* @returns {{ featureGate: Object }|null} Feature gate or null
|
|
125
|
+
*/
|
|
126
|
+
function loadFeatureGate() {
|
|
127
|
+
try {
|
|
128
|
+
return require('../../../../pro/license/feature-gate');
|
|
129
|
+
} catch {
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Try to load the pro scaffolder via lazy import.
|
|
136
|
+
*
|
|
137
|
+
* @returns {{ scaffoldProContent: Function }|null} Scaffolder or null
|
|
138
|
+
*/
|
|
139
|
+
function loadProScaffolder() {
|
|
140
|
+
try {
|
|
141
|
+
return require('../pro/pro-scaffolder');
|
|
142
|
+
} catch {
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Step 1: License Gate — validate license key.
|
|
149
|
+
*
|
|
150
|
+
* In CI mode, reads from AIOS_PRO_KEY env var.
|
|
151
|
+
* In interactive mode, prompts with masked input.
|
|
152
|
+
*
|
|
153
|
+
* @param {Object} [options={}] - Options
|
|
154
|
+
* @param {string} [options.key] - Pre-provided key (from CLI args or env)
|
|
155
|
+
* @returns {Promise<Object>} Result with { success, key, activationResult }
|
|
156
|
+
*/
|
|
157
|
+
async function stepLicenseGate(options = {}) {
|
|
158
|
+
showStep(1, 3, 'License Validation');
|
|
159
|
+
|
|
160
|
+
const isCI = isCIEnvironment();
|
|
161
|
+
let key = options.key || null;
|
|
162
|
+
|
|
163
|
+
// CI mode: read from env var
|
|
164
|
+
if (!key && isCI) {
|
|
165
|
+
key = process.env.AIOS_PRO_KEY || null;
|
|
166
|
+
|
|
167
|
+
if (!key) {
|
|
168
|
+
return {
|
|
169
|
+
success: false,
|
|
170
|
+
error: 'CI mode: AIOS_PRO_KEY environment variable not set.',
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Interactive mode: prompt for key
|
|
176
|
+
if (!key && !isCI) {
|
|
177
|
+
const inquirer = require('inquirer');
|
|
178
|
+
|
|
179
|
+
for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
|
|
180
|
+
const { licenseKey } = await inquirer.prompt([
|
|
181
|
+
{
|
|
182
|
+
type: 'password',
|
|
183
|
+
name: 'licenseKey',
|
|
184
|
+
message: colors.primary('Enter your Pro license key:'),
|
|
185
|
+
mask: '*',
|
|
186
|
+
validate: (input) => {
|
|
187
|
+
if (!input || !input.trim()) {
|
|
188
|
+
return 'License key is required';
|
|
189
|
+
}
|
|
190
|
+
if (!validateKeyFormat(input)) {
|
|
191
|
+
return 'Invalid format. Expected: PRO-XXXX-XXXX-XXXX-XXXX';
|
|
192
|
+
}
|
|
193
|
+
return true;
|
|
194
|
+
},
|
|
195
|
+
},
|
|
196
|
+
]);
|
|
197
|
+
|
|
198
|
+
key = licenseKey.trim().toUpperCase();
|
|
199
|
+
|
|
200
|
+
// Validate with API
|
|
201
|
+
const result = await validateKeyWithApi(key);
|
|
202
|
+
|
|
203
|
+
if (result.success) {
|
|
204
|
+
showSuccess(`License validated: ${maskLicenseKey(key)}`);
|
|
205
|
+
return { success: true, key, activationResult: result.data };
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// Show error and retry
|
|
209
|
+
const remaining = MAX_RETRIES - attempt;
|
|
210
|
+
if (remaining > 0) {
|
|
211
|
+
showError(`${result.error} (${remaining} attempt${remaining > 1 ? 's' : ''} remaining)`);
|
|
212
|
+
} else {
|
|
213
|
+
showError(`${result.error} — no attempts remaining.`);
|
|
214
|
+
return { success: false, error: result.error };
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// Validate key format
|
|
220
|
+
if (!validateKeyFormat(key)) {
|
|
221
|
+
return {
|
|
222
|
+
success: false,
|
|
223
|
+
error: `Invalid key format: ${maskLicenseKey(key)}. Expected: PRO-XXXX-XXXX-XXXX-XXXX`,
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// Validate with API
|
|
228
|
+
const spinner = createSpinner(`Validating license ${maskLicenseKey(key)}...`);
|
|
229
|
+
spinner.start();
|
|
230
|
+
|
|
231
|
+
const result = await validateKeyWithApi(key);
|
|
232
|
+
|
|
233
|
+
if (result.success) {
|
|
234
|
+
spinner.succeed(`License validated: ${maskLicenseKey(key)}`);
|
|
235
|
+
return { success: true, key, activationResult: result.data };
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
spinner.fail(result.error);
|
|
239
|
+
return { success: false, error: result.error };
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Validate a key against the license API.
|
|
244
|
+
*
|
|
245
|
+
* @param {string} key - License key
|
|
246
|
+
* @returns {Promise<Object>} Result with { success, data?, error? }
|
|
247
|
+
*/
|
|
248
|
+
async function validateKeyWithApi(key) {
|
|
249
|
+
// Use exports._testing for testability (allows mock injection)
|
|
250
|
+
const loader = module.exports._testing ? module.exports._testing.loadLicenseApi : loadLicenseApi;
|
|
251
|
+
const licenseModule = loader();
|
|
252
|
+
|
|
253
|
+
if (!licenseModule) {
|
|
254
|
+
return {
|
|
255
|
+
success: false,
|
|
256
|
+
error: 'Pro license module not available. Ensure @aios-fullstack/pro is installed.',
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
const { LicenseApiClient } = licenseModule;
|
|
261
|
+
const client = new LicenseApiClient();
|
|
262
|
+
|
|
263
|
+
try {
|
|
264
|
+
// Check if API is reachable
|
|
265
|
+
const online = await client.isOnline();
|
|
266
|
+
|
|
267
|
+
if (!online) {
|
|
268
|
+
return {
|
|
269
|
+
success: false,
|
|
270
|
+
error: 'License server is unreachable. Check your internet connection and try again.',
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// Generate a simple machine fingerprint
|
|
275
|
+
const os = require('os');
|
|
276
|
+
const crypto = require('crypto');
|
|
277
|
+
const machineId = crypto
|
|
278
|
+
.createHash('sha256')
|
|
279
|
+
.update(`${os.hostname()}-${os.platform()}-${os.arch()}`)
|
|
280
|
+
.digest('hex')
|
|
281
|
+
.substring(0, 32);
|
|
282
|
+
|
|
283
|
+
// Read aios-core version
|
|
284
|
+
let aiosCoreVersion = 'unknown';
|
|
285
|
+
try {
|
|
286
|
+
const path = require('path');
|
|
287
|
+
const fs = require('fs');
|
|
288
|
+
const pkgPath = path.join(__dirname, '..', '..', '..', '..', 'package.json');
|
|
289
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
|
|
290
|
+
aiosCoreVersion = pkg.version || 'unknown';
|
|
291
|
+
} catch {
|
|
292
|
+
// Keep 'unknown'
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
const activationResult = await client.activate(key, machineId, aiosCoreVersion);
|
|
296
|
+
|
|
297
|
+
return { success: true, data: activationResult };
|
|
298
|
+
} catch (error) {
|
|
299
|
+
// Handle specific error codes from license-api
|
|
300
|
+
if (error.code === 'INVALID_KEY') {
|
|
301
|
+
return { success: false, error: 'Invalid license key.' };
|
|
302
|
+
}
|
|
303
|
+
if (error.code === 'EXPIRED_KEY') {
|
|
304
|
+
return { success: false, error: 'License key has expired.' };
|
|
305
|
+
}
|
|
306
|
+
if (error.code === 'SEAT_LIMIT_EXCEEDED') {
|
|
307
|
+
return { success: false, error: 'Maximum activations reached for this key.' };
|
|
308
|
+
}
|
|
309
|
+
if (error.code === 'RATE_LIMITED') {
|
|
310
|
+
return { success: false, error: 'Too many requests. Please wait and try again.' };
|
|
311
|
+
}
|
|
312
|
+
if (error.code === 'NETWORK_ERROR') {
|
|
313
|
+
return {
|
|
314
|
+
success: false,
|
|
315
|
+
error: 'License server is unreachable. Check your internet connection and try again.',
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
return {
|
|
320
|
+
success: false,
|
|
321
|
+
error: `License validation failed: ${error.message || 'Unknown error'}`,
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
* Step 2: Install/Scaffold — copy pro content into the project.
|
|
328
|
+
*
|
|
329
|
+
* @param {string} targetDir - Project root directory
|
|
330
|
+
* @param {Object} [options={}] - Options
|
|
331
|
+
* @returns {Promise<Object>} Result with { success, scaffoldResult }
|
|
332
|
+
*/
|
|
333
|
+
async function stepInstallScaffold(targetDir, options = {}) {
|
|
334
|
+
showStep(2, 3, 'Pro Content Installation');
|
|
335
|
+
|
|
336
|
+
const scaffolderModule = loadProScaffolder();
|
|
337
|
+
|
|
338
|
+
if (!scaffolderModule) {
|
|
339
|
+
showWarning('Pro scaffolder not available. Ensure @aios-fullstack/pro is installed.');
|
|
340
|
+
return { success: false, error: 'Pro scaffolder module not found.' };
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
const { scaffoldProContent } = scaffolderModule;
|
|
344
|
+
const path = require('path');
|
|
345
|
+
|
|
346
|
+
// Determine pro source directory
|
|
347
|
+
const proSourceDir = path.join(targetDir, 'node_modules', '@aios-fullstack', 'pro');
|
|
348
|
+
|
|
349
|
+
const spinner = createSpinner('Scaffolding pro content...');
|
|
350
|
+
spinner.start();
|
|
351
|
+
|
|
352
|
+
try {
|
|
353
|
+
const scaffoldResult = await scaffoldProContent(targetDir, proSourceDir, {
|
|
354
|
+
onProgress: (progress) => {
|
|
355
|
+
spinner.text = `Scaffolding: ${progress.message}`;
|
|
356
|
+
},
|
|
357
|
+
force: options.force || false,
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
if (scaffoldResult.success) {
|
|
361
|
+
spinner.succeed(`Pro content installed (${scaffoldResult.copiedFiles.length} files)`);
|
|
362
|
+
|
|
363
|
+
if (scaffoldResult.warnings.length > 0) {
|
|
364
|
+
for (const warning of scaffoldResult.warnings) {
|
|
365
|
+
showWarning(warning);
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
return { success: true, scaffoldResult };
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
spinner.fail('Scaffolding failed');
|
|
373
|
+
for (const error of scaffoldResult.errors) {
|
|
374
|
+
showError(error);
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
return { success: false, error: scaffoldResult.errors.join('; '), scaffoldResult };
|
|
378
|
+
} catch (error) {
|
|
379
|
+
spinner.fail(`Scaffolding error: ${error.message}`);
|
|
380
|
+
return { success: false, error: error.message };
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
/**
|
|
385
|
+
* Step 3: Verify — check installed pro content and list features.
|
|
386
|
+
*
|
|
387
|
+
* @param {Object} [scaffoldResult] - Result from step 2
|
|
388
|
+
* @returns {Promise<Object>} Verification result
|
|
389
|
+
*/
|
|
390
|
+
async function stepVerify(scaffoldResult) {
|
|
391
|
+
showStep(3, 3, 'Verification');
|
|
392
|
+
|
|
393
|
+
const result = {
|
|
394
|
+
success: true,
|
|
395
|
+
features: [],
|
|
396
|
+
squads: [],
|
|
397
|
+
configs: [],
|
|
398
|
+
};
|
|
399
|
+
|
|
400
|
+
// Show scaffolded content summary
|
|
401
|
+
if (scaffoldResult && scaffoldResult.copiedFiles) {
|
|
402
|
+
const files = scaffoldResult.copiedFiles;
|
|
403
|
+
|
|
404
|
+
// Categorize files
|
|
405
|
+
result.squads = files.filter((f) => f.startsWith('squads/'));
|
|
406
|
+
result.configs = files.filter(
|
|
407
|
+
(f) => f.endsWith('.yaml') || f.endsWith('.json'),
|
|
408
|
+
);
|
|
409
|
+
|
|
410
|
+
showInfo(`Files installed: ${files.length}`);
|
|
411
|
+
|
|
412
|
+
if (result.squads.length > 0) {
|
|
413
|
+
// Extract unique squad names
|
|
414
|
+
const squadNames = [...new Set(
|
|
415
|
+
result.squads
|
|
416
|
+
.map((f) => f.split('/')[1])
|
|
417
|
+
.filter(Boolean),
|
|
418
|
+
)];
|
|
419
|
+
showSuccess(`Squads: ${squadNames.join(', ')}`);
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
if (result.configs.length > 0) {
|
|
423
|
+
showSuccess(`Configs: ${result.configs.length} files`);
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
// Check feature gate if available
|
|
428
|
+
const featureModule = loadFeatureGate();
|
|
429
|
+
|
|
430
|
+
if (featureModule) {
|
|
431
|
+
const { featureGate } = featureModule;
|
|
432
|
+
featureGate.reload();
|
|
433
|
+
|
|
434
|
+
const available = featureGate.listAvailable();
|
|
435
|
+
result.features = available;
|
|
436
|
+
|
|
437
|
+
if (available.length > 0) {
|
|
438
|
+
showSuccess(`Features unlocked: ${available.length}`);
|
|
439
|
+
for (const feature of available.slice(0, 5)) {
|
|
440
|
+
console.log(colors.dim(` ${feature}`));
|
|
441
|
+
}
|
|
442
|
+
if (available.length > 5) {
|
|
443
|
+
console.log(colors.dim(` ... and ${available.length - 5} more`));
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
// Final status
|
|
449
|
+
console.log('');
|
|
450
|
+
console.log(gold(' ════════════════════════════════════════════════'));
|
|
451
|
+
console.log(status.celebrate('AIOS Pro installation complete!'));
|
|
452
|
+
console.log(gold(' ════════════════════════════════════════════════'));
|
|
453
|
+
console.log('');
|
|
454
|
+
|
|
455
|
+
return result;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
/**
|
|
459
|
+
* Run the full Pro Installation Wizard.
|
|
460
|
+
*
|
|
461
|
+
* Main entry point. Orchestrates the 3-step flow:
|
|
462
|
+
* 1. License Gate (validate key)
|
|
463
|
+
* 2. Install/Scaffold (copy pro content)
|
|
464
|
+
* 3. Verify (list installed features)
|
|
465
|
+
*
|
|
466
|
+
* @param {Object} [options={}] - Wizard options
|
|
467
|
+
* @param {string} [options.key] - Pre-provided license key
|
|
468
|
+
* @param {string} [options.targetDir] - Project root (default: process.cwd())
|
|
469
|
+
* @param {boolean} [options.force] - Force overwrite existing content
|
|
470
|
+
* @param {boolean} [options.quiet] - Suppress non-essential output
|
|
471
|
+
* @returns {Promise<Object>} Wizard result
|
|
472
|
+
*/
|
|
473
|
+
async function runProWizard(options = {}) {
|
|
474
|
+
const targetDir = options.targetDir || process.cwd();
|
|
475
|
+
const isCI = isCIEnvironment();
|
|
476
|
+
|
|
477
|
+
const result = {
|
|
478
|
+
success: false,
|
|
479
|
+
licenseValidated: false,
|
|
480
|
+
scaffolded: false,
|
|
481
|
+
verified: false,
|
|
482
|
+
};
|
|
483
|
+
|
|
484
|
+
// Show branding (skip in CI or quiet mode)
|
|
485
|
+
if (!isCI && !options.quiet) {
|
|
486
|
+
showProHeader();
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
// Step 1: License Gate
|
|
490
|
+
const licenseResult = await stepLicenseGate({
|
|
491
|
+
key: options.key || process.env.AIOS_PRO_KEY,
|
|
492
|
+
});
|
|
493
|
+
|
|
494
|
+
if (!licenseResult.success) {
|
|
495
|
+
showError(licenseResult.error);
|
|
496
|
+
|
|
497
|
+
if (!isCI) {
|
|
498
|
+
showInfo('Need help? Run: npx aios-pro recover');
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
result.error = licenseResult.error;
|
|
502
|
+
return result;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
result.licenseValidated = true;
|
|
506
|
+
|
|
507
|
+
// Step 2: Install/Scaffold
|
|
508
|
+
const scaffoldResult = await stepInstallScaffold(targetDir, {
|
|
509
|
+
force: options.force,
|
|
510
|
+
});
|
|
511
|
+
|
|
512
|
+
if (!scaffoldResult.success) {
|
|
513
|
+
result.error = scaffoldResult.error;
|
|
514
|
+
return result;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
result.scaffolded = true;
|
|
518
|
+
|
|
519
|
+
// Step 3: Verify
|
|
520
|
+
const verifyResult = await stepVerify(scaffoldResult.scaffoldResult);
|
|
521
|
+
result.verified = verifyResult.success;
|
|
522
|
+
result.features = verifyResult.features;
|
|
523
|
+
result.squads = verifyResult.squads;
|
|
524
|
+
result.success = true;
|
|
525
|
+
|
|
526
|
+
return result;
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
module.exports = {
|
|
530
|
+
runProWizard,
|
|
531
|
+
stepLicenseGate,
|
|
532
|
+
stepInstallScaffold,
|
|
533
|
+
stepVerify,
|
|
534
|
+
maskLicenseKey,
|
|
535
|
+
validateKeyFormat,
|
|
536
|
+
isCIEnvironment,
|
|
537
|
+
showProHeader,
|
|
538
|
+
// Internal helpers exported for testing
|
|
539
|
+
_testing: {
|
|
540
|
+
validateKeyWithApi,
|
|
541
|
+
loadLicenseApi,
|
|
542
|
+
loadFeatureGate,
|
|
543
|
+
loadProScaffolder,
|
|
544
|
+
MAX_RETRIES,
|
|
545
|
+
LICENSE_KEY_PATTERN,
|
|
546
|
+
},
|
|
547
|
+
};
|
|
@@ -186,26 +186,30 @@ function getEnvironmentQuestions() {
|
|
|
186
186
|
}
|
|
187
187
|
|
|
188
188
|
/**
|
|
189
|
-
* Get
|
|
189
|
+
* Get Squad selection questions
|
|
190
190
|
*
|
|
191
|
-
* Available
|
|
192
|
-
* -
|
|
191
|
+
* Available squads for v4.0:
|
|
192
|
+
* - squad-creator: Tools to create custom squads
|
|
193
193
|
* - etl: ETL pipeline for knowledge base creation
|
|
194
194
|
*
|
|
195
|
+
* Note: This function is currently DISABLED. Squad selection is handled
|
|
196
|
+
* directly in aios-init.js using the squads/ directory.
|
|
197
|
+
*
|
|
195
198
|
* @returns {Object[]} Array of inquirer question objects
|
|
199
|
+
* @deprecated Use squads/ directory directly in aios-init.js
|
|
196
200
|
*/
|
|
197
|
-
function
|
|
201
|
+
function getSquadQuestions() {
|
|
198
202
|
return [
|
|
199
203
|
{
|
|
200
204
|
type: 'checkbox',
|
|
201
|
-
name: '
|
|
202
|
-
message: colors.primary('Select
|
|
205
|
+
name: 'selectedSquads',
|
|
206
|
+
message: colors.primary('Select Squads to install (optional):'),
|
|
203
207
|
choices: [
|
|
204
208
|
{
|
|
205
209
|
name:
|
|
206
|
-
colors.highlight('
|
|
207
|
-
colors.dim(' - Tools to create custom
|
|
208
|
-
value: '
|
|
210
|
+
colors.highlight('squad-creator') +
|
|
211
|
+
colors.dim(' - Tools to create custom squads'),
|
|
212
|
+
value: 'squad-creator',
|
|
209
213
|
checked: false,
|
|
210
214
|
},
|
|
211
215
|
{
|
|
@@ -215,7 +219,7 @@ function getExpansionPackQuestions() {
|
|
|
215
219
|
},
|
|
216
220
|
],
|
|
217
221
|
validate: () => {
|
|
218
|
-
// Allow empty selection (user can skip
|
|
222
|
+
// Allow empty selection (user can skip squad installation)
|
|
219
223
|
return true;
|
|
220
224
|
},
|
|
221
225
|
},
|
|
@@ -277,9 +281,9 @@ function buildQuestionSequence(_context = {}) {
|
|
|
277
281
|
// TODO: Remove entirely in future version - each project has unique MCP needs
|
|
278
282
|
// questions.push(...getMCPQuestions());
|
|
279
283
|
|
|
280
|
-
//
|
|
281
|
-
// TODO:
|
|
282
|
-
// questions.push(...
|
|
284
|
+
// Squad Selection - DISABLED: Handled directly in aios-init.js
|
|
285
|
+
// TODO: Consider removing getSquadQuestions() entirely in future version
|
|
286
|
+
// questions.push(...getSquadQuestions());
|
|
283
287
|
|
|
284
288
|
// Tech Preset Selection
|
|
285
289
|
questions.push(...getTechPresetQuestion());
|
|
@@ -320,7 +324,9 @@ module.exports = {
|
|
|
320
324
|
getProjectTypeQuestion,
|
|
321
325
|
getIDEQuestions,
|
|
322
326
|
getMCPQuestions,
|
|
323
|
-
|
|
327
|
+
getSquadQuestions,
|
|
328
|
+
// Backward compat alias (deprecated)
|
|
329
|
+
getExpansionPackQuestions: getSquadQuestions,
|
|
324
330
|
getTechPresetQuestion,
|
|
325
331
|
getEnvironmentQuestions,
|
|
326
332
|
getPackageManagerQuestion,
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* OWASP-compliant validators for all wizard inputs
|
|
5
5
|
* Protects against: command injection, path traversal, XSS, buffer overflow
|
|
6
6
|
*
|
|
7
|
-
* @see docs/stories/
|
|
7
|
+
* @see docs/stories/v4.0.4/sprint-1/story-1.2-interactive-wizard-foundation.md
|
|
8
8
|
* @module wizard/validators
|
|
9
9
|
*/
|
|
10
10
|
|