@mpgd/cli 0.46.1 → 0.48.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/dist/capacitor-shell-starter.d.ts +57 -1
  2. package/dist/capacitor-shell-starter.js +311 -34
  3. package/dist/command-launcher.d.ts +13 -0
  4. package/dist/command-launcher.js +24 -0
  5. package/dist/index.d.ts +1 -0
  6. package/dist/index.js +149 -12
  7. package/dist/microsoft-store-submission.js +11 -6
  8. package/dist/native-build-info.json +2 -2
  9. package/dist/native-build-target.js +2413 -345
  10. package/dist/windows-host/Core/BridgeCommand.cs +17 -0
  11. package/dist/windows-host/Core/Mpgd.MicrosoftStore.Host.csproj +9 -0
  12. package/dist/windows-host/Core/NativeBridgeContracts.cs +33 -0
  13. package/dist/windows-host/Core/NativeBridgeHost.cs +162 -0
  14. package/dist/windows-host/Core/NativeBridgeOrigin.cs +19 -0
  15. package/dist/windows-host/Core/NativeBridgeProtocol.cs +335 -0
  16. package/dist/windows-host/Core/NativeBridgeRequestGate.cs +76 -0
  17. package/dist/windows-host/Core/NativeBridgeResult.cs +65 -0
  18. package/dist/windows-host/README.md +118 -0
  19. package/dist/windows-host/WinUI/DispatcherQueueBridgeDispatcher.cs +39 -0
  20. package/dist/windows-host/WinUI/WebView2DocumentTransport.cs +46 -0
  21. package/dist/windows-host/WinUI/WebView2NativeBridge.cs +185 -0
  22. package/dist/windows-host/WinUI/WindowStoreContext.cs +20 -0
  23. package/dist/windows-native-tools.d.ts +4 -0
  24. package/dist/windows-native-tools.js +21 -0
  25. package/native-tools/windows-msix/WindowsMsixAcceptance.psm1 +333 -0
  26. package/package.json +24 -14
  27. package/templates/lite-overlay/.agents/skills/use-mpgd-kit/SKILL.md +22 -0
  28. package/templates/lite-overlay/AGENTS.md +27 -0
  29. package/templates/lite-overlay/README.md +37 -0
  30. package/templates/lite-overlay/agent/acceptance.md +27 -0
  31. package/templates/lite-overlay/agent/brief.md +17 -0
  32. package/templates/lite-overlay/apps/target-devvit/README.md +134 -0
  33. package/templates/lite-overlay/docs/MPGD_KIT_WORKFLOWS.md +37 -0
  34. package/templates/lite-overlay/src/lite/tapGame.ts +77 -0
  35. package/templates/lite-overlay/src/platform/devvitEntrypoint.ts +116 -0
  36. package/templates/lite-overlay/src/platform/devvitInlineMode.css +146 -0
  37. package/templates/lite-overlay/src/runtime/createGame.ts +57 -0
  38. package/templates/phaser-game/README.md +5 -3
  39. package/templates/phaser-game/apps/target-ait/package.json +2 -2
  40. package/templates/phaser-game/apps/target-ait/src/main.ts +17 -0
  41. package/templates/phaser-game/apps/target-ait/src/runtime-config.types.ts +2 -0
  42. package/templates/phaser-game/apps/target-ait/vite.config.ts +27 -0
  43. package/templates/phaser-game/apps/target-devvit/package.json +2 -1
  44. package/templates/phaser-game/apps/target-devvit/src/server/bridge.ts +137 -18
  45. package/templates/phaser-game/apps/target-devvit/src/server/index.ts +35 -1
  46. package/templates/phaser-game/package.json +3 -1
  47. package/templates/phaser-game/pnpm-workspace.yaml +2 -0
  48. package/templates/phaser-game/src/env.d.ts +2 -0
  49. package/templates/phaser-game/src/main.ts +8 -1
  50. package/templates/phaser-game/src/platform/buildGateways/ait.ts +9 -0
  51. package/templates/phaser-game/src/platform/buildGateways/aitSandbox.ts +13 -1
  52. package/templates/phaser-game/src/platform/buildGateways/capacitorAndroid.ts +51 -2
  53. package/templates/phaser-game/src/platform/buildGateways/capacitorIos.ts +51 -2
  54. package/templates/phaser-game/src/platform/buildGateways/microsoftStore.ts +59 -26
  55. package/templates/phaser-game/src/platform/buildGateways/reddit.ts +36 -1
  56. package/templates/phaser-game/src/platform/buildGateways/verse8.ts +39 -2
  57. package/templates/phaser-game/src/platform/commerceAccountBinding.ts +79 -0
  58. package/templates/phaser-game/src/platform/commerceProducts.ts +69 -0
  59. package/templates/phaser-game/src/platform/gameServices.ts +22 -2
  60. package/templates/phaser-game/src/platform/monetizationJournal.ts +286 -0
  61. package/templates/phaser-game/src/platform/verse8Agent8Client.ts +22 -0
  62. package/templates/phaser-game/vite.shared.ts +40 -1
