185 lines
7.9 KiB
Python
185 lines
7.9 KiB
Python
#!/usr/bin/env python3
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
|
|
def _add_lib_path() -> None:
|
|
root = Path(__file__).resolve().parents[3]
|
|
lib_path = root / "lib"
|
|
if str(lib_path) not in sys.path:
|
|
sys.path.insert(0, str(lib_path))
|
|
|
|
|
|
_add_lib_path()
|
|
|
|
from air_runtime.engine import (
|
|
build_engine_plan,
|
|
dispatch_worker_group,
|
|
enter_engine,
|
|
intervene_engine,
|
|
merge_worker_result,
|
|
monitor_engine,
|
|
run_engine_once,
|
|
status_engine,
|
|
)
|
|
from air_runtime.paths import todo_path as workflow_todo_path
|
|
from air_runtime.project_bootstrap import ensure_project_bootstrap
|
|
|
|
|
|
def parse_args() -> argparse.Namespace:
|
|
parser = argparse.ArgumentParser(description="AirEng public scheduler runtime")
|
|
parser.add_argument(
|
|
"--mode",
|
|
choices=["enter", "status", "plan", "dispatch", "merge", "monitor", "run", "intervene"],
|
|
default="status",
|
|
)
|
|
parser.add_argument("--project", default=".")
|
|
parser.add_argument("--todo", default="")
|
|
parser.add_argument("--result", default="")
|
|
parser.add_argument("--dispatch-group", default="")
|
|
return parser.parse_args()
|
|
|
|
|
|
def main() -> None:
|
|
args = parse_args()
|
|
project_root = Path(args.project).expanduser().resolve()
|
|
ensure_project_bootstrap(project_root)
|
|
|
|
if args.mode == "enter":
|
|
state_path, health = enter_engine(project_root)
|
|
print("aireng_mode=enabled")
|
|
print(f"project_root={project_root}")
|
|
print(f"state_path={state_path}")
|
|
for key, value in health.items():
|
|
print(f"{key}={'ok' if value else 'missing'}")
|
|
return
|
|
|
|
if args.mode == "status":
|
|
state = status_engine(project_root)
|
|
print(f"aireng_mode={'enabled' if state.get('enabled') else 'disabled'}")
|
|
print(f"project_root={project_root}")
|
|
print(f"engine_mode={state.get('engineMode', '')}")
|
|
print(f"active_wave_id={state.get('activeWaveId', '')}")
|
|
print(f"active_dispatch_path={state.get('activeDispatchPath', '')}")
|
|
print(f"active_worker_count={len(state.get('activeWorkers', []))}")
|
|
print(f"merged_results={len(state.get('mergedResults', []))}")
|
|
print(f"pending_global_updates={len(state.get('pendingGlobalUpdates', []))}")
|
|
print(f"planning_source={state.get('planningSource', '')}")
|
|
print(f"xdb_sessions={len(state.get('xdbSessions', []))}")
|
|
print(f"xdb_policy_enabled={state.get('xdbPolicy', {}).get('enabled')}")
|
|
print(f"debug_sessions={len(state.get('debugSessions', []))}")
|
|
print(f"debug_policy_enabled={state.get('debugPolicy', {}).get('enabled')}")
|
|
print(f"repair_attempts={len(state.get('repairAttempts', []))}")
|
|
print(f"active_repairs={state.get('activeRepairCount', 0)}")
|
|
print(f"repair_policy_enabled={state.get('repairPolicy', {}).get('enabled')}")
|
|
print(f"monitor_interval_seconds={state.get('monitoringPolicy', {}).get('checkIntervalSeconds', 0)}")
|
|
print(f"last_loop_at={state.get('lastLoopAt', '')}")
|
|
print(f"last_intervention_at={state.get('lastInterventionAt', '')}")
|
|
print(f"next_action={state.get('nextAction', '')}")
|
|
for key, value in state.get("artifactHealth", {}).items():
|
|
print(f"{key}={'ok' if value else 'missing'}")
|
|
return
|
|
|
|
if args.mode == "plan":
|
|
current_todo_path = Path(args.todo).expanduser().resolve() if args.todo else workflow_todo_path(project_root)
|
|
result = build_engine_plan(project_root, current_todo_path)
|
|
print("aireng_mode=planned")
|
|
print(f"project_root={project_root}")
|
|
print(f"todo_path={current_todo_path}")
|
|
print(f"plan_path={result['planPath']}")
|
|
print(f"plan_markdown_path={result['planMarkdownPath']}")
|
|
print(f"review_json_path={result['reviewJsonPath']}")
|
|
print(f"review_markdown_path={result['reviewMarkdownPath']}")
|
|
print(f"planning_source={result['planningSource']}")
|
|
print(f"review_source_path={result['reviewSourcePath']}")
|
|
print(f"selected_tasks={','.join(result['selectedTasks'])}")
|
|
print(f"parallel_group_count={result['parallelGroupCount']}")
|
|
print(f"conflict_count={result['conflictCount']}")
|
|
return
|
|
|
|
if args.mode == "dispatch":
|
|
result = dispatch_worker_group(project_root, args.dispatch_group)
|
|
print("aireng_mode=dispatched")
|
|
print(f"project_root={project_root}")
|
|
print(f"dispatch_path={result['dispatchPath']}")
|
|
print(f"group_name={result['groupName']}")
|
|
print(f"wave_id={result['waveId']}")
|
|
print(f"task_ids={','.join(result['taskIds'])}")
|
|
print(f"recommended_concurrency={result['recommendedConcurrency']}")
|
|
return
|
|
|
|
if args.mode == "monitor":
|
|
result = monitor_engine(project_root)
|
|
print("aireng_mode=monitored")
|
|
print(f"project_root={project_root}")
|
|
print(f"engine_mode={result['engineMode']}")
|
|
print(f"active_worker_count={result['activeWorkerCount']}")
|
|
print(f"ready_to_merge_count={result['readyToMergeCount']}")
|
|
print(f"merged_count={result['mergedCount']}")
|
|
print(f"stalled_count={result['stalledCount']}")
|
|
print(f"intervention_count={result['interventionCount']}")
|
|
print(f"blocked_task_count={result['blockedTaskCount']}")
|
|
print(f"repair_queue_path={result['repairQueuePath']}")
|
|
print(f"next_action={result['nextAction']}")
|
|
return
|
|
|
|
if args.mode == "intervene":
|
|
result = intervene_engine(project_root)
|
|
print("aireng_mode=intervened")
|
|
print(f"project_root={project_root}")
|
|
print(f"engine_mode={result['engineMode']}")
|
|
print(f"stalled_count={result['stalledCount']}")
|
|
print(f"intervention_count={result['interventionCount']}")
|
|
print(f"blocked_task_count={result['blockedTaskCount']}")
|
|
print(f"next_action={result['nextAction']}")
|
|
return
|
|
|
|
if args.mode == "run":
|
|
current_todo_path = Path(args.todo).expanduser().resolve() if args.todo else workflow_todo_path(project_root)
|
|
result = run_engine_once(project_root, current_todo_path)
|
|
print("aireng_mode=ran")
|
|
print(f"project_root={project_root}")
|
|
print(f"action={result['action']}")
|
|
print(f"steps={','.join(result['steps'])}")
|
|
print(f"plan_path={result['planPath']}")
|
|
print(f"engine_mode={result['engineMode']}")
|
|
print(f"next_action={result['nextAction']}")
|
|
if 'dispatchPath' in result:
|
|
print(f"dispatch_path={result['dispatchPath']}")
|
|
if 'waveId' in result:
|
|
print(f"wave_id={result['waveId']}")
|
|
if 'taskIds' in result:
|
|
print(f"task_ids={','.join(result['taskIds'])}")
|
|
if 'activeWorkerCount' in result:
|
|
print(f"active_worker_count={result['activeWorkerCount']}")
|
|
return
|
|
|
|
if not args.result:
|
|
raise SystemExit("--result is required for merge mode")
|
|
|
|
result_path = Path(args.result).expanduser().resolve()
|
|
merged = merge_worker_result(project_root, result_path)
|
|
print("aireng_mode=merged")
|
|
print(f"project_root={project_root}")
|
|
print(f"task_id={merged['taskId']}")
|
|
print(f"task_status={merged['status']}")
|
|
print(f"archived_result_path={merged['archivedResultPath']}")
|
|
print(f"pending_global_update_count={merged['pendingGlobalUpdateCount']}")
|
|
print(f"doc_queue_path={merged['docQueuePath']}")
|
|
print(f"repair_queue_path={merged['repairQueuePath']}")
|
|
print(f"todo_path={merged['todoPath']}")
|
|
print(f"applied_doc_path_count={merged['appliedDocPathCount']}")
|
|
print(f"xdb_session_count={merged['xdbSessionCount']}")
|
|
print(f"debug_session_count={merged['debugSessionCount']}")
|
|
print(f"repair_attempt_count={merged['repairAttemptCount']}")
|
|
print(f"repair_prepared={merged['repairPrepared']}")
|
|
print(f"repair_dispatch_path={merged['repairDispatchPath']}")
|
|
print(f"next_action={merged['nextAction']}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|