????param([switch]$Execute) $ErrorActionPreference='Stop' # Chemins $root = "\\DS-918\chatgpt\ChatGPT-Gouvernance-Projets\_registry" $kb = Join-Path $root "bug_kb\BUG_KB.json.txt" $bootDir= Join-Path $root "bootpack" $boot = Join-Path $bootDir "bootpack.txt" $paste = Join-Path $bootDir "bootpack_paste.txt" $logs = Join-Path $root "logs" $stage = "C:\Temp_Gouvernance" foreach($d in @($bootDir,$logs,$stage)){ if(!(Test-Path $d)){ New-Item -ItemType Directory -Force -Path $d | Out-Null } } $Utf8NoBom = New-Object Text.UTF8Encoding($false) function Get-FileSha256Hex([string]$p){ $sha=[Security.Cryptography.SHA256]::Create() try { $bytes=[IO.File]::ReadAllBytes($p); ([BitConverter]::ToString($sha.ComputeHash($bytes))).Replace('-','').ToLower() } finally { $sha.Dispose() } } function Write-SafeTxt([string]$Dest,[string]$Body,[string]$Tag){ $iso=(Get-Date -Format 'yyyy-MM-ddTHH:mm:ssK') $policy='TXT-ONLY v1.0; SAFE-WRITE v1.1; GOV_SCRIPT_GATE v1.3; KB_DUAL_BOOTPACK v1.0' if(!(Test-Path (Split-Path $Dest -Parent))){ New-Item -ItemType Directory -Force -Path (Split-Path $Dest -Parent)|Out-Null } if(!(Test-Path $stage)){ New-Item -ItemType Directory -Force -Path $stage|Out-Null } $tmpLocal = Join-Path $stage ([IO.Path]::GetFileName($Dest)) [IO.File]::WriteAllText($tmpLocal,$Body,$Utf8NoBom) $h = Get-FileSha256Hex $tmpLocal [IO.File]::AppendAllText($tmpLocal,"`r`n`r`n--- DOC-VERSION-FOOTER ---`r`nGenerated: $iso`r`nSHA-256: $h`r`nPolicy: $policy`r`nSource: KB_EMIT_BOOTPACK_DUAL`r`n",$Utf8NoBom) $tmp="$Dest.tmp" Copy-Item $tmpLocal $tmp -Force if(Test-Path $Dest){ Copy-Item $Dest ($Dest+".bak_"+(Get-Date -Format 'yyyyMMdd_HHmmss')) -Force } Move-Item $tmp $Dest -Force } function Read-KbJsonBodyStreaming([string]$Path){ if(!(Test-Path $Path)){ throw "KB introuvable: $Path" } $fs=[IO.File]::Open($Path,[IO.FileMode]::Open,[IO.FileAccess]::Read,[IO.FileShare]::ReadWrite) $sr=New-Object IO.StreamReader($fs,[Text.UTF8Encoding]::new($false),$true,65536) try{ $sb = New-Object System.Text.StringBuilder $footer='--- DOC-VERSION-FOOTER ---' while(-not $sr.EndOfStream){ $line=$sr.ReadLine() if($line -like "$footer*"){ break } [void]$sb.AppendLine($line) } return $sb.ToString().Trim() } finally { $sr.Close(); $fs.Close() } } # 1) Lire & compacter la KB $raw = Read-KbJsonBodyStreaming $kb try { $obj = $raw | ConvertFrom-Json } catch { throw "KB JSON invalide (ConvertFrom-Json): $($_.Exception.Message)" } if(-not $obj -or -not $obj.entries){ $obj=[pscustomobject]@{ entries=@(); updated=(Get-Date -Format 'yyyy-MM-ddTHH:mm:ssK') } } # S?lectionner un sous-ensemble stable des champs pour le compact $compactEntries = @() foreach($e in $obj.entries){ $compactEntries += [pscustomobject]@{ id = $e.id; title = $e.title; blocking = $e.blocking workaround = $e.workaround; note = $e.note; fix = $e.fix tags = $e.tags; seen_in_threads = $e.seen_in_threads; last_seen = $e.last_seen } } $compact = [pscustomobject]@{ entries = $compactEntries; updated = $obj.updated } $compactJson = $compact | ConvertTo-Json -Depth 40 -Compress # 2) ?crire bootpack_paste.txt (compact JSON) $pasteBody = $compactJson # Pr?-calcul SHA pour pointer $shaPaste = "" # on ?crit d'abord dans un fichier temporaire local pour calculer le SHA r?el $tmpLocalPaste = Join-Path $stage "bootpack_paste.preview" [IO.File]::WriteAllText($tmpLocalPaste,$pasteBody,$Utf8NoBom) $shaPaste = Get-FileSha256Hex $tmpLocalPaste Remove-Item $tmpLocalPaste -ErrorAction SilentlyContinue # 3) ?crire bootpack.txt (r?gles + pointeur vers paste) $iso=(Get-Date -Format 'yyyy-MM-ddTHH:mm:ssK') $pol = @( "GOV_SCRIPT_GATE v1.3", "SAFE-WRITE-RULE v1.1", "TXT-ONLY", "SYNC-MEM-ARCHIVE-RULE", "KB_DUAL_BOOTPACK v1.0" ) $pointer = @() $pointer += "[BOOT-PACK]" $pointer += "Name=ChatGPT-Gouvernance-Projets BootPack" $pointer += "Version=1.0" $pointer += "Generated=$iso" $pointer += "" $pointer += "[POLICY]" foreach($p in $pol){ $pointer += $p } $pointer += "" $pointer += "[BUG_KB_JSON_POINTER]" $pointer += ("Path="+$paste) $pointer += ("SHA256="+$shaPaste) $pointer += ("Updated="+$obj.updated) $pointer += ("Entries="+($compactEntries.Count)) $pointerTxt = ($pointer -join "`r`n") if(-not $Execute){ "{0,-12} {1}" -f "entries~:", ($compactEntries.Count) | Write-Host "{0,-12} {1:N1} MB" -f "kb.raw:", ((Get-Item $kb).Length/1MB) | Write-Host $szPaste = [Text.Encoding]::UTF8.GetByteCount($pasteBody) "{0,-12} {1:N1} MB" -f "paste.size:", ($szPaste/1MB) | Write-Host "{0,-12} {1}" -f "paste.sha:", $shaPaste | Write-Host Write-Host "[PREVIEW] Utilise -Execute pour ?crire:" Write-Host (" - " + $paste) Write-Host (" - " + $boot) exit 0 } Write-SafeTxt -Dest $paste -Body $pasteBody -Tag "BOOTPACK_PASTE_JSON" Write-SafeTxt -Dest $boot -Body $pointerTxt -Tag "BOOTPACK_POINTER" # Log $log = Join-Path $logs 'registry_activity.log' $old=@(); if(Test-Path $log){ $old += Get-Content -LiteralPath $log -ErrorAction SilentlyContinue } $old += ("[{0}] bootpack_emit_dual_v1.0: paste_sha={1} entries={2}" -f (Get-Date -Format 'yyyy-MM-ddTHH:mm:ssK'),$shaPaste,($compactEntries.Count)) [IO.File]::WriteAllText($log, ($old -join "`r`n"), (New-Object Text.UTF8Encoding($false))) Write-Host "[EMIT] bootpack_paste.txt + bootpack.txt ?crits."