@ornexus/neocortex 3.9.21 → 3.9.23
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/install.ps1 +67 -11
- package/install.sh +89 -10
- package/package.json +6 -3
- package/packages/client/dist/agent/refresh-stubs.js +22 -1
- package/packages/client/dist/commands/activate.js +23 -2
- package/packages/client/dist/commands/invoke.js +46 -22
- package/packages/client/dist/config/secure-config.d.ts +69 -0
- package/packages/client/dist/config/secure-config.js +179 -0
- package/postinstall.js +102 -0
- package/targets-stubs/antigravity/gemini.md +1 -1
- package/targets-stubs/antigravity/skill/SKILL.md +1 -1
- package/targets-stubs/claude-code/neocortex.agent.yaml +1 -1
- package/targets-stubs/claude-code/neocortex.md +2 -2
- package/targets-stubs/codex/agents.md +1 -1
- package/targets-stubs/cursor/agent.md +2 -2
- package/targets-stubs/gemini-cli/agent.md +2 -2
- package/targets-stubs/vscode/agent.md +2 -2
- package/packages/client/dist/adapters/adapter-registry.d.ts.map +0 -1
- package/packages/client/dist/adapters/adapter-registry.js.map +0 -1
- package/packages/client/dist/adapters/antigravity-adapter.d.ts.map +0 -1
- package/packages/client/dist/adapters/antigravity-adapter.js.map +0 -1
- package/packages/client/dist/adapters/claude-code-adapter.d.ts.map +0 -1
- package/packages/client/dist/adapters/claude-code-adapter.js.map +0 -1
- package/packages/client/dist/adapters/codex-adapter.d.ts.map +0 -1
- package/packages/client/dist/adapters/codex-adapter.js.map +0 -1
- package/packages/client/dist/adapters/cursor-adapter.d.ts.map +0 -1
- package/packages/client/dist/adapters/cursor-adapter.js.map +0 -1
- package/packages/client/dist/adapters/gemini-adapter.d.ts.map +0 -1
- package/packages/client/dist/adapters/gemini-adapter.js.map +0 -1
- package/packages/client/dist/adapters/index.d.ts.map +0 -1
- package/packages/client/dist/adapters/index.js.map +0 -1
- package/packages/client/dist/adapters/platform-detector.d.ts.map +0 -1
- package/packages/client/dist/adapters/platform-detector.js.map +0 -1
- package/packages/client/dist/adapters/target-adapter.d.ts.map +0 -1
- package/packages/client/dist/adapters/target-adapter.js.map +0 -1
- package/packages/client/dist/adapters/vscode-adapter.d.ts.map +0 -1
- package/packages/client/dist/adapters/vscode-adapter.js.map +0 -1
- package/packages/client/dist/agent/refresh-stubs.d.ts.map +0 -1
- package/packages/client/dist/agent/refresh-stubs.js.map +0 -1
- package/packages/client/dist/agent/update-agent-yaml.d.ts.map +0 -1
- package/packages/client/dist/agent/update-agent-yaml.js.map +0 -1
- package/packages/client/dist/agent/update-description.d.ts.map +0 -1
- package/packages/client/dist/agent/update-description.js.map +0 -1
- package/packages/client/dist/cache/crypto-utils.d.ts.map +0 -1
- package/packages/client/dist/cache/crypto-utils.js.map +0 -1
- package/packages/client/dist/cache/encrypted-cache.d.ts.map +0 -1
- package/packages/client/dist/cache/encrypted-cache.js.map +0 -1
- package/packages/client/dist/cache/index.d.ts.map +0 -1
- package/packages/client/dist/cache/index.js.map +0 -1
- package/packages/client/dist/cli.d.ts.map +0 -1
- package/packages/client/dist/cli.js.map +0 -1
- package/packages/client/dist/commands/activate.d.ts.map +0 -1
- package/packages/client/dist/commands/activate.js.map +0 -1
- package/packages/client/dist/commands/cache-status.d.ts.map +0 -1
- package/packages/client/dist/commands/cache-status.js.map +0 -1
- package/packages/client/dist/commands/invoke.d.ts.map +0 -1
- package/packages/client/dist/commands/invoke.js.map +0 -1
- package/packages/client/dist/config/resolver-selection.d.ts.map +0 -1
- package/packages/client/dist/config/resolver-selection.js.map +0 -1
- package/packages/client/dist/context/context-collector.d.ts.map +0 -1
- package/packages/client/dist/context/context-collector.js.map +0 -1
- package/packages/client/dist/context/context-sanitizer.d.ts.map +0 -1
- package/packages/client/dist/context/context-sanitizer.js.map +0 -1
- package/packages/client/dist/index.d.ts.map +0 -1
- package/packages/client/dist/index.js.map +0 -1
- package/packages/client/dist/license/index.d.ts.map +0 -1
- package/packages/client/dist/license/index.js.map +0 -1
- package/packages/client/dist/license/license-client.d.ts.map +0 -1
- package/packages/client/dist/license/license-client.js.map +0 -1
- package/packages/client/dist/machine/fingerprint.d.ts.map +0 -1
- package/packages/client/dist/machine/fingerprint.js.map +0 -1
- package/packages/client/dist/machine/index.d.ts.map +0 -1
- package/packages/client/dist/machine/index.js.map +0 -1
- package/packages/client/dist/resilience/circuit-breaker.d.ts.map +0 -1
- package/packages/client/dist/resilience/circuit-breaker.js.map +0 -1
- package/packages/client/dist/resilience/degradation-manager.d.ts.map +0 -1
- package/packages/client/dist/resilience/degradation-manager.js.map +0 -1
- package/packages/client/dist/resilience/freshness-indicator.d.ts.map +0 -1
- package/packages/client/dist/resilience/freshness-indicator.js.map +0 -1
- package/packages/client/dist/resilience/index.d.ts.map +0 -1
- package/packages/client/dist/resilience/index.js.map +0 -1
- package/packages/client/dist/resilience/recovery-detector.d.ts.map +0 -1
- package/packages/client/dist/resilience/recovery-detector.js.map +0 -1
- package/packages/client/dist/resolvers/asset-resolver.d.ts.map +0 -1
- package/packages/client/dist/resolvers/asset-resolver.js.map +0 -1
- package/packages/client/dist/resolvers/local-resolver.d.ts.map +0 -1
- package/packages/client/dist/resolvers/local-resolver.js.map +0 -1
- package/packages/client/dist/resolvers/remote-resolver.d.ts.map +0 -1
- package/packages/client/dist/resolvers/remote-resolver.js.map +0 -1
- package/packages/client/dist/telemetry/index.d.ts.map +0 -1
- package/packages/client/dist/telemetry/index.js.map +0 -1
- package/packages/client/dist/telemetry/offline-queue.d.ts.map +0 -1
- package/packages/client/dist/telemetry/offline-queue.js.map +0 -1
- package/packages/client/dist/tier/index.d.ts.map +0 -1
- package/packages/client/dist/tier/index.js.map +0 -1
- package/packages/client/dist/tier/tier-aware-client.d.ts.map +0 -1
- package/packages/client/dist/tier/tier-aware-client.js.map +0 -1
- package/packages/client/dist/types/index.d.ts.map +0 -1
- package/packages/client/dist/types/index.js.map +0 -1
package/install.ps1
CHANGED
|
@@ -15,7 +15,7 @@ param(
|
|
|
15
15
|
[string]$ServerUrl = "https://api.neocortex.ornexus.com"
|
|
16
16
|
)
|
|
17
17
|
|
|
18
|
-
$VERSION = "3.9.
|
|
18
|
+
$VERSION = "3.9.23"
|
|
19
19
|
|
|
20
20
|
# =============================================================================
|
|
21
21
|
# CONFIGURACOES
|
|
@@ -388,6 +388,20 @@ function Invoke-AutoCleanupLegacy {
|
|
|
388
388
|
# --- Categoria 7: Antigravity ---
|
|
389
389
|
# Configs legadas de Antigravity sao gerenciadas pelo adapter, sem path fixo global
|
|
390
390
|
|
|
391
|
+
# --- Categoria 8: Plaintext cache cleanup (Epic 62 - GAP 1) ---
|
|
392
|
+
$cacheDir = "$env:USERPROFILE\.neocortex\cache"
|
|
393
|
+
if (Test-Path $cacheDir -PathType Container) {
|
|
394
|
+
# Remove plaintext menu-cache.json
|
|
395
|
+
Remove-LegacyItem "$cacheDir\menu-cache.json" "cache plaintext (menu)"
|
|
396
|
+
|
|
397
|
+
# Remove any non-.enc files in cache dir (excluding directories)
|
|
398
|
+
Get-ChildItem -Path $cacheDir -File -ErrorAction SilentlyContinue | Where-Object {
|
|
399
|
+
$_.Extension -ne ".enc"
|
|
400
|
+
} | ForEach-Object {
|
|
401
|
+
Remove-LegacyItem $_.FullName "cache plaintext"
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
|
|
391
405
|
# --- Resultado ---
|
|
392
406
|
$removed = $script:autoRemoved
|
|
393
407
|
if ($removed -gt 0) {
|
|
@@ -586,6 +600,25 @@ function Set-ThinClientConfig {
|
|
|
586
600
|
try {
|
|
587
601
|
$existingConfig = Get-Content $configFile -Raw | ConvertFrom-Json -ErrorAction SilentlyContinue
|
|
588
602
|
if ($existingConfig.mode -eq "active" -or $existingConfig.mode -eq "local" -or $existingConfig.mode -eq "remote") {
|
|
603
|
+
# --- Config schema migration (Epic 62 - GAP 4) ---
|
|
604
|
+
if (-not $existingConfig.configVersion) {
|
|
605
|
+
Write-Dbg "Migrando schema do config.json (adicionando configVersion)"
|
|
606
|
+
try {
|
|
607
|
+
# Add configVersion
|
|
608
|
+
$existingConfig | Add-Member -NotePropertyName "configVersion" -NotePropertyValue 1 -Force
|
|
609
|
+
# Remove known obsolete fields
|
|
610
|
+
$existingConfig.PSObject.Properties.Remove("version")
|
|
611
|
+
$existingConfig.PSObject.Properties.Remove("cache")
|
|
612
|
+
# Clean obsolete tier:3 from old base template
|
|
613
|
+
if ($existingConfig.tier -eq 3 -and $existingConfig.mode -eq "pending-activation") {
|
|
614
|
+
$existingConfig.PSObject.Properties.Remove("tier")
|
|
615
|
+
}
|
|
616
|
+
$existingConfig | ConvertTo-Json -Depth 5 | Out-File -FilePath $configFile -Encoding utf8
|
|
617
|
+
Write-Dbg "Config migrada para configVersion 1"
|
|
618
|
+
} catch {
|
|
619
|
+
Write-Dbg "Falha na migracao do config: $_"
|
|
620
|
+
}
|
|
621
|
+
}
|
|
589
622
|
Write-Dbg "Config existente preservada (mode=$($existingConfig.mode))"
|
|
590
623
|
return
|
|
591
624
|
}
|
|
@@ -593,15 +626,9 @@ function Set-ThinClientConfig {
|
|
|
593
626
|
}
|
|
594
627
|
|
|
595
628
|
$config = @{
|
|
596
|
-
|
|
629
|
+
configVersion = 1
|
|
597
630
|
mode = "pending-activation"
|
|
598
631
|
serverUrl = $NEOCORTEX_SERVER_URL
|
|
599
|
-
tier = 3
|
|
600
|
-
cache = @{
|
|
601
|
-
enabled = $true
|
|
602
|
-
directory = "$configDir\cache"
|
|
603
|
-
encryption = "AES-256-GCM"
|
|
604
|
-
}
|
|
605
632
|
resilience = @{
|
|
606
633
|
circuitBreaker = $true
|
|
607
634
|
maxRetries = 3
|
|
@@ -656,13 +683,42 @@ function Install-Core {
|
|
|
656
683
|
Set-ThinClientConfig
|
|
657
684
|
}
|
|
658
685
|
|
|
659
|
-
#
|
|
686
|
+
# --- Version-aware cache purge on upgrade (Epic 62 - GAP 2+3) ---
|
|
660
687
|
$pkgJsonPath = Join-Path $script:SourceDir "package.json"
|
|
688
|
+
$pkgVersion = $null
|
|
661
689
|
if (Test-Path $pkgJsonPath) {
|
|
662
690
|
$pkgJson = Get-Content $pkgJsonPath -Raw | ConvertFrom-Json -ErrorAction SilentlyContinue
|
|
663
|
-
|
|
664
|
-
|
|
691
|
+
$pkgVersion = $pkgJson.version
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
if ($pkgVersion) {
|
|
695
|
+
$oldVersion = $null
|
|
696
|
+
# Read existing .version from either location
|
|
697
|
+
$versionPaths = @("$script:DestDir\.version", "$env:USERPROFILE\.neocortex\.version")
|
|
698
|
+
foreach ($vp in $versionPaths) {
|
|
699
|
+
if (Test-Path $vp -PathType Leaf) {
|
|
700
|
+
$oldVersion = (Get-Content $vp -Raw -ErrorAction SilentlyContinue).Trim()
|
|
701
|
+
if ($oldVersion) { break }
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
# If version changed, purge all cache files
|
|
706
|
+
if ($oldVersion -and $oldVersion -ne $pkgVersion) {
|
|
707
|
+
$cachePurgeDir = "$env:USERPROFILE\.neocortex\cache"
|
|
708
|
+
if (Test-Path $cachePurgeDir -PathType Container) {
|
|
709
|
+
$purged = 0
|
|
710
|
+
Get-ChildItem -Path $cachePurgeDir -File -ErrorAction SilentlyContinue | ForEach-Object {
|
|
711
|
+
Remove-Item $_.FullName -Force -ErrorAction SilentlyContinue
|
|
712
|
+
$purged++
|
|
713
|
+
}
|
|
714
|
+
if ($purged -gt 0) {
|
|
715
|
+
Write-Info "Cache purgado: versao alterada de $oldVersion para $pkgVersion ($purged arquivo(s))"
|
|
716
|
+
}
|
|
717
|
+
}
|
|
665
718
|
}
|
|
719
|
+
|
|
720
|
+
# Write version file
|
|
721
|
+
$pkgVersion | Out-File -FilePath "$script:DestDir\.version" -Encoding utf8 -NoNewline
|
|
666
722
|
}
|
|
667
723
|
|
|
668
724
|
if ($LOCAL_MODE) {
|
package/install.sh
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
# Development Orchestrator
|
|
5
5
|
|
|
6
6
|
# Versao do instalador
|
|
7
|
-
VERSION="3.9.
|
|
7
|
+
VERSION="3.9.23"
|
|
8
8
|
|
|
9
9
|
# Flags
|
|
10
10
|
MIGRATION_DETECTED=false
|
|
@@ -415,6 +415,22 @@ auto_cleanup_legacy() {
|
|
|
415
415
|
# ─── Categoria 7: Antigravity ─────────────────────────────────────────
|
|
416
416
|
# Configs legadas de Antigravity sao gerenciadas pelo adapter, sem path fixo global
|
|
417
417
|
|
|
418
|
+
# ─── Categoria 8: Plaintext cache cleanup (Epic 62 - GAP 1) ─────────
|
|
419
|
+
local cache_dir="$HOME/.neocortex/cache"
|
|
420
|
+
if [ -d "$cache_dir" ]; then
|
|
421
|
+
# Remove plaintext menu-cache.json
|
|
422
|
+
_remove_legacy "$cache_dir/menu-cache.json" "cache plaintext (menu)"
|
|
423
|
+
|
|
424
|
+
# Remove any non-.enc files in cache dir (excluding directories)
|
|
425
|
+
for cache_file in "$cache_dir"/*; do
|
|
426
|
+
[ -f "$cache_file" ] || continue
|
|
427
|
+
case "$cache_file" in
|
|
428
|
+
*.enc) continue ;; # Keep encrypted cache files
|
|
429
|
+
*) _remove_legacy "$cache_file" "cache plaintext" ;;
|
|
430
|
+
esac
|
|
431
|
+
done
|
|
432
|
+
fi
|
|
433
|
+
|
|
418
434
|
# ─── Resultado ────────────────────────────────────────────────────────
|
|
419
435
|
if [ $removed -gt 0 ]; then
|
|
420
436
|
ok "$removed artefato(s) legado(s) removido(s) automaticamente"
|
|
@@ -569,12 +585,45 @@ setup_thin_client_config() {
|
|
|
569
585
|
mkdir -p "$config_dir" 2>/dev/null
|
|
570
586
|
mkdir -p "$config_dir/cache" 2>/dev/null
|
|
571
587
|
|
|
588
|
+
# Story 61.4 - F4 remediation: restrictive permissions
|
|
589
|
+
chmod 700 "$config_dir" 2>/dev/null
|
|
590
|
+
chmod 700 "$config_dir/cache" 2>/dev/null
|
|
591
|
+
|
|
572
592
|
if [ -f "$config_file" ]; then
|
|
573
593
|
# Preservar config existente, atualizar apenas serverUrl se necessario
|
|
574
594
|
local existing_mode
|
|
575
595
|
existing_mode=$(grep '"mode"' "$config_file" 2>/dev/null | head -1 | sed 's/.*"mode"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/')
|
|
576
596
|
|
|
577
597
|
if [ "$existing_mode" = "active" ] || [ "$existing_mode" = "local" ] || [ "$existing_mode" = "remote" ]; then
|
|
598
|
+
# ─── Config schema migration (Epic 62 - GAP 4) ────────────────
|
|
599
|
+
# Check if config needs schema migration
|
|
600
|
+
local has_config_version
|
|
601
|
+
has_config_version=$(grep '"configVersion"' "$config_file" 2>/dev/null)
|
|
602
|
+
if [ -z "$has_config_version" ]; then
|
|
603
|
+
debug "Migrando schema do config.json (adicionando configVersion)"
|
|
604
|
+
if command -v node >/dev/null 2>&1; then
|
|
605
|
+
node -e "
|
|
606
|
+
const fs = require('fs');
|
|
607
|
+
const path = '$config_file';
|
|
608
|
+
try {
|
|
609
|
+
const cfg = JSON.parse(fs.readFileSync(path, 'utf-8'));
|
|
610
|
+
// Add configVersion
|
|
611
|
+
cfg.configVersion = 1;
|
|
612
|
+
// Remove known obsolete fields from old base template
|
|
613
|
+
delete cfg.version;
|
|
614
|
+
delete cfg.cache;
|
|
615
|
+
// Clean obsolete tier:3 from old base template (preserve real tier values)
|
|
616
|
+
if (cfg.tier === 3 && cfg.mode === 'pending-activation') {
|
|
617
|
+
delete cfg.tier;
|
|
618
|
+
}
|
|
619
|
+
fs.writeFileSync(path, JSON.stringify(cfg, null, 2) + '\n');
|
|
620
|
+
}" 2>/dev/null
|
|
621
|
+
chmod 600 "$config_file" 2>/dev/null
|
|
622
|
+
debug "Config migrada para configVersion 1"
|
|
623
|
+
fi
|
|
624
|
+
fi
|
|
625
|
+
# Story 61.4 - ensure permissions are always enforced even on existing configs
|
|
626
|
+
chmod 600 "$config_file" 2>/dev/null
|
|
578
627
|
debug "Config existente preservada (mode=$existing_mode)"
|
|
579
628
|
return 0
|
|
580
629
|
fi
|
|
@@ -583,15 +632,9 @@ setup_thin_client_config() {
|
|
|
583
632
|
# Criar config base para thin client
|
|
584
633
|
cat > "$config_file" << EOFCONFIG
|
|
585
634
|
{
|
|
586
|
-
"
|
|
635
|
+
"configVersion": 1,
|
|
587
636
|
"mode": "pending-activation",
|
|
588
637
|
"serverUrl": "${NEOCORTEX_SERVER_URL}",
|
|
589
|
-
"tier": 3,
|
|
590
|
-
"cache": {
|
|
591
|
-
"enabled": true,
|
|
592
|
-
"directory": "${config_dir}/cache",
|
|
593
|
-
"encryption": "AES-256-GCM"
|
|
594
|
-
},
|
|
595
638
|
"resilience": {
|
|
596
639
|
"circuitBreaker": true,
|
|
597
640
|
"maxRetries": 3,
|
|
@@ -602,6 +645,9 @@ setup_thin_client_config() {
|
|
|
602
645
|
}
|
|
603
646
|
EOFCONFIG
|
|
604
647
|
|
|
648
|
+
# Story 61.4 - F4 remediation: config file readable only by owner
|
|
649
|
+
chmod 600 "$config_file" 2>/dev/null
|
|
650
|
+
|
|
605
651
|
debug "Thin client config criada: $config_file"
|
|
606
652
|
}
|
|
607
653
|
|
|
@@ -637,12 +683,45 @@ install_core() {
|
|
|
637
683
|
# Thin-client ONLY: zero IP on client, all content from server
|
|
638
684
|
setup_thin_client_config
|
|
639
685
|
|
|
640
|
-
#
|
|
686
|
+
# ─── Version-aware cache purge on upgrade (Epic 62 - GAP 2+3) ───────
|
|
641
687
|
local pkg_version=""
|
|
642
688
|
if [ -f "$SOURCE_DIR/package.json" ]; then
|
|
643
689
|
pkg_version=$(grep '"version"' "$SOURCE_DIR/package.json" 2>/dev/null | head -1 | sed 's/.*"version"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/')
|
|
644
690
|
fi
|
|
645
|
-
|
|
691
|
+
|
|
692
|
+
if [ -n "$pkg_version" ]; then
|
|
693
|
+
local old_version=""
|
|
694
|
+
# Read existing .version from either location
|
|
695
|
+
if [ -f "$DEST_DIR/.version" ]; then
|
|
696
|
+
old_version=$(cat "$DEST_DIR/.version" 2>/dev/null | tr -d '[:space:]')
|
|
697
|
+
elif [ -f "$HOME/.neocortex/.version" ]; then
|
|
698
|
+
old_version=$(cat "$HOME/.neocortex/.version" 2>/dev/null | tr -d '[:space:]')
|
|
699
|
+
fi
|
|
700
|
+
|
|
701
|
+
# If version changed, purge all cache files
|
|
702
|
+
if [ -n "$old_version" ] && [ "$old_version" != "$pkg_version" ]; then
|
|
703
|
+
local cache_dir="$HOME/.neocortex/cache"
|
|
704
|
+
if [ -d "$cache_dir" ]; then
|
|
705
|
+
local purged=0
|
|
706
|
+
# Remove all .enc files
|
|
707
|
+
for enc_file in "$cache_dir"/*.enc; do
|
|
708
|
+
[ -f "$enc_file" ] || continue
|
|
709
|
+
rm -f "$enc_file" 2>/dev/null && purged=$((purged + 1))
|
|
710
|
+
done
|
|
711
|
+
# Remove menu-cache.json (redundancy with 62.1)
|
|
712
|
+
[ -f "$cache_dir/menu-cache.json" ] && rm -f "$cache_dir/menu-cache.json" 2>/dev/null && purged=$((purged + 1))
|
|
713
|
+
# Remove any other non-directory files
|
|
714
|
+
for cache_file in "$cache_dir"/*; do
|
|
715
|
+
[ -f "$cache_file" ] || continue
|
|
716
|
+
rm -f "$cache_file" 2>/dev/null && purged=$((purged + 1))
|
|
717
|
+
done
|
|
718
|
+
[ $purged -gt 0 ] && info "Cache purgado: versao alterada de $old_version para $pkg_version ($purged arquivo(s))"
|
|
719
|
+
fi
|
|
720
|
+
fi
|
|
721
|
+
|
|
722
|
+
# Write version file
|
|
723
|
+
echo "$pkg_version" > "$DEST_DIR/.version"
|
|
724
|
+
fi
|
|
646
725
|
|
|
647
726
|
if [ $errors -eq 0 ]; then
|
|
648
727
|
ok "Core instalado ${DIM}(thin client configurado) [modo remoto]${NC}"
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ornexus/neocortex",
|
|
3
|
-
"version": "3.9.
|
|
4
|
-
"description": "Neocortex v3.9.
|
|
3
|
+
"version": "3.9.23",
|
|
4
|
+
"description": "Neocortex v3.9.23 - Orquestrador de Desenvolvimento de Epics & Stories para Claude Code",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"claude",
|
|
7
7
|
"claude-code",
|
|
@@ -52,7 +52,10 @@
|
|
|
52
52
|
"install.js",
|
|
53
53
|
"install.sh",
|
|
54
54
|
"install.ps1",
|
|
55
|
+
"postinstall.js",
|
|
55
56
|
"packages/client/dist/",
|
|
57
|
+
"!packages/client/dist/**/*.js.map",
|
|
58
|
+
"!packages/client/dist/**/*.d.ts.map",
|
|
56
59
|
"targets-stubs/"
|
|
57
60
|
],
|
|
58
61
|
"scripts": {
|
|
@@ -60,7 +63,7 @@
|
|
|
60
63
|
"build:clean": "rm -rf packages/shared/dist packages/client/dist && npm run build",
|
|
61
64
|
"build:client": "npm run build -w packages/client",
|
|
62
65
|
"build:shared": "npm run build -w packages/shared",
|
|
63
|
-
"postinstall": "node
|
|
66
|
+
"postinstall": "node postinstall.js",
|
|
64
67
|
"sync": "node sync-version.js",
|
|
65
68
|
"validate": "bash scripts/validate-pre-publish.sh",
|
|
66
69
|
"test:e2e": "bash scripts/e2e-smoke-test.sh",
|
|
@@ -39,6 +39,26 @@ const STUB_TARGETS = [
|
|
|
39
39
|
sourceDir: 'gemini-cli',
|
|
40
40
|
files: ['agent.md'],
|
|
41
41
|
},
|
|
42
|
+
{
|
|
43
|
+
destDir: join(homedir(), '.cursor', 'agents'),
|
|
44
|
+
sourceDir: 'cursor',
|
|
45
|
+
files: ['agent.md'],
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
destDir: join(homedir(), '.codex'),
|
|
49
|
+
sourceDir: 'codex',
|
|
50
|
+
files: ['agents.md'],
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
destDir: join(homedir(), '.vscode'),
|
|
54
|
+
sourceDir: 'vscode',
|
|
55
|
+
files: ['agent.md'],
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
destDir: join(homedir(), '.agent', 'skills', 'neocortex'),
|
|
59
|
+
sourceDir: 'antigravity',
|
|
60
|
+
files: ['skill/SKILL.md'],
|
|
61
|
+
},
|
|
42
62
|
];
|
|
43
63
|
// -- Package Root Resolution --------------------------------------------------
|
|
44
64
|
/**
|
|
@@ -127,7 +147,8 @@ export function refreshStubs(cliVersion) {
|
|
|
127
147
|
const src = join(sourceDir, file);
|
|
128
148
|
const dest = join(target.destDir, file);
|
|
129
149
|
if (existsSync(src)) {
|
|
130
|
-
|
|
150
|
+
// Ensure parent directory exists (handles nested paths like skill/SKILL.md)
|
|
151
|
+
mkdirSync(dirname(dest), { recursive: true });
|
|
131
152
|
copyFileSync(src, dest);
|
|
132
153
|
copiedAny = true;
|
|
133
154
|
}
|
|
@@ -29,6 +29,7 @@ import { getMachineFingerprint } from '../machine/fingerprint.js';
|
|
|
29
29
|
import { updateAgentDescription } from '../agent/update-description.js';
|
|
30
30
|
import { refreshStubs } from '../agent/refresh-stubs.js';
|
|
31
31
|
import { updateAgentYaml } from '../agent/update-agent-yaml.js';
|
|
32
|
+
import { saveSecureConfig, setSecureDirPermissions } from '../config/secure-config.js';
|
|
32
33
|
// ── Version Resolution ────────────────────────────────────────────────────
|
|
33
34
|
function getInstalledVersion() {
|
|
34
35
|
try {
|
|
@@ -94,11 +95,31 @@ function loadExistingConfig() {
|
|
|
94
95
|
}
|
|
95
96
|
/**
|
|
96
97
|
* Save user config after successful activation.
|
|
97
|
-
* License key is
|
|
98
|
+
* License key is encrypted using machine fingerprint (Story 61.2).
|
|
99
|
+
* File permissions set to 600 (Story 61.4).
|
|
98
100
|
*/
|
|
99
101
|
function saveConfig(config) {
|
|
102
|
+
// Ensure directories exist with secure permissions
|
|
100
103
|
mkdirSync(CONFIG_DIR, { recursive: true });
|
|
101
|
-
|
|
104
|
+
setSecureDirPermissions(CONFIG_DIR);
|
|
105
|
+
const cacheDir = join(CONFIG_DIR, 'cache');
|
|
106
|
+
mkdirSync(cacheDir, { recursive: true });
|
|
107
|
+
setSecureDirPermissions(cacheDir);
|
|
108
|
+
if (config.licenseKey) {
|
|
109
|
+
// Use secure config writer which encrypts the license key
|
|
110
|
+
saveSecureConfig({
|
|
111
|
+
serverUrl: config.serverUrl,
|
|
112
|
+
mode: config.mode,
|
|
113
|
+
machineId: config.machineId,
|
|
114
|
+
activatedAt: config.activatedAt,
|
|
115
|
+
tier: config.tier,
|
|
116
|
+
licenseKey: config.licenseKey,
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
// Fallback: write without license key (should not happen in normal flow)
|
|
121
|
+
writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2) + '\n', 'utf-8');
|
|
122
|
+
}
|
|
102
123
|
}
|
|
103
124
|
// ── Activate Command ──────────────────────────────────────────────────────
|
|
104
125
|
/**
|
|
@@ -19,17 +19,17 @@
|
|
|
19
19
|
*
|
|
20
20
|
* Story 45.2 - AC1-AC6
|
|
21
21
|
*/
|
|
22
|
-
import { existsSync, readFileSync,
|
|
22
|
+
import { existsSync, readFileSync, unlinkSync } from 'node:fs';
|
|
23
23
|
import { join } from 'node:path';
|
|
24
24
|
import { homedir } from 'node:os';
|
|
25
25
|
import { LicenseClient } from '../license/license-client.js';
|
|
26
26
|
import { EncryptedCache } from '../cache/encrypted-cache.js';
|
|
27
27
|
import { NoOpCache } from '../types/index.js';
|
|
28
28
|
import { TierAwareClient } from '../tier/tier-aware-client.js';
|
|
29
|
+
import { loadSecureConfig } from '../config/secure-config.js';
|
|
29
30
|
// ── Constants ─────────────────────────────────────────────────────────────
|
|
30
31
|
const DEFAULT_SERVER_URL = 'https://api.neocortex.ornexus.com';
|
|
31
32
|
const CONFIG_DIR = join(homedir(), '.neocortex');
|
|
32
|
-
const CONFIG_FILE = join(CONFIG_DIR, 'config.json');
|
|
33
33
|
const CACHE_DIR = join(CONFIG_DIR, 'cache');
|
|
34
34
|
const MENU_CACHE_FILE = join(CACHE_DIR, 'menu-cache.json');
|
|
35
35
|
const MENU_CACHE_TTL_MS = 24 * 60 * 60 * 1000; // 24 hours
|
|
@@ -115,18 +115,24 @@ export function collectStateSnapshot(projectRoot) {
|
|
|
115
115
|
epics,
|
|
116
116
|
};
|
|
117
117
|
}
|
|
118
|
-
// ── Menu Cache
|
|
119
|
-
|
|
118
|
+
// ── Menu Cache (Encrypted - Story 61.1) ──────────────────────────────────
|
|
119
|
+
const MENU_CACHE_KEY = 'neocortex:menu:cache';
|
|
120
|
+
/**
|
|
121
|
+
* Read menu cache from EncryptedCache.
|
|
122
|
+
* Falls back gracefully: if decryption fails or data is stale, returns null.
|
|
123
|
+
* Also cleans up legacy plaintext menu-cache.json if it exists.
|
|
124
|
+
*/
|
|
125
|
+
async function getMenuCache(encryptedCache) {
|
|
120
126
|
try {
|
|
121
|
-
|
|
127
|
+
const raw = await encryptedCache.get(MENU_CACHE_KEY);
|
|
128
|
+
if (!raw)
|
|
122
129
|
return null;
|
|
123
|
-
const raw = readFileSync(MENU_CACHE_FILE, 'utf-8');
|
|
124
130
|
const cache = JSON.parse(raw);
|
|
125
131
|
// Invalidate on version mismatch (stale cache from previous install)
|
|
126
132
|
if (cache.version !== CLIENT_VERSION) {
|
|
127
133
|
return null;
|
|
128
134
|
}
|
|
129
|
-
// Check TTL
|
|
135
|
+
// Check TTL (EncryptedCache also has TTL, but we double-check for version-based invalidation)
|
|
130
136
|
if (Date.now() - cache.cachedAt > MENU_CACHE_TTL_MS) {
|
|
131
137
|
return null; // Expired
|
|
132
138
|
}
|
|
@@ -136,33 +142,47 @@ function getMenuCache() {
|
|
|
136
142
|
return null;
|
|
137
143
|
}
|
|
138
144
|
}
|
|
139
|
-
|
|
145
|
+
/**
|
|
146
|
+
* Write menu cache to EncryptedCache.
|
|
147
|
+
* Deletes legacy plaintext menu-cache.json on first encrypted write.
|
|
148
|
+
*/
|
|
149
|
+
async function setMenuCache(encryptedCache, instructions, metadata) {
|
|
140
150
|
try {
|
|
141
|
-
mkdirSync(CACHE_DIR, { recursive: true });
|
|
142
151
|
const cache = {
|
|
143
152
|
instructions,
|
|
144
153
|
metadata,
|
|
145
154
|
cachedAt: Date.now(),
|
|
146
155
|
version: CLIENT_VERSION,
|
|
147
156
|
};
|
|
148
|
-
|
|
157
|
+
await encryptedCache.set(MENU_CACHE_KEY, JSON.stringify(cache), MENU_CACHE_TTL_MS);
|
|
158
|
+
// Delete legacy plaintext menu-cache.json if it exists (F1 remediation)
|
|
159
|
+
deleteLegacyMenuCache();
|
|
149
160
|
}
|
|
150
161
|
catch {
|
|
151
162
|
// Cache write failure is non-critical
|
|
152
163
|
}
|
|
153
164
|
}
|
|
154
|
-
|
|
155
|
-
|
|
165
|
+
/**
|
|
166
|
+
* Remove legacy plaintext menu-cache.json file.
|
|
167
|
+
* Called after successful encrypted write to prevent IP leakage.
|
|
168
|
+
*/
|
|
169
|
+
function deleteLegacyMenuCache() {
|
|
156
170
|
try {
|
|
157
|
-
if (existsSync(
|
|
158
|
-
|
|
159
|
-
return JSON.parse(raw);
|
|
171
|
+
if (existsSync(MENU_CACHE_FILE)) {
|
|
172
|
+
unlinkSync(MENU_CACHE_FILE);
|
|
160
173
|
}
|
|
161
174
|
}
|
|
162
175
|
catch {
|
|
163
|
-
//
|
|
176
|
+
// Non-critical: best-effort cleanup
|
|
164
177
|
}
|
|
165
|
-
|
|
178
|
+
}
|
|
179
|
+
// ── Config Loading (Story 61.2 - Secure) ─────────────────────────────────
|
|
180
|
+
/**
|
|
181
|
+
* Load config with automatic decryption of license key.
|
|
182
|
+
* Handles migration from plaintext licenseKey to encryptedLicenseKey.
|
|
183
|
+
*/
|
|
184
|
+
function loadConfig() {
|
|
185
|
+
return loadSecureConfig();
|
|
166
186
|
}
|
|
167
187
|
async function getAuthTokenAndClient(serverUrl, licenseKey) {
|
|
168
188
|
try {
|
|
@@ -262,10 +282,14 @@ export async function invoke(options) {
|
|
|
262
282
|
const serverUrl = (options.serverUrl ?? config?.serverUrl ?? DEFAULT_SERVER_URL).replace(/\/+$/, '');
|
|
263
283
|
// 2. Collect state snapshot
|
|
264
284
|
const stateSnapshot = collectStateSnapshot(projectRoot);
|
|
285
|
+
// 2a. Create encrypted cache for menu (uses licenseKey as passphrase)
|
|
286
|
+
const menuCache = config?.licenseKey
|
|
287
|
+
? new EncryptedCache({ cacheDir: CACHE_DIR, passphrase: config.licenseKey })
|
|
288
|
+
: null;
|
|
265
289
|
// 3. Check menu cache for empty invocations (AC6)
|
|
266
290
|
const trimmedArgs = options.args.trim();
|
|
267
|
-
if (!trimmedArgs) {
|
|
268
|
-
const cachedMenu = getMenuCache();
|
|
291
|
+
if (!trimmedArgs && menuCache) {
|
|
292
|
+
const cachedMenu = await getMenuCache(menuCache);
|
|
269
293
|
if (cachedMenu) {
|
|
270
294
|
return {
|
|
271
295
|
success: true,
|
|
@@ -331,9 +355,9 @@ export async function invoke(options) {
|
|
|
331
355
|
exitCode,
|
|
332
356
|
};
|
|
333
357
|
}
|
|
334
|
-
// 6. Cache menu responses (AC6)
|
|
335
|
-
if (!trimmedArgs && result.data.metadata?.mode === 'menu') {
|
|
336
|
-
setMenuCache(result.data.instructions, result.data.metadata);
|
|
358
|
+
// 6. Cache menu responses (AC6) - encrypted (Story 61.1)
|
|
359
|
+
if (!trimmedArgs && result.data.metadata?.mode === 'menu' && menuCache) {
|
|
360
|
+
setMenuCache(menuCache, result.data.instructions, result.data.metadata).catch(() => { });
|
|
337
361
|
}
|
|
338
362
|
// 6a. Update cached quota from server response metadata (Epic 60)
|
|
339
363
|
if (result.data.metadata) {
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license FSL-1.1
|
|
3
|
+
* Copyright (c) 2026 OrNexus AI
|
|
4
|
+
*
|
|
5
|
+
* This file is part of Neocortex CLI, licensed under the
|
|
6
|
+
* Functional Source License, Version 1.1 (FSL-1.1).
|
|
7
|
+
*
|
|
8
|
+
* Change Date: February 20, 2029
|
|
9
|
+
* Change License: MIT
|
|
10
|
+
*
|
|
11
|
+
* See the LICENSE file in the project root for full license text.
|
|
12
|
+
*/
|
|
13
|
+
/** Resolved config with decrypted license key */
|
|
14
|
+
export interface SecureConfig {
|
|
15
|
+
readonly serverUrl?: string;
|
|
16
|
+
readonly mode?: string;
|
|
17
|
+
readonly machineId?: string;
|
|
18
|
+
readonly activatedAt?: string;
|
|
19
|
+
readonly tier?: string;
|
|
20
|
+
readonly licenseKey?: string;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Encrypt a license key using the machine fingerprint as passphrase.
|
|
24
|
+
* Returns the encrypted envelope string.
|
|
25
|
+
*/
|
|
26
|
+
export declare function encryptLicenseKey(licenseKey: string): string;
|
|
27
|
+
/**
|
|
28
|
+
* Decrypt a license key using the machine fingerprint as passphrase.
|
|
29
|
+
* Returns the plaintext key or null if decryption fails (hardware changed).
|
|
30
|
+
*/
|
|
31
|
+
export declare function decryptLicenseKey(encryptedKey: string): string | null;
|
|
32
|
+
/**
|
|
33
|
+
* Set restrictive file permissions (600) on config files.
|
|
34
|
+
* Skipped on Windows where chmod is not meaningful.
|
|
35
|
+
* Story 61.4 - F4 remediation.
|
|
36
|
+
*/
|
|
37
|
+
export declare function setSecureFilePermissions(filePath: string): void;
|
|
38
|
+
/**
|
|
39
|
+
* Set restrictive directory permissions (700) on config directories.
|
|
40
|
+
* Skipped on Windows where chmod is not meaningful.
|
|
41
|
+
* Story 61.4 - F4 remediation.
|
|
42
|
+
*/
|
|
43
|
+
export declare function setSecureDirPermissions(dirPath: string): void;
|
|
44
|
+
/**
|
|
45
|
+
* Load config from ~/.neocortex/config.json with automatic migration.
|
|
46
|
+
*
|
|
47
|
+
* If the config contains a plaintext `licenseKey` (old format), it is:
|
|
48
|
+
* 1. Encrypted using machine fingerprint
|
|
49
|
+
* 2. Stored as `encryptedLicenseKey`
|
|
50
|
+
* 3. Old `licenseKey` field removed
|
|
51
|
+
* 4. Config rewritten to disk
|
|
52
|
+
*
|
|
53
|
+
* If decryption of `encryptedLicenseKey` fails (hardware change),
|
|
54
|
+
* returns config with licenseKey = undefined.
|
|
55
|
+
*/
|
|
56
|
+
export declare function loadSecureConfig(): SecureConfig | null;
|
|
57
|
+
/**
|
|
58
|
+
* Save config after activation with encrypted license key.
|
|
59
|
+
* This is the primary write path called from activate.ts.
|
|
60
|
+
*/
|
|
61
|
+
export declare function saveSecureConfig(config: {
|
|
62
|
+
serverUrl: string;
|
|
63
|
+
mode: string;
|
|
64
|
+
machineId: string;
|
|
65
|
+
activatedAt: string;
|
|
66
|
+
tier?: string;
|
|
67
|
+
licenseKey: string;
|
|
68
|
+
}): void;
|
|
69
|
+
//# sourceMappingURL=secure-config.d.ts.map
|