fix(opencode): isolate explicit home contexts

This commit is contained in:
haelyra
2026-08-25 12:17:21 -04:00
parent 2331afbfd3
commit 6ceab105bc
16 changed files with 50 additions and 10 deletions
+1
View File
@@ -96,6 +96,7 @@ function main() {
const report = buildDoctorReport({
repoRoot: require('path').join(__dirname, '..'),
homeDir: process.env.HOME || os.homedir(),
env: process.env,
projectRoot: process.cwd(),
targets: options.targets,
});
+1
View File
@@ -164,6 +164,7 @@ async function main() {
const rawPlan = createInstallPlanFromRequest(request, {
projectRoot: process.cwd(),
homeDir: process.env.HOME || os.homedir(),
env: process.env,
claudeRulesDir: process.env.CLAUDE_RULES_DIR || null,
});
+3 -1
View File
@@ -7,6 +7,7 @@ const { toCursorAgentRelativePath } = require('./cursor-agent-names');
const { LEGACY_INSTALL_TARGETS, parseInstallArgs } = require('./install/request');
const { SUPPORTED_INSTALL_TARGETS, listLegacyCompatibilityLanguages, resolveLegacyCompatibilitySelection, resolveInstallPlan } = require('./install-manifests');
const { getInstallTargetAdapter } = require('./install-targets/registry');
const { resolveInvocationEnvironment } = require('./invocation-environment');
const LANGUAGE_NAME_PATTERN = /^[a-zA-Z0-9_-]+$/;
const CLAUDE_ECC_NAMESPACE = 'ecc';
@@ -645,7 +646,7 @@ function createLegacyCompatInstallPlan(options = {}) {
sourceRoot,
projectRoot,
homeDir: options.homeDir,
env: options.env || process.env,
env: resolveInvocationEnvironment(options),
target,
profileId: null,
moduleIds: selection.moduleIds,
@@ -775,6 +776,7 @@ function createManifestInstallPlan(options = {}) {
repoRoot: sourceRoot,
projectRoot,
homeDir: options.homeDir,
env: resolveInvocationEnvironment(options),
profileId: options.profileId || null,
moduleIds: options.moduleIds || [],
includeComponentIds: options.includeComponentIds || [],
+6 -5
View File
@@ -22,6 +22,7 @@ const {
const { adaptAntigravityAgent } = require('./install/antigravity-agent');
const { buildInstallIndex, rewriteRelativeLinks } = require('./install/link-rewrite');
const { getInstallTargetAdapter, listInstallTargetAdapters } = require('./install-targets/registry');
const { resolveInvocationEnvironment } = require('./invocation-environment');
const OPENCODE_BUILD_ARTIFACT = path.join('.opencode', 'dist');
const OPENCODE_BUILD_SCRIPT = path.join('scripts', 'build-opencode.js');
const OPENCODE_PLUGIN_NOT_BUILT_CODE = 'opencode-plugin-not-built';
@@ -1275,7 +1276,7 @@ function discoverInstalledStates(options = {}) {
const context = {
homeDir: options.homeDir || process.env.HOME || os.homedir(),
projectRoot: options.projectRoot || process.cwd(),
env: options.env || process.env,
env: resolveInvocationEnvironment(options),
};
const targets = normalizeTargets(options.targets);
@@ -1546,13 +1547,13 @@ function buildDoctorReport(options = {}) {
homeDir: options.homeDir,
projectRoot: options.projectRoot,
targets: options.targets,
env: options.env,
env: resolveInvocationEnvironment(options),
}).filter(record => record.exists);
const context = {
repoRoot,
homeDir: options.homeDir || process.env.HOME || os.homedir(),
projectRoot: options.projectRoot || process.cwd(),
env: options.env || process.env,
env: resolveInvocationEnvironment(options),
manifestVersion: manifests.modulesVersion,
packageVersion: readPackageVersion(repoRoot)
};
@@ -1718,7 +1719,7 @@ function repairInstalledStates(options = {}) {
repoRoot,
homeDir: options.homeDir || process.env.HOME || os.homedir(),
projectRoot: options.projectRoot || process.cwd(),
env: options.env || process.env,
env: resolveInvocationEnvironment(options),
manifestVersion: manifests.modulesVersion,
packageVersion: readPackageVersion(repoRoot)
};
@@ -2045,7 +2046,7 @@ function uninstallInstalledStates(options = {}) {
homeDir: options.homeDir,
projectRoot: options.projectRoot,
targets: options.targets,
env: options.env,
env: resolveInvocationEnvironment(options),
}).filter(record => record.exists);
const results = records.map(record => {
+2 -1
View File
@@ -2,6 +2,7 @@ const fs = require('fs');
const os = require('os');
const path = require('path');
const { getInstallTargetAdapter, planInstallTargetScaffold } = require('./install-targets/registry');
const { resolveInvocationEnvironment } = require('./invocation-environment');
const DEFAULT_REPO_ROOT = path.join(__dirname, '../..');
const SUPPORTED_INSTALL_TARGETS = ['claude', 'claude-project', 'cursor', 'antigravity', 'codex', 'gemini', 'opencode', 'codebuddy', 'joycode', 'qwen', 'zed', 'hermes', 'openclaw', 'kimi'];
@@ -595,7 +596,7 @@ function resolveInstallPlan(options = {}) {
repoRoot: manifests.repoRoot,
projectRoot: validatedProjectRoot || manifests.repoRoot,
homeDir: validatedHomeDir || os.homedir(),
env: options.env || process.env,
env: resolveInvocationEnvironment(options),
}
: null;
const targetAdapter = target ? getInstallTargetAdapter(target) : null;
+1
View File
@@ -51,6 +51,7 @@ async function reconcileCanonicalInstallStates(options = {}) {
homeDir: options.homeDir,
projectRoot: options.projectRoot,
targets: options.targets,
env: options.env,
discoverInstalledStates: options.discoverInstalledStates,
}));
}
+2 -1
View File
@@ -12,6 +12,7 @@ const openclawHome = require('./openclaw-home');
const opencodeHome = require('./opencode-home');
const qwenHome = require('./qwen-home');
const zedProject = require('./zed-project');
const { resolveInvocationEnvironment } = require('../invocation-environment');
const ADAPTERS = Object.freeze([
claudeHome,
@@ -52,7 +53,7 @@ function planInstallTargetScaffold(options = {}) {
repoRoot: options.repoRoot,
projectRoot: options.projectRoot || options.repoRoot,
homeDir: options.homeDir,
env: options.env || process.env,
env: resolveInvocationEnvironment(options),
};
const validationIssues = adapter.validate(planningInput);
const blockingIssues = validationIssues.filter(issue => (
+3
View File
@@ -5,6 +5,7 @@ const {
createLegacyInstallPlan,
createManifestInstallPlan,
} = require('../install-executor');
const { resolveInvocationEnvironment } = require('../invocation-environment');
function createInstallPlanFromRequest(request, options = {}) {
if (!request || typeof request !== 'object') {
@@ -20,6 +21,7 @@ function createInstallPlanFromRequest(request, options = {}) {
excludeComponentIds: request.excludeComponentIds,
projectRoot: options.projectRoot,
homeDir: options.homeDir,
env: resolveInvocationEnvironment(options),
sourceRoot: options.sourceRoot,
});
}
@@ -32,6 +34,7 @@ function createInstallPlanFromRequest(request, options = {}) {
excludeComponentIds: request.excludeComponentIds,
projectRoot: options.projectRoot,
homeDir: options.homeDir,
env: resolveInvocationEnvironment(options),
claudeRulesDir: options.claudeRulesDir,
sourceRoot: options.sourceRoot,
});
+17
View File
@@ -0,0 +1,17 @@
'use strict';
function resolveInvocationEnvironment(options = {}) {
if (Object.prototype.hasOwnProperty.call(options, 'env')) {
return options.env || {};
}
if (typeof options.homeDir === 'string' && options.homeDir.trim() !== '') {
return {};
}
return process.env;
}
module.exports = {
resolveInvocationEnvironment,
};
@@ -4,6 +4,7 @@ const fs = require('fs');
const os = require('os');
const path = require('path');
const { resolveOpencodeConfigRoot } = require('../../opencode-paths');
const { resolveInvocationEnvironment } = require('../../invocation-environment');
// OpenCode stores MCP servers under "mcp" in its resolved configuration root.
// Shape differs from Claude/Codex:
@@ -39,7 +40,10 @@ function mapOpencodeServer(name, raw, configPath) {
function readOpencodeMcp(options = {}) {
const homeDir = options.homeDir || os.homedir();
const configRoot = resolveOpencodeConfigRoot({ homeDir, env: options.env });
const configRoot = resolveOpencodeConfigRoot({
homeDir,
env: resolveInvocationEnvironment(options),
});
const candidatePaths = options.configPath
? [options.configPath]
: [
+2 -1
View File
@@ -2,6 +2,7 @@
const os = require('os');
const path = require('path');
const { resolveInvocationEnvironment } = require('./invocation-environment');
function configuredDirectory(environment, name) {
const value = environment && environment[name];
@@ -11,7 +12,7 @@ function configuredDirectory(environment, name) {
}
function resolveOpencodeConfigRoot(options = {}) {
const environment = options.env || process.env;
const environment = resolveInvocationEnvironment(options);
const explicitRoot = configuredDirectory(environment, 'OPENCODE_CONFIG_DIR');
if (explicitRoot) {
return explicitRoot;
@@ -317,6 +317,7 @@ function reconcileCurrentInstallState(store, options = {}) {
homeDir: options.homeDir,
projectRoot: options.projectRoot,
targets: options.targets,
env: options.env,
});
let result = reconcileInstallStateProjections(store, records);
try {
+1
View File
@@ -72,6 +72,7 @@ function main() {
const records = discoverInstalledStates({
homeDir: process.env.HOME || os.homedir(),
env: process.env,
projectRoot: process.cwd(),
targets: options.targets,
}).filter(record => record.exists);
+2
View File
@@ -81,6 +81,7 @@ async function main() {
const result = repairInstalledStates({
repoRoot: require('path').join(__dirname, '..'),
homeDir: process.env.HOME || os.homedir(),
env: process.env,
projectRoot: process.cwd(),
targets: options.targets,
dryRun: options.dryRun,
@@ -89,6 +90,7 @@ async function main() {
const { reconcileCanonicalInstallStates } = require('./lib/install-state-store-sync');
result.installStateProjection = await reconcileCanonicalInstallStates({
homeDir: process.env.HOME || os.homedir(),
env: process.env,
projectRoot: process.cwd(),
targets: options.targets,
});
+1
View File
@@ -467,6 +467,7 @@ async function main() {
const installStateProjection = reconcileCurrentInstallState(store, {
homeDir: process.env.HOME || os.homedir(),
env: process.env,
projectRoot: process.cwd(),
});
const storedStatus = store.getStatus({
+2
View File
@@ -141,6 +141,7 @@ async function main() {
} else {
result = uninstallInstalledStates({
homeDir: process.env.HOME || os.homedir(),
env: process.env,
projectRoot: process.cwd(),
targets: options.targets,
dryRun: options.dryRun,
@@ -162,6 +163,7 @@ async function main() {
const { reconcileCanonicalInstallStates } = require('./lib/install-state-store-sync');
result.installStateProjection = await reconcileCanonicalInstallStates({
homeDir: process.env.HOME || os.homedir(),
env: process.env,
projectRoot: process.cwd(),
targets: options.targets,
});