Files
rea/src/application/ClientRegistrationEnvironment.ts
T
morluto ec56c1da04 fix(setup): preserve registration identity and recovery ownership
Keep layered environment choices, current byte-exact recovery snapshots, and package-manager failure details. Report pinned package drift without claiming runner resolution. Retain failed Linux installer cleanup for host-owned retry while preserving the install outcome.

Reviewed fixes include #2632, #2769, #2783, #2959, #2979, #2983, #2995, #3009, and #2559.

Validation: 98 focused setup/registration tests passed; 45 Linux installer tests passed. Real Linux package installation remains unverified.
2026-10-14 08:25:53 +08:00

66 lines
2.4 KiB
TypeScript

import { z } from "zod";
import {
parseEnvironment,
parseEnvironmentValues,
} from "../config/environment.js";
import { ConfigurationError } from "../domain/configurationErrors.js";
import { err, ok, type Result } from "../domain/result.js";
import {
effectiveClientServer,
type ClientConfigurationDocument,
} from "./ClientConfigurationDocument.js";
import { PRODUCT_IDENTITY } from "../identity.js";
import { compareUnicodeCodePoints } from "../domain/unicodeCodePointOrder.js";
import { dshDocument } from "./DshCordisPatch.js";
/** Read explicit REA overrides without dropping peers when one value is invalid. */
export const clientRegistrationEnvironment = (
parsed: ClientConfigurationDocument | undefined,
overrides: Readonly<Record<string, string>> = {},
validation: "fragment" | "effective" = "effective",
): Result<Readonly<Record<string, string>>, ConfigurationError> => {
if (parsed?.dialect === "dsh")
return dshDocument(parsed.document).environment(overrides, validation);
const key =
parsed?.dialect === "opencode" || parsed?.dialect === "opencode_v2"
? "environment"
: "env";
const registration =
parsed === undefined
? undefined
: effectiveClientServer(parsed, PRODUCT_IDENTITY.mcpServerKey);
const raw =
typeof registration === "object" && registration !== null
? Reflect.get(registration, key)
: undefined;
const existing = z
.record(z.string(), z.string())
.safeParse(raw === undefined ? {} : raw);
if (!existing.success)
return err(
new ConfigurationError("Invalid REA server environment", {
settings: existing.error.issues.map((issue) => ({
setting: [key, ...issue.path].join("."),
constraint:
"Server environment must be an object containing only string values",
})),
}),
);
// Validate known settings with the runtime's parser, but retain the original
// string overrides, including unknown client-specific keys and no defaults.
const environment = { ...existing.data, ...overrides };
const validated =
validation === "fragment"
? parseEnvironmentValues(environment)
: parseEnvironment(environment);
if (!validated.ok) return validated;
return ok(
Object.fromEntries(
Object.entries(environment).sort(([left], [right]) =>
compareUnicodeCodePoints(left, right),
),
),
);
};