mirror of
https://github.com/morluto/rea.git
synced 2026-11-14 18:52:48 +00:00
* fix(cli)!: own MCP client registration boundaries * fix(cli): bind registration to native connection facts * test(cli): isolate registration failure and fixture controls * test(cli): distinguish native syntax and launcher alignment * fix(cli): preserve registration selection and cancellation * fix(cli): honor Goose and Zed native settings paths * fix(cli): honor registration output controls * fix(cli): reject disabled Junie global locations
79 lines
2.3 KiB
TypeScript
79 lines
2.3 KiB
TypeScript
import { chmodSync, lstatSync } from "node:fs";
|
|
import { readFile, writeFile } from "node:fs/promises";
|
|
|
|
import {
|
|
preserveCurrentRecoveryBackup,
|
|
type RecoveryBackupPreservationFailure,
|
|
} from "./SetupRecoveryBackup.js";
|
|
import { err, ok, type Result } from "../domain/result.js";
|
|
|
|
const errorDetail = (cause: unknown): string =>
|
|
cause instanceof Error ? cause.message : String(cause);
|
|
|
|
const isMissing = (cause: unknown): boolean =>
|
|
typeof cause === "object" &&
|
|
cause !== null &&
|
|
"code" in cause &&
|
|
cause.code === "ENOENT";
|
|
|
|
const restrictExistingBackup = (
|
|
backupPath: string,
|
|
): Result<void, RecoveryBackupPreservationFailure> => {
|
|
let existing: ReturnType<typeof lstatSync>;
|
|
try {
|
|
existing = lstatSync(backupPath);
|
|
} catch (cause: unknown) {
|
|
if (isMissing(cause)) return ok(undefined);
|
|
return err({
|
|
kind: "unreadable",
|
|
path: backupPath,
|
|
detail: `The existing client configuration backup could not be checked: ${errorDetail(cause)}`,
|
|
});
|
|
}
|
|
if (!existing.isFile())
|
|
return err({
|
|
kind: "occupied",
|
|
path: backupPath,
|
|
detail: "The existing client configuration backup is not a regular file.",
|
|
});
|
|
try {
|
|
chmodSync(backupPath, 0o600);
|
|
} catch (cause: unknown) {
|
|
return err({
|
|
kind: "unreadable",
|
|
path: backupPath,
|
|
detail: `The existing client configuration backup permissions could not be secured: ${errorDetail(cause)}`,
|
|
});
|
|
}
|
|
return ok(undefined);
|
|
};
|
|
|
|
const copyPrivateBackup = async (
|
|
sourcePath: string,
|
|
backupPath: string,
|
|
): Promise<void> => {
|
|
await writeFile(backupPath, await readFile(sourcePath), {
|
|
flag: "wx",
|
|
mode: 0o600,
|
|
});
|
|
};
|
|
|
|
export const writeClientRegistrationBackup = async (
|
|
settingsPath: string,
|
|
settingsText: string,
|
|
): Promise<Result<string, RecoveryBackupPreservationFailure>> => {
|
|
const firstBackupPath = `${settingsPath}.rea.backup`;
|
|
const firstRestricted = restrictExistingBackup(firstBackupPath);
|
|
if (!firstRestricted.ok) return firstRestricted;
|
|
const preserved = await preserveCurrentRecoveryBackup(
|
|
firstBackupPath,
|
|
settingsPath,
|
|
settingsText,
|
|
{ copy: copyPrivateBackup },
|
|
);
|
|
if (!preserved.ok) return preserved;
|
|
const selectedRestricted = restrictExistingBackup(preserved.value);
|
|
if (!selectedRestricted.ok) return selectedRestricted;
|
|
return ok(preserved.value);
|
|
};
|