Initial EveTui runtime

This commit is contained in:
Meta Vens 2026-07-10 16:40:04 +01:00
commit b01f481483
23 changed files with 2159 additions and 0 deletions

48
.github/workflows/runtime.yml vendored Normal file
View file

@ -0,0 +1,48 @@
name: EveTui runtime
on:
push:
workflow_dispatch:
permissions:
contents: read
jobs:
test-and-pack:
runs-on: windows-latest
env:
GIT_LFS_SKIP_SMUDGE: "1"
steps:
- uses: actions/checkout@v7
with:
path: EveTui
- uses: actions/checkout@v7
with:
repository: GameCult/Eve
path: Eve
- uses: actions/checkout@v7
with:
repository: GameCult/Aetheria
path: Aetheria
lfs: false
- name: Verify runtime lifecycle
working-directory: EveTui
shell: powershell
run: .\scripts\run-evetui-lifecycle-smoke.ps1
- name: Pack runtime
working-directory: EveTui
shell: powershell
run: |
New-Item -ItemType Directory -Force artifacts\release | Out-Null
npm pack --pack-destination artifacts\release
- name: Upload runtime package
uses: actions/upload-artifact@v7
with:
name: eve-tui-${{ github.sha }}
path: EveTui/artifacts/release/*.tgz
if-no-files-found: error

2
.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
artifacts/
node_modules/

15
README.md Normal file
View file

@ -0,0 +1,15 @@
# EveTui
EveTui is the provider-agnostic compact terminal lowerer for Eve surfaces. It
consumes provider advertisements and surface documents, emits typed command
intents through provider-owned boundaries, and publishes terminal-grid capture
evidence without owning provider state or plugin semantics.
Eve owns the surface contract. EveTui owns terminal projection, package and test
lifecycle, and capture artifacts. Providers remain the only writers of live
state.
```powershell
npm test
powershell -ExecutionPolicy Bypass -File .\scripts\run-evetui-lifecycle-smoke.ps1
```

View file

@ -0,0 +1,20 @@
{
"schema": "gamecult.eve.runtime_capture_request.v1",
"runtimeId": "tui",
"ownerRepo": "EveTui",
"providerId": "aetheria",
"surfaceId": "aetheria.daemon.game",
"surfaceKind": "interactive-world",
"targetId": "tui",
"projectionKind": "provider-authored-world-surface",
"commandBoundary": "aetheria.daemon.commands",
"receiptSchema": "aetheria.eve_command_acceptance_status.v1",
"captureKind": "terminal-cell-grid",
"artifactKind": "json-grid",
"artifactPath": "artifacts/evetui-capture/latest/tui-grid.json",
"conformanceAttachment": "runtime.captureArtifacts[]",
"authority": "Capture observes terminal projection only; provider state, command acceptance, and receipts stay provider-owned.",
"sourceAdvertisementPath": "E:\\Projects\\Eve\\..\\Aetheria\\conformance\\eve\\aetheria.provider-advertisement.json",
"sourceCapabilityManifestPath": "E:\\Projects\\Eve\\runtimes\\incubating\\eve-tui\\eve-runtime-capability.json",
"issuedAtUtc": "latest"
}

View file

@ -0,0 +1,40 @@
{
"schema": "gamecult.eve.tui_grid.v1",
"runtimeId": "tui",
"providerId": "aetheria",
"surfaceId": "aetheria.daemon.game",
"projectionKind": "provider-authored-world-surface",
"commandBoundary": "aetheria.daemon.commands",
"receiptSchema": "aetheria.eve_command_acceptance_status.v1",
"ownership": "provider-owns-world-state-assets-command-acceptance-and-receipts",
"width": 80,
"lines": [
"EveTui aetheria",
"surface aetheria.daemon.game",
"command aetheria.daemon.commands",
"receipt aetheria.eve_command_acceptance_status.v1",
"surface aetheria.daemon.game.root",
" card aetheria.daemon.game.summary Daemon World",
" text aetheria.daemon.game.summary.text Provider-owned world state project...",
" metric aetheria.daemon.game.frame Frame",
" partition aetheria.daemon.game.partition",
" world aetheria.daemon.game.playable Daemon ARPG View",
" world aetheria.daemon.game.entity.player Vanguard",
" world aetheria.daemon.game.entity.raider Raider Scout",
" world aetheria.daemon.game.entity.station Anchor Station",
" world aetheria.daemon.game.flow Volumetric Flow",
" world aetheria.daemon.game.gravity Current Zone Gravity",
" card aetheria.daemon.game.commands Command Boundary",
" command aetheria.daemon.game.command.focus Focus Relay",
" command aetheria.daemon.game.command.move Issue Move Intent",
" text aetheria.daemon.game.authority Commands are intents against Aether..."
],
"embeddedDocumentSlots": [],
"pluginProjections": [],
"lossiness": "terminal-grid-command-surface; summarizes provider-authored world surface without owning provider state",
"artifactKind": "json-grid",
"captureKind": "terminal-cell-grid",
"sourceAdvertisementPath": "E:\\Projects\\Eve\\..\\Aetheria\\conformance\\eve\\aetheria.provider-advertisement.json",
"sourceCapabilityManifestPath": "E:\\Projects\\Eve\\runtimes\\incubating\\eve-tui\\eve-runtime-capability.json",
"issuedAtUtc": "latest"
}

View file

@ -0,0 +1,20 @@
{
"schema": "gamecult.eve.runtime_capture_request.v1",
"runtimeId": "tui",
"ownerRepo": "EveTui",
"providerId": "eve.world-smoke",
"surfaceId": "eve.world-smoke.surface",
"surfaceKind": "interactive-world",
"targetId": "tui",
"projectionKind": "provider-authored-world-surface",
"commandBoundary": "eve.world-smoke.commands",
"receiptSchema": "eve.world_smoke.command_receipt.v1",
"captureKind": "terminal-cell-grid",
"artifactKind": "json-grid",
"artifactPath": "artifacts/evetui-capture/latest/tui-grid.json",
"conformanceAttachment": "runtime.captureArtifacts[]",
"authority": "Capture observes terminal projection only; provider state, command acceptance, and receipts stay provider-owned.",
"sourceAdvertisementPath": "E:\\Projects\\Eve\\web\\fixtures\\eve-world-smoke.provider-advertisement.json",
"sourceCapabilityManifestPath": "E:\\Projects\\Eve\\runtimes\\incubating\\eve-tui\\eve-runtime-capability.json",
"issuedAtUtc": "latest"
}

View file

@ -0,0 +1,33 @@
{
"schema": "gamecult.eve.tui_grid.v1",
"runtimeId": "tui",
"providerId": "eve.world-smoke",
"surfaceId": "eve.world-smoke.surface",
"projectionKind": "provider-authored-world-surface",
"commandBoundary": "eve.world-smoke.commands",
"receiptSchema": "eve.world_smoke.command_receipt.v1",
"ownership": "provider-owns-world-state-and-command-receipts",
"width": 80,
"lines": [
"EveTui eve.world-smoke",
"surface eve.world-smoke.surface",
"command eve.world-smoke.commands",
"receipt eve.world_smoke.command_receipt.v1",
"surface eve.world-smoke.root",
" card eve.world-smoke.summary World Contract Control",
" text eve.world-smoke.summary.text A tiny provider-owned world surface use...",
" metric eve.world-smoke.tick Tick",
" world eve.world-smoke.field Contract Field",
" card eve.world-smoke.commands Command Boundary",
" command eve.world-smoke.command.focus Focus Beacon",
" text eve.world-smoke.authority The provider owns accepted state and recei..."
],
"embeddedDocumentSlots": [],
"pluginProjections": [],
"lossiness": "terminal-grid-command-surface; summarizes provider-authored world surface without owning provider state",
"artifactKind": "json-grid",
"captureKind": "terminal-cell-grid",
"sourceAdvertisementPath": "E:\\Projects\\Eve\\web\\fixtures\\eve-world-smoke.provider-advertisement.json",
"sourceCapabilityManifestPath": "E:\\Projects\\Eve\\runtimes\\incubating\\eve-tui\\eve-runtime-capability.json",
"issuedAtUtc": "latest"
}

195
eve-runtime-capability.json Normal file
View file

@ -0,0 +1,195 @@
{
"schema": "gamecult.eve.runtime_capability.v1",
"runtimeId": "tui",
"title": "Compact TUI",
"owner": "EveTui",
"incubation": {
"status": "graduated",
"ownerRepo": "EveTui",
"currentHostRepo": "EveTui",
"splitTarget": "EveTui",
"graduationTrigger": "Graduated: EveTui owns package, tests, terminal lowering, command transport, lifecycle, and capture evidence."
},
"supportedFeatures": [
"providerAdvertisements",
"commandTransport",
"terminalGridSummary",
"terminalGridLowering",
"embeddedDocuments",
"pluginProjection"
],
"supportedPlugins": [
{
"pluginId": "sai.vn",
"capabilities": [
"vn.stage",
"story.choose",
"story.continue",
"story.jump"
],
"projectionAdapter": "EveTuiShell.buildPluginProjection",
"lossiness": "terminal-summary"
},
{
"pluginId": "norn.graph",
"capabilities": [
"embed.norn"
],
"projectionAdapter": "EveTuiShell.buildPluginProjection",
"lossiness": "terminal-outline"
},
{
"pluginId": "tex.math",
"capabilities": [
"embed.tex",
"tex.inline",
"tex.block"
],
"projectionAdapter": "EveTuiShell.buildPluginProjection",
"lossiness": "source-text"
}
],
"unsupportedPlugins": [],
"worldSurfaceLowering": [
{
"targetId": "tui",
"surfaceKinds": ["interactive-world", "interactive-world-editor"],
"projectionKinds": ["provider-authored-world-surface", "provider-authored-world-editor-surface"],
"supportLevel": "terminal-grid-command-surface",
"ownership": "runtime-lowers-provider-world-surface-without-owning-world-state",
"lossiness": "terminal-grid-summary-only",
"evidencePaths": [
"src/eve-tui-shell.mjs",
"test/eve-tui-shell.test.mjs",
"../Eve/web/fixtures/sai-vn-surface.json",
"../Aetheria/conformance/eve/aetheria.provider-advertisement.json",
"../Eve/web/fixtures/eve-world-smoke.provider-advertisement.json"
]
}
],
"commandTransport": {
"schema": "gamecult.eve.command_invocation.v1",
"status": "provider-shell-contract-skeleton",
"emitter": "EveTuiShell.createCommandIntent",
"clientId": "tui",
"authority": "TUI emits command intents through provider-advertised command boundaries; providers accept, deny, and publish receipts."
},
"lifecycle": {
"release": {
"ownerRepo": "EveTui",
"status": "owner-npm-package",
"releaseContract": {
"ownerRepo": "EveTui",
"packageName": "eve-tui",
"packageRoot": ".",
"versionSource": "package.json",
"tagPattern": "evetui-v{version}",
"artifactKind": "terminal-runtime",
"requestSchema": "gamecult.eve.runtime_release_request.v1",
"requestBuilder": "tools/evetui/evetui-release-contract.mjs",
"artifactPattern": "artifacts/evetui-release/{version}/eve-tui-{version}.tgz",
"publishProof": "Terminal runtime release is created from the EveTui repo and references the provider-agnostic TUI package root."
},
"evidencePaths": [
"package.json",
"src/eve-tui-shell.mjs",
"tools/evetui/evetui-release-contract.mjs",
"tools/evetui/evetui-release-contract.test.mjs",
"scripts/run-evetui-release-contract-smoke.ps1"
],
"pendingProofs": []
},
"test": {
"ownerRepo": "EveTui",
"status": "provider-shell-contract-skeleton",
"testContract": {
"ownerRepo": "EveTui",
"runnerKind": "terminal-provider-shell-smoke",
"runnerScript": "scripts/run-evetui-provider-shell-smoke.ps1",
"consumerProject": "../Aetheria",
"packageName": "eve-tui",
"testAssembly": "eve-tui-shell.test.mjs",
"testPlatform": "Node terminal-grid contract skeleton",
"resultsArtifact": "artifacts/evetui-provider-shell/{stamp}/provider-shell-results.json",
"logArtifact": "artifacts/evetui-provider-shell/{stamp}/provider-shell.log",
"manifestMutation": "None; the TUI consumes provider advertisements and must not mutate provider product manifests."
},
"evidencePaths": [
"package.json",
"src/eve-tui-shell.mjs",
"test/eve-tui-shell.test.mjs",
"scripts/run-evetui-provider-shell-smoke.ps1",
"conformance/captures/aetheria/tui-grid.json",
"conformance/captures/world-smoke/tui-grid.json"
],
"pendingProofs": []
},
"capture": {
"ownerRepo": "EveTui",
"status": "cell-grid-json-capture",
"captureContract": {
"ownerRepo": "EveTui",
"runtimeId": "tui",
"targetId": "tui",
"requestSchema": "gamecult.eve.runtime_capture_request.v1",
"requestBuilder": "tools/evetui/evetui-capture-contract.mjs",
"advertisementPath": "../Aetheria/conformance/eve/aetheria.provider-advertisement.json",
"captureKind": "terminal-cell-grid",
"artifactKind": "json-grid",
"artifactPattern": "artifacts/evetui-capture/{stamp}/tui-grid.json",
"conformanceAttachment": "runtime.captureArtifacts[]",
"requiredProvider": "aetheria",
"requiredSurface": "aetheria.daemon.game",
"additionalProviderSurfaces": [
{
"providerId": "eve.world-smoke",
"surfaceId": "eve.world-smoke.surface",
"purpose": "Generic non-product interactive-world capture control."
},
{
"providerId": "aetheria",
"surfaceId": "aetheria.daemon.editor",
"purpose": "Aetheria interactive-world-editor capture control."
}
],
"authority": "Capture observes terminal projection only; provider state, command acceptance, and receipts stay provider-owned.",
"publishProof": "Terminal cell-grid JSON capture artifact is attached to the runtime conformance report from EveTui."
},
"evidencePaths": [
"tools/evetui/evetui-capture-contract.mjs",
"tools/evetui/evetui-capture-contract.test.mjs",
"tools/evetui/evetui-capture-artifact.mjs",
"tools/evetui/evetui-capture-artifact.test.mjs",
"scripts/run-evetui-capture-contract-smoke.ps1",
"scripts/run-evetui-capture-smoke.ps1",
"conformance/captures/aetheria/tui-grid.json",
"conformance/captures/world-smoke/tui-grid.json"
],
"pendingProofs": []
}
},
"capture": {
"status": "json-grid",
"note": "EveTui publishes provider-advertisement-backed gamecult.eve.tui_grid.v1 release captures.",
"artifacts": [
{
"kind": "json-grid",
"schema": "gamecult.eve.tui_grid.v1",
"path": "conformance/captures/aetheria/tui-grid.json",
"providerId": "aetheria",
"surfaceId": "aetheria.daemon.game",
"sourceSurfacePath": "../Aetheria/conformance/eve/aetheria-world-surface.json",
"requestPath": "conformance/captures/aetheria/capture-request.json"
},
{
"kind": "json-grid",
"schema": "gamecult.eve.tui_grid.v1",
"path": "conformance/captures/world-smoke/tui-grid.json",
"providerId": "eve.world-smoke",
"surfaceId": "eve.world-smoke.surface",
"sourceSurfacePath": "../Eve/web/fixtures/eve-world-smoke-surface.json",
"requestPath": "conformance/captures/world-smoke/capture-request.json"
}
]
}
}

18
package.json Normal file
View file

@ -0,0 +1,18 @@
{
"name": "eve-tui",
"version": "0.1.0",
"type": "module",
"description": "Provider-agnostic compact TUI shell skeleton for Eve surfaces.",
"main": "src/eve-tui-shell.mjs",
"scripts": {
"test": "C:\\Progra~1\\nodejs\\node.exe --test test/*.test.mjs",
"test:all": "C:\\Progra~1\\nodejs\\node.exe --test test/*.test.mjs tools/evetui/*.test.mjs"
},
"keywords": [
"Eve",
"TUI",
"CultUI",
"provider-advertisement"
],
"license": "MPL-2.0"
}

View file

@ -0,0 +1,99 @@
param(
[string] $CapabilityManifestPath = "eve-runtime-capability.json",
[string] $AdvertisementPath = "..\Aetheria\conformance\eve\aetheria.provider-advertisement.json",
[string] $OutputPath = "artifacts\evetui-capture-contract\latest\capture-request.json",
[string] $Stamp = "smoke"
)
$ErrorActionPreference = "Stop"
$projectRoot = Split-Path -Parent $PSScriptRoot
$absoluteCapabilityPath = if ([System.IO.Path]::IsPathRooted($CapabilityManifestPath)) {
$CapabilityManifestPath
} else {
Join-Path $projectRoot $CapabilityManifestPath
}
$absoluteAdvertisementPath = if ([System.IO.Path]::IsPathRooted($AdvertisementPath)) {
$AdvertisementPath
} else {
Join-Path $projectRoot $AdvertisementPath
}
$absoluteOutputPath = if ([System.IO.Path]::IsPathRooted($OutputPath)) {
$OutputPath
} else {
Join-Path $projectRoot $OutputPath
}
foreach ($path in @($absoluteCapabilityPath, $absoluteAdvertisementPath)) {
if (-not (Test-Path -LiteralPath $path)) {
throw "EveTui capture contract input not found: $path"
}
}
$manifest = Get-Content -LiteralPath $absoluteCapabilityPath -Raw | ConvertFrom-Json
$captureContract = $manifest.lifecycle.capture.captureContract
if ($null -eq $captureContract) {
throw "EveTui capture contract missing lifecycle.capture.captureContract"
}
foreach ($field in @("ownerRepo", "runtimeId", "targetId", "requestSchema", "requestBuilder", "advertisementPath", "captureKind", "artifactKind", "artifactPattern", "conformanceAttachment", "requiredProvider", "requiredSurface", "authority", "publishProof")) {
if (-not $captureContract.$field) {
throw "EveTui capture contract missing $field"
}
}
if ($captureContract.ownerRepo -ne "EveTui") {
throw "Unexpected EveTui capture owner: $($captureContract.ownerRepo)"
}
if ($captureContract.runtimeId -ne "tui") {
throw "Unexpected EveTui capture runtime: $($captureContract.runtimeId)"
}
if ($captureContract.targetId -ne "tui") {
throw "Unexpected EveTui capture target: $($captureContract.targetId)"
}
if ($captureContract.requestSchema -ne "gamecult.eve.runtime_capture_request.v1") {
throw "Unexpected EveTui capture request schema: $($captureContract.requestSchema)"
}
$builderPath = Join-Path $projectRoot $captureContract.requestBuilder
if (-not (Test-Path -LiteralPath $builderPath)) {
throw "EveTui capture request builder not found: $($captureContract.requestBuilder)"
}
if ((Join-Path $projectRoot $captureContract.advertisementPath) -ne $absoluteAdvertisementPath) {
throw "EveTui capture advertisement path mismatch: $($captureContract.advertisementPath) vs $AdvertisementPath"
}
node --test (Join-Path $projectRoot "tools\evetui\evetui-capture-contract.test.mjs")
if ($LASTEXITCODE -ne 0) {
throw "EveTui capture contract tests failed with exit code $LASTEXITCODE"
}
node $builderPath --capability $absoluteCapabilityPath --advertisement $absoluteAdvertisementPath --output $absoluteOutputPath --stamp $Stamp
if ($LASTEXITCODE -ne 0) {
throw "EveTui capture request builder failed with exit code $LASTEXITCODE"
}
if (-not (Test-Path -LiteralPath $absoluteOutputPath)) {
throw "EveTui capture request was not written: $absoluteOutputPath"
}
$request = Get-Content -LiteralPath $absoluteOutputPath -Raw | ConvertFrom-Json
foreach ($expectation in @(
@{ field = "schema"; value = "gamecult.eve.runtime_capture_request.v1" },
@{ field = "runtimeId"; value = "tui" },
@{ field = "ownerRepo"; value = "EveTui" },
@{ field = "providerId"; value = "aetheria" },
@{ field = "surfaceId"; value = "aetheria.daemon.game" },
@{ field = "targetId"; value = "tui" },
@{ field = "commandBoundary"; value = "aetheria.daemon.commands" },
@{ field = "receiptSchema"; value = "aetheria.eve_command_acceptance_status.v1" },
@{ field = "captureKind"; value = "terminal-cell-grid" },
@{ field = "artifactKind"; value = "json-grid" },
@{ field = "conformanceAttachment"; value = "runtime.captureArtifacts[]" }
)) {
if ($request.($expectation.field) -ne $expectation.value) {
throw "EveTui capture request $($expectation.field) expected $($expectation.value) got $($request.($expectation.field))"
}
}
if ($request.artifactPath -ne "artifacts/evetui-capture/$Stamp/tui-grid.json") {
throw "Unexpected EveTui capture artifact path: $($request.artifactPath)"
}
Write-Host "EveTui capture contract smoke passed: $absoluteOutputPath"

View file

@ -0,0 +1,77 @@
param(
[string] $CapabilityManifestPath = "eve-runtime-capability.json",
[string] $AdvertisementPath = "..\Aetheria\conformance\eve\aetheria.provider-advertisement.json",
[string] $SurfacePath = "..\Aetheria\conformance\eve\aetheria-world-surface.json",
[string] $OutputPath = "artifacts\evetui-capture\latest\tui-grid.json",
[string] $RequestOutputPath = "artifacts\evetui-capture\latest\capture-request.json",
[string] $Stamp = "latest",
[string] $ExpectedProviderId = "aetheria",
[string] $ExpectedSurfaceId = "aetheria.daemon.game",
[string] $ExpectedCommandBoundary = "aetheria.daemon.commands",
[string] $ExpectedReceiptSchema = "aetheria.eve_command_acceptance_status.v1"
)
$ErrorActionPreference = "Stop"
$projectRoot = Split-Path -Parent $PSScriptRoot
function Resolve-RepoPath([string] $candidate) {
if ([System.IO.Path]::IsPathRooted($candidate)) {
return $candidate
}
return Join-Path $projectRoot $candidate
}
$absoluteCapabilityPath = Resolve-RepoPath $CapabilityManifestPath
$absoluteAdvertisementPath = Resolve-RepoPath $AdvertisementPath
$absoluteSurfacePath = Resolve-RepoPath $SurfacePath
$absoluteOutputPath = Resolve-RepoPath $OutputPath
$absoluteRequestOutputPath = Resolve-RepoPath $RequestOutputPath
foreach ($path in @($absoluteCapabilityPath, $absoluteAdvertisementPath, $absoluteSurfacePath)) {
if (-not (Test-Path -LiteralPath $path)) {
throw "EveTui capture input not found: $path"
}
}
node --test (Join-Path $projectRoot "tools\evetui\evetui-capture-artifact.test.mjs")
if ($LASTEXITCODE -ne 0) {
throw "EveTui capture artifact tests failed with exit code $LASTEXITCODE"
}
node (Join-Path $projectRoot "tools\evetui\evetui-capture-artifact.mjs") `
--capability $absoluteCapabilityPath `
--advertisement $absoluteAdvertisementPath `
--surface $absoluteSurfacePath `
--output $absoluteOutputPath `
--request-output $absoluteRequestOutputPath `
--stamp $Stamp
if ($LASTEXITCODE -ne 0) {
throw "EveTui capture artifact builder failed with exit code $LASTEXITCODE"
}
foreach ($path in @($absoluteOutputPath, $absoluteRequestOutputPath)) {
if (-not (Test-Path -LiteralPath $path)) {
throw "EveTui capture artifact output missing: $path"
}
}
$grid = Get-Content -LiteralPath $absoluteOutputPath -Raw | ConvertFrom-Json
foreach ($expectation in @(
@{ field = "schema"; value = "gamecult.eve.tui_grid.v1" },
@{ field = "runtimeId"; value = "tui" },
@{ field = "providerId"; value = $ExpectedProviderId },
@{ field = "surfaceId"; value = $ExpectedSurfaceId },
@{ field = "commandBoundary"; value = $ExpectedCommandBoundary },
@{ field = "receiptSchema"; value = $ExpectedReceiptSchema },
@{ field = "artifactKind"; value = "json-grid" },
@{ field = "captureKind"; value = "terminal-cell-grid" }
)) {
if ($grid.($expectation.field) -ne $expectation.value) {
throw "EveTui grid $($expectation.field) expected $($expectation.value) got $($grid.($expectation.field))"
}
}
if (@($grid.lines).Count -lt 1) {
throw "EveTui grid capture must contain terminal lines"
}
Write-Host "EveTui capture smoke passed: $absoluteOutputPath"

View file

@ -0,0 +1,195 @@
param(
[string] $CapabilityManifestPath = "eve-runtime-capability.json"
)
$ErrorActionPreference = "Stop"
$projectRoot = Split-Path -Parent $PSScriptRoot
$absoluteManifestPath = if ([System.IO.Path]::IsPathRooted($CapabilityManifestPath)) {
$CapabilityManifestPath
} else {
Join-Path $projectRoot $CapabilityManifestPath
}
if (-not (Test-Path -LiteralPath $absoluteManifestPath)) {
throw "EveTui runtime capability manifest not found: $absoluteManifestPath"
}
$manifest = Get-Content -LiteralPath $absoluteManifestPath -Raw | ConvertFrom-Json
if ($manifest.schema -ne "gamecult.eve.runtime_capability.v1") {
throw "Unexpected EveTui manifest schema: $($manifest.schema)"
}
if ($manifest.runtimeId -ne "tui") {
throw "Unexpected EveTui runtime id: $($manifest.runtimeId)"
}
if ($manifest.owner -ne "EveTui") {
throw "Unexpected EveTui owner: $($manifest.owner)"
}
if ($manifest.incubation.ownerRepo -ne "EveTui") {
throw "Unexpected EveTui incubation owner: $($manifest.incubation.ownerRepo)"
}
if ($manifest.incubation.currentHostRepo -ne "EveTui") {
throw "Unexpected EveTui current host repo: $($manifest.incubation.currentHostRepo)"
}
if ($manifest.incubation.splitTarget -ne "EveTui") {
throw "Unexpected EveTui split target: $($manifest.incubation.splitTarget)"
}
foreach ($feature in @("providerAdvertisements", "commandTransport", "terminalGridSummary", "terminalGridLowering", "embeddedDocuments", "pluginProjection")) {
if (-not (@($manifest.supportedFeatures) -contains $feature)) {
throw "EveTui manifest missing provider-shell feature: $feature"
}
}
$expectedPlugins = @{
"sai.vn" = @("vn.stage", "story.choose", "story.continue", "story.jump")
"norn.graph" = @("embed.norn")
"tex.math" = @("embed.tex", "tex.inline", "tex.block")
}
foreach ($pluginId in $expectedPlugins.Keys) {
$supported = @($manifest.supportedPlugins) | Where-Object { $_.pluginId -eq $pluginId } | Select-Object -First 1
if (-not $supported) {
throw "EveTui manifest missing supported plugin declaration: $pluginId"
}
foreach ($capability in $expectedPlugins[$pluginId]) {
if (-not (@($supported.capabilities) -contains $capability)) {
throw "EveTui supported plugin $pluginId missing capability: $capability"
}
}
if ($supported.projectionAdapter -ne "EveTuiShell.buildPluginProjection") {
throw "EveTui supported plugin $pluginId has unexpected projection adapter: $($supported.projectionAdapter)"
}
}
$worldSurfaceLoweringClaims = @()
if ($null -ne $manifest.worldSurfaceLowering) {
$worldSurfaceLoweringClaims = @($manifest.worldSurfaceLowering)
}
if ($worldSurfaceLoweringClaims.Count -ne 1) {
throw "EveTui must claim exactly one world-surface lowering target"
}
$worldSurfaceLoweringClaim = $worldSurfaceLoweringClaims | Where-Object { $_.targetId -eq "tui" } | Select-Object -First 1
if (-not $worldSurfaceLoweringClaim) {
throw "EveTui manifest missing tui world-surface lowering claim"
}
if ($worldSurfaceLoweringClaim.supportLevel -ne "terminal-grid-command-surface") {
throw "Unexpected EveTui world-surface support level: $($worldSurfaceLoweringClaim.supportLevel)"
}
if ($worldSurfaceLoweringClaim.ownership -ne "runtime-lowers-provider-world-surface-without-owning-world-state") {
throw "Unexpected EveTui world-surface ownership: $($worldSurfaceLoweringClaim.ownership)"
}
foreach ($evidencePath in @($worldSurfaceLoweringClaim.evidencePaths)) {
$absoluteEvidencePath = Join-Path $projectRoot $evidencePath
if (-not (Test-Path -LiteralPath $absoluteEvidencePath)) {
throw "EveTui world-surface claim missing evidence path: $evidencePath"
}
}
foreach ($pluginId in $expectedPlugins.Keys) {
$unsupported = @($manifest.unsupportedPlugins) | Where-Object { $_.pluginId -eq $pluginId } | Select-Object -First 1
if ($unsupported) {
throw "EveTui manifest must not report $pluginId unsupported while terminal fallback projection is declared"
}
}
if ($manifest.commandTransport.schema -ne "gamecult.eve.command_invocation.v1") {
throw "Unexpected EveTui command schema: $($manifest.commandTransport.schema)"
}
if ($manifest.commandTransport.status -ne "provider-shell-contract-skeleton") {
throw "Unexpected EveTui command transport status: $($manifest.commandTransport.status)"
}
foreach ($stage in @("release", "test", "capture")) {
$stageDocument = $manifest.lifecycle.$stage
if ($null -eq $stageDocument) {
throw "EveTui lifecycle missing stage: $stage"
}
if ($stageDocument.ownerRepo -ne "EveTui") {
throw "EveTui lifecycle stage $stage has unexpected owner: $($stageDocument.ownerRepo)"
}
if (-not $stageDocument.status) {
throw "EveTui lifecycle stage $stage missing status"
}
foreach ($evidencePath in @($stageDocument.evidencePaths)) {
$absoluteEvidencePath = Join-Path $projectRoot $evidencePath
if (-not (Test-Path -LiteralPath $absoluteEvidencePath)) {
throw "EveTui lifecycle stage $stage missing evidence path: $evidencePath"
}
}
}
$releaseContract = $manifest.lifecycle.release.releaseContract
foreach ($field in @("ownerRepo", "packageName", "packageRoot", "versionSource", "tagPattern", "artifactKind", "requestSchema", "requestBuilder", "artifactPattern", "publishProof")) {
if (-not $releaseContract.$field) {
throw "EveTui release contract missing $field"
}
}
if ($releaseContract.requestSchema -ne "gamecult.eve.runtime_release_request.v1") {
throw "EveTui release contract has unexpected request schema: $($releaseContract.requestSchema)"
}
foreach ($pathProperty in @("packageRoot", "versionSource")) {
$relativePath = $releaseContract.$pathProperty
$absolutePath = Join-Path $projectRoot $relativePath
if (-not (Test-Path -LiteralPath $absolutePath)) {
throw "EveTui release contract $pathProperty does not exist: $relativePath"
}
}
foreach ($pathProperty in @("requestBuilder")) {
$relativePath = $releaseContract.$pathProperty
$absolutePath = Join-Path $projectRoot $relativePath
if (-not (Test-Path -LiteralPath $absolutePath)) {
throw "EveTui release contract $pathProperty does not exist: $relativePath"
}
}
$testContract = $manifest.lifecycle.test.testContract
foreach ($field in @("ownerRepo", "runnerKind", "runnerScript", "consumerProject", "packageName", "testAssembly", "testPlatform", "resultsArtifact", "logArtifact", "manifestMutation")) {
if (-not $testContract.$field) {
throw "EveTui test contract missing $field"
}
}
foreach ($pathProperty in @("runnerScript", "consumerProject")) {
$candidate = $testContract.$pathProperty
$absolutePath = if ([System.IO.Path]::IsPathRooted($candidate)) {
$candidate
} else {
Join-Path $projectRoot $candidate
}
if (-not (Test-Path -LiteralPath $absolutePath)) {
throw "EveTui test contract $pathProperty does not exist: $candidate"
}
}
$captureContract = $manifest.lifecycle.capture.captureContract
foreach ($field in @("ownerRepo", "runtimeId", "targetId", "requestSchema", "requestBuilder", "advertisementPath", "captureKind", "artifactKind", "artifactPattern", "conformanceAttachment", "requiredProvider", "requiredSurface", "authority", "publishProof")) {
if (-not $captureContract.$field) {
throw "EveTui capture contract missing $field"
}
}
if ($captureContract.runtimeId -ne "tui") {
throw "EveTui capture contract has unexpected runtime: $($captureContract.runtimeId)"
}
if ($captureContract.targetId -ne "tui") {
throw "EveTui capture contract has unexpected target: $($captureContract.targetId)"
}
if ($captureContract.requestSchema -ne "gamecult.eve.runtime_capture_request.v1") {
throw "EveTui capture contract has unexpected request schema: $($captureContract.requestSchema)"
}
if ($captureContract.captureKind -ne "terminal-cell-grid") {
throw "EveTui capture contract has unexpected capture kind: $($captureContract.captureKind)"
}
foreach ($pathProperty in @("requestBuilder", "advertisementPath")) {
$relativePath = $captureContract.$pathProperty
$absolutePath = Join-Path $projectRoot $relativePath
if (-not (Test-Path -LiteralPath $absolutePath)) {
throw "EveTui capture contract $pathProperty does not exist: $relativePath"
}
}
& (Join-Path $projectRoot "scripts\run-evetui-release-contract-smoke.ps1")
& (Join-Path $projectRoot "scripts\run-evetui-provider-shell-smoke.ps1")
& (Join-Path $projectRoot "scripts\run-evetui-capture-contract-smoke.ps1")
& (Join-Path $projectRoot "scripts\run-evetui-capture-smoke.ps1")
Write-Host "EveTui lifecycle smoke passed: $absoluteManifestPath"

View file

@ -0,0 +1,111 @@
param(
[string] $CapabilityManifestPath = "eve-runtime-capability.json"
)
$ErrorActionPreference = "Stop"
$projectRoot = Split-Path -Parent $PSScriptRoot
$absoluteManifestPath = if ([System.IO.Path]::IsPathRooted($CapabilityManifestPath)) {
$CapabilityManifestPath
} else {
Join-Path $projectRoot $CapabilityManifestPath
}
if (-not (Test-Path -LiteralPath $absoluteManifestPath)) {
throw "EveTui runtime capability manifest not found: $absoluteManifestPath"
}
$manifest = Get-Content -LiteralPath $absoluteManifestPath -Raw | ConvertFrom-Json
if ($manifest.runtimeId -ne "tui") {
throw "Unexpected EveTui runtime id: $($manifest.runtimeId)"
}
if ($manifest.lifecycle.test.status -ne "provider-shell-contract-skeleton") {
throw "Unexpected EveTui provider shell status: $($manifest.lifecycle.test.status)"
}
foreach ($feature in @("providerAdvertisements", "commandTransport", "terminalGridSummary", "terminalGridLowering", "pluginProjection")) {
if (-not (@($manifest.supportedFeatures) -contains $feature)) {
throw "EveTui provider shell missing supported feature: $feature"
}
}
foreach ($pluginId in @("sai.vn", "norn.graph", "tex.math")) {
$supported = @($manifest.supportedPlugins) | Where-Object { $_.pluginId -eq $pluginId } | Select-Object -First 1
if (-not $supported) {
throw "EveTui provider shell missing supported plugin projection: $pluginId"
}
}
$worldSurfaceLoweringClaims = @()
if ($null -ne $manifest.worldSurfaceLowering) {
$worldSurfaceLoweringClaims = @($manifest.worldSurfaceLowering)
}
if ($worldSurfaceLoweringClaims.Count -ne 1) {
throw "EveTui must claim exactly one provider world-surface lowering target"
}
$worldSurfaceLoweringClaim = $worldSurfaceLoweringClaims | Where-Object { $_.targetId -eq "tui" } | Select-Object -First 1
if (-not $worldSurfaceLoweringClaim) {
throw "EveTui missing tui world-surface lowering claim"
}
foreach ($surfaceKind in @("interactive-world", "interactive-world-editor")) {
if (-not (@($worldSurfaceLoweringClaim.surfaceKinds) -contains $surfaceKind)) {
throw "EveTui world-surface claim missing surface kind: $surfaceKind"
}
}
foreach ($projectionKind in @("provider-authored-world-surface", "provider-authored-world-editor-surface")) {
if (-not (@($worldSurfaceLoweringClaim.projectionKinds) -contains $projectionKind)) {
throw "EveTui world-surface claim missing projection kind: $projectionKind"
}
}
if ($worldSurfaceLoweringClaim.supportLevel -ne "terminal-grid-command-surface") {
throw "Unexpected EveTui support level: $($worldSurfaceLoweringClaim.supportLevel)"
}
if ($worldSurfaceLoweringClaim.ownership -ne "runtime-lowers-provider-world-surface-without-owning-world-state") {
throw "Unexpected EveTui ownership: $($worldSurfaceLoweringClaim.ownership)"
}
foreach ($evidencePath in @($worldSurfaceLoweringClaim.evidencePaths)) {
$absoluteEvidencePath = Join-Path $projectRoot $evidencePath
if (-not (Test-Path -LiteralPath $absoluteEvidencePath)) {
throw "EveTui world-surface claim missing evidence path: $evidencePath"
}
}
$expectedFiles = @(
"package.json",
"src\eve-tui-shell.mjs",
"test\eve-tui-shell.test.mjs"
)
foreach ($relativePath in $expectedFiles) {
$absolutePath = Join-Path $projectRoot $relativePath
if (-not (Test-Path -LiteralPath $absolutePath)) {
throw "EveTui provider shell missing file: $relativePath"
}
}
$shellSource = Get-Content -LiteralPath (Join-Path $projectRoot "src\eve-tui-shell.mjs") -Raw
foreach ($symbol in @("EveTuiShell", "selectSurface", "lowerSurface", "normalizeSurfaceDocument", "buildComponentLines", "buildPluginProjection", "collectPluginProjections", "terminalElementKind", "sai.vn", "norn.graph", "tex.math", "sidecar-advertised-plugin-abi", "createCommandIntent", "renderSummary", "gamecult.eve.tui_grid.v1", "commandBoundary", "receiptSchema")) {
if (-not $shellSource.Contains($symbol)) {
throw "EveTui provider shell source missing symbol: $symbol"
}
}
$testSource = Get-Content -LiteralPath (Join-Path $projectRoot "test\eve-tui-shell.test.mjs") -Raw
foreach ($symbol in @("selects advertised provider surfaces", "command intents carry provider-advertised boundaries", "summary grid is explicitly lossy", "lowers provider surface trees into a terminal grid", "lowers Sai, Norn, and TeX plugin surfaces into compact terminal fallbacks", "pluginProjections", "sai-vn-terminal-stage-summary", "norn-graph-terminal-outline", "tex-math-terminal-block-source", "rejects surface documents that do not match the advertised TUI target", "aetheria.daemon.commands")) {
if (-not $testSource.Contains($symbol)) {
throw "EveTui provider shell test missing symbol: $symbol"
}
}
Push-Location $projectRoot
try {
npm test
if ($LASTEXITCODE -ne 0) {
throw "EveTui provider shell tests failed with exit code $LASTEXITCODE"
}
} finally {
Pop-Location
}
Write-Host "EveTui provider shell smoke passed: $absoluteManifestPath"

View file

@ -0,0 +1,98 @@
param(
[string] $CapabilityManifestPath = "eve-runtime-capability.json",
[string] $OutputPath = "artifacts\evetui-release-contract\latest\release-request.json"
)
$ErrorActionPreference = "Stop"
$projectRoot = Split-Path -Parent $PSScriptRoot
$absoluteCapabilityPath = if ([System.IO.Path]::IsPathRooted($CapabilityManifestPath)) {
$CapabilityManifestPath
} else {
Join-Path $projectRoot $CapabilityManifestPath
}
$absoluteOutputPath = if ([System.IO.Path]::IsPathRooted($OutputPath)) {
$OutputPath
} else {
Join-Path $projectRoot $OutputPath
}
if (-not (Test-Path -LiteralPath $absoluteCapabilityPath)) {
throw "EveTui release contract input not found: $absoluteCapabilityPath"
}
$manifest = Get-Content -LiteralPath $absoluteCapabilityPath -Raw | ConvertFrom-Json
$releaseContract = $manifest.lifecycle.release.releaseContract
if ($null -eq $releaseContract) {
throw "EveTui release contract missing lifecycle.release.releaseContract"
}
foreach ($field in @("ownerRepo", "packageName", "packageRoot", "versionSource", "tagPattern", "artifactKind", "requestSchema", "requestBuilder", "artifactPattern", "publishProof")) {
if (-not $releaseContract.$field) {
throw "EveTui release contract missing $field"
}
}
if ($releaseContract.ownerRepo -ne "EveTui") {
throw "Unexpected EveTui release owner: $($releaseContract.ownerRepo)"
}
if ($releaseContract.packageName -ne "eve-tui") {
throw "Unexpected EveTui package name: $($releaseContract.packageName)"
}
if ($releaseContract.requestSchema -ne "gamecult.eve.runtime_release_request.v1") {
throw "Unexpected EveTui release request schema: $($releaseContract.requestSchema)"
}
if ($releaseContract.artifactKind -ne "terminal-runtime") {
throw "Unexpected EveTui release artifact kind: $($releaseContract.artifactKind)"
}
$builderPath = Join-Path $projectRoot $releaseContract.requestBuilder
$packagePath = Join-Path $projectRoot $releaseContract.versionSource
$packageRoot = Join-Path $projectRoot $releaseContract.packageRoot
foreach ($path in @($builderPath, $packagePath, $packageRoot)) {
if (-not (Test-Path -LiteralPath $path)) {
throw "EveTui release contract path not found: $path"
}
}
node --test (Join-Path $projectRoot "tools\evetui\evetui-release-contract.test.mjs")
if ($LASTEXITCODE -ne 0) {
throw "EveTui release contract tests failed with exit code $LASTEXITCODE"
}
node $builderPath --capability $absoluteCapabilityPath --package $packagePath --output $absoluteOutputPath
if ($LASTEXITCODE -ne 0) {
throw "EveTui release request builder failed with exit code $LASTEXITCODE"
}
if (-not (Test-Path -LiteralPath $absoluteOutputPath)) {
throw "EveTui release request was not written: $absoluteOutputPath"
}
$request = Get-Content -LiteralPath $absoluteOutputPath -Raw | ConvertFrom-Json
$packageManifest = Get-Content -LiteralPath $packagePath -Raw | ConvertFrom-Json
foreach ($expectation in @(
@{ field = "schema"; value = "gamecult.eve.runtime_release_request.v1" },
@{ field = "ownerRepo"; value = "EveTui" },
@{ field = "repository"; value = "GameCult/EveTui" },
@{ field = "packageName"; value = "eve-tui" },
@{ field = "version"; value = $packageManifest.version },
@{ field = "packageRoot"; value = "." },
@{ field = "versionSource"; value = "package.json" },
@{ field = "artifactKind"; value = "terminal-runtime" }
)) {
if ($request.($expectation.field) -ne $expectation.value) {
throw "EveTui release request $($expectation.field) expected $($expectation.value) got $($request.($expectation.field))"
}
}
$expectedTagName = $releaseContract.tagPattern.Replace("{version}", $packageManifest.version)
if ($request.tagName -ne $expectedTagName) {
throw "Unexpected EveTui release tag: $($request.tagName)"
}
$expectedArtifactPath = $releaseContract.artifactPattern.Replace("{version}", $packageManifest.version)
if ($request.artifactPath -ne $expectedArtifactPath) {
throw "Unexpected EveTui release artifact path: $($request.artifactPath)"
}
if ($request.main -ne $packageManifest.main) {
throw "EveTui release request lost package main entry"
}
Write-Host "EveTui release contract smoke passed: $absoluteOutputPath"

297
src/eve-tui-shell.mjs Normal file
View file

@ -0,0 +1,297 @@
export class EveTuiShell {
constructor({ clientId = "tui", width = 80 } = {}) {
this.clientId = clientId;
this.width = Math.max(20, Number(width) || 80);
}
selectSurface(providerAdvertisement, requestedSurfaceId = "") {
const provider = normalizeProviderAdvertisement(providerAdvertisement);
const surface = requestedSurfaceId
? provider.surfaces.find(candidate => candidate.surfaceId === requestedSurfaceId)
: provider.surfaces[0];
if (!surface) {
throw new Error(`Provider ${provider.providerId} does not advertise surface ${requestedSurfaceId || "(first)"}`);
}
return {
providerId: provider.providerId,
surfaceId: surface.surfaceId,
title: surface.title || provider.title || surface.surfaceId,
transport: surface.transport || "",
url: surface.url || surface.key || "",
worldInteraction: normalizeWorldInteraction(surface.worldInteraction),
};
}
createCommandIntent(providerAdvertisement, requestedSurfaceId, command, payload = {}) {
if (!command) throw new Error("Command is required.");
const selected = this.selectSurface(providerAdvertisement, requestedSurfaceId);
const action = objectValue(payload.action);
const commandBoundary = firstString(
selected.worldInteraction.commandBoundary,
payload.commandBoundary,
action.commandBoundary,
action.target,
);
const receiptSchema = firstString(
selected.worldInteraction.receiptSchema,
payload.receiptSchema,
action.receiptSchema,
);
const intent = {
type: "surface-command",
schema: "gamecult.eve.command_invocation.v1",
providerId: selected.providerId,
surfaceId: selected.surfaceId,
command,
payload: {
...payload,
transport: payload.transport ?? null,
},
issuedAt: new Date().toISOString(),
clientId: this.clientId,
};
if (commandBoundary) intent.commandBoundary = commandBoundary;
if (receiptSchema) intent.receiptSchema = receiptSchema;
return intent;
}
lowerSurface(surfaceDocument, providerAdvertisement, requestedSurfaceId = "") {
const selected = this.selectSurface(providerAdvertisement, requestedSurfaceId);
const document = normalizeSurfaceDocument(surfaceDocument);
if (document.surfaceId !== selected.surfaceId) {
throw new Error(`Surface document ${document.surfaceId} does not match advertised TUI surface ${selected.surfaceId}.`);
}
const lines = [
fitLine(`EveTui ${selected.providerId}`, this.width),
fitLine(`surface ${selected.surfaceId}`, this.width),
fitLine(`command ${selected.worldInteraction.commandBoundary || "unadvertised"}`, this.width),
fitLine(`receipt ${selected.worldInteraction.receiptSchema || "unadvertised"}`, this.width),
...buildComponentLines(document.root, this.width),
];
return {
schema: "gamecult.eve.tui_grid.v1",
runtimeId: "tui",
providerId: selected.providerId,
surfaceId: selected.surfaceId,
projectionKind: selected.worldInteraction.projectionKind,
commandBoundary: selected.worldInteraction.commandBoundary,
receiptSchema: selected.worldInteraction.receiptSchema,
ownership: selected.worldInteraction.ownership,
width: this.width,
lines,
embeddedDocumentSlots: collectEmbeddedDocumentSlots(document.root),
pluginProjections: collectPluginProjections(document.root),
lossiness: "terminal-grid-command-surface; summarizes provider-authored world surface without owning provider state",
};
}
renderSummary(providerAdvertisement, requestedSurfaceId = "") {
const selected = this.selectSurface(providerAdvertisement, requestedSurfaceId);
const lines = [
fitLine(`EveTui ${selected.providerId}`, this.width),
fitLine(`surface ${selected.surfaceId}`, this.width),
fitLine(`transport ${selected.transport || "unknown"}`, this.width),
fitLine(`command ${selected.worldInteraction.commandBoundary || "unadvertised"}`, this.width),
fitLine(`receipt ${selected.worldInteraction.receiptSchema || "unadvertised"}`, this.width),
];
return {
schema: "gamecult.eve.tui_grid.v1",
runtimeId: "tui",
providerId: selected.providerId,
surfaceId: selected.surfaceId,
width: this.width,
lines,
lossiness: "provider-shell-summary-only; use lowerSurface for TUI world-surface lowering",
};
}
}
export function normalizeProviderAdvertisement(providerAdvertisement) {
if (!providerAdvertisement || typeof providerAdvertisement !== "object") {
throw new Error("Provider advertisement is required.");
}
const providerId = String(providerAdvertisement.providerId || "");
if (!providerId) throw new Error("Provider advertisement missing providerId.");
const surfaces = Array.isArray(providerAdvertisement.surfaces)
? providerAdvertisement.surfaces
.filter(surface => surface && typeof surface === "object" && surface.surfaceId)
.map(surface => ({ ...surface, surfaceId: String(surface.surfaceId) }))
: [];
if (!surfaces.length) throw new Error(`Provider ${providerId} advertises no surfaces.`);
return { ...providerAdvertisement, providerId, surfaces };
}
export function normalizeSurfaceDocument(surfaceDocument) {
if (!surfaceDocument || typeof surfaceDocument !== "object") {
throw new Error("Surface document is required.");
}
if (surfaceDocument.schema !== "gamecult.eve.surface.v1") {
throw new Error(`Unexpected surface schema: ${surfaceDocument.schema || ""}`);
}
const surface = objectValue(surfaceDocument.surface);
const surfaceId = firstString(surface.id);
if (!surfaceId) throw new Error("Surface document missing surface.id.");
const root = objectValue(surface.root);
if (!root.id) throw new Error(`Surface document ${surfaceId} missing surface.root.`);
return { ...surfaceDocument, surfaceId, root };
}
function normalizeWorldInteraction(value) {
const source = objectValue(value);
return {
projectionKind: firstString(source.projectionKind),
commandBoundary: firstString(source.commandBoundary),
receiptSchema: firstString(source.receiptSchema),
ownership: firstString(source.ownership),
};
}
function buildComponentLines(component, width, depth = 0) {
const source = objectValue(component);
const kind = firstString(source.kind) || "component";
const id = firstString(source.id) || "(anonymous)";
const props = objectValue(source.props);
const label = firstString(props.label, props.title, props.text, props.bind, props.command, props.fieldId);
const indent = " ".repeat(Math.min(depth, 6));
const pluginProjection = buildPluginProjection(kind, source);
const pluginSuffix = pluginProjection
? ` [${pluginProjection.pluginId}:${pluginProjection.semanticOwner}]`
: "";
const ownLine = fitLine(`${indent}${terminalElementKind(kind)} ${id}${label ? ` ${label}` : ""}${pluginSuffix}`, width);
const children = Array.isArray(source.children) ? source.children : [];
const embeddedDocuments = normalizeEmbeddedDocuments(source.embeddedDocuments);
return [
ownLine,
...embeddedDocuments.map(document => fitLine(`${indent} slot ${document.slotId || "(slot)"} ${document.documentId || "(document)"}`, width)),
...children.flatMap(child => buildComponentLines(child, width, depth + 1)),
];
}
function collectEmbeddedDocumentSlots(component, slots = []) {
const source = objectValue(component);
const ownerId = firstString(source.id);
for (const document of normalizeEmbeddedDocuments(source.embeddedDocuments)) {
slots.push({ ownerId, ...document });
}
for (const child of Array.isArray(source.children) ? source.children : []) {
collectEmbeddedDocumentSlots(child, slots);
}
return slots;
}
function collectPluginProjections(component, projections = []) {
const source = objectValue(component);
const projection = buildPluginProjection(firstString(source.kind), source);
if (projection) {
projections.push({
ownerId: firstString(source.id),
...projection,
});
}
for (const child of Array.isArray(source.children) ? source.children : []) {
collectPluginProjections(child, projections);
}
return projections;
}
function normalizeEmbeddedDocuments(value) {
return Array.isArray(value)
? value
.filter(document => document && typeof document === "object")
.map(document => ({
slotId: firstString(document.slotId, document.id),
documentId: firstString(document.documentId, document.href, document.url),
schemaId: firstString(document.schemaId, document.schema),
presentationKind: firstString(document.presentationKind, document.kind),
}))
.filter(document => document.slotId || document.documentId)
: [];
}
function terminalElementKind(componentKind) {
if (!componentKind) return "empty";
if (componentKind === "vn.stage") return "sai-vn";
if (componentKind === "embed.norn") return "norn";
if (componentKind === "embed.tex") return "tex";
if (componentKind.startsWith("control.")) return "command";
if (componentKind.startsWith("embed.")) return "plugin";
if (componentKind === "surface.slot") return "slot";
if (componentKind.startsWith("field.") || componentKind.startsWith("world.")) return "world";
if (componentKind.startsWith("text.") || componentKind === "text" || componentKind === "label") return "text";
if (componentKind === "metric") return "metric";
return componentKind;
}
function buildPluginProjection(componentKind, component) {
const props = objectValue(component.props);
if (componentKind === "vn.stage") {
return {
pluginId: "sai.vn",
projectionKind: "sai-vn-terminal-stage-summary",
abiSchema: "gamecult.eve.plugin_abi.v1",
commandBoundary: "sidecar-advertised-plugin-abi",
capabilities: ["vn.stage", "story.choose", "story.continue", "story.jump"],
documentId: firstString(props.storyId, component.id),
semanticOwner: "Sai",
availability: "required",
fallbackKind: "terminal-summary",
};
}
if (componentKind === "embed.norn") {
return {
pluginId: "norn.graph",
projectionKind: "norn-graph-terminal-outline",
abiSchema: "gamecult.eve.plugin_abi.v1",
commandBoundary: "sidecar-advertised-plugin-abi",
capabilities: ["embed.norn"],
documentId: firstString(props.sourceUri, props.documentId, component.id),
semanticOwner: "Norn",
availability: "optional-nested",
fallbackKind: "terminal-outline",
};
}
if (componentKind === "embed.tex") {
return {
pluginId: "tex.math",
projectionKind: texProjectionKind(props),
abiSchema: "gamecult.eve.plugin_abi.v1",
commandBoundary: "sidecar-advertised-plugin-abi",
capabilities: ["embed.tex", "tex.inline", "tex.block"],
documentId: firstString(props.sourceUri, props.source, component.id),
semanticOwner: "EvePlugins",
availability: "optional-nested",
fallbackKind: "source-text",
};
}
return null;
}
function texProjectionKind(props) {
const display = firstString(props.display, "inline");
if (display === "block") return "tex-math-terminal-block-source";
if (display === "page") return "tex-math-terminal-page-source";
return "tex-math-terminal-inline-source";
}
function fitLine(value, width) {
const text = String(value || "");
if (text.length <= width) return text;
return `${text.slice(0, Math.max(0, width - 3))}...`;
}
function objectValue(value) {
return value && typeof value === "object" && !Array.isArray(value) ? value : {};
}
function firstString(...values) {
for (const value of values) {
if (typeof value === "string" && value.trim()) return value;
if (typeof value === "number" && Number.isFinite(value)) return String(value);
}
return "";
}

View file

@ -0,0 +1,71 @@
export function validateSchemaSubset(schema, value, pointer = "$", rootSchema = schema) {
const errors = [];
if (!schema || typeof schema !== "object") return errors;
if (schema.$ref) {
const resolved = resolveSchemaReference(rootSchema, schema.$ref);
return resolved
? validateSchemaSubset(resolved, value, pointer, rootSchema)
: [`${pointer}:unresolved-ref:${schema.$ref}`];
}
if (schema.const !== undefined && value !== schema.const) {
errors.push(`${pointer}:const:${schema.const}`);
}
if (schema.enum && !schema.enum.includes(value)) {
errors.push(`${pointer}:enum:${schema.enum.join("|")}`);
}
if (schema.type && !matchesSchemaType(value, schema.type)) {
errors.push(`${pointer}:type:${schema.type}`);
return errors;
}
if (schema.minLength !== undefined && typeof value === "string" && value.length < schema.minLength) {
errors.push(`${pointer}:minLength:${schema.minLength}`);
}
if (schema.minItems !== undefined && Array.isArray(value) && value.length < schema.minItems) {
errors.push(`${pointer}:minItems:${schema.minItems}`);
}
if (schema.required && value && typeof value === "object" && !Array.isArray(value)) {
for (const key of schema.required) {
if (value[key] === undefined) errors.push(`${pointer}.${key}:required`);
}
}
if (schema.properties && value && typeof value === "object" && !Array.isArray(value)) {
for (const [key, childSchema] of Object.entries(schema.properties)) {
if (value[key] !== undefined) {
errors.push(...validateSchemaSubset(childSchema, value[key], `${pointer}.${key}`, rootSchema));
}
}
}
if (schema.items && Array.isArray(value)) {
value.forEach((item, index) => {
errors.push(...validateSchemaSubset(schema.items, item, `${pointer}[${index}]`, rootSchema));
});
}
return errors;
}
function resolveSchemaReference(rootSchema, reference) {
if (!reference.startsWith("#/")) return null;
return reference.slice(2).split("/").reduce((current, segment) => {
if (!current || typeof current !== "object") return null;
return current[segment.replaceAll("~1", "/").replaceAll("~0", "~")];
}, rootSchema);
}
function matchesSchemaType(value, type) {
switch (type) {
case "array":
return Array.isArray(value);
case "object":
return value !== null && typeof value === "object" && !Array.isArray(value);
case "string":
return typeof value === "string";
case "number":
return typeof value === "number";
case "boolean":
return typeof value === "boolean";
default:
return true;
}
}

256
test/eve-tui-shell.test.mjs Normal file
View file

@ -0,0 +1,256 @@
import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import path from "node:path";
import test from "node:test";
import { fileURLToPath } from "node:url";
import { validateSchemaSubset } from "../test-support/schema-subset.mjs";
import { EveTuiShell } from "../src/eve-tui-shell.mjs";
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../../Eve");
const tuiGridSchema = JSON.parse(readFileSync(
path.join(repoRoot, "schemas/gamecult.eve.tui_grid.v1.schema.json"),
"utf8",
));
test("selects advertised provider surfaces for compact projection", () => {
const shell = new EveTuiShell({ width: 48 });
const selected = shell.selectSurface(advertisement(), "aetheria.daemon.editor");
assert.equal(selected.providerId, "aetheria");
assert.equal(selected.surfaceId, "aetheria.daemon.editor");
assert.equal(selected.worldInteraction.commandBoundary, "aetheria.daemon.commands");
assert.equal(selected.worldInteraction.receiptSchema, "aetheria.eve_command_acceptance_status.v1");
});
test("command intents carry provider-advertised boundaries", () => {
const shell = new EveTuiShell();
const intent = shell.createCommandIntent(
advertisement(),
"aetheria.daemon.game",
"aetheria.daemon.commands",
{
action: {
commandBoundary: "component.fallback.boundary",
receiptSchema: "component.fallback.receipt.v1",
target: "component.fallback.target",
},
commandId: "aetheria.daemon.focus",
},
);
assert.equal(intent.schema, "gamecult.eve.command_invocation.v1");
assert.equal(intent.providerId, "aetheria");
assert.equal(intent.surfaceId, "aetheria.daemon.game");
assert.equal(intent.commandBoundary, "aetheria.daemon.commands");
assert.equal(intent.receiptSchema, "aetheria.eve_command_acceptance_status.v1");
assert.equal(intent.clientId, "tui");
});
test("summary grid is explicitly lossy provider-shell evidence", () => {
const shell = new EveTuiShell({ width: 36 });
const grid = shell.renderSummary(advertisement(), "aetheria.daemon.game");
assert.deepEqual(validateSchemaSubset(tuiGridSchema, grid), []);
assert.equal(grid.schema, "gamecult.eve.tui_grid.v1");
assert.equal(grid.runtimeId, "tui");
assert.equal(grid.surfaceId, "aetheria.daemon.game");
assert.match(grid.lossiness, /use lowerSurface/);
assert.ok(grid.lines.every(line => line.length <= 36));
});
test("lowers provider surface trees into a terminal grid", () => {
const shell = new EveTuiShell({ width: 44 });
const grid = shell.lowerSurface(surfaceDocument(), advertisement(), "aetheria.daemon.game");
assert.deepEqual(validateSchemaSubset(tuiGridSchema, grid), []);
assert.equal(grid.schema, "gamecult.eve.tui_grid.v1");
assert.equal(grid.runtimeId, "tui");
assert.equal(grid.providerId, "aetheria");
assert.equal(grid.surfaceId, "aetheria.daemon.game");
assert.equal(grid.projectionKind, "provider-authored-world-surface");
assert.equal(grid.commandBoundary, "aetheria.daemon.commands");
assert.equal(grid.receiptSchema, "aetheria.eve_command_acceptance_status.v1");
assert.match(grid.lossiness, /terminal-grid-command-surface/);
assert.ok(grid.lines.every(line => line.length <= 44));
assert.ok(grid.lines.some(line => line.includes("world aetheria.daemon.game.gravity")));
assert.ok(grid.lines.some(line => line.includes("command aetheria.daemon.game.focus")));
assert.ok(grid.lines.some(line => line.includes("norn aetheria.daemon.game.norn")));
assert.ok(grid.lines.some(line => line.includes("slot norn.map")));
assert.deepEqual(grid.pluginProjections, [
{
ownerId: "aetheria.daemon.game.norn",
pluginId: "norn.graph",
projectionKind: "norn-graph-terminal-outline",
abiSchema: "gamecult.eve.plugin_abi.v1",
commandBoundary: "sidecar-advertised-plugin-abi",
capabilities: ["embed.norn"],
documentId: "aetheria.daemon.game.norn",
semanticOwner: "Norn",
availability: "optional-nested",
fallbackKind: "terminal-outline",
},
]);
assert.deepEqual(grid.embeddedDocumentSlots, [
{
ownerId: "aetheria.daemon.game.norn",
slotId: "norn.map",
documentId: "cultmesh://aetheria/norn/map",
schemaId: "gamecult.eve.surface.v1",
presentationKind: "terminal-grid-slot",
},
]);
});
test("rejects surface documents that do not match the advertised TUI target", () => {
const shell = new EveTuiShell();
assert.throws(
() => shell.lowerSurface(surfaceDocument("aetheria.daemon.game"), advertisement(), "aetheria.daemon.editor"),
/does not match advertised TUI surface/,
);
});
test("lowers Sai, Norn, and TeX plugin surfaces into compact terminal fallbacks", () => {
const shell = new EveTuiShell({ width: 72 });
const saiSurface = JSON.parse(readFileSync(path.join(repoRoot, "web/fixtures/sai-vn-surface.json"), "utf8"));
const grid = shell.lowerSurface(saiSurface, saiAdvertisement(), "sai.visual_novel.surface");
assert.deepEqual(validateSchemaSubset(tuiGridSchema, grid), []);
assert.equal(grid.providerId, "gamecult.home.vn");
assert.equal(grid.surfaceId, "sai.visual_novel.surface");
assert.ok(grid.lines.every(line => line.length <= 72));
assert.ok(grid.lines.some(line => line.includes("sai-vn sai.root")));
assert.ok(grid.lines.some(line => line.includes("norn sai.graph")));
assert.ok(grid.lines.some(line => line.includes("tex sai.tex.log-power")));
const byPlugin = new Map(grid.pluginProjections.map(projection => [projection.pluginId, projection]));
assert.equal(byPlugin.get("sai.vn").semanticOwner, "Sai");
assert.equal(byPlugin.get("sai.vn").availability, "required");
assert.equal(byPlugin.get("sai.vn").projectionKind, "sai-vn-terminal-stage-summary");
assert.deepEqual(byPlugin.get("sai.vn").capabilities, [
"vn.stage",
"story.choose",
"story.continue",
"story.jump",
]);
assert.equal(byPlugin.get("norn.graph").semanticOwner, "Norn");
assert.equal(byPlugin.get("norn.graph").availability, "optional-nested");
assert.equal(byPlugin.get("norn.graph").fallbackKind, "terminal-outline");
assert.equal(byPlugin.get("tex.math").semanticOwner, "EvePlugins");
assert.equal(byPlugin.get("tex.math").availability, "optional-nested");
assert.equal(byPlugin.get("tex.math").projectionKind, "tex-math-terminal-block-source");
assert.equal(byPlugin.get("tex.math").fallbackKind, "source-text");
assert.equal(byPlugin.get("tex.math").documentId, "\\\\mathrm{votes}(p)=1+\\\\lfloor\\\\log_b(1+p)\\\\rfloor");
});
function advertisement() {
return {
schema: "gamecult.eve.provider_advertisement.v1",
providerId: "aetheria",
title: "Aetheria",
surfaces: [
{
surfaceId: "aetheria.daemon.game",
transport: "local-json",
url: "conformance/eve/aetheria-world-surface.json",
worldInteraction: {
projectionKind: "provider-authored-world-surface",
commandBoundary: "aetheria.daemon.commands",
receiptSchema: "aetheria.eve_command_acceptance_status.v1",
ownership: "provider-owns-world-state-assets-command-acceptance-and-receipts",
},
},
{
surfaceId: "aetheria.daemon.editor",
transport: "local-json",
url: "conformance/eve/aetheria-world-surface.json",
worldInteraction: {
projectionKind: "provider-authored-world-editor-surface",
commandBoundary: "aetheria.daemon.commands",
receiptSchema: "aetheria.eve_command_acceptance_status.v1",
ownership: "provider-owns-editor-state-assets-command-acceptance-and-receipts",
},
},
],
};
}
function saiAdvertisement() {
return {
schema: "gamecult.eve.provider_advertisement.v1",
providerId: "gamecult.home.vn",
title: "GameCult Compound VN",
surfaces: [
{
surfaceId: "sai.visual_novel.surface",
transport: "local-json",
url: "web/fixtures/sai-vn-surface.json",
worldInteraction: {
projectionKind: "provider-authored-world-surface",
commandBoundary: "sai.vn.plugin.commands",
receiptSchema: "gamecult.eve.command_receipt.v1",
ownership: "provider-owns-story-state-plugin-sidecars-own-nested-semantics",
},
},
],
};
}
function surfaceDocument(surfaceId = "aetheria.daemon.game") {
return {
type: "surface-state",
schema: "gamecult.eve.surface.v1",
providerId: "aetheria",
providerKind: "game.runtime",
title: "Aetheria world",
version: 1,
updatedAtUtc: "2026-07-09T00:00:00Z",
surface: {
id: surfaceId,
root: {
id: `${surfaceId}.root`,
kind: "surface",
props: {},
children: [
{
id: `${surfaceId}.gravity`,
kind: "field.surface2d",
props: {
label: "Current Zone Gravity",
bind: "aetheria.daemon.soaView.gravityField",
fieldId: "aetheria.zone.gravity",
},
children: [],
},
{
id: `${surfaceId}.focus`,
kind: "control.button",
props: {
label: "Focus Relay",
command: "aetheria.daemon.commands",
commandId: "aetheria.daemon.focus",
},
children: [],
},
{
id: `${surfaceId}.norn`,
kind: "embed.norn",
props: {
label: "Norn tactical map",
},
embeddedDocuments: [
{
slotId: "norn.map",
documentId: "cultmesh://aetheria/norn/map",
schemaId: "gamecult.eve.surface.v1",
presentationKind: "terminal-grid-slot",
},
],
children: [],
},
],
},
styles: [],
},
commands: [],
};
}

View file

@ -0,0 +1,118 @@
import { mkdirSync, writeFileSync } from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";
import { EveTuiShell } from "../../src/eve-tui-shell.mjs";
import { buildTuiCaptureRequest, loadJsonFile, writeCaptureRequest } from "./evetui-capture-contract.mjs";
export function buildTuiCaptureArtifact({
advertisement,
capabilityManifest,
surfaceDocument,
advertisementPath = "",
capabilityManifestPath = "",
captureSurfaceId = "",
stamp = "latest",
width = 80,
} = {}) {
const request = buildTuiCaptureRequest({
advertisement,
capabilityManifest,
advertisementPath,
capabilityManifestPath,
captureSurfaceId,
stamp,
});
const shell = new EveTuiShell({ width });
const grid = shell.lowerSurface(surfaceDocument, advertisement, request.surfaceId);
validateGridArtifact(grid, request);
return {
request,
grid: {
...grid,
artifactKind: request.artifactKind,
captureKind: request.captureKind,
sourceAdvertisementPath: request.sourceAdvertisementPath,
sourceCapabilityManifestPath: request.sourceCapabilityManifestPath,
issuedAtUtc: request.issuedAtUtc,
},
};
}
export function writeTuiCaptureArtifact({ request, grid }, { outputPath, requestOutputPath = "" } = {}) {
if (!outputPath) throw new Error("outputPath is required");
mkdirSync(path.dirname(outputPath), { recursive: true });
writeFileSync(outputPath, `${JSON.stringify(grid, null, 2)}\n`, "utf8");
if (requestOutputPath) {
writeCaptureRequest(request, requestOutputPath);
}
}
function validateGridArtifact(grid, request) {
if (grid.schema !== "gamecult.eve.tui_grid.v1") {
throw new Error(`Unexpected TUI grid schema: ${grid.schema || ""}`);
}
for (const [field, expected] of [
["runtimeId", request.runtimeId],
["providerId", request.providerId],
["surfaceId", request.surfaceId],
["projectionKind", request.projectionKind],
["commandBoundary", request.commandBoundary],
["receiptSchema", request.receiptSchema],
]) {
if ((grid[field] || "") !== (expected || "")) {
throw new Error(`TUI grid ${field} expected ${expected || ""} got ${grid[field] || ""}`);
}
}
if (!Array.isArray(grid.lines) || !grid.lines.length) {
throw new Error("TUI grid artifact must contain at least one line");
}
}
function parseArgs(argv) {
const args = {};
for (let index = 0; index < argv.length; index += 1) {
const value = argv[index];
if (!value.startsWith("--")) continue;
const key = value.slice(2);
const next = argv[index + 1];
if (!next || next.startsWith("--")) {
args[key] = true;
} else {
args[key] = next;
index += 1;
}
}
return args;
}
function runCli() {
const args = parseArgs(process.argv.slice(2));
for (const required of ["advertisement", "capability", "surface", "output"]) {
if (!args[required]) {
throw new Error(`missing required --${required}`);
}
}
const stamp = args.stamp || "latest";
const artifact = buildTuiCaptureArtifact({
advertisement: loadJsonFile(args.advertisement),
capabilityManifest: loadJsonFile(args.capability),
surfaceDocument: loadJsonFile(args.surface),
advertisementPath: args.advertisement,
capabilityManifestPath: args.capability,
captureSurfaceId: args["surface-id"] || "",
stamp,
width: Number(args.width || 80),
});
writeTuiCaptureArtifact(artifact, {
outputPath: args.output,
requestOutputPath: args["request-output"] || "",
});
process.stdout.write(`${args.output}\n`);
}
const invokedPath = process.argv[1] ? path.resolve(process.argv[1]) : "";
const modulePath = fileURLToPath(import.meta.url);
if (invokedPath === modulePath) {
runCli();
}

View file

@ -0,0 +1,57 @@
import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import test from "node:test";
import { buildTuiCaptureArtifact } from "./evetui-capture-artifact.mjs";
const advertisement = JSON.parse(readFileSync(new URL("../../../Aetheria/conformance/eve/aetheria.provider-advertisement.json", import.meta.url), "utf8"));
const capabilityManifest = JSON.parse(readFileSync(new URL("../../eve-runtime-capability.json", import.meta.url), "utf8"));
const surfaceDocument = JSON.parse(readFileSync(new URL("../../../Aetheria/conformance/eve/aetheria-world-surface.json", import.meta.url), "utf8"));
const worldSmokeAdvertisement = JSON.parse(readFileSync(new URL("../../../Eve/web/fixtures/eve-world-smoke.provider-advertisement.json", import.meta.url), "utf8"));
const worldSmokeSurfaceDocument = JSON.parse(readFileSync(new URL("../../../Eve/web/fixtures/eve-world-smoke-surface.json", import.meta.url), "utf8"));
test("builds TUI JSON grid capture artifact from provider advertisement and surface", () => {
const { request, grid } = buildTuiCaptureArtifact({
advertisement,
capabilityManifest,
surfaceDocument,
advertisementPath: "conformance/eve/aetheria.provider-advertisement.json",
capabilityManifestPath: "eve-runtime-capability.json",
stamp: "smoke",
width: 72,
});
assert.equal(request.artifactKind, "json-grid");
assert.equal(request.artifactPath, "artifacts/evetui-capture/smoke/tui-grid.json");
assert.equal(grid.schema, "gamecult.eve.tui_grid.v1");
assert.equal(grid.runtimeId, "tui");
assert.equal(grid.providerId, "aetheria");
assert.equal(grid.surfaceId, "aetheria.daemon.game");
assert.equal(grid.commandBoundary, "aetheria.daemon.commands");
assert.equal(grid.receiptSchema, "aetheria.eve_command_acceptance_status.v1");
assert.equal(grid.artifactKind, "json-grid");
assert.equal(grid.captureKind, "terminal-cell-grid");
assert.equal(grid.width, 72);
assert.ok(grid.lines.some(line => line.includes("aetheria.daemon.game")));
});
test("builds TUI JSON grid capture artifact from a generic world provider", () => {
const { grid } = buildTuiCaptureArtifact({
advertisement: worldSmokeAdvertisement,
capabilityManifest,
surfaceDocument: worldSmokeSurfaceDocument,
advertisementPath: "web/fixtures/eve-world-smoke.provider-advertisement.json",
capabilityManifestPath: "eve-runtime-capability.json",
stamp: "smoke",
width: 72,
});
assert.equal(grid.schema, "gamecult.eve.tui_grid.v1");
assert.equal(grid.runtimeId, "tui");
assert.equal(grid.providerId, "eve.world-smoke");
assert.equal(grid.surfaceId, "eve.world-smoke.surface");
assert.equal(grid.commandBoundary, "eve.world-smoke.commands");
assert.equal(grid.receiptSchema, "eve.world_smoke.command_receipt.v1");
assert.equal(grid.artifactKind, "json-grid");
assert.equal(grid.captureKind, "terminal-cell-grid");
assert.ok(grid.lines.some(line => line.includes("eve.world-smoke.surface")));
});

View file

@ -0,0 +1,160 @@
import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";
export function buildTuiCaptureRequest({
advertisement,
capabilityManifest,
advertisementPath = "",
capabilityManifestPath = "",
captureSurfaceId = "",
stamp = new Date().toISOString().replace(/[:.]/g, "-"),
} = {}) {
if (!advertisement || typeof advertisement !== "object") {
throw new Error("advertisement is required");
}
if (!capabilityManifest || typeof capabilityManifest !== "object") {
throw new Error("capabilityManifest is required");
}
const captureContract = capabilityManifest.lifecycle?.capture?.captureContract;
if (!captureContract) {
throw new Error("capability manifest is missing lifecycle.capture.captureContract");
}
const captureSurface = selectCaptureSurface(captureContract, advertisement.providerId, captureSurfaceId);
if (!captureSurface) {
if (captureSurfaceId) {
throw new Error(
`capture surface mismatch: expected ${captureSurfaceList(captureContract)} got ${advertisement.providerId || "missing"}:${captureSurfaceId}`,
);
}
throw new Error(
`capture provider mismatch: expected ${captureProviderList(captureContract)} got ${advertisement.providerId || "missing"}`,
);
}
const targetId = captureContract.targetId || captureContract.runtimeId;
const surface = (advertisement.surfaces || []).find(candidate => candidate.surfaceId === captureSurface.surfaceId);
if (!surface) {
throw new Error(`capture surface not advertised: ${captureSurface.surfaceId}`);
}
const loweringTargets = surface.worldInteraction?.loweringTargets || [];
if (!loweringTargets.includes(targetId)) {
throw new Error(`surface ${surface.surfaceId} does not advertise lowering target ${targetId}`);
}
const artifactPath = replaceStamp(captureContract.artifactPattern, stamp);
return {
schema: captureContract.requestSchema || "gamecult.eve.runtime_capture_request.v1",
runtimeId: capabilityManifest.runtimeId,
ownerRepo: captureContract.ownerRepo,
providerId: advertisement.providerId,
surfaceId: surface.surfaceId,
surfaceKind: surface.surfaceKind || "",
targetId,
projectionKind: surface.worldInteraction?.projectionKind || "",
commandBoundary: surface.worldInteraction?.commandBoundary || "",
receiptSchema: surface.worldInteraction?.receiptSchema || "",
captureKind: captureContract.captureKind,
artifactKind: captureContract.artifactKind,
artifactPath,
conformanceAttachment: captureContract.conformanceAttachment,
authority: captureContract.authority,
sourceAdvertisementPath: advertisementPath,
sourceCapabilityManifestPath: capabilityManifestPath,
issuedAtUtc: stamp,
};
}
export function loadJsonFile(filePath) {
return JSON.parse(readFileSync(filePath, "utf8"));
}
export function writeCaptureRequest(request, outputPath) {
mkdirSync(path.dirname(outputPath), { recursive: true });
writeFileSync(outputPath, `${JSON.stringify(request, null, 2)}\n`, "utf8");
}
function replaceStamp(pattern, stamp) {
if (!pattern || !pattern.includes("{stamp}")) {
throw new Error(`capture artifact pattern must include {stamp}: ${pattern || "missing"}`);
}
return pattern.replace("{stamp}", stamp);
}
function selectCaptureSurface(captureContract, providerId, surfaceId = "") {
const claims = captureSurfaceClaims(captureContract).filter(claim => claim.providerId === providerId);
if (surfaceId) return claims.find(claim => claim.surfaceId === surfaceId) || null;
return claims[0] || null;
}
function captureProviderList(captureContract) {
return captureSurfaceClaims(captureContract).map(claim => claim.providerId).filter(Boolean).join(", ") || "missing";
}
function captureSurfaceList(captureContract) {
return captureSurfaceClaims(captureContract).map(claim => `${claim.providerId}:${claim.surfaceId}`).join(", ") || "missing";
}
function captureSurfaceClaims(captureContract) {
return [
{
providerId: captureContract.requiredProvider || "",
surfaceId: captureContract.requiredSurface || "",
},
...(Array.isArray(captureContract.additionalProviderSurfaces) ? captureContract.additionalProviderSurfaces : []),
]
.filter(claim => claim && typeof claim === "object")
.map(claim => ({
providerId: String(claim.providerId || ""),
surfaceId: String(claim.surfaceId || ""),
}))
.filter(claim => claim.providerId && claim.surfaceId);
}
function parseArgs(argv) {
const args = {};
for (let index = 0; index < argv.length; index += 1) {
const value = argv[index];
if (!value.startsWith("--")) continue;
const key = value.slice(2);
const next = argv[index + 1];
if (!next || next.startsWith("--")) {
args[key] = true;
} else {
args[key] = next;
index += 1;
}
}
return args;
}
function runCli() {
const args = parseArgs(process.argv.slice(2));
for (const required of ["advertisement", "capability", "output"]) {
if (!args[required]) {
throw new Error(`missing required --${required}`);
}
}
const stamp = args.stamp || new Date().toISOString().replace(/[:.]/g, "-");
const request = buildTuiCaptureRequest({
advertisement: loadJsonFile(args.advertisement),
capabilityManifest: loadJsonFile(args.capability),
advertisementPath: args.advertisement,
capabilityManifestPath: args.capability,
captureSurfaceId: args["surface-id"] || "",
stamp,
});
writeCaptureRequest(request, args.output);
process.stdout.write(`${args.output}\n`);
}
const invokedPath = process.argv[1] ? path.resolve(process.argv[1]) : "";
const modulePath = fileURLToPath(import.meta.url);
if (invokedPath === modulePath) {
runCli();
}

View file

@ -0,0 +1,78 @@
import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import test from "node:test";
import { buildTuiCaptureRequest } from "./evetui-capture-contract.mjs";
const advertisement = JSON.parse(readFileSync(new URL("../../../Aetheria/conformance/eve/aetheria.provider-advertisement.json", import.meta.url), "utf8"));
const worldSmokeAdvertisement = JSON.parse(readFileSync(new URL("../../../Eve/web/fixtures/eve-world-smoke.provider-advertisement.json", import.meta.url), "utf8"));
const capabilityManifest = JSON.parse(readFileSync(new URL("../../eve-runtime-capability.json", import.meta.url), "utf8"));
test("builds TUI capture request from provider advertisement", () => {
const request = buildTuiCaptureRequest({
advertisement,
capabilityManifest,
advertisementPath: "conformance/eve/aetheria.provider-advertisement.json",
capabilityManifestPath: "eve-runtime-capability.json",
stamp: "smoke",
});
assert.equal(request.schema, "gamecult.eve.runtime_capture_request.v1");
assert.equal(request.runtimeId, "tui");
assert.equal(request.ownerRepo, "EveTui");
assert.equal(request.providerId, "aetheria");
assert.equal(request.surfaceId, "aetheria.daemon.game");
assert.equal(request.targetId, "tui");
assert.equal(request.projectionKind, "provider-authored-world-surface");
assert.equal(request.commandBoundary, "aetheria.daemon.commands");
assert.equal(request.receiptSchema, "aetheria.eve_command_acceptance_status.v1");
assert.equal(request.captureKind, "terminal-cell-grid");
assert.equal(request.artifactKind, "json-grid");
assert.equal(request.artifactPath, "artifacts/evetui-capture/smoke/tui-grid.json");
assert.match(request.authority, /provider state/);
});
test("builds TUI capture request for Aetheria editor surface", () => {
const request = buildTuiCaptureRequest({
advertisement,
capabilityManifest,
captureSurfaceId: "aetheria.daemon.editor",
advertisementPath: "conformance/eve/aetheria.provider-advertisement.json",
capabilityManifestPath: "eve-runtime-capability.json",
stamp: "smoke",
});
assert.equal(request.runtimeId, "tui");
assert.equal(request.providerId, "aetheria");
assert.equal(request.surfaceId, "aetheria.daemon.editor");
assert.equal(request.surfaceKind, "interactive-world-editor");
assert.equal(request.projectionKind, "provider-authored-world-editor-surface");
assert.equal(request.commandBoundary, "aetheria.daemon.commands");
assert.equal(request.receiptSchema, "aetheria.eve_command_acceptance_status.v1");
});
test("builds TUI capture request from a declared generic world provider", () => {
const request = buildTuiCaptureRequest({
advertisement: worldSmokeAdvertisement,
capabilityManifest,
advertisementPath: "web/fixtures/eve-world-smoke.provider-advertisement.json",
capabilityManifestPath: "eve-runtime-capability.json",
stamp: "smoke",
});
assert.equal(request.runtimeId, "tui");
assert.equal(request.providerId, "eve.world-smoke");
assert.equal(request.surfaceId, "eve.world-smoke.surface");
assert.equal(request.commandBoundary, "eve.world-smoke.commands");
assert.equal(request.receiptSchema, "eve.world_smoke.command_receipt.v1");
});
test("rejects capture request when the surface does not advertise TUI", () => {
const withoutTarget = structuredClone(advertisement);
const surface = withoutTarget.surfaces.find(candidate => candidate.surfaceId === "aetheria.daemon.game");
surface.worldInteraction.loweringTargets = surface.worldInteraction.loweringTargets.filter(target => target !== "tui");
assert.throws(
() => buildTuiCaptureRequest({ advertisement: withoutTarget, capabilityManifest, stamp: "smoke" }),
/does not advertise lowering target tui/,
);
});

View file

@ -0,0 +1,114 @@
import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";
export function buildTuiReleaseRequest({
capabilityManifest,
packageManifest,
capabilityManifestPath = "",
packageManifestPath = "",
repository = "GameCult/EveTui",
} = {}) {
if (!capabilityManifest || typeof capabilityManifest !== "object") {
throw new Error("capabilityManifest is required");
}
if (!packageManifest || typeof packageManifest !== "object") {
throw new Error("packageManifest is required");
}
const releaseContract = capabilityManifest.lifecycle?.release?.releaseContract;
if (!releaseContract) {
throw new Error("capability manifest is missing lifecycle.release.releaseContract");
}
if (packageManifest.name !== releaseContract.packageName) {
throw new Error(`package name mismatch: expected ${releaseContract.packageName} got ${packageManifest.name || "missing"}`);
}
if (!packageManifest.version || !/^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?$/.test(packageManifest.version)) {
throw new Error(`package version is not a SemVer-compatible release version: ${packageManifest.version || "missing"}`);
}
const tagName = replaceVersion(releaseContract.tagPattern, packageManifest.version);
const artifactPath = replaceVersion(
releaseContract.artifactPattern || `artifacts/evetui-release/{version}/${releaseContract.packageName}-{version}.tgz`,
packageManifest.version,
);
return {
schema: releaseContract.requestSchema || "gamecult.eve.runtime_release_request.v1",
ownerRepo: releaseContract.ownerRepo,
repository,
packageName: releaseContract.packageName,
version: packageManifest.version,
packageRoot: releaseContract.packageRoot,
versionSource: releaseContract.versionSource,
tagName,
artifactKind: releaseContract.artifactKind,
artifactPath,
publishProof: releaseContract.publishProof,
sourceCapabilityManifestPath: capabilityManifestPath,
sourcePackageManifestPath: packageManifestPath,
main: packageManifest.main || "",
scripts: packageManifest.scripts || {},
dependencies: packageManifest.dependencies || {},
devDependencies: packageManifest.devDependencies || {},
};
}
export function loadJsonFile(filePath) {
return JSON.parse(readFileSync(filePath, "utf8"));
}
export function writeReleaseRequest(request, outputPath) {
mkdirSync(path.dirname(outputPath), { recursive: true });
writeFileSync(outputPath, `${JSON.stringify(request, null, 2)}\n`, "utf8");
}
function replaceVersion(pattern, version) {
if (!pattern || !pattern.includes("{version}")) {
throw new Error(`release pattern must include {version}: ${pattern || "missing"}`);
}
return pattern.replaceAll("{version}", version);
}
function parseArgs(argv) {
const args = {};
for (let index = 0; index < argv.length; index += 1) {
const value = argv[index];
if (!value.startsWith("--")) continue;
const key = value.slice(2);
const next = argv[index + 1];
if (!next || next.startsWith("--")) {
args[key] = true;
} else {
args[key] = next;
index += 1;
}
}
return args;
}
function runCli() {
const args = parseArgs(process.argv.slice(2));
for (const required of ["capability", "package", "output"]) {
if (!args[required]) {
throw new Error(`missing required --${required}`);
}
}
const request = buildTuiReleaseRequest({
capabilityManifest: loadJsonFile(args.capability),
packageManifest: loadJsonFile(args.package),
capabilityManifestPath: args.capability,
packageManifestPath: args.package,
repository: args.repository || "GameCult/EveTui",
});
writeReleaseRequest(request, args.output);
process.stdout.write(`${args.output}\n`);
}
const invokedPath = process.argv[1] ? path.resolve(process.argv[1]) : "";
const modulePath = fileURLToPath(import.meta.url);
if (invokedPath === modulePath) {
runCli();
}

View file

@ -0,0 +1,37 @@
import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import test from "node:test";
import { buildTuiReleaseRequest } from "./evetui-release-contract.mjs";
const capabilityManifest = JSON.parse(readFileSync(new URL("../../eve-runtime-capability.json", import.meta.url), "utf8"));
const packageManifest = JSON.parse(readFileSync(new URL("../../package.json", import.meta.url), "utf8"));
test("builds EveTui release request from package manifest", () => {
const request = buildTuiReleaseRequest({
capabilityManifest,
packageManifest,
capabilityManifestPath: "eve-runtime-capability.json",
packageManifestPath: "package.json",
});
assert.equal(request.schema, "gamecult.eve.runtime_release_request.v1");
assert.equal(request.ownerRepo, "EveTui");
assert.equal(request.repository, "GameCult/EveTui");
assert.equal(request.packageName, "eve-tui");
assert.equal(request.version, "0.1.0");
assert.equal(request.packageRoot, ".");
assert.equal(request.versionSource, "package.json");
assert.equal(request.tagName, "evetui-v0.1.0");
assert.equal(request.artifactKind, "terminal-runtime");
assert.equal(request.artifactPath, "artifacts/evetui-release/0.1.0/eve-tui-0.1.0.tgz");
assert.equal(request.main, "src/eve-tui-shell.mjs");
});
test("rejects package manifests that do not match the TUI release contract", () => {
const wrongPackage = { ...packageManifest, name: "eve-wrong" };
assert.throws(
() => buildTuiReleaseRequest({ capabilityManifest, packageManifest: wrongPackage }),
/package name mismatch/,
);
});