Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 46 additions & 10 deletions install.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,12 @@ $MarketplacePlugins = @("simplicio", "simplicio-loop", "simplicio-prompt", "simp
$Target = "windows-x64"
$Asset = "simplicio-windows-x64.exe"
$Ed25519PublicKey = "2RoVWAoqA/DtDkT5PZdzQYIP82zFskQqJx4S1w06Wok="
$Ed25519HelperUrl = "https://raw.githubusercontent.com/wesleysimplicio/simplicio/master/scripts/verify_ed25519.py"
$Ed25519HelperSha256 = "f03a0719dd557ddea27dc4cf1456d6f06a47b9056505e4d4b8453090697600d0"
$PinnedPublicKey = ([string]$Ed25519PublicKey).Trim()
$Ed25519HelperUrl = "https://raw.githubusercontent.com/wesleysimplicio/simplicio/master/scripts/verify_ed25519.py"
$Ed25519HelperSha256 = "f03a0719dd557ddea27dc4cf1456d6f06a47b9056505e4d4b8453090697600d0"
$PublicRouteRef = "cc9950025baf823cdecc657228ff1e89d7701e7e"
$PublicRouteUrl = "https://raw.githubusercontent.com/$Repo/$PublicRouteRef/codex/mcp-route.ps1"
$PublicRouteSha256 = "fea2b06c95c9f75bf17fc46b603c8e3817aa6f680af468fa73c066210970c89c"
$PinnedPublicKey = ([string]$Ed25519PublicKey).Trim()
$script:Ed25519VerifyError = ""
$SimplicioMcpUrl = if ($env:SIMPLICIO_MCP_URL) { $env:SIMPLICIO_MCP_URL } else { "http://127.0.0.1:8787/mcp" }

Expand Down Expand Up @@ -189,7 +192,7 @@ function Report-LoginState {
Write-Warning "Google login missing or entitlement inactive; run: `"$DestPath`" auth login"
}

function Test-McpToolSurface([string]$BinaryPath) {
function Test-McpToolSurface([string]$BinaryPath) {
if (-not (Test-Path $BinaryPath)) { return $false }
try {
$env:SIMPLICIO_MCP_URL = $SimplicioMcpUrl
Expand All @@ -209,6 +212,33 @@ function Invoke-NativeHostCommand([string]$Command, [string[]]$Arguments) {
}
}

function Sync-PublicRouteOverlay {
$hookDir = Join-Path $PurgeDir "hooks"
$hookPath = Join-Path $hookDir "mcp-route.ps1"
if (Test-Path $hookPath) {
$currentHash = (Get-FileHash -Algorithm SHA256 -Path $hookPath).Hash.ToLowerInvariant()
if ($currentHash -eq $PublicRouteSha256) { return $true }
}

New-Item -ItemType Directory -Force -Path $hookDir | Out-Null
$hookTemp = Join-Path $hookDir (".mcp-route.ps1.download-$PID")
try {
Invoke-WebRequest -Uri $PublicRouteUrl -OutFile $hookTemp -UseBasicParsing
$downloadHash = (Get-FileHash -Algorithm SHA256 -Path $hookTemp).Hash.ToLowerInvariant()
$downloadText = Get-Content -Raw -Path $hookTemp
if ($downloadHash -ne $PublicRouteSha256 -or
$downloadText -notmatch 'simplicio-hook-version: 3240-v11') {
return $false
}
Move-Item -Force -Path $hookTemp -Destination $hookPath
return $true
} catch {
return $false
} finally {
if (Test-Path $hookTemp) { Remove-Item -Force $hookTemp -ErrorAction SilentlyContinue }
}
}

function Test-AnyPath([string[]]$Paths) {
foreach ($path in $Paths) {
if (-not [string]::IsNullOrWhiteSpace($path) -and (Test-Path $path)) { return $true }
Expand Down Expand Up @@ -650,12 +680,18 @@ if (-not (Test-RuntimeReleaseContract $DestPath)) {
Write-Host " ✓ Runtime release contract verified"

# ─── Register MCP and native hooks for every detected client ──────────────
if (Test-McpToolSurface $DestPath) {
Write-Host " ✓ MCP and hooks registered automatically for detected clients"
} else {
Write-Error "Runtime installed, but automatic MCP/hooks registration failed: $DestPath mcp register --binary $DestPath --json"
exit 1
}
if (Test-McpToolSurface $DestPath) {
Write-Host " ✓ MCP and hooks registered automatically for detected clients"
} else {
Write-Error "Runtime installed, but automatic MCP/hooks registration failed: $DestPath mcp register --binary $DestPath --json"
exit 1
}
if (Sync-PublicRouteOverlay) {
Write-Host " ✓ verified public v11 hook reconciled after Runtime registration"
} else {
Write-Error "Runtime registered MCP, but the public v11 hook could not be verified and activated"
exit 1
}

# Host packages add skills, commands and host-specific lifecycle behavior on
# top of Runtime MCP registration. Every detected host with a documented
Expand Down
35 changes: 35 additions & 0 deletions install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ GITHUB="https://github.com/$REPO"
ED25519_PUBLIC_KEY="2RoVWAoqA/DtDkT5PZdzQYIP82zFskQqJx4S1w06Wok="
ED25519_HELPER_URL="https://raw.githubusercontent.com/$REPO/master/scripts/verify_ed25519.py"
ED25519_HELPER_SHA256="f03a0719dd557ddea27dc4cf1456d6f06a47b9056505e4d4b8453090697600d0"
PUBLIC_ROUTE_REF="cc9950025baf823cdecc657228ff1e89d7701e7e"
PUBLIC_ROUTE_URL="https://raw.githubusercontent.com/$REPO/$PUBLIC_ROUTE_REF/codex/mcp-route.sh"
PUBLIC_ROUTE_SHA256="0f3a6a32c6f224fb3aedea5336f60c5c63917b134113d4c4e524e8ae7b4a31a4"
BIN_NAME="simplicio"
MARKETPLACE_PLUGINS="simplicio simplicio-loop simplicio-prompt simplicio-sprint simplicio-hermes"

Expand Down Expand Up @@ -98,6 +101,33 @@ sha256_of() {
fi
}

reconcile_public_route_overlay() {
hook_dir="$PURGE_DIR/hooks"
hook_path="$hook_dir/mcp-route.sh"

if [ -f "$hook_path" ] && [ "$(sha256_of "$hook_path")" = "$PUBLIC_ROUTE_SHA256" ]; then
return 0
fi

mkdir -p "$hook_dir" || return 1
hook_tmp="$hook_dir/.mcp-route.sh.download-$$"
if command -v curl >/dev/null 2>&1; then
curl -fsSL "$PUBLIC_ROUTE_URL" -o "$hook_tmp" || { rm -f "$hook_tmp"; return 1; }
elif command -v wget >/dev/null 2>&1; then
wget -q "$PUBLIC_ROUTE_URL" -O "$hook_tmp" || { rm -f "$hook_tmp"; return 1; }
else
return 1
fi

if [ "$(sha256_of "$hook_tmp")" != "$PUBLIC_ROUTE_SHA256" ] ||
! grep -q 'simplicio-hook-version: 3240-v11' "$hook_tmp"; then
rm -f "$hook_tmp"
return 1
fi
chmod 0755 "$hook_tmp" || { rm -f "$hook_tmp"; return 1; }
mv -f "$hook_tmp" "$hook_path"
}

verify_ed25519_signature() {
binary_path="$1"
signature="$2"
Expand Down Expand Up @@ -740,6 +770,11 @@ if verify_mcp_tools "$DEST_PATH"; then
else
err "o Runtime foi instalado, mas o registro automático de MCP/hooks falhou: $DEST_PATH mcp register --binary $DEST_PATH --json"
fi
if reconcile_public_route_overlay; then
ok "hook público v11 verificado e reconciliado após o registro do Runtime"
else
err "o Runtime registrou o MCP, mas o hook público v11 não pôde ser verificado e ativado"
fi

# Host packages add skills, commands and host-specific lifecycle behavior on
# top of Runtime MCP registration. Every detected host with a documented
Expand Down
16 changes: 14 additions & 2 deletions tests/test_codex_install_contract.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"""Static contract checks for the public Codex integration."""

import hashlib
import json
import os
from pathlib import Path
Expand Down Expand Up @@ -70,18 +71,29 @@ def test_installers_preserve_stable_login_state_during_upgrades():
assert "auth login" in powershell


def test_windows_installer_migrates_legacy_unix_hooks():
def test_windows_installer_reconciles_the_pinned_public_hook_after_runtime_registration():
powershell = (ROOT / "install.ps1").read_text(encoding="utf-8")
hook_digest = hashlib.sha256((ROOT / "codex" / "mcp-route.ps1").read_bytes()).hexdigest()
assert "Install-CodexRouteHook" not in powershell
assert "SIMPLICIO_CODEX_HOOK_REF" not in powershell
assert "mcp register --binary $BinaryPath --json" in powershell
assert '$PublicRouteRef = "cc9950025baf823cdecc657228ff1e89d7701e7e"' in powershell
assert f'$PublicRouteSha256 = "{hook_digest}"' in powershell
assert "Sync-PublicRouteOverlay" in powershell
assert powershell.index("Test-McpToolSurface $DestPath") < powershell.index("if (Sync-PublicRouteOverlay)")

def test_unix_installer_migrates_legacy_hook_events():

def test_unix_installer_reconciles_the_pinned_public_hook_after_runtime_registration():
shell = (ROOT / "install.sh").read_text(encoding="utf-8")
hook_digest = hashlib.sha256((ROOT / "codex" / "mcp-route.sh").read_bytes()).hexdigest()
assert "CODEX_ROUTE_HOOK_URL" not in shell
assert "install_codex_route_hook" not in shell
assert "SIMPLICIO_CODEX_HOOK_REF" not in shell
assert 'mcp register --binary "$binary_path" --json' in shell
assert 'PUBLIC_ROUTE_REF="cc9950025baf823cdecc657228ff1e89d7701e7e"' in shell
assert f'PUBLIC_ROUTE_SHA256="{hook_digest}"' in shell
assert "reconcile_public_route_overlay" in shell
assert shell.index('if verify_mcp_tools "$DEST_PATH"') < shell.index("if reconcile_public_route_overlay")

def test_unix_installer_accepts_release_manifest_target_aliases():
shell = (ROOT / "install.sh").read_text(encoding="utf-8")
Expand Down
Loading