Files
reverse-engineering-anything/src/cli/ampRegistrationBackup.ts
T
STDquantum f25d1e3e9f fix(cli): preserve Amp MCP registration settings (#7995)
* fix(cli): preserve Amp registration environment and report failures (#7538, #7536)

* fix(cli): normalize equals-form mcp add agents before Incur parses them (#7538)

* fix(cli): remove the Amp settings backup after a verified registration (#7538)

* fix(cli): secure Amp registration backups

* test(cli): cover Amp registration backup permissions

* fix(cli): retain Amp registration recovery backups

* fix(cli): create private Amp recovery copies

* test(cli): fix Amp backup assertion typing

* fix(cli): preserve typed Amp backup failures

* fix(cli): handle Amp backup cleanup failures
2026-10-29 15:23:42 +08:00

79 lines
2.3 KiB
TypeScript

import { chmodSync, lstatSync } from "node:fs";
import { readFile, writeFile } from "node:fs/promises";
import {
preserveCurrentRecoveryBackup,
type RecoveryBackupPreservationFailure,
} from "../application/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 Amp settings backup could not be checked: ${errorDetail(cause)}`,
});
}
if (!existing.isFile())
return err({
kind: "occupied",
path: backupPath,
detail: "The existing Amp settings backup is not a regular file.",
});
try {
chmodSync(backupPath, 0o600);
} catch (cause: unknown) {
return err({
kind: "unreadable",
path: backupPath,
detail: `The existing Amp settings 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 writeAmpRegistrationBackup = 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);
};