@@ -0,0 +1,333 @@
1
+ # Reusable isolated Windows MSIX acceptance. Game policy belongs to the caller.
2
+ Set-StrictMode -Version Latest
3
+
4
+ function Get-PeMachine {
5
+ param([Parameter(Mandatory = $true)][string]$Path)
6
+ $bytes = [IO.File]::ReadAllBytes($Path)
7
+ if ($bytes.Length -lt 64 -or $bytes[0] -ne 0x4D -or $bytes[1] -ne 0x5A) {
8
+ throw "Not a PE image: $Path"
9
+ }
10
+ $offset = [BitConverter]::ToInt32($bytes, 0x3C)
11
+ if ($offset -lt 64 -or $offset -gt $bytes.Length - 6 -or
12
+ $bytes[$offset] -ne 0x50 -or $bytes[$offset + 1] -ne 0x45 -or
13
+ $bytes[$offset + 2] -ne 0 -or $bytes[$offset + 3] -ne 0) {
14
+ throw "Invalid PE header: $Path"
15
+ }
16
+ return [BitConverter]::ToUInt16($bytes, $offset + 4)
17
+ }
18
+
19
+ function Test-PathWithin {
20
+ param([string]$Path, [string]$Root)
21
+ $prefix = [IO.Path]::GetFullPath($Root).TrimEnd('\', '/') + [IO.Path]::DirectorySeparatorChar
22
+ return [IO.Path]::GetFullPath($Path).StartsWith($prefix, [StringComparison]::OrdinalIgnoreCase)
23
+ }
24
+
25
+ function Assert-MsixPayload {
26
+ param([string]$Directory, [string]$Identity, [string]$Publisher,
27
+ [ValidateSet('x64', 'ARM64')][string]$Architecture)
28
+ [xml]$manifest = Get-Content -LiteralPath (Join-Path $Directory 'AppxManifest.xml') -Raw
29
+ $packageIdentity = $manifest.Package.Identity
30
+ if ($packageIdentity.Name -cne $Identity -or $packageIdentity.Publisher -cne $Publisher -or
31
+ $packageIdentity.ProcessorArchitecture -ine $Architecture) {
32
+ throw 'MSIX identity, publisher or architecture does not match the selected game.'
33
+ }
34
+ $applications = @($manifest.Package.Applications.Application)
35
+ if ($applications.Count -ne 1) { throw 'Expected one packaged native application.' }
36
+ $application = $applications[0]
37
+ $executable = [string]$application.Executable
38
+ if (-not $executable.EndsWith('.exe', [StringComparison]::OrdinalIgnoreCase) -or
39
+ [IO.Path]::IsPathRooted($executable) -or $executable -match '(^|[\\/])\.\.([\\/]|$)') {
40
+ throw 'The manifest does not name a safe relative native executable.'
41
+ }
42
+ $executablePath = Join-Path $Directory $executable
43
+ if (-not (Test-PathWithin $executablePath $Directory)) { throw 'Executable escapes the package.' }
44
+ $machine = @{ x64 = 0x8664; ARM64 = 0xAA64 }[$Architecture]
45
+ if ((Get-PeMachine $executablePath) -ne $machine) { throw 'Wrong native executable architecture.' }
46
+ $appRoot = Split-Path -Parent $executablePath
47
+ foreach ($name in @('hostfxr.dll', 'hostpolicy.dll', 'coreclr.dll', 'System.Private.CoreLib.dll')) {
48
+ if (-not (Test-Path -LiteralPath (Join-Path $appRoot $name) -PathType Leaf)) {
49
+ throw "Missing bundled runtime file: $name"
50
+ }
51
+ }
52
+ if ((Get-PeMachine (Join-Path $appRoot 'coreclr.dll')) -ne $machine) {
53
+ throw 'The package contains another architecture''s .NET runtime.'
54
+ }
55
+ $processName = [IO.Path]::GetFileNameWithoutExtension($executable)
56
+ $runtime = Get-Content -LiteralPath (Join-Path $appRoot "$processName.runtimeconfig.json") -Raw | ConvertFrom-Json
57
+ $options = $runtime.runtimeOptions
58
+ $included = $options.PSObject.Properties['includedFrameworks']
59
+ if ($null -ne $options.PSObject.Properties['framework'] -or
60
+ $null -ne $options.PSObject.Properties['frameworks'] -or $null -eq $included -or
61
+ @($included.Value | Where-Object { $_.name -eq 'Microsoft.NETCore.App' }).Count -eq 0) {
62
+ throw 'The native package is framework-dependent or does not include Microsoft.NETCore.App.'
63
+ }
64
+ return @{
65
+ Identity = [string]$packageIdentity.Name; Publisher = [string]$packageIdentity.Publisher
66
+ Version = [string]$packageIdentity.Version; Architecture = $Architecture
67
+ AppId = [string]$application.Id; Executable = $executable; ProcessName = $processName
68
+ }
69
+ }
70
+
71
+ function Get-DescendantProcesses {
72
+ param([int]$RootProcessId, [object[]]$Processes)
73
+ $ids = [Collections.Generic.HashSet[int]]::new()
74
+ [void]$ids.Add($RootProcessId)
75
+ do {
76
+ $changed = $false
77
+ foreach ($process in $Processes) {
78
+ if ($ids.Contains([int]$process.ParentProcessId) -and $ids.Add([int]$process.ProcessId)) {
79
+ $changed = $true
80
+ }
81
+ }
82
+ } while ($changed)
83
+ return @($Processes | Where-Object {
84
+ [int]$_.ProcessId -ne $RootProcessId -and $ids.Contains([int]$_.ProcessId)
85
+ })
86
+ }
87
+
88
+ function Read-AtomicJson {
89
+ param([string]$Path)
90
+ # Permit the host's atomic rename while this sample reads the previous file.
91
+ $stream = [IO.File]::Open($Path, [IO.FileMode]::Open, [IO.FileAccess]::Read,
92
+ ([IO.FileShare]::ReadWrite -bor [IO.FileShare]::Delete))
93
+ $reader = [IO.StreamReader]::new($stream)
94
+ try { return ($reader.ReadToEnd() | ConvertFrom-Json) }
95
+ finally { $reader.Dispose() }
96
+ }
97
+ function Assert-MsixAcceptanceSource {
98
+ param([Parameter(Mandatory = $true)]$Source)
99
+ if ($Source.GitSha -cnotmatch '^[a-f0-9]{40}$' -or [string]$Source.RunId -notmatch '^[0-9]{1,20}$' -or
100
+ $Source.Owner -cnotmatch '^[a-z0-9][a-z0-9-]{0,63}$' -or [string]::IsNullOrWhiteSpace($Source.Workflow)) {
101
+ throw 'Native acceptance requires a full source SHA, run ID, owner and workflow.'
102
+ }
103
+ }
104
+ function Copy-MsixAdditionalEvidence {
105
+ param($Receipt, [hashtable]$Fields, [string[]]$Reserved)
106
+ foreach ($key in $Fields.Keys) {
107
+ if ($Reserved -icontains [string]$key) {
108
+ throw 'Additional evidence cannot overwrite native acceptance evidence.'
109
+ }
110
+ $Receipt[$key] = $Fields[$key]
111
+ }
112
+ }
113
+
114
+ function Complete-MsixAcceptanceEvidence {
115
+ param($Receipt, $Installed, [string]$OutputDirectory, [hashtable]$Fields,
116
+ [string[]]$Reserved, [scriptblock]$Collector)
117
+ if ($null -ne $Collector) {
118
+ & $Collector $Installed $OutputDirectory $Fields | Out-Null
119
+ Copy-MsixAdditionalEvidence $Receipt $Fields $Reserved
120
+ }
121
+ # Evidence collection must finish before an invocation can pass.
122
+ $Receipt.status = 'passed'
123
+ }
124
+
125
+ function Invoke-MsixNativeAcceptance {
126
+ [CmdletBinding()]
127
+ param(
128
+ [Parameter(Mandatory = $true)][string]$PackageDirectory,
129
+ [Parameter(Mandatory = $true)][string]$OutputDirectory,
130
+ [Parameter(Mandatory = $true)][string]$Identity,
131
+ [Parameter(Mandatory = $true)][string]$Publisher,
132
+ [Parameter(Mandatory = $true)][ValidateSet('x64', 'ARM64')][string]$Architecture,
133
+ [Parameter(Mandatory = $true)]$Source,
134
+ [ValidateRange(10, 120)][int]$ObserveSeconds = 30,
135
+ [hashtable]$ReceiptFields = @{},
136
+ [scriptblock]$InspectPayload,
137
+ [scriptblock]$TestReadiness,
138
+ [scriptblock]$CollectEvidence
139
+ )
140
+ $ErrorActionPreference = 'Stop'
141
+ if ($env:GITHUB_ACTIONS -ne 'true' -or $env:OS -ne 'Windows_NT') {
142
+ throw 'Native MSIX acceptance runs on an isolated GitHub Windows runner.'
143
+ }
144
+ $cpu = @(Get-CimInstance Win32_Processor)[0].Architecture
145
+ $expectedCpu = @{ x64 = 9; ARM64 = 12 }[$Architecture]
146
+ if ($cpu -ne $expectedCpu) { throw "Native $Architecture acceptance requires a matching Windows runner." }
147
+
148
+ New-Item -ItemType Directory -Force -Path $OutputDirectory | Out-Null
149
+ Assert-MsixAcceptanceSource $Source
150
+ $receipt = [ordered]@{
151
+ schemaVersion = 1; game = $Source.Owner; architecture = $Architecture; status = 'failed'
152
+ sourceRunId = $Source.RunId; sourceGitSha = $Source.GitSha
153
+ sourceWorkflow = $Source.Workflow
154
+ acceptanceGitSha = $env:GITHUB_SHA
155
+ acceptanceRun = "https://github.com/$env:GITHUB_REPOSITORY/actions/runs/$env:GITHUB_RUN_ID"
156
+ startedAt = [DateTime]::UtcNow.ToString('o'); observeSeconds = $ObserveSeconds
157
+ packageIdentityVerified = $false; selfContainedRuntimeVerified = $false
158
+ nativeInstallationVerified = $false; nativeLaunchVerified = $false
159
+ packagedCoreClrVerified = $false; winUiLoaded = $false; windowObserved = $false
160
+ webViewRendererObserved = $false
161
+ gameplayAcceptance = 'not-run'; loginPurchaseAcceptance = 'not-run'; storeUploaded = $false
162
+ cleanupErrors = @()
163
+ }
164
+ $reserved = @($receipt.Keys) + @('packageSha256', 'packageFile', 'identity', 'packageVersion',
165
+ 'processId', 'loadedCoreClr', 'loadedWinUi', 'failure', 'finishedAt')
166
+ $extraEvidence = @{}
167
+ foreach ($key in $ReceiptFields.Keys) { $extraEvidence[$key] = $ReceiptFields[$key] }
168
+ Copy-MsixAdditionalEvidence $receipt $extraEvidence $reserved
169
+ $temporary = Join-Path $env:RUNNER_TEMP ('native-msix-' + [Guid]::NewGuid().ToString('N'))
170
+ New-Item -ItemType Directory -Path $temporary | Out-Null
171
+ $installed = $null
172
+ $certificate = $null
173
+ $installAttempted = $false
174
+ $hostProcess = $null
175
+ $metadata = $null
176
+ $started = Get-Date
177
+ $failure = $null
178
+
179
+ try {
180
+ $packages = @(Get-ChildItem -LiteralPath $PackageDirectory -Filter *.msix -File -Recurse)
181
+ if ($packages.Count -ne 1) { throw 'Expected exactly one native MSIX artifact for this architecture.' }
182
+ $original = $packages[0].FullName
183
+ $originalHash = (Get-FileHash -LiteralPath $original -Algorithm SHA256).Hash
184
+ $receipt.packageSha256 = $originalHash.ToLowerInvariant()
185
+ $receipt.packageFile = $packages[0].Name
186
+ $sdkRoot = Join-Path ([Environment]::GetEnvironmentVariable('ProgramFiles(x86)')) 'Windows Kits\10\bin'
187
+ $tools = @(Get-ChildItem -Path (Join-Path $sdkRoot '*\*\MakeAppx.exe') -File |
188
+ Where-Object { $_.Directory.Name -in @($Architecture.ToLowerInvariant(), 'x64') } |
189
+ Sort-Object @{ Expression = { [version]$_.Directory.Parent.Name }; Descending = $true },
190
+ @{ Expression = { $_.Directory.Name -eq $Architecture.ToLowerInvariant() }; Descending = $true })
191
+ if ($tools.Count -eq 0) { throw 'Windows SDK MakeAppx was not found.' }
192
+ $makeAppx = $tools[0].FullName
193
+ $signTool = Join-Path $tools[0].DirectoryName 'signtool.exe'
194
+ if (-not (Test-Path -LiteralPath $signTool -PathType Leaf)) { throw 'Windows SDK SignTool was not found.' }
195
+ $inspection = Join-Path $temporary 'inspection'
196
+ & $makeAppx unpack /p $original /d $inspection /o
197
+ if ($LASTEXITCODE -ne 0) { throw 'MakeAppx could not inspect the package.' }
198
+ $metadata = Assert-MsixPayload $inspection $Identity $Publisher $Architecture
199
+ if ($null -ne $InspectPayload) {
200
+ & $InspectPayload $inspection $extraEvidence | Out-Null
201
+ Copy-MsixAdditionalEvidence $receipt $extraEvidence $reserved
202
+ }
203
+ $receipt.packageIdentityVerified = $true
204
+ $receipt.selfContainedRuntimeVerified = $true
205
+ $receipt.identity = $metadata.Identity
206
+ $receipt.packageVersion = $metadata.Version
207
+
208
+ # Refuse to replace any pre-existing installation, including another version.
209
+ if (@(Get-AppxPackage -Name $metadata.Identity).Count -ne 0) {
210
+ throw 'The selected game is already installed; this is not a clean acceptance runner.'
211
+ }
212
+ $copy = Join-Path $temporary 'test-signed.msix'
213
+ Copy-Item -LiteralPath $original -Destination $copy
214
+ $certificate = New-SelfSignedCertificate -Type Custom -Subject $metadata.Publisher -KeyUsage DigitalSignature -CertStoreLocation 'Cert:\CurrentUser\My' -TextExtension @('2.5.29.37={text}1.3.6.1.5.5.7.3.3', '2.5.29.19={text}')
215
+ & $signTool sign /fd SHA256 /sha1 $certificate.Thumbprint $copy
216
+ if ($LASTEXITCODE -ne 0) { throw 'Could not test-sign the package copy.' }
217
+ $publicCertificate = Join-Path $temporary 'test-signing.cer'
218
+ Export-Certificate -Cert $certificate -FilePath $publicCertificate | Out-Null
219
+ Import-Certificate -FilePath $publicCertificate -CertStoreLocation 'Cert:\LocalMachine\TrustedPeople' | Out-Null
220
+ $installAttempted = $true
221
+ Add-AppxPackage -Path $copy
222
+ $matches = @(Get-AppxPackage -Name $metadata.Identity)
223
+ if ($matches.Count -ne 1 -or $matches[0].Version.ToString() -cne $metadata.Version) {
224
+ throw 'Installed package differs from the inspected artifact.'
225
+ }
226
+ $installed = $matches[0]
227
+ $receipt.nativeInstallationVerified = $true
228
+ $hostLog = Join-Path $env:LOCALAPPDATA "Packages\$($installed.PackageFamilyName)\LocalState\host-error.log"
229
+ $expectedExecutable = Join-Path $installed.InstallLocation $metadata.Executable
230
+ $started = Get-Date
231
+ Start-Process -FilePath "shell:AppsFolder\$($installed.PackageFamilyName)!$($metadata.AppId)" -WindowStyle Hidden
232
+ $clock = [Diagnostics.Stopwatch]::StartNew()
233
+ $readyAt = $null
234
+ while ($clock.Elapsed.TotalSeconds -lt (60 + $ObserveSeconds)) {
235
+ Start-Sleep -Seconds 1
236
+ if (Test-Path -LiteralPath $hostLog) { throw 'The installed host recorded a startup exception.' }
237
+ $candidates = @(Get-Process -Name $metadata.ProcessName -ErrorAction SilentlyContinue | Where-Object {
238
+ $_.StartTime -ge $started -and $_.MainModule.FileName -ieq $expectedExecutable
239
+ })
240
+ if ($candidates.Count -gt 1) { throw 'Multiple newly activated native host processes were found.' }
241
+ if ($candidates.Count -eq 0) {
242
+ if ($null -ne $readyAt -or $clock.Elapsed.TotalSeconds -gt 60) { throw 'The installed native host exited or never started.' }
243
+ continue
244
+ }
245
+ $hostProcess = $candidates[0]
246
+ $modules = @($hostProcess.Modules)
247
+ $core = @($modules | Where-Object { $_.ModuleName -ieq 'coreclr.dll' })
248
+ $xaml = @($modules | Where-Object { $_.ModuleName -ieq 'Microsoft.UI.Xaml.dll' })
249
+ $receipt.packagedCoreClrVerified = $core.Count -eq 1 -and (Test-PathWithin $core[0].FileName $installed.InstallLocation)
250
+ $receipt.winUiLoaded = $xaml.Count -eq 1 -and (Test-PathWithin $xaml[0].FileName $installed.InstallLocation)
251
+ $receipt.windowObserved = $hostProcess.MainWindowHandle -ne [IntPtr]::Zero
252
+ $children = @(Get-DescendantProcesses $hostProcess.Id @(Get-CimInstance Win32_Process))
253
+ $renderers = @($children | Where-Object {
254
+ $_.Name -ieq 'msedgewebview2.exe' -and $_.CommandLine -match '(^|\s)--type=renderer(\s|$)'
255
+ })
256
+ $receipt.webViewRendererObserved = $renderers.Count -gt 0
257
+ $applicationReady = $true
258
+ if ($null -ne $TestReadiness) {
259
+ $applicationReady = & $TestReadiness $installed $extraEvidence
260
+ if ($applicationReady -isnot [bool]) { throw 'Application readiness callback must return one Boolean.' }
261
+ Copy-MsixAdditionalEvidence $receipt $extraEvidence $reserved
262
+ }
263
+ $ready = $receipt.packagedCoreClrVerified -and $receipt.winUiLoaded -and $receipt.windowObserved -and $receipt.webViewRendererObserved -and $applicationReady
264
+ if (-not $ready) {
265
+ if ($null -ne $readyAt -or $clock.Elapsed.TotalSeconds -gt 60) {
266
+ throw 'The installed host did not sustain packaged CLR, WinUI, a native window and a WebView2 renderer.'
267
+ }
268
+ continue
269
+ }
270
+ if ($null -eq $readyAt) {
271
+ $readyAt = $clock.Elapsed.TotalSeconds
272
+ $receipt.processId = $hostProcess.Id
273
+ $receipt.loadedCoreClr = $core[0].FileName
274
+ $receipt.loadedWinUi = $xaml[0].FileName
275
+ }
276
+ if ($clock.Elapsed.TotalSeconds - $readyAt -ge $ObserveSeconds) { break }
277
+ }
278
+ if ($null -eq $readyAt -or $clock.Elapsed.TotalSeconds - $readyAt -lt $ObserveSeconds) {
279
+ throw 'Native readiness was not observed for the required duration.'
280
+ }
281
+ if ((Get-FileHash -LiteralPath $original -Algorithm SHA256).Hash -cne $originalHash) {
282
+ throw 'The original unsigned Store artifact changed during acceptance.'
283
+ }
284
+ if (Test-Path -LiteralPath $hostLog) { throw 'The installed host recorded a startup exception.' }
285
+ $receipt.nativeLaunchVerified = $true
286
+ Complete-MsixAcceptanceEvidence $receipt $installed $OutputDirectory $extraEvidence $reserved $CollectEvidence
287
+ } catch {
288
+ $failure = $_.Exception.Message
289
+ $receipt.failure = $failure
290
+ if ($null -ne $installed) {
291
+ $log = Join-Path $env:LOCALAPPDATA "Packages\$($installed.PackageFamilyName)\LocalState\host-error.log"
292
+ if (Test-Path -LiteralPath $log) { Copy-Item -LiteralPath $log -Destination (Join-Path $OutputDirectory 'host-error.log') }
293
+ }
294
+ $events = @()
295
+ foreach ($name in @('Application', 'Microsoft-Windows-AppModel-Runtime/Admin', 'Microsoft-Windows-AppXDeploymentServer/Operational')) {
296
+ $events += @(Get-WinEvent -FilterHashtable @{ LogName = $name; StartTime = $started; Level = 1, 2, 3 } -ErrorAction SilentlyContinue |
297
+ Select-Object -First 10 TimeCreated, ProviderName, Id, LevelDisplayName, Message)
298
+ }
299
+ ConvertTo-Json -InputObject $events -Depth 6 | Set-Content -LiteralPath (Join-Path $OutputDirectory 'windows-events.json') -Encoding UTF8
300
+ } finally {
301
+ # Clean up only this invocation's package/process and exact generated certificate.
302
+ if ($null -ne $hostProcess) {
303
+ $current = Get-Process -Id $hostProcess.Id -ErrorAction SilentlyContinue
304
+ if ($null -ne $current -and $current.StartTime -eq $hostProcess.StartTime) {
305
+ Stop-Process -Id $current.Id -ErrorAction SilentlyContinue
306
+ }
307
+ }
308
+ if ($installAttempted -and $null -ne $metadata) {
309
+ try {
310
+ foreach ($package in @(Get-AppxPackage -Name $metadata.Identity)) {
311
+ if ($package.Version.ToString() -ceq $metadata.Version) {
312
+ Remove-AppxPackage -Package $package.PackageFullName -ErrorAction Stop
313
+ }
314
+ }
315
+ } catch { $receipt.cleanupErrors += $_.Exception.Message }
316
+ }
317
+ if ($null -ne $certificate) {
318
+ try {
319
+ $trustPath = "Cert:\LocalMachine\TrustedPeople\$($certificate.Thumbprint)"
320
+ if (Test-Path -LiteralPath $trustPath) { Remove-Item -LiteralPath $trustPath -ErrorAction Stop }
321
+ Remove-Item -LiteralPath "Cert:\CurrentUser\My\$($certificate.Thumbprint)" -DeleteKey -ErrorAction Stop
322
+ } catch { $receipt.cleanupErrors += $_.Exception.Message }
323
+ }
324
+ if ($receipt.cleanupErrors.Count -gt 0) { $receipt.status = 'failed' }
325
+ $receipt.finishedAt = [DateTime]::UtcNow.ToString('o')
326
+ $receipt | ConvertTo-Json -Depth 10 | Set-Content -LiteralPath (Join-Path $OutputDirectory 'native-smoke-receipt.json') -Encoding UTF8
327
+ }
328
+ if ($receipt.status -ne 'passed') { throw "Native MSIX acceptance failed: $failure; cleanup errors: $($receipt.cleanupErrors.Count)" }
329
+ Write-Output "Installed $Identity ($Architecture): packaged CLR, WinUI window, WebView2 and application readiness sustained for $ObserveSeconds seconds."
330
+
331
+ }
332
+
333
+ Export-ModuleMember -Function Get-PeMachine, Test-PathWithin, Assert-MsixPayload, Get-DescendantProcesses, Read-AtomicJson, Assert-MsixAcceptanceSource, Invoke-MsixNativeAcceptance
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mpgd/cli",
3
- "version": "0.46.1",
3
+ "version": "0.48.0",
4
4
  "description": "Gunshi CLI for mpgd-kit starter generation and target workflow orchestration.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -27,6 +27,10 @@
27
27
  "type": "module",
28
28
  "sideEffects": false,
29
29
  "exports": {
30
+ "./windows-native-tools": {
31
+ "types": "./dist/windows-native-tools.d.ts",
32
+ "default": "./dist/windows-native-tools.js"
33
+ },
30
34
  ".": {
31
35
  "types": "./dist/index.d.ts",
32
36
  "default": "./dist/index.js"
@@ -34,6 +38,7 @@
34
38
  },
35
39
  "files": [
36
40
  "dist",
41
+ "native-tools/windows-msix",
37
42
  "templates"
38
43
  ],
39
44
  "bin": {
@@ -58,9 +63,13 @@
58
63
  "undici": "^7.29.1",
59
64
  "vite": "8.1.3",
60
65
  "yauzl": "3.4.0",
61
- "@mpgd/catalog": "0.9.3",
66
+ "parse5": "7.3.0",
67
+ "postcss": "8.5.28",
68
+ "postcss-value-parser": "4.2.0",
69
+ "@mpgd/catalog": "0.10.0",
62
70
  "@mpgd/phaser-assets": "0.9.0",
63
- "@mpgd/target-config": "0.22.1"
71
+ "@mpgd/platform": "0.23.0",
72
+ "@mpgd/target-config": "0.23.0"
64
73
  },
65
74
  "devDependencies": {
66
75
  "@types/node": "^24.0.0",
@@ -68,17 +77,18 @@
68
77
  "@types/yauzl": "3.4.0",
69
78
  "ttsc": "0.30.4",
70
79
  "typescript": "7.0.2",
71
- "@mpgd/adapter-ait": "0.15.2",
72
- "@mpgd/adapter-capacitor": "0.9.2",
73
- "@mpgd/adapter-browser": "0.10.2",
74
- "@mpgd/adapter-devvit": "0.10.1",
75
- "@mpgd/adapter-verse8": "0.6.0",
76
- "@mpgd/analytics": "0.4.8",
77
- "@mpgd/bridge": "0.12.3",
78
- "@mpgd/game-services": "0.22.0",
79
- "@mpgd/game-runtime": "0.6.5",
80
- "@mpgd/platform": "0.22.0",
81
- "@mpgd/i18n": "0.6.13"
80
+ "@mpgd/adapter-ait": "0.17.0",
81
+ "@mpgd/adapter-browser": "0.11.0",
82
+ "@mpgd/adapter-capacitor": "0.10.0",
83
+ "@mpgd/adapter-devvit": "0.12.0",
84
+ "@mpgd/analytics": "0.4.9",
85
+ "@mpgd/adapter-verse8": "0.7.0",
86
+ "@mpgd/capacitor-play-billing": "0.3.0",
87
+ "@mpgd/bridge": "0.13.0",
88
+ "@mpgd/capacitor-storekit": "0.3.0",
89
+ "@mpgd/game-services": "0.24.0",
90
+ "@mpgd/game-runtime": "0.7.0",
91
+ "@mpgd/i18n": "0.6.14"
82
92
  },
83
93
  "main": "./dist/index.js",
84
94
  "types": "./dist/index.d.ts",
@@ -0,0 +1,22 @@
1
+ ---
2
+ name: use-mpgd-kit
3
+ description: Route lite Canvas2D game development, platform integration, target builds, acceptance, and release work through this generated game's mpgd-kit contracts.
4
+ ---
5
+
6
+ # Use mpgd-kit
7
+
8
+ Read `AGENTS.md`, `agent/game-manifest.json`, and `docs/MPGD_KIT_WORKFLOWS.md` before editing.
9
+
10
+ 1. Identify capability ownership and configured targets from the manifest.
11
+ 2. Keep deterministic rules in `src/game` and Canvas2D rendering/input in `src/lite` through `@mpgd/lite-shell`.
12
+ 3. Use `PlatformGateway`; keep SDK calls in adapters or target wrappers.
13
+ 4. Use backend ledger or verifier APIs before mutating durable purchases, rewarded grants, or verified scores.
14
+ 5. Keep runtime, presentation, recovery, and audio ownership above renderer lifetimes; preserve durable encrypted journals and late observers.
15
+ 6. Update the manifest, brief, and acceptance guidance when capabilities or handoff contracts change.
16
+ 7. Run focused checks, then `pnpm check`, `pnpm build`, and `pnpm accept` for target handoffs.
17
+ 8. Preserve generated JSON/Markdown acceptance and release evidence.
18
+
19
+ Read a target skill declared in the manifest before changing its release flow.
20
+ Use official target documentation or the configured MCP for SDK and submission requirements.
21
+ Keep the lite game free of Phaser, preserve native scrolling and history restoration, and verify Devvit inline budget.
22
+ Do not commit tokens, credentials, signing material, personal MCP configuration, or platform login state.
@@ -0,0 +1,27 @@
1
+ # Generated Lite Game Agent Instructions
2
+
3
+ This is a Canvas2D game generated with the mpgd lite template.
4
+
5
+ ## Start here
6
+
7
+ - Read `agent/game-manifest.json`, `agent/brief.md`, and `agent/acceptance.md`.
8
+ - Use `.agents/skills/use-mpgd-kit/SKILL.md` for platform, target, acceptance, and release work.
9
+ - Read `docs/MPGD_KIT_WORKFLOWS.md` for capability ownership.
10
+
11
+ ## Boundaries
12
+
13
+ - Keep deterministic rules in `src/game`; rendering and input belong in `src/lite`.
14
+ - Use `@mpgd/lite-shell` and Canvas2D. Keep the lite template free of Phaser.
15
+ - Put platform SDK imports in adapters or target wrappers; use `PlatformGateway` from game code.
16
+ - Keep pure game packages free of DOM, network calls, and SDK imports.
17
+ - Purchases and rewarded-ad callbacks are evidence; backend ledger grants precede durable state changes.
18
+ - Create one game runtime above renderer lifetimes. Renderer disposal must not release another owner's native presentation or audio.
19
+ - Keep safe-area geometry reserved once and preserve native scrolling in Devvit inline mode.
20
+ - Do not commit secrets, signing keys, personal MCP configuration, or platform authentication state.
21
+
22
+ ## Tooling and checks
23
+
24
+ - Use `ttsx`, `ttsc`, and `@ttsc/lint`; do not add `tsx`, ESLint, or Prettier.
25
+ - Run focused checks, then `pnpm check` and `pnpm build`.
26
+ - Run `pnpm accept` for a target handoff; preserve its generated evidence.
27
+ - Keep the manifest, brief, and acceptance guidance aligned with material changes.
@@ -0,0 +1,37 @@
1
+ # __GAME_TITLE__
2
+
3
+ Standalone Canvas2D lite game generated by `@mpgd/create-game --template lite --workspace`.
4
+ It uses `@mpgd/lite-shell`, `PlatformGateway`, and the shared mpgd game runtime.
5
+
6
+ ## Start
7
+
8
+ ```sh
9
+ pnpm install
10
+ pnpm dev
11
+ pnpm check
12
+ pnpm build
13
+ pnpm accept
14
+ ```
15
+
16
+ The lite-shell package is private until its initial npm registration. Create lite games with `--workspace` and a local kit checkout.
17
+ The CLI supports the canvas renderer; DOM and SVG renderers are planned.
18
+
19
+ ## Ownership
20
+
21
+ - `src/game/state.ts`: deterministic state.
22
+ - `src/lite/tapGame.ts`: Canvas2D renderer and input.
23
+ - `src/runtime/createGame.ts`: mount, audio binding, and teardown.
24
+ - `src/platform`: gateway selection and authoritative services.
25
+ - `mpgd.targets.json`: configured target build and feature ownership.
26
+
27
+ Read `AGENTS.md`, `agent/brief.md`, `agent/acceptance.md`, and `docs/MPGD_KIT_WORKFLOWS.md` before material edits.
28
+ Use generated icon, localization, build/smoke, and acceptance commands; preserve their evidence.
29
+ Keep SDK calls outside game and renderer code. Backend ledger grants precede durable commerce or rewarded changes.
30
+ Never commit platform credentials or signing material.
31
+
32
+ ## Devvit and browser behavior
33
+
34
+ Inline and expanded entries both use the lite renderer. Preserve feed scrolling and browser touch gestures.
35
+ Verify static imports, preloads, and CSS imports against the inline budget; defer optional engines only through explicit dynamic imports.
36
+ Returning through browser history must retain a persisted game. Verify resize, DPR, safe areas, lifecycle, and native presentation ownership on configured targets.
37
+ Target smoke verifies build artifacts; real SDK and device acceptance is required for platform release evidence.
@@ -0,0 +1,27 @@
1
+ # Lite Game Acceptance
2
+
3
+ Use `agent/game-manifest.json` and `.agents/skills/use-mpgd-kit/SKILL.md` to select configured target checks.
4
+ Run `pnpm check`, `pnpm build`, `pnpm icons:generate`, `pnpm icons:verify`, and `pnpm accept`.
5
+ Preserve the generated JSON and Markdown in `artifacts/acceptance` or the target release-output directory.
6
+
7
+ - Confirm the first screen is playable and uses Canvas2D without a Phaser dependency or scene imports.
8
+ - Resize the game, verify DPR handling, and reserve safe-area geometry exactly once.
9
+ - Check pause/resume, browser history restoration, and audio ownership. A persisted `pagehide` must retain the game.
10
+ - Use the game-owned driver for gameplay and performance acceptance; unit tests do not certify live SDK behavior.
11
+ - Verify renderer teardown removes its own listeners and never updates a replacement view from an old promise.
12
+ - Purchases and rewarded-ad callbacks never grant locally; verify authoritative ledger grants and durable reconciliation before release.
13
+ - Keep unknown native outcomes and late recovery observers; closing one lease must not release another owner's presentation or audio.
14
+
15
+ ## Reddit Devvit
16
+
17
+ Keep SDK calls in the adapter or `apps/target-devvit`. Verify both inline and expanded game modes, preserve feed scrolling, and run the configured inline build budget.
18
+ The inline first load must reject forbidden modules and include static imports, preloads, and CSS imports. Only declared dynamic imports can be deferred.
19
+ Run login/init/playtest for a real Devvit handoff after platform setup. Artifact smoke is not deployed-platform evidence.
20
+
21
+ ## Other configured targets
22
+
23
+ Use the configured official MCP or SDK documentation before SDK or submission changes. Keep target secrets and signing material outside source control.
24
+ ONE play requires issued IDs, authenticated issuer routes, signed SSV/PNS, purchase verification, ledger-before-finalization, and atomic cancellation fencing.
25
+ Verse8 server records require authenticated senders and a game-specific completion verifier.
26
+ Apps in Toss and native Store builds require real-app identity, lifecycle, and SDK acceptance; generic fixtures do not certify live commerce or ads.
27
+ Uploading and deployment follow the target's release procedure after acceptance.
@@ -0,0 +1,17 @@
1
+ # __GAME_TITLE__ Brief
2
+
3
+ Build a Canvas2D game using `@mpgd/lite-shell` and the mpgd platform boundary.
4
+ Read `AGENTS.md`, the agent manifest, and the `use-mpgd-kit` router before editing.
5
+
6
+ - Deterministic rules and state belong in `src/game/state.ts`.
7
+ - Rendering and input belong in `src/lite/tapGame.ts`; mounting belongs in `src/runtime/createGame.ts`.
8
+ - Use `PlatformGateway` for identity, storage, commerce, ads, lifecycle, and host presentation.
9
+ - Keep SDK calls in adapters or target wrappers and use backend ledger APIs before durable grants.
10
+ - Create one `createGamePlatformRuntime` during bootstrap. Bind audio through `bindGameAudio` with the game-owned execution scope.
11
+ - Keep recovery above renderer lifetimes; reconcile existing operations on startup and resume without opening SDK UI.
12
+ - Rewarded v2 needs an application-owned durable encrypted journal. An in-memory store is not recovery evidence.
13
+ - Reserve safe-area geometry once and preserve feed scrolling and browser touch gestures.
14
+
15
+ Run `pnpm dev`, `pnpm check`, `pnpm build`, and `pnpm accept`.
16
+ Devvit inline and expanded entries both use the lite renderer. Keep optional engines behind explicit dynamic import boundaries and verify the inline budget.
17
+ Production commerce and ads remain unavailable until authenticated backend, issued IDs, durable stores, and real-platform acceptance are configured.