aios-core 3.11.0 → 3.11.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/.aios-core/cli/commands/validate/index.js +1 -1
- package/.aios-core/install-manifest.yaml +4 -4
- package/bin/aios-init.js +12 -1
- package/bin/aios-minimal.js +33 -17
- package/bin/aios.js +237 -27
- package/package.json +1 -1
- package/packages/installer/src/installer/aios-core-installer.js +81 -1
- package/packages/installer/src/updater/index.js +812 -0
- package/packages/installer/src/wizard/index.js +4 -4
|
@@ -35,7 +35,7 @@ const ExitCode = {
|
|
|
35
35
|
};
|
|
36
36
|
|
|
37
37
|
// Resolve validator module path
|
|
38
|
-
const validatorPath = path.resolve(__dirname, '../../../../src/installer/post-install-validator');
|
|
38
|
+
const validatorPath = path.resolve(__dirname, '../../../../packages/installer/src/installer/post-install-validator');
|
|
39
39
|
let PostInstallValidator, formatReport;
|
|
40
40
|
|
|
41
41
|
let validatorLoadError = null;
|
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
# - SHA256 hashes for change detection
|
|
8
8
|
# - File types for categorization
|
|
9
9
|
#
|
|
10
|
-
version: 3.11.
|
|
11
|
-
generated_at: "2026-02-
|
|
10
|
+
version: 3.11.2
|
|
11
|
+
generated_at: "2026-02-03T14:51:52.983Z"
|
|
12
12
|
generator: scripts/generate-install-manifest.js
|
|
13
13
|
file_count: 839
|
|
14
14
|
files:
|
|
@@ -109,9 +109,9 @@ files:
|
|
|
109
109
|
type: cli
|
|
110
110
|
size: 5320
|
|
111
111
|
- path: cli/commands/validate/index.js
|
|
112
|
-
hash: sha256:
|
|
112
|
+
hash: sha256:dece3a1a8d3ed4ef7dbd8f1ac840c6ec69fb9c759fc43d5813a71ac59479d3a0
|
|
113
113
|
type: cli
|
|
114
|
-
size:
|
|
114
|
+
size: 12901
|
|
115
115
|
- path: cli/commands/workers/formatters/info-formatter.js
|
|
116
116
|
hash: sha256:6f0d25f4033828616656178c55e50d1eeec9457279807c1b06e111d9dd79c53e
|
|
117
117
|
type: cli
|
package/bin/aios-init.js
CHANGED
|
@@ -1,10 +1,21 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* AIOS-FullStack Installation Wizard v5
|
|
4
|
+
* AIOS-FullStack Installation Wizard v5 (LEGACY)
|
|
5
5
|
* Based on the original beautiful visual design with ASCII art
|
|
6
6
|
* Version: 2.1.0
|
|
7
7
|
*
|
|
8
|
+
* ⚠️ DEPRECATION NOTICE (v3.11.2):
|
|
9
|
+
* This file is the LEGACY installer and will be removed in v4.0.0.
|
|
10
|
+
* The new modular wizard is located at: packages/installer/src/wizard/index.js
|
|
11
|
+
*
|
|
12
|
+
* This file is kept as a fallback for edge cases where the new wizard
|
|
13
|
+
* is not available. All new development should use the new wizard.
|
|
14
|
+
*
|
|
15
|
+
* Migration path:
|
|
16
|
+
* - Use `npx aios-core` which routes through bin/aios.js to the new wizard
|
|
17
|
+
* - Do NOT call this file directly
|
|
18
|
+
*
|
|
8
19
|
* Supported IDEs (8 total):
|
|
9
20
|
* - Claude Code, Cursor, Windsurf, Trae, Roo Code, Cline, Gemini CLI, GitHub Copilot
|
|
10
21
|
*/
|
package/bin/aios-minimal.js
CHANGED
|
@@ -2,25 +2,41 @@
|
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* AIOS-FullStack Minimal Installation
|
|
5
|
-
* Wrapper that launches aios-init.js in minimal mode
|
|
6
5
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
6
|
+
* DEPRECATION NOTICE (v3.11.1):
|
|
7
|
+
* The --minimal mode was designed for expansion-packs which have been
|
|
8
|
+
* replaced by the Squads system (OSR-8). This command now runs the
|
|
9
|
+
* standard wizard through the main router.
|
|
10
|
+
*
|
|
11
|
+
* This file is kept for backwards compatibility but will be removed
|
|
12
|
+
* in a future major version.
|
|
9
13
|
*/
|
|
10
14
|
|
|
11
|
-
const {
|
|
15
|
+
const { spawn } = require('child_process');
|
|
12
16
|
const path = require('path');
|
|
13
17
|
|
|
14
|
-
//
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
18
|
+
// Show deprecation warning
|
|
19
|
+
console.log('\n⚠️ DEPRECATION WARNING: aios-minimal is deprecated.');
|
|
20
|
+
console.log(' The --minimal mode (expansion-packs) was replaced by Squads.');
|
|
21
|
+
console.log(' Running standard installation wizard instead.\n');
|
|
22
|
+
|
|
23
|
+
// Get the path to the main router (aios.js)
|
|
24
|
+
const routerPath = path.join(__dirname, 'aios.js');
|
|
25
|
+
|
|
26
|
+
// Forward all arguments to the main router
|
|
27
|
+
const args = process.argv.slice(2);
|
|
28
|
+
|
|
29
|
+
// Spawn the main router
|
|
30
|
+
const child = spawn('node', [routerPath, ...args], {
|
|
31
|
+
stdio: 'inherit',
|
|
32
|
+
cwd: process.cwd(),
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
child.on('close', (code) => {
|
|
36
|
+
process.exit(code || 0);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
child.on('error', (error) => {
|
|
40
|
+
console.error('❌ Failed to start AIOS:', error.message);
|
|
41
|
+
process.exit(1);
|
|
42
|
+
});
|
package/bin/aios.js
CHANGED
|
@@ -20,8 +20,8 @@ const command = args[0];
|
|
|
20
20
|
|
|
21
21
|
// Helper: Run initialization wizard
|
|
22
22
|
async function runWizard() {
|
|
23
|
-
// Use the new v2.1 wizard from src/wizard/index.js
|
|
24
|
-
const wizardPath = path.join(__dirname, '..', 'src', 'wizard', 'index.js');
|
|
23
|
+
// Use the new v2.1 wizard from packages/installer/src/wizard/index.js
|
|
24
|
+
const wizardPath = path.join(__dirname, '..', 'packages', 'installer', 'src', 'wizard', 'index.js');
|
|
25
25
|
|
|
26
26
|
if (!fs.existsSync(wizardPath)) {
|
|
27
27
|
// Fallback to legacy wizard if new wizard not found
|
|
@@ -56,12 +56,21 @@ USAGE:
|
|
|
56
56
|
npx @synkra/aios-core@latest # Run installation wizard
|
|
57
57
|
npx @synkra/aios-core@latest install # Install in current project
|
|
58
58
|
npx @synkra/aios-core@latest init <name> # Create new project
|
|
59
|
+
npx @synkra/aios-core@latest update # Update to latest version
|
|
59
60
|
npx @synkra/aios-core@latest validate # Validate installation integrity
|
|
60
61
|
npx @synkra/aios-core@latest info # Show system info
|
|
61
62
|
npx @synkra/aios-core@latest doctor # Run diagnostics
|
|
62
63
|
npx @synkra/aios-core@latest --version # Show version
|
|
64
|
+
npx @synkra/aios-core@latest --version -d # Show detailed version info
|
|
63
65
|
npx @synkra/aios-core@latest --help # Show this help
|
|
64
66
|
|
|
67
|
+
UPDATE:
|
|
68
|
+
aios update # Update to latest version
|
|
69
|
+
aios update --check # Check for updates without applying
|
|
70
|
+
aios update --dry-run # Preview what would be updated
|
|
71
|
+
aios update --force # Force update even if up-to-date
|
|
72
|
+
aios update --verbose # Show detailed output
|
|
73
|
+
|
|
65
74
|
VALIDATION:
|
|
66
75
|
aios validate # Validate installation integrity
|
|
67
76
|
aios validate --repair # Repair missing/corrupted files
|
|
@@ -92,8 +101,64 @@ For more information, visit: https://github.com/SynkraAI/aios-core
|
|
|
92
101
|
}
|
|
93
102
|
|
|
94
103
|
// Helper: Show version
|
|
95
|
-
function showVersion() {
|
|
96
|
-
|
|
104
|
+
async function showVersion() {
|
|
105
|
+
const isDetailed = args.includes('--detailed') || args.includes('-d');
|
|
106
|
+
|
|
107
|
+
if (!isDetailed) {
|
|
108
|
+
// Simple version output (backwards compatible)
|
|
109
|
+
console.log(packageJson.version);
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Detailed version output (Story 7.2: Version Tracking)
|
|
114
|
+
console.log(`AIOS-FullStack v${packageJson.version}`);
|
|
115
|
+
console.log('Package: @synkra/aios-core');
|
|
116
|
+
|
|
117
|
+
// Check for local installation
|
|
118
|
+
const localVersionPath = path.join(process.cwd(), '.aios-core', 'version.json');
|
|
119
|
+
|
|
120
|
+
if (fs.existsSync(localVersionPath)) {
|
|
121
|
+
try {
|
|
122
|
+
const versionInfo = JSON.parse(fs.readFileSync(localVersionPath, 'utf8'));
|
|
123
|
+
console.log('\n📦 Local Installation:');
|
|
124
|
+
console.log(` Version: ${versionInfo.version}`);
|
|
125
|
+
console.log(` Mode: ${versionInfo.mode || 'unknown'}`);
|
|
126
|
+
|
|
127
|
+
if (versionInfo.installedAt) {
|
|
128
|
+
const installedDate = new Date(versionInfo.installedAt);
|
|
129
|
+
console.log(` Installed: ${installedDate.toLocaleDateString()}`);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (versionInfo.updatedAt) {
|
|
133
|
+
const updatedDate = new Date(versionInfo.updatedAt);
|
|
134
|
+
console.log(` Updated: ${updatedDate.toLocaleDateString()}`);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (versionInfo.fileHashes) {
|
|
138
|
+
const fileCount = Object.keys(versionInfo.fileHashes).length;
|
|
139
|
+
console.log(` Files: ${fileCount} tracked`);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
if (versionInfo.customized && versionInfo.customized.length > 0) {
|
|
143
|
+
console.log(` Customized: ${versionInfo.customized.length} files`);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Version comparison
|
|
147
|
+
if (versionInfo.version !== packageJson.version) {
|
|
148
|
+
console.log('\n⚠️ Version mismatch!');
|
|
149
|
+
console.log(` Local: ${versionInfo.version}`);
|
|
150
|
+
console.log(` Latest: ${packageJson.version}`);
|
|
151
|
+
console.log(' Run \'npx aios-core update\' to update.');
|
|
152
|
+
} else {
|
|
153
|
+
console.log('\n✅ Up to date');
|
|
154
|
+
}
|
|
155
|
+
} catch (error) {
|
|
156
|
+
console.log(`\n⚠️ Could not read version.json: ${error.message}`);
|
|
157
|
+
}
|
|
158
|
+
} else {
|
|
159
|
+
console.log('\n📭 No local installation found');
|
|
160
|
+
console.log(' Run \'npx aios-core install\' to install AIOS in this project.');
|
|
161
|
+
}
|
|
97
162
|
}
|
|
98
163
|
|
|
99
164
|
// Helper: Show system info
|
|
@@ -138,8 +203,8 @@ async function runValidate() {
|
|
|
138
203
|
const { createValidateCommand } = require('../.aios-core/cli/commands/validate/index.js');
|
|
139
204
|
const validateCmd = createValidateCommand();
|
|
140
205
|
|
|
141
|
-
// Parse and execute
|
|
142
|
-
await validateCmd.parseAsync(['node', 'aios',
|
|
206
|
+
// Parse and execute (Note: don't include 'validate' as it's the command name, not an argument)
|
|
207
|
+
await validateCmd.parseAsync(['node', 'aios', ...validateArgs]);
|
|
143
208
|
} catch (_error) {
|
|
144
209
|
// Fallback: Run quick validation inline
|
|
145
210
|
console.log('Running installation validation...\n');
|
|
@@ -177,6 +242,67 @@ async function runValidate() {
|
|
|
177
242
|
}
|
|
178
243
|
}
|
|
179
244
|
|
|
245
|
+
// Helper: Run update command
|
|
246
|
+
async function runUpdate() {
|
|
247
|
+
const updateArgs = args.slice(1);
|
|
248
|
+
const isCheck = updateArgs.includes('--check');
|
|
249
|
+
const isDryRun = updateArgs.includes('--dry-run');
|
|
250
|
+
const isForce = updateArgs.includes('--force');
|
|
251
|
+
const isVerbose = updateArgs.includes('--verbose') || updateArgs.includes('-v');
|
|
252
|
+
|
|
253
|
+
try {
|
|
254
|
+
const updaterPath = path.join(__dirname, '..', 'packages', 'installer', 'src', 'updater', 'index.js');
|
|
255
|
+
|
|
256
|
+
if (!fs.existsSync(updaterPath)) {
|
|
257
|
+
console.error('❌ Updater module not found');
|
|
258
|
+
console.error('Please ensure AIOS-FullStack is installed correctly.');
|
|
259
|
+
process.exit(1);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
const { AIOSUpdater, formatCheckResult, formatUpdateResult } = require(updaterPath);
|
|
263
|
+
|
|
264
|
+
const updater = new AIOSUpdater(process.cwd(), {
|
|
265
|
+
verbose: isVerbose,
|
|
266
|
+
force: isForce,
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
if (isCheck) {
|
|
270
|
+
// Check only mode
|
|
271
|
+
console.log('🔍 Checking for updates...\n');
|
|
272
|
+
const result = await updater.checkForUpdates();
|
|
273
|
+
console.log(formatCheckResult(result, { colors: true }));
|
|
274
|
+
|
|
275
|
+
if (result.status === 'check_failed') {
|
|
276
|
+
process.exit(1);
|
|
277
|
+
}
|
|
278
|
+
} else {
|
|
279
|
+
// Update mode
|
|
280
|
+
console.log('🔄 AIOS Update\n');
|
|
281
|
+
|
|
282
|
+
const result = await updater.update({
|
|
283
|
+
dryRun: isDryRun,
|
|
284
|
+
onProgress: (phase, message) => {
|
|
285
|
+
if (isVerbose) {
|
|
286
|
+
console.log(`[${phase}] ${message}`);
|
|
287
|
+
}
|
|
288
|
+
},
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
console.log(formatUpdateResult(result, { colors: true }));
|
|
292
|
+
|
|
293
|
+
if (!result.success && result.error !== 'Already up to date') {
|
|
294
|
+
process.exit(1);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
} catch (error) {
|
|
298
|
+
console.error(`❌ Update error: ${error.message}`);
|
|
299
|
+
if (args.includes('--verbose') || args.includes('-v')) {
|
|
300
|
+
console.error(error.stack);
|
|
301
|
+
}
|
|
302
|
+
process.exit(1);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
180
306
|
// Helper: Run doctor diagnostics
|
|
181
307
|
function runDoctor() {
|
|
182
308
|
console.log('🏥 AIOS System Diagnostics\n');
|
|
@@ -242,46 +368,126 @@ function runDoctor() {
|
|
|
242
368
|
}
|
|
243
369
|
|
|
244
370
|
// Helper: Create new project
|
|
245
|
-
|
|
371
|
+
// Helper: Show init help
|
|
372
|
+
function showInitHelp() {
|
|
373
|
+
console.log(`
|
|
374
|
+
Usage: npx aios-core init <project-name> [options]
|
|
375
|
+
|
|
376
|
+
Create a new AIOS project with the specified name.
|
|
377
|
+
|
|
378
|
+
Options:
|
|
379
|
+
--force Force creation in non-empty directory
|
|
380
|
+
--skip-install Skip npm dependency installation
|
|
381
|
+
--template <name> Use specific template (default: default)
|
|
382
|
+
-t <name> Shorthand for --template
|
|
383
|
+
-h, --help Show this help message
|
|
384
|
+
|
|
385
|
+
Available Templates:
|
|
386
|
+
default Full installation with all agents, tasks, and workflows
|
|
387
|
+
minimal Essential files only (dev agent + basic tasks)
|
|
388
|
+
enterprise Everything + dashboards + team integrations
|
|
389
|
+
|
|
390
|
+
Examples:
|
|
391
|
+
npx aios-core init my-project
|
|
392
|
+
npx aios-core init my-project --template minimal
|
|
393
|
+
npx aios-core init my-project --force --skip-install
|
|
394
|
+
npx aios-core init . --template enterprise
|
|
395
|
+
`);
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
async function initProject() {
|
|
399
|
+
// 1. Parse ALL args after 'init'
|
|
400
|
+
const initArgs = args.slice(1);
|
|
401
|
+
|
|
402
|
+
// 2. Handle --help FIRST (before creating any directories)
|
|
403
|
+
if (initArgs.includes('--help') || initArgs.includes('-h')) {
|
|
404
|
+
showInitHelp();
|
|
405
|
+
return;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
// 3. Parse flags
|
|
409
|
+
const isForce = initArgs.includes('--force');
|
|
410
|
+
const skipInstall = initArgs.includes('--skip-install');
|
|
411
|
+
|
|
412
|
+
// Template with argument
|
|
413
|
+
const templateIndex = initArgs.findIndex((a) => a === '--template' || a === '-t');
|
|
414
|
+
let template = 'default';
|
|
415
|
+
if (templateIndex !== -1) {
|
|
416
|
+
template = initArgs[templateIndex + 1];
|
|
417
|
+
if (!template || template.startsWith('-')) {
|
|
418
|
+
console.error('❌ --template requires a template name');
|
|
419
|
+
console.error('Available templates: default, minimal, enterprise');
|
|
420
|
+
process.exit(1);
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
// Validate template
|
|
425
|
+
const validTemplates = ['default', 'minimal', 'enterprise'];
|
|
426
|
+
if (!validTemplates.includes(template)) {
|
|
427
|
+
console.error(`❌ Unknown template: ${template}`);
|
|
428
|
+
console.error(`Available templates: ${validTemplates.join(', ')}`);
|
|
429
|
+
process.exit(1);
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
// 4. Extract project name (anything that doesn't start with - and isn't a template value)
|
|
433
|
+
const projectName = initArgs.find((arg, i) => {
|
|
434
|
+
if (arg.startsWith('-')) return false;
|
|
435
|
+
// Skip if it's the value after --template
|
|
436
|
+
const prevArg = initArgs[i - 1];
|
|
437
|
+
if (prevArg === '--template' || prevArg === '-t') return false;
|
|
438
|
+
return true;
|
|
439
|
+
});
|
|
440
|
+
|
|
246
441
|
if (!projectName) {
|
|
247
442
|
console.error('❌ Project name is required');
|
|
248
|
-
console.log('\nUsage: npx
|
|
443
|
+
console.log('\nUsage: npx aios-core init <project-name> [options]');
|
|
444
|
+
console.log('Run with --help for more information.');
|
|
249
445
|
process.exit(1);
|
|
250
446
|
}
|
|
251
447
|
|
|
252
|
-
// Handle "." to install in current directory
|
|
448
|
+
// 5. Handle "." to install in current directory
|
|
253
449
|
const isCurrentDir = projectName === '.';
|
|
254
450
|
const targetPath = isCurrentDir ? process.cwd() : path.join(process.cwd(), projectName);
|
|
255
451
|
const displayName = isCurrentDir ? path.basename(process.cwd()) : projectName;
|
|
256
452
|
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
// Check if directory exists
|
|
260
|
-
if (fs.existsSync(targetPath)) {
|
|
261
|
-
// Allow if directory is empty or only has hidden files
|
|
453
|
+
// 6. Check if directory exists
|
|
454
|
+
if (fs.existsSync(targetPath) && !isCurrentDir) {
|
|
262
455
|
const contents = fs.readdirSync(targetPath).filter((f) => !f.startsWith('.'));
|
|
263
|
-
if (contents.length > 0 && !
|
|
456
|
+
if (contents.length > 0 && !isForce) {
|
|
264
457
|
console.error(`❌ Directory already exists and is not empty: ${projectName}`);
|
|
265
|
-
console.
|
|
458
|
+
console.error('Use --force to overwrite.');
|
|
266
459
|
process.exit(1);
|
|
267
460
|
}
|
|
268
|
-
|
|
269
|
-
|
|
461
|
+
if (contents.length > 0 && isForce) {
|
|
462
|
+
console.log(`⚠️ Using --force: overwriting existing directory: ${projectName}`);
|
|
463
|
+
} else {
|
|
270
464
|
console.log(`✓ Using existing empty directory: ${projectName}`);
|
|
271
465
|
}
|
|
272
|
-
} else {
|
|
273
|
-
// Create project directory
|
|
466
|
+
} else if (!fs.existsSync(targetPath)) {
|
|
274
467
|
fs.mkdirSync(targetPath, { recursive: true });
|
|
275
468
|
console.log(`✓ Created directory: ${projectName}`);
|
|
276
469
|
}
|
|
277
470
|
|
|
278
|
-
|
|
471
|
+
console.log(`Creating new AIOS project: ${displayName}`);
|
|
472
|
+
if (template !== 'default') {
|
|
473
|
+
console.log(`Template: ${template}`);
|
|
474
|
+
}
|
|
475
|
+
if (skipInstall) {
|
|
476
|
+
console.log('Skip install: enabled');
|
|
477
|
+
}
|
|
478
|
+
console.log('');
|
|
479
|
+
|
|
480
|
+
// 7. Change to project directory (if not already there)
|
|
279
481
|
if (!isCurrentDir) {
|
|
280
482
|
process.chdir(targetPath);
|
|
281
483
|
}
|
|
282
484
|
|
|
283
|
-
// Run the initialization wizard
|
|
284
|
-
await runWizard(
|
|
485
|
+
// 8. Run the initialization wizard with options
|
|
486
|
+
await runWizard({
|
|
487
|
+
template,
|
|
488
|
+
skipInstall,
|
|
489
|
+
force: isForce,
|
|
490
|
+
});
|
|
285
491
|
}
|
|
286
492
|
|
|
287
493
|
// Command routing (async main function)
|
|
@@ -305,9 +511,8 @@ async function main() {
|
|
|
305
511
|
break;
|
|
306
512
|
|
|
307
513
|
case 'init': {
|
|
308
|
-
// Create new project
|
|
309
|
-
|
|
310
|
-
await initProject(projectName);
|
|
514
|
+
// Create new project (flags parsed inside initProject)
|
|
515
|
+
await initProject();
|
|
311
516
|
break;
|
|
312
517
|
}
|
|
313
518
|
|
|
@@ -324,10 +529,15 @@ async function main() {
|
|
|
324
529
|
await runValidate();
|
|
325
530
|
break;
|
|
326
531
|
|
|
532
|
+
case 'update':
|
|
533
|
+
// Update to latest version - Epic 7
|
|
534
|
+
await runUpdate();
|
|
535
|
+
break;
|
|
536
|
+
|
|
327
537
|
case '--version':
|
|
328
538
|
case '-v':
|
|
329
539
|
case '-V':
|
|
330
|
-
showVersion();
|
|
540
|
+
await showVersion();
|
|
331
541
|
break;
|
|
332
542
|
|
|
333
543
|
case '--help':
|
package/package.json
CHANGED
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
const fs = require('fs-extra');
|
|
12
12
|
const path = require('path');
|
|
13
13
|
const ora = require('ora');
|
|
14
|
+
const { hashFile } = require('./file-hasher');
|
|
14
15
|
|
|
15
16
|
/**
|
|
16
17
|
* Get the path to the source .aios-core directory in the package
|
|
@@ -75,6 +76,72 @@ function replaceRootPlaceholder(content, rootPath = '.aios-core') {
|
|
|
75
76
|
return content.replace(/\{root\}/g, rootPath);
|
|
76
77
|
}
|
|
77
78
|
|
|
79
|
+
/**
|
|
80
|
+
* Generate file hashes for installed files
|
|
81
|
+
* Story 7.2: Version Tracking
|
|
82
|
+
*
|
|
83
|
+
* @param {string} targetAiosCore - Path to .aios-core directory
|
|
84
|
+
* @param {string[]} installedFiles - List of installed files (relative to .aios-core)
|
|
85
|
+
* @returns {Promise<Object>} Object mapping file paths to their sha256 hashes
|
|
86
|
+
*/
|
|
87
|
+
async function generateFileHashes(targetAiosCore, installedFiles) {
|
|
88
|
+
const fileHashes = {};
|
|
89
|
+
|
|
90
|
+
for (const filePath of installedFiles) {
|
|
91
|
+
const absolutePath = path.join(targetAiosCore, filePath);
|
|
92
|
+
|
|
93
|
+
try {
|
|
94
|
+
if (await fs.pathExists(absolutePath)) {
|
|
95
|
+
const stats = await fs.stat(absolutePath);
|
|
96
|
+
if (stats.isFile()) {
|
|
97
|
+
const hash = hashFile(absolutePath);
|
|
98
|
+
fileHashes[filePath] = `sha256:${hash}`;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
} catch (_error) {
|
|
102
|
+
// Skip files that can't be hashed (permissions, etc.)
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return fileHashes;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Generate version.json for installation tracking
|
|
112
|
+
* Story 7.2: Version Tracking - Enables update command to detect changes
|
|
113
|
+
*
|
|
114
|
+
* @param {Object} options - Options
|
|
115
|
+
* @param {string} options.targetAiosCore - Path to .aios-core directory
|
|
116
|
+
* @param {string} options.version - Package version
|
|
117
|
+
* @param {string[]} options.installedFiles - List of installed files
|
|
118
|
+
* @param {string} [options.mode='project-development'] - Installation mode
|
|
119
|
+
* @returns {Promise<Object>} version.json content
|
|
120
|
+
*/
|
|
121
|
+
async function generateVersionJson(options) {
|
|
122
|
+
const {
|
|
123
|
+
targetAiosCore,
|
|
124
|
+
version,
|
|
125
|
+
installedFiles,
|
|
126
|
+
mode = 'project-development',
|
|
127
|
+
} = options;
|
|
128
|
+
|
|
129
|
+
const fileHashes = await generateFileHashes(targetAiosCore, installedFiles);
|
|
130
|
+
|
|
131
|
+
const versionJson = {
|
|
132
|
+
version,
|
|
133
|
+
installedAt: new Date().toISOString(),
|
|
134
|
+
mode,
|
|
135
|
+
fileHashes,
|
|
136
|
+
customized: [],
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
const versionJsonPath = path.join(targetAiosCore, 'version.json');
|
|
140
|
+
await fs.writeJson(versionJsonPath, versionJson, { spaces: 2 });
|
|
141
|
+
|
|
142
|
+
return versionJson;
|
|
143
|
+
}
|
|
144
|
+
|
|
78
145
|
/**
|
|
79
146
|
* Copy a single file with optional {root} replacement
|
|
80
147
|
* @param {string} sourcePath - Source file path
|
|
@@ -226,8 +293,9 @@ async function installAiosCore(options = {}) {
|
|
|
226
293
|
|
|
227
294
|
// Create install manifest
|
|
228
295
|
spinner.text = 'Creating installation manifest...';
|
|
296
|
+
const packageVersion = require('../../../../package.json').version;
|
|
229
297
|
const manifest = {
|
|
230
|
-
version:
|
|
298
|
+
version: packageVersion,
|
|
231
299
|
installed_at: new Date().toISOString(),
|
|
232
300
|
install_type: 'full',
|
|
233
301
|
files: result.installedFiles,
|
|
@@ -239,6 +307,16 @@ async function installAiosCore(options = {}) {
|
|
|
239
307
|
'utf8',
|
|
240
308
|
);
|
|
241
309
|
|
|
310
|
+
// Story 7.2: Create version.json with file hashes for update tracking
|
|
311
|
+
spinner.text = 'Generating version tracking info...';
|
|
312
|
+
const versionInfo = await generateVersionJson({
|
|
313
|
+
targetAiosCore,
|
|
314
|
+
version: packageVersion,
|
|
315
|
+
installedFiles: result.installedFiles,
|
|
316
|
+
mode: 'project-development',
|
|
317
|
+
});
|
|
318
|
+
result.versionInfo = versionInfo;
|
|
319
|
+
|
|
242
320
|
result.success = true;
|
|
243
321
|
spinner.succeed(`AIOS core installed (${result.installedFiles.length} files)`);
|
|
244
322
|
|
|
@@ -314,6 +392,8 @@ module.exports = {
|
|
|
314
392
|
getAiosCoreSourcePath,
|
|
315
393
|
copyFileWithRootReplacement,
|
|
316
394
|
copyDirectoryWithRootReplacement,
|
|
395
|
+
generateVersionJson,
|
|
396
|
+
generateFileHashes,
|
|
317
397
|
FOLDERS_TO_COPY,
|
|
318
398
|
ROOT_FILES_TO_COPY,
|
|
319
399
|
};
|