* feat(setup)!: expand agent integrations and simplify onboarding Add native configuration formats for OpenCode, Antigravity, GitHub Copilot CLI, VS Code, and Devin. Preserve JSONC comments and unrelated settings while making setup and uninstall ownership-aware and idempotent. Select agent targets before planning, separate discovery from intent, and keep agent-only setup independent of optional provider prerequisites. Cover configuration boundaries and interactive behavior in packaged checks. BREAKING CHANGE: unscoped setup refreshes only existing REA-owned registrations. Use --all-detected to configure every detected client. Setup JSON contains a compact doctor summary; use rea doctor for full diagnostics. Cancellation and successful dry runs now exit successfully. * feat(cli)!: replace upgrade with verified updates Resolve an exact release through npm's configured registry, update only the identified installation, and verify its new executable before reporting success. Return a read-only maintenance plan for existing enabled REA integrations. BREAKING CHANGE: replace rea upgrade with rea update; no alias is retained. Successful update JSON uses status "updated", reports the verified installed version, and returns an unapplied maintenance plan instead of opening setup.
52 lines
1.6 KiB
TypeScript
52 lines
1.6 KiB
TypeScript
import { analysisErrorProjectionSchema } from "./contracts/errorSchemas.js";
|
|
import type { Logger } from "./logger.js";
|
|
|
|
const isRecord = (value: unknown): value is Readonly<Record<string, unknown>> =>
|
|
typeof value === "object" && value !== null && !Array.isArray(value);
|
|
|
|
/** Identify caller-visible CLI outcomes that mean the requested operation failed. */
|
|
export const isCliOperationFailure = (value: unknown): boolean => {
|
|
if (!isRecord(value)) return false;
|
|
if (typeof value.error === "string") return true;
|
|
if (analysisErrorProjectionSchema.safeParse(value).success) return true;
|
|
if (value.healthy === false) return true;
|
|
return (
|
|
value.status === "failed" ||
|
|
value.status === "needs_confirmation" ||
|
|
value.status === "needs_human"
|
|
);
|
|
};
|
|
|
|
/** Log one CLI command with duration and a stable success or failure status. */
|
|
export const logCliCommand = async <Value>(
|
|
logger: Logger,
|
|
command: string,
|
|
execute: () => Promise<Value>,
|
|
): Promise<Value> => {
|
|
const startedAt = performance.now();
|
|
try {
|
|
const value = await execute();
|
|
const failed = isCliOperationFailure(value);
|
|
logger[failed ? "error" : "info"](
|
|
{
|
|
command,
|
|
durationMs: Math.round((performance.now() - startedAt) * 100) / 100,
|
|
status: failed ? "error" : "ok",
|
|
},
|
|
failed ? "CLI command failed" : "CLI command completed",
|
|
);
|
|
if (failed) process.exitCode = 1;
|
|
return value;
|
|
} catch (cause: unknown) {
|
|
logger.error(
|
|
{
|
|
command,
|
|
durationMs: Math.round((performance.now() - startedAt) * 100) / 100,
|
|
status: "error",
|
|
},
|
|
"CLI command failed",
|
|
);
|
|
throw cause;
|
|
}
|
|
};
|