Files
open-design/shells/terminal/ps1/terminal.ps1
8ab7c3d5f1 feat(standalone): establish the exact lifecycle and Terminal carrier (#7716)
* feat(standalone): add shell-neutral exact lifecycle

* feat(terminal): add native exact cold-start carrier

* test(terminal): exercise mutable exact channel heads

* feat(standalone): model synced blobs and Shell updates

* feat(terminal): exercise the complete Shell lifecycle

* feat(release): add the independent exact Terminal line

* test(exact): run the full local Terminal release line

* feat(terminal): harden exact Sidecar lifecycle

* feat(standalone): prove exact update transactions

* refactor(standalone): separate shell lifecycle dimensions

* refactor(e2e): decouple terminal exact drivers

* refactor(terminal): isolate exact sidecar fixture host

* feat(terminal): gate handoff on physical retirement

* refactor(release): gate exact activation on public acceptance

* test(terminal): formalize guarded lifecycle algebra

* fix(terminal): guard shell installer handoff

* feat(terminal): add exact bootloader handoff

* feat(terminal): integrate exact sidecar lifecycle

* refactor(exact): rename validation channels

* test(terminal): harden exact release acceptance

* fix(postinstall): build standalone dependencies first

Generated-By: looper 0.11.2 (runner=fixer, agent=codex)

* fix(ci): build the native Terminal scene

Generated-By: looper 0.11.2 (runner=fixer, agent=codex)

* test(ci): cover exact convergence handoff routing

Generated-By: looper 0.11.2 (runner=fixer, agent=codex)

* fix(exact): isolate terminal validation from main CI

* fix(terminal): complete shell update confirmation

Generated-By: looper 0.11.2 (runner=fixer, agent=codex)

* fix(exact): bind acceptance to installed manifest

Generated-By: looper 0.11.2 (runner=fixer, agent=codex)

* fix(standalone): block occupied shell installs

Generated-By: looper 0.11.2 (runner=fixer, agent=codex)

---------

Co-authored-by: Looper <looper@noreply.github.com>
2026-09-03 03:50:34 +00:00

118 lines
7.3 KiB
PowerShell

param(
[string]$Root,
[Parameter(Mandatory = $true)][string]$Channel,
[Parameter(Mandatory = $true)][string]$Namespace,
[ValidateSet("probe", "start", "heartbeat", "release", "stop", "status", "prepare-update", "apply-update", "apply-update-force", "shell-update-status", "shell-update-check", "shell-update-download", "shell-update-install", "shell-update-later", "shell-update-force", "shell-update-confirm", "shell-update-abandon")][string]$Operation = "start",
[string]$AttachmentId,
[string]$AttachmentCapability,
[string]$StoreRoot,
[string]$ChannelHeadUrl,
[ValidateSet("observe", "authorize-silent", "authorize-user", "revoke-silent")][string]$ActivationPolicy,
[string]$Result,
[string]$Feedback
)
$ErrorActionPreference = "Stop"
Set-StrictMode -Version 2.0
function Fail([string]$Message) { throw "terminal: $Message" }
function Emit-Feedback([string]$Phase, [string]$State) {
if (-not $Feedback) { return }
$parent = Split-Path -Parent ([IO.Path]::GetFullPath($Feedback))
[IO.Directory]::CreateDirectory($parent) | Out-Null
$event = [ordered]@{ phase = $Phase; schemaVersion = 1; source = "shell"; state = $State }
[IO.File]::AppendAllText($Feedback, (($event | ConvertTo-Json -Compress) + "`n"), [Text.UTF8Encoding]::new($false))
}
function Read-Lock([string]$Path) {
$values = @{}
foreach ($line in [IO.File]::ReadAllLines($Path)) {
if ($line -notmatch '^([a-z0-9_]+)=([A-Za-z0-9._/-]+)$') { Fail "invalid carrier.lock line: $line" }
if ($values.ContainsKey($Matches[1])) { Fail "duplicate carrier.lock key: $($Matches[1])" }
$values[$Matches[1]] = $Matches[2]
}
return $values
}
if (-not $Root) { $Root = Join-Path $PSScriptRoot ".." }
$Root = [IO.Path]::GetFullPath($Root)
if ($Channel -eq "local" -or $Channel -notmatch '^[a-z0-9]{1,12}$') { Fail "invalid exact channel" }
if ($Namespace -notmatch '^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$') { Fail "invalid namespace" }
if ($AttachmentId -and $AttachmentId -notmatch '^[A-Za-z0-9._-]{1,128}$') { Fail "invalid attachment id" }
if ($AttachmentCapability -and $AttachmentCapability -notmatch '^[a-f0-9]{64}$') { Fail "invalid attachment capability" }
if ($Operation -eq "prepare-update" -and -not $ActivationPolicy) { Fail "prepare-update requires an explicit activation policy" }
$lockPath = Join-Path $Root "carrier.lock"
$manifestPath = Join-Path $Root "install-manifest.json"
$manifestDigestPath = Join-Path $Root "install-manifest.sha256"
if (-not (Test-Path -LiteralPath $lockPath -PathType Leaf)) { Fail "carrier.lock not found" }
if (-not (Test-Path -LiteralPath $manifestPath -PathType Leaf)) { Fail "install-manifest.json not found" }
if (-not (Test-Path -LiteralPath $manifestDigestPath -PathType Leaf)) { Fail "install-manifest.sha256 not found" }
$lock = Read-Lock $lockPath
foreach ($key in @("target", "shell_version", "node_version", "node_executable", "node_sha256", "fossil_entrypoint")) {
if (-not $lock.ContainsKey($key)) { Fail "carrier.lock lacks $key" }
}
if ($lock.target -notin @("darwin-arm64", "darwin-x64", "win32-x64")) { Fail "unsupported carrier target" }
if ($lock.shell_version -notmatch '^\d+\.\d+\.\d+$' -or $lock.node_version -notmatch '^\d+\.\d+\.\d+$') { Fail "invalid carrier version" }
if ($lock.node_executable -notmatch '^carrier/node/' -or "/$($lock.node_executable)/" -match '/\.\./') { Fail "Node executable escaped carrier root" }
if ($lock.fossil_entrypoint -ne "runtime/fossil.mjs") { Fail "invalid fossil entrypoint" }
if ($lock.node_sha256 -notmatch '^[a-f0-9]{64}$') { Fail "invalid Node digest" }
Emit-Feedback "node-verification" "begin"
$nodePath = [IO.Path]::GetFullPath((Join-Path $Root $lock.node_executable))
$fossilPath = [IO.Path]::GetFullPath((Join-Path $Root $lock.fossil_entrypoint))
$rootPrefix = $Root.TrimEnd([IO.Path]::DirectorySeparatorChar, [IO.Path]::AltDirectorySeparatorChar) + [IO.Path]::DirectorySeparatorChar
if (-not $nodePath.StartsWith($rootPrefix, [StringComparison]::OrdinalIgnoreCase) -or -not $fossilPath.StartsWith($rootPrefix, [StringComparison]::OrdinalIgnoreCase)) { Fail "carrier path escaped install root" }
if (-not (Test-Path -LiteralPath $nodePath -PathType Leaf)) { Fail "verified Node executable is unavailable" }
if (-not (Test-Path -LiteralPath $fossilPath -PathType Leaf)) { Fail "fossil adapter is unavailable" }
$nodeDigest = (Get-FileHash -LiteralPath $nodePath -Algorithm SHA256).Hash.ToLowerInvariant()
if ($nodeDigest -ne $lock.node_sha256) { Fail "installed Node digest mismatch" }
$observedVersion = (& $nodePath --version).Trim()
if ($observedVersion -ne "v$($lock.node_version)") { Fail "installed Node version mismatch" }
Emit-Feedback "node-verification" "complete"
$manifestDigest = ([IO.File]::ReadAllText($manifestDigestPath).Trim() -split '\s+')[0].ToLowerInvariant()
if ($manifestDigest -notmatch '^[a-f0-9]{64}$') { Fail "invalid manifest digest" }
if ((Get-FileHash -LiteralPath $manifestPath -Algorithm SHA256).Hash.ToLowerInvariant() -ne $manifestDigest) { Fail "installed manifest digest mismatch" }
$exchangeRoot = Join-Path ([IO.Path]::GetTempPath()) ("nexu-terminal-" + [Guid]::NewGuid().ToString("N"))
[IO.Directory]::CreateDirectory($exchangeRoot) | Out-Null
$fossilExitCode = 0
try {
$resolutionPath = Join-Path $exchangeRoot "carrier-resolution.json"
$requestPath = Join-Path $exchangeRoot "fossil-request.json"
if (-not $Result) { $Result = Join-Path $exchangeRoot "fossil-result.json" }
$resolution = [ordered]@{
installRoot = $Root
manifestFile = $manifestPath
runtime = [ordered]@{ digest = $nodeDigest; executablePath = $nodePath; name = "node"; version = $lock.node_version }
schemaVersion = 1
shell = [ordered]@{ digest = $manifestDigest; type = "terminal"; version = $lock.shell_version }
target = $lock.target
}
[IO.File]::WriteAllText($resolutionPath, (($resolution | ConvertTo-Json -Compress -Depth 5) + "`n"), [Text.UTF8Encoding]::new($false))
$request = [ordered]@{
carrierResolutionFile = $resolutionPath
channel = $Channel
namespace = $Namespace
operation = $Operation
schemaVersion = 1
}
if ($AttachmentId) { $request.attachmentId = $AttachmentId }
if ($AttachmentCapability) { $request.attachmentCapability = $AttachmentCapability }
if ($StoreRoot) { $request.storeRoot = [IO.Path]::GetFullPath($StoreRoot) }
if ($ChannelHeadUrl) { $request.channelHeadUrl = $ChannelHeadUrl }
if ($ActivationPolicy) { $request.activationPolicy = $ActivationPolicy }
if ($Operation -in @("prepare-update", "apply-update", "apply-update-force")) { $request.updateProtocolVersion = 3 }
if ($Feedback) { $request.feedbackFile = [IO.Path]::GetFullPath($Feedback) }
[IO.File]::WriteAllText($requestPath, (($request | ConvertTo-Json -Compress -Depth 5) + "`n"), [Text.UTF8Encoding]::new($false))
$env:OD_TERMINAL_FOSSIL_REQUEST_V1 = $requestPath
$env:OD_TERMINAL_FOSSIL_RESULT_V1 = $Result
& $nodePath $fossilPath
$fossilExitCode = $LASTEXITCODE
if (-not (Test-Path -LiteralPath $Result -PathType Leaf)) { Fail "fossil produced no result" }
if ($Result -like "$exchangeRoot*") { [Console]::Out.Write([IO.File]::ReadAllText($Result)) } else { Write-Output $Result }
} finally {
Remove-Item -LiteralPath $exchangeRoot -Recurse -Force -ErrorAction SilentlyContinue
Remove-Item Env:OD_TERMINAL_FOSSIL_REQUEST_V1 -ErrorAction SilentlyContinue
Remove-Item Env:OD_TERMINAL_FOSSIL_RESULT_V1 -ErrorAction SilentlyContinue
}
if ($fossilExitCode -ne 0) { exit $fossilExitCode }