Sync: Complete project state with all MEGA SPRINT V1-V3 features and Codex stubs
This commit is contained in:
292
ralph/scripts/Start-RalphInboxDaemon.ps1
Normal file
292
ralph/scripts/Start-RalphInboxDaemon.ps1
Normal file
@@ -0,0 +1,292 @@
|
||||
param(
|
||||
[int]$PollSeconds = 15,
|
||||
[string]$Implementer = "",
|
||||
[string[]]$Reviewers = @(),
|
||||
[switch]$DisableCodexMaster,
|
||||
[switch]$DisableAutoFix,
|
||||
[switch]$DryRun,
|
||||
[switch]$Once,
|
||||
[int]$MaxTasks = 0,
|
||||
[string]$InboxDirectory = ""
|
||||
)
|
||||
|
||||
. (Join-Path $PSScriptRoot "Common.ps1")
|
||||
|
||||
$roots = Get-RalphTaskRoots
|
||||
foreach ($path in $roots.Values) {
|
||||
Ensure-Directory -Path $path
|
||||
}
|
||||
|
||||
if ([string]::IsNullOrWhiteSpace($InboxDirectory)) {
|
||||
$InboxDirectory = $roots.Inbox
|
||||
}
|
||||
Ensure-Directory -Path $InboxDirectory
|
||||
|
||||
$lockFile = Get-RalphStateFile -Name "inbox_daemon.lock.json"
|
||||
$daemonStateFile = Get-RalphStateFile -Name "inbox_daemon_state.json"
|
||||
$script:daemonRunId = "daemon-" + (Get-Date -Format "yyyyMMdd-HHmmss")
|
||||
$script:processedCount = 0
|
||||
|
||||
function Save-DaemonState {
|
||||
param(
|
||||
[string]$Status,
|
||||
[string]$Message,
|
||||
[hashtable]$Extra = @{}
|
||||
)
|
||||
|
||||
$state = [ordered]@{
|
||||
pid = $PID
|
||||
status = $Status
|
||||
message = $Message
|
||||
updated_at = (Get-Date).ToString("o")
|
||||
poll_seconds = $PollSeconds
|
||||
dry_run = [bool]$DryRun
|
||||
inbox_directory = $InboxDirectory
|
||||
processed_count = $script:processedCount
|
||||
}
|
||||
foreach ($key in $Extra.Keys) {
|
||||
$state[$key] = $Extra[$key]
|
||||
}
|
||||
Write-JsonFile -Path $daemonStateFile -Object $state
|
||||
}
|
||||
|
||||
function Acquire-DaemonLock {
|
||||
if (Test-Path $lockFile) {
|
||||
try {
|
||||
$existing = Read-JsonFile -Path $lockFile
|
||||
$existingPid = 0
|
||||
try { $existingPid = [int]$existing.pid } catch { $existingPid = 0 }
|
||||
if ($existingPid -gt 0) {
|
||||
$proc = Get-Process -Id $existingPid -ErrorAction SilentlyContinue
|
||||
if ($null -ne $proc) {
|
||||
throw "Ralph inbox daemon already running with PID $existingPid."
|
||||
}
|
||||
}
|
||||
}
|
||||
catch {
|
||||
# stale or malformed lock; overwrite it
|
||||
}
|
||||
}
|
||||
|
||||
Write-JsonFile -Path $lockFile -Object ([ordered]@{
|
||||
pid = $PID
|
||||
started_at = (Get-Date).ToString("o")
|
||||
inbox_directory = $InboxDirectory
|
||||
})
|
||||
}
|
||||
|
||||
function Release-DaemonLock {
|
||||
if (Test-Path $lockFile) {
|
||||
Remove-Item -LiteralPath $lockFile -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
}
|
||||
|
||||
function Get-NextInboxItem {
|
||||
$directories = @(Get-ChildItem -LiteralPath $InboxDirectory -Directory -ErrorAction SilentlyContinue | Sort-Object LastWriteTime, Name)
|
||||
foreach ($dir in $directories) {
|
||||
if (Test-Path (Join-Path $dir.FullName "TASK.md")) {
|
||||
return [ordered]@{
|
||||
kind = "taskpack"
|
||||
path = $dir.FullName
|
||||
name = $dir.Name
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$files = @(Get-ChildItem -LiteralPath $InboxDirectory -File -Filter *.md -ErrorAction SilentlyContinue | Sort-Object LastWriteTime, Name)
|
||||
foreach ($file in $files) {
|
||||
return [ordered]@{
|
||||
kind = "markdown"
|
||||
path = $file.FullName
|
||||
name = $file.Name
|
||||
}
|
||||
}
|
||||
|
||||
return $null
|
||||
}
|
||||
|
||||
function Move-InboxItemToProcessing {
|
||||
param([hashtable]$Item)
|
||||
|
||||
$id = "{0}-{1}" -f (Get-Date -Format "yyyyMMdd-HHmmss"), (Convert-ToRalphSlug -Text ([System.IO.Path]::GetFileNameWithoutExtension($Item.name)))
|
||||
$targetDir = Join-Path $roots.Processing $id
|
||||
Ensure-Directory -Path $targetDir
|
||||
|
||||
if ($Item.kind -eq "taskpack") {
|
||||
$targetParent = Split-Path -Parent $targetDir
|
||||
if (-not (Test-Path $targetParent)) {
|
||||
Ensure-Directory -Path $targetParent
|
||||
}
|
||||
Remove-Item -LiteralPath $targetDir -Recurse -Force -ErrorAction SilentlyContinue
|
||||
Move-Item -LiteralPath $Item.path -Destination $targetDir
|
||||
return [ordered]@{
|
||||
id = $id
|
||||
task_directory = $targetDir
|
||||
title = $Item.name
|
||||
source = $Item.path
|
||||
}
|
||||
}
|
||||
|
||||
$taskInfo = New-RalphTaskPackFromMarkdown -MarkdownPath $Item.path -TaskId $id -TargetDirectory $targetDir
|
||||
Remove-Item -LiteralPath $Item.path -Force
|
||||
return [ordered]@{
|
||||
id = $taskInfo.id
|
||||
task_directory = $taskInfo.task_directory
|
||||
title = $taskInfo.title
|
||||
source = $Item.path
|
||||
}
|
||||
}
|
||||
|
||||
function Finalize-ProcessedItem {
|
||||
param(
|
||||
[hashtable]$Processed,
|
||||
[string]$DestinationRoot,
|
||||
[string]$State,
|
||||
[string]$RunId = "",
|
||||
[string]$Summary = ""
|
||||
)
|
||||
|
||||
Ensure-Directory -Path $DestinationRoot
|
||||
$destination = Join-Path $DestinationRoot ([System.IO.Path]::GetFileName($Processed.task_directory))
|
||||
if (Test-Path $destination) {
|
||||
Remove-Item -LiteralPath $destination -Recurse -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
Move-Item -LiteralPath $Processed.task_directory -Destination $destination
|
||||
|
||||
$submissionFile = Join-Path $destination "submission.json"
|
||||
$submission = [ordered]@{
|
||||
id = $Processed.id
|
||||
title = $Processed.title
|
||||
state = $State
|
||||
finished_at = (Get-Date).ToString("o")
|
||||
source = $Processed.source
|
||||
run_id = $RunId
|
||||
summary = $Summary
|
||||
}
|
||||
Write-JsonFile -Path $submissionFile -Object $submission
|
||||
return $destination
|
||||
}
|
||||
|
||||
Acquire-DaemonLock
|
||||
Save-DaemonState -Status "running" -Message "Inbox daemon started"
|
||||
Add-RalphEvent -RunId $script:daemonRunId -Stage "daemon" -Status "running" -Actor "daemon" -Message "Ralph inbox daemon started" -Data @{
|
||||
inbox_directory = $InboxDirectory
|
||||
}
|
||||
Send-TelegramNotification `
|
||||
-EventName "daemon_started" `
|
||||
-Title "Ralph daemon started" `
|
||||
-Message ("Inbox: " + $InboxDirectory) `
|
||||
-RunId $script:daemonRunId `
|
||||
-Stage "daemon" `
|
||||
-Status "running" | Out-Null
|
||||
|
||||
try {
|
||||
while ($true) {
|
||||
$item = Get-NextInboxItem
|
||||
if ($null -eq $item) {
|
||||
if ($Once) {
|
||||
Save-DaemonState -Status "idle" -Message "No tasks in inbox; exiting because -Once was used"
|
||||
break
|
||||
}
|
||||
Save-DaemonState -Status "idle" -Message "Waiting for inbox tasks"
|
||||
Start-Sleep -Seconds $PollSeconds
|
||||
continue
|
||||
}
|
||||
|
||||
$processed = Move-InboxItemToProcessing -Item $item
|
||||
$script:processedCount += 1
|
||||
Save-DaemonState -Status "running" -Message ("Processing task " + $processed.id) -Extra @{
|
||||
current_task = $processed.id
|
||||
current_task_directory = $processed.task_directory
|
||||
}
|
||||
Add-RalphEvent -RunId $script:daemonRunId -Stage "queue" -Status "started" -Actor "daemon" -Message ("Dequeued task " + $processed.id) -Data @{
|
||||
task_directory = $processed.task_directory
|
||||
}
|
||||
Send-TelegramNotification `
|
||||
-EventName "task_processing" `
|
||||
-Title "Task processing" `
|
||||
-Message ($processed.title + "`n" + $processed.task_directory) `
|
||||
-RunId $processed.id `
|
||||
-Stage "queue" `
|
||||
-Status "started" | Out-Null
|
||||
|
||||
$runId = ""
|
||||
$summary = ""
|
||||
try {
|
||||
$args = @(
|
||||
"-ExecutionPolicy", "Bypass",
|
||||
"-File", (Join-Path $PSScriptRoot "Start-RalphAutopilot.ps1"),
|
||||
"-TaskDirectory", $processed.task_directory,
|
||||
"-RunLabel", "queue"
|
||||
)
|
||||
if ($DryRun) {
|
||||
$args += "-DryRun"
|
||||
}
|
||||
if (-not [string]::IsNullOrWhiteSpace($Implementer)) {
|
||||
$args += @("-Implementer", $Implementer)
|
||||
}
|
||||
foreach ($reviewer in $Reviewers) {
|
||||
$args += @("-Reviewers", $reviewer)
|
||||
}
|
||||
if ($DisableCodexMaster) {
|
||||
$args += "-UseCodexMaster:$false"
|
||||
}
|
||||
if ($DisableAutoFix) {
|
||||
$args += "-AutoFix:$false"
|
||||
}
|
||||
|
||||
& powershell.exe @args
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
throw "Start-RalphAutopilot.ps1 failed with exit code $LASTEXITCODE"
|
||||
}
|
||||
|
||||
$currentRunState = Read-JsonFile -Path (Get-RalphStateFile -Name "current_run.json")
|
||||
$runId = [string]$currentRunState.run_id
|
||||
$summary = [string]$currentRunState.latest_message
|
||||
$finalPath = Finalize-ProcessedItem -Processed $processed -DestinationRoot $roots.Completed -State "completed" -RunId $runId -Summary $summary
|
||||
Add-RalphEvent -RunId $script:daemonRunId -Stage "queue" -Status "completed" -Actor "daemon" -Message ("Completed task " + $processed.id) -Data @{
|
||||
task_directory = $finalPath
|
||||
run_id = $runId
|
||||
}
|
||||
Send-TelegramNotification `
|
||||
-EventName "task_completed" `
|
||||
-Title "Task completed" `
|
||||
-Message ($processed.title + "`n" + $summary) `
|
||||
-RunId $runId `
|
||||
-Stage "queue" `
|
||||
-Status "completed" | Out-Null
|
||||
}
|
||||
catch {
|
||||
$summary = $_.Exception.Message
|
||||
$failedPath = Finalize-ProcessedItem -Processed $processed -DestinationRoot $roots.Failed -State "failed" -RunId $runId -Summary $summary
|
||||
Add-RalphEvent -RunId $script:daemonRunId -Stage "queue" -Status "failed" -Actor "daemon" -Message ("Failed task " + $processed.id + ": " + $summary) -Data @{
|
||||
task_directory = $failedPath
|
||||
run_id = $runId
|
||||
}
|
||||
Send-TelegramNotification `
|
||||
-EventName "task_failed" `
|
||||
-Title "Task failed" `
|
||||
-Message ($processed.title + "`n" + $summary) `
|
||||
-RunId $runId `
|
||||
-Stage "queue" `
|
||||
-Status "failed" | Out-Null
|
||||
}
|
||||
|
||||
if ($MaxTasks -gt 0 -and $script:processedCount -ge $MaxTasks) {
|
||||
Save-DaemonState -Status "completed" -Message "MaxTasks limit reached"
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
finally {
|
||||
Save-DaemonState -Status "stopped" -Message "Inbox daemon stopped"
|
||||
Add-RalphEvent -RunId $script:daemonRunId -Stage "daemon" -Status "stopped" -Actor "daemon" -Message "Ralph inbox daemon stopped"
|
||||
Send-TelegramNotification `
|
||||
-EventName "daemon_stopped" `
|
||||
-Title "Ralph daemon stopped" `
|
||||
-Message "Inbox daemon stopped" `
|
||||
-RunId $script:daemonRunId `
|
||||
-Stage "daemon" `
|
||||
-Status "stopped" | Out-Null
|
||||
Release-DaemonLock
|
||||
}
|
||||
Reference in New Issue
Block a user