mirror of
https://github.com/morluto/rea.git
synced 2026-10-12 02:19:24 +00:00
* refactor(setup): resolve skill destinations per client * fix(setup): honor active client configuration and skill roots * fix(setup): validate effective client scopes and Gemini trust * fix(setup): treat empty OpenCode overrides as unset An empty OPENCODE_CONFIG_DIR now stays on the XDG directory, and doctor and setup inspect global OpenCode files when a custom directory is absent. Uninstall reports one status per client so a missing earlier layer does not hide a removed registration. --------- Co-authored-by: Cursor Agent <[email protected]>
132 lines
4.2 KiB
JavaScript
132 lines
4.2 KiB
JavaScript
import { chmod, mkdir, symlink, writeFile } from "node:fs/promises";
|
|
import { join } from "node:path";
|
|
import { supportedClients } from "../dist/application/SupportedClients.js";
|
|
|
|
/** Create an isolated prefix, fake PATH, client configs, and environment. */
|
|
export async function verifyPackageEnvironment({
|
|
root,
|
|
workspace,
|
|
evidenceRoot,
|
|
referenceRoot,
|
|
}) {
|
|
const prefix = join(workspace, "prefix");
|
|
const home = join(workspace, "home");
|
|
const fakeBin = join(workspace, "bin");
|
|
await mkdir(fakeBin, { recursive: true });
|
|
await Promise.all([
|
|
mkdir(evidenceRoot, { recursive: true }),
|
|
mkdir(join(referenceRoot, "src"), { recursive: true }),
|
|
]);
|
|
await Promise.all([
|
|
writeFile(
|
|
join(referenceRoot, "src", "main.ts"),
|
|
'import "./dependency";\n',
|
|
),
|
|
writeFile(
|
|
join(referenceRoot, "src", "dependency.ts"),
|
|
"export const value = 1;\n",
|
|
),
|
|
writeFile(join(referenceRoot, ".env"), "PACKAGE_SECRET_SENTINEL=1\n"),
|
|
]);
|
|
const brew = join(fakeBin, "brew");
|
|
const swVers = join(fakeBin, "sw_vers");
|
|
const npx = join(fakeBin, "npx");
|
|
const npxLog = join(workspace, "npx.log");
|
|
const hopper = join(workspace, "hopper");
|
|
await writeFile(
|
|
brew,
|
|
'#!/bin/sh\nif [ "$1" = "--prefix" ]; then echo /fake/cask; fi\nexit 0\n',
|
|
);
|
|
await writeFile(swVers, "#!/bin/sh\necho 14.5\n");
|
|
await writeFile(
|
|
npx,
|
|
'#!/bin/sh\nprintf "%s\\n" "$*" > "$REA_NPX_LOG"\necho "│ ✓ Cursor: isolated │"\n',
|
|
);
|
|
await writeFile(hopper, "#!/bin/sh\nexit 0\n");
|
|
await Promise.all([
|
|
chmod(brew, 0o755),
|
|
chmod(swVers, 0o755),
|
|
chmod(hopper, 0o755),
|
|
chmod(npx, 0o755),
|
|
]);
|
|
const claudeDir = supportedClients(home, process.platform, {}).find(
|
|
({ name }) => name === "claude_desktop",
|
|
)?.markerPath;
|
|
if (claudeDir === undefined)
|
|
throw new Error("Claude Desktop fixture has no configuration directory");
|
|
const codexDir = join(home, ".codex");
|
|
const cursorDir = join(home, ".cursor");
|
|
await Promise.all([
|
|
mkdir(claudeDir, { recursive: true }),
|
|
mkdir(codexDir, { recursive: true }),
|
|
mkdir(cursorDir, { recursive: true }),
|
|
]);
|
|
const claudeConfig = join(claudeDir, "claude_desktop_config.json");
|
|
const codexConfig = join(codexDir, "config.toml");
|
|
const codexTarget = join(home, "managed-codex.toml");
|
|
const cursorConfig = join(cursorDir, "mcp.json");
|
|
const cursorTarget = join(home, "managed-cursor.json");
|
|
await writeFile(claudeConfig, '{"existing":true}\n');
|
|
await writeFile(codexTarget, 'model = "gpt-5"\n');
|
|
await writeFile(cursorTarget, '{"existing":true}\n');
|
|
await symlink(codexTarget, codexConfig);
|
|
await symlink(cursorTarget, cursorConfig);
|
|
const environment = {
|
|
...process.env,
|
|
HOME: home,
|
|
USERPROFILE: home,
|
|
APPDATA: join(home, "AppData", "Roaming"),
|
|
CLAUDE_CONFIG_DIR: home,
|
|
CODEX_HOME: codexDir,
|
|
COPILOT_HOME: join(home, ".copilot"),
|
|
GROK_HOME: join(home, ".grok"),
|
|
SAND_DATA_ROOT: join(home, ".grokbot"),
|
|
OMP_PROFILE: undefined,
|
|
PI_CODING_AGENT_DIR: undefined,
|
|
PI_CONFIG_DIR: undefined,
|
|
PI_PROFILE: undefined,
|
|
QWEN_HOME: undefined,
|
|
OPENCODE_CONFIG: undefined,
|
|
OPENCODE_CONFIG_DIR: undefined,
|
|
GEMINI_CLI_HOME: undefined,
|
|
GEMINI_CLI_SYSTEM_SETTINGS_PATH: undefined,
|
|
GEMINI_CLI_SYSTEM_DEFAULTS_PATH: undefined,
|
|
XDG_CONFIG_HOME: join(home, ".config"),
|
|
PATH: `${fakeBin}:${process.env.PATH ?? ""}`,
|
|
REA_ANALYSIS_PROVIDER: "auto",
|
|
...packageHopperEnvironment(root),
|
|
REA_NPX_LOG: npxLog,
|
|
REA_REFERENCE_SECRET_PATTERNS_JSON: JSON.stringify([".env"]),
|
|
};
|
|
return {
|
|
prefix,
|
|
home,
|
|
npxLog,
|
|
claudeConfig,
|
|
codexConfig,
|
|
codexTarget,
|
|
cursorConfig,
|
|
cursorTarget,
|
|
environment,
|
|
};
|
|
}
|
|
|
|
export const packageHopperEnvironment = (root, platform = process.platform) => {
|
|
const fakeLauncher = join(root, "tests/fixtures/fakeLauncher.mjs");
|
|
if (platform !== "linux")
|
|
return {
|
|
HOPPER_LAUNCHER_PATH: process.execPath,
|
|
HOPPER_LOADER_ARGS_JSON: JSON.stringify([fakeLauncher]),
|
|
};
|
|
return {
|
|
HOPPER_LAUNCHER_PATH: "/bin/sh",
|
|
HOPPER_LOADER_ARGS_JSON: JSON.stringify([
|
|
"-c",
|
|
'node_path=$1; shift; "$node_path" "$@"',
|
|
"rea-package-hopper",
|
|
process.execPath,
|
|
fakeLauncher,
|
|
]),
|
|
};
|
|
};
|