feat: add AI Workspace Menu Bar App design and enhance MCP server with resource definitions and read functionality

This commit is contained in:
2026-05-20 15:22:37 -06:00
parent cfd61bdee3
commit b21889c4ab
8 changed files with 368 additions and 43 deletions

View File

@@ -220,6 +220,37 @@ def health_ok(config: dict[str, Any], timeout: float = 1.0) -> tuple[bool | None
return None, f"unknown health type: {kind}"
def service_status(profile: str, ref: ServiceRef) -> dict[str, Any]:
enabled = ref.config.get("enabled", True)
kind = ref.config.get("kind", "process")
command = ref.config.get("command") or []
pid = read_pid(profile, ref.name) if enabled and kind != "app-launcher" else None
running = is_running(pid)
ok, detail = health_ok(ref.config) if enabled else (None, "health skipped")
if not enabled:
label = "disabled"
elif kind == "app-launcher":
label = "launcher"
elif running and ok is not False:
label = "running"
elif running:
label = "unhealthy"
elif ok is True:
label = "externally running"
else:
label = "stopped"
return {
"name": ref.name,
"enabled": enabled,
"kind": kind,
"status": label,
"pid": pid,
"command": command,
"health": {"ok": ok, "detail": detail},
"state": read_state(profile, ref.name),
}
def wait_for_health(config: dict[str, Any], seconds: float = 8.0) -> tuple[bool | None, str]:
deadline = time.time() + seconds
last: tuple[bool | None, str] = (None, "no health check")
@@ -319,29 +350,17 @@ def stop_service(profile: str, ref: ServiceRef) -> None:
def status_service(profile: str, ref: ServiceRef) -> None:
enabled = ref.config.get("enabled", True)
kind = ref.config.get("kind", "process")
if not enabled:
status = service_status(profile, ref)
if not status["enabled"]:
print(f"{ref.name}: disabled")
return
if kind == "app-launcher":
state = read_state(profile, ref.name)
if status["kind"] == "app-launcher":
state = status["state"]
launched = state.get("launched_at")
suffix = f"last launched {time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(launched))}" if launched else "not launched by manager"
print(f"{ref.name}: launcher ({suffix})")
return
pid = read_pid(profile, ref.name)
running = is_running(pid)
ok, detail = health_ok(ref.config)
if running and ok is not False:
label = "running"
elif running:
label = "unhealthy"
elif ok is True:
label = "externally running"
else:
label = "stopped"
print(f"{ref.name}: {label} pid={pid or '-'} ({detail})")
print(f"{ref.name}: {status['status']} pid={status['pid'] or '-'} ({status['health']['detail']})")
def tail_log(profile: str, service: str, lines: int) -> None:
@@ -354,42 +373,64 @@ def tail_log(profile: str, service: str, lines: int) -> None:
print(line)
def run_doctor(profile: str, manifest: dict[str, Any]) -> None:
def doctor_report(profile: str, manifest: dict[str, Any]) -> dict[str, Any]:
errors = validate_manifest(manifest)
service_reports = []
for ref in service_items(manifest, include_disabled=True):
command = ref.config.get("command") or []
first = command[0] if command else ""
doctor = ref.config.get("doctor") or {}
checks = []
for command_name in doctor.get("required_commands") or []:
checks.append({"type": "required_command", "name": command_name, "ok": command_exists(command_name)})
for command_name in doctor.get("optional_commands") or []:
checks.append({"type": "optional_command", "name": command_name, "ok": command_exists(command_name)})
for raw_path in doctor.get("required_paths") or []:
checks.append({"type": "required_path", "name": str(raw_path), "ok": resolve_workspace_path(str(raw_path)).exists()})
for raw_path in doctor.get("optional_paths") or []:
checks.append({"type": "optional_path", "name": str(raw_path), "ok": resolve_workspace_path(str(raw_path)).exists()})
status = service_status(profile, ref)
status["command_ok"] = command_exists(first) if first else False
status["checks"] = checks
service_reports.append(status)
return {
"profile": profile,
"workspace": str(ROOT),
"manifest": str(manifest_path(profile)),
"runtime": str(RUNTIME_DIR),
"manifest_ok": not errors,
"manifest_errors": errors,
"services": service_reports,
}
def run_doctor(profile: str, manifest: dict[str, Any], json_output: bool = False) -> None:
report = doctor_report(profile, manifest)
if json_output:
print(json.dumps(report, ensure_ascii=False, indent=2, sort_keys=True))
return
print(f"AI Workspace doctor profile={profile}")
print(f"workspace: {ROOT}")
print(f"manifest: {manifest_path(profile)}")
ensure_runtime()
print(f"runtime: {RUNTIME_DIR}")
errors = validate_manifest(manifest)
errors = report["manifest_errors"]
if errors:
print("manifest: invalid")
for error in errors:
print(f" ! {error}")
else:
print("manifest: ok")
for ref in service_items(manifest, include_disabled=True):
enabled = ref.config.get("enabled", True)
command = ref.config.get("command") or []
first = command[0] if command else ""
command_ok = command_exists(first) if first else False
enabled_text = "enabled" if enabled else "disabled"
if not enabled:
print(f"- {ref.name}: {enabled_text}; command={'ok' if command_ok else 'missing'}; health skipped")
for service in report["services"]:
enabled_text = "enabled" if service["enabled"] else "disabled"
if not service["enabled"]:
print(f"- {service['name']}: {enabled_text}; command={'ok' if service['command_ok'] else 'missing'}; health skipped")
continue
ok, detail = health_ok(ref.config)
health_text = detail if ok is not None else "no health check"
print(f"- {ref.name}: {enabled_text}; command={'ok' if command_ok else 'missing'}; {health_text}")
doctor = ref.config.get("doctor") or {}
for command_name in doctor.get("required_commands") or []:
print(f" required command {command_name}: {'ok' if command_exists(command_name) else 'missing'}")
for command_name in doctor.get("optional_commands") or []:
print(f" optional command {command_name}: {'ok' if command_exists(command_name) else 'missing'}")
for raw_path in doctor.get("required_paths") or []:
path = resolve_workspace_path(str(raw_path))
print(f" required path {raw_path}: {'ok' if path.exists() else 'missing'}")
for raw_path in doctor.get("optional_paths") or []:
path = resolve_workspace_path(str(raw_path))
print(f" optional path {raw_path}: {'ok' if path.exists() else 'missing'}")
health_text = service["health"]["detail"] if service["health"]["ok"] is not None else "no health check"
print(f"- {service['name']}: {enabled_text}; command={'ok' if service['command_ok'] else 'missing'}; {health_text}")
for check in service["checks"]:
label = check["type"].replace("_", " ")
print(f" {label} {check['name']}: {'ok' if check['ok'] else 'missing'}")
def shutil_which(command: str) -> str | None:
@@ -408,13 +449,14 @@ def main() -> None:
parser.add_argument("--profile", default=os.getenv("AIW_PROJECT_PROFILE", "fidelity"))
parser.add_argument("--group", default="", help="Start/stop/status services in a group, e.g. communication or inbox.")
parser.add_argument("--lines", type=int, default=80, help="Number of log lines for logs action.")
parser.add_argument("--json", action="store_true", help="Emit machine-readable JSON for supported actions such as doctor.")
args = parser.parse_args()
ensure_runtime()
manifest = load_manifest(args.profile)
if args.action == "doctor":
run_doctor(args.profile, manifest)
run_doctor(args.profile, manifest, json_output=args.json)
return
errors = validate_manifest(manifest)