mirror of
https://gitee.com/mateos/mateclaw.git
synced 2026-09-13 03:13:41 +08:00
fix(dsh): isolate runtime child environment
This commit is contained in:
parent
ab5a0a651b
commit
fb94027038
@ -33,6 +33,7 @@ import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Files;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
@ -256,28 +257,14 @@ public class DshRuntimeService implements AgentRuntimeProvider {
|
||||
ProcessBuilder builder = new ProcessBuilder(command)
|
||||
.directory(session.workingDirectory().toFile())
|
||||
.redirectError(ProcessBuilder.Redirect.PIPE);
|
||||
builder.environment().put("DSH_CWD", session.workingDirectory().toString());
|
||||
// The packaged binary gives the environment variable precedence
|
||||
// over argv. Set the resolved path explicitly so IDEA/.env
|
||||
// inheritance cannot select a different composition.
|
||||
if (!configuration.cordisConfigPath().isBlank()) {
|
||||
builder.environment().put("DSH_CORDIS_CONFIG", configuration.cordisConfigPath());
|
||||
} else {
|
||||
builder.environment().remove("DSH_CORDIS_CONFIG");
|
||||
}
|
||||
log.debug("[DSH] child environment: cordisConfig={}, exists={}",
|
||||
builder.environment().getOrDefault("DSH_CORDIS_CONFIG", "<empty>"),
|
||||
Map<String, String> environment = builder.environment();
|
||||
Map<String, String> childEnvironment = childEnvironment(environment, session, configuration, provider);
|
||||
environment.clear();
|
||||
environment.putAll(childEnvironment);
|
||||
log.debug("[DSH] child environment: keys={}, cordisConfig={}, exists={}",
|
||||
environment.keySet(),
|
||||
environment.getOrDefault("DSH_CORDIS_CONFIG", "<empty>"),
|
||||
!configuration.cordisConfigPath().isBlank() && Files.isRegularFile(Path.of(configuration.cordisConfigPath())));
|
||||
String apiKey = configuration.apiKey();
|
||||
if ((apiKey == null || apiKey.isBlank()) && provider != null) apiKey = provider.getApiKey();
|
||||
if (apiKey != null && !apiKey.isBlank()) {
|
||||
builder.environment().put("DEEPSEEK_API_KEY", apiKey);
|
||||
}
|
||||
String baseUrl = configuration.baseUrl();
|
||||
if ((baseUrl == null || baseUrl.isBlank()) && provider != null) baseUrl = provider.getBaseUrl();
|
||||
if (baseUrl != null && !baseUrl.isBlank()) {
|
||||
builder.environment().put("DEEPSEEK_BASE_URL", baseUrl);
|
||||
}
|
||||
process = builder.start();
|
||||
processRef.set(process);
|
||||
if (sink.isCancelled()) {
|
||||
@ -463,6 +450,43 @@ public class DshRuntimeService implements AgentRuntimeProvider {
|
||||
return result;
|
||||
}
|
||||
|
||||
static Map<String, String> childEnvironment(Map<String, String> inherited,
|
||||
RuntimeSession session,
|
||||
DshRuntimeConfiguration configuration,
|
||||
ModelProviderEntity provider) {
|
||||
Map<String, String> environment = new LinkedHashMap<>();
|
||||
copyIfPresent(inherited, environment, "PATH");
|
||||
copyIfPresent(inherited, environment, "HOME");
|
||||
copyIfPresent(inherited, environment, "USERPROFILE");
|
||||
copyIfPresent(inherited, environment, "TMPDIR");
|
||||
copyIfPresent(inherited, environment, "TEMP");
|
||||
copyIfPresent(inherited, environment, "TMP");
|
||||
copyIfPresent(inherited, environment, "SystemRoot");
|
||||
copyIfPresent(inherited, environment, "WINDIR");
|
||||
|
||||
environment.put("DSH_CWD", session.workingDirectory().toString());
|
||||
putIfPresent(environment, "DSH_CORDIS_CONFIG", configuration.cordisConfigPath());
|
||||
putIfPresent(environment, "DEEPSEEK_API_KEY",
|
||||
firstNonBlank(configuration.apiKey(), provider == null ? null : provider.getApiKey()));
|
||||
putIfPresent(environment, "DEEPSEEK_BASE_URL",
|
||||
firstNonBlank(configuration.baseUrl(), provider == null ? null : provider.getBaseUrl()));
|
||||
return environment;
|
||||
}
|
||||
|
||||
private static void copyIfPresent(Map<String, String> source, Map<String, String> target, String key) {
|
||||
if (source == null) return;
|
||||
putIfPresent(target, key, source.get(key));
|
||||
}
|
||||
|
||||
private static void putIfPresent(Map<String, String> target, String key, String value) {
|
||||
if (value == null || value.isBlank()) return;
|
||||
target.put(key, value);
|
||||
}
|
||||
|
||||
private static String firstNonBlank(String primary, String fallback) {
|
||||
return primary != null && !primary.isBlank() ? primary : fallback;
|
||||
}
|
||||
|
||||
private ModelProviderEntity resolveProvider(String modelName) {
|
||||
ModelConfigEntity model = null;
|
||||
try {
|
||||
|
||||
@ -10,10 +10,12 @@ import vip.mate.agent.runtime.contract.RuntimeEventType;
|
||||
import vip.mate.agent.runtime.contract.RuntimeSession;
|
||||
import vip.mate.agent.runtime.dsh.management.DshRuntimeConfigService;
|
||||
import vip.mate.agent.runtime.dsh.management.DshRuntimeConfiguration;
|
||||
import vip.mate.llm.model.ModelProviderEntity;
|
||||
import vip.mate.llm.service.ModelConfigService;
|
||||
import vip.mate.llm.service.ModelProviderService;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
@ -99,6 +101,36 @@ class DshRuntimeServiceTest {
|
||||
DshRuntimeService.commandLine("\"/opt/Deep Seek/dsh-jsonrpc-agent\" --stdio"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void childEnvironmentKeepsOnlyRuntimeVariablesAndExplicitCredentials() {
|
||||
RuntimeSession session = session(Path.of("/workspace/project"));
|
||||
DshRuntimeConfiguration configuration = new DshRuntimeConfiguration(
|
||||
"/bin/dsh", "/opt/dsh/cordis.yml", "/workspace/global",
|
||||
"https://configured.example/v1", "model", "configured-key");
|
||||
ModelProviderEntity provider = new ModelProviderEntity();
|
||||
provider.setApiKey("provider-key");
|
||||
provider.setBaseUrl("https://provider.example/v1");
|
||||
Map<String, String> inherited = new HashMap<>();
|
||||
inherited.put("PATH", "/usr/bin");
|
||||
inherited.put("HOME", "/Users/mate");
|
||||
inherited.put("AWS_SECRET_ACCESS_KEY", "must-not-leak");
|
||||
inherited.put("DEEPSEEK_API_KEY", "inherited-key");
|
||||
inherited.put("DSH_CORDIS_CONFIG", "/stale/cordis.yml");
|
||||
|
||||
Map<String, String> environment = DshRuntimeService.childEnvironment(
|
||||
inherited, session, configuration, provider);
|
||||
|
||||
assertEquals("/usr/bin", environment.get("PATH"));
|
||||
assertEquals("/Users/mate", environment.get("HOME"));
|
||||
assertEquals("/workspace/project", environment.get("DSH_CWD"));
|
||||
assertEquals("/opt/dsh/cordis.yml", environment.get("DSH_CORDIS_CONFIG"));
|
||||
assertEquals("configured-key", environment.get("DEEPSEEK_API_KEY"));
|
||||
assertEquals("https://configured.example/v1", environment.get("DEEPSEEK_BASE_URL"));
|
||||
assertFalse(environment.containsKey("AWS_SECRET_ACCESS_KEY"));
|
||||
assertFalse(environment.containsValue("inherited-key"));
|
||||
assertFalse(environment.containsValue("/stale/cordis.yml"));
|
||||
}
|
||||
|
||||
private static DshRuntimeService service() {
|
||||
return service("/bin/dsh");
|
||||
}
|
||||
@ -111,4 +143,9 @@ class DshRuntimeServiceTest {
|
||||
Mockito.mock(ModelConfigService.class),
|
||||
Mockito.mock(ModelProviderService.class), config);
|
||||
}
|
||||
|
||||
private static RuntimeSession session(Path workingDirectory) {
|
||||
return new RuntimeSession("session-1", "conversation-1", 1L, 2L,
|
||||
"model", workingDirectory, Map.of());
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user