xworkmate-app/lib/runtime/opencode_config_bridge.dart
2026-03-19 12:33:50 +08:00

119 lines
3.4 KiB
Dart

import 'dart:io';
class OpencodeConfigBridge {
OpencodeConfigBridge({String? opencodeHome})
: opencodeHome =
opencodeHome ?? '${Platform.environment['HOME'] ?? ''}/.opencode';
static const String _managedMcpBlockStart =
'# BEGIN XWORKMATE MANAGED MCP BLOCK';
static const String _managedMcpBlockEnd = '# END XWORKMATE MANAGED MCP BLOCK';
final String opencodeHome;
Future<void> configureManagedMcpServers({
required List<OpencodeMcpServer> servers,
}) async {
final configDir = Directory(opencodeHome);
if (!await configDir.exists()) {
await configDir.create(recursive: true);
}
final configFile = File('$opencodeHome/config.toml');
final existingConfig = await configFile.exists()
? await configFile.readAsString()
: '';
final preserved = _stripManagedMcpBlock(existingConfig).trimRight();
final managedBlock = _buildManagedMcpBlock(servers);
final merged = preserved.isEmpty
? '$managedBlock\n'
: '$preserved\n\n$managedBlock\n';
await configFile.writeAsString(merged);
}
Future<String> readConfig() async {
final configFile = File('$opencodeHome/config.toml');
if (!await configFile.exists()) {
return '';
}
return configFile.readAsString();
}
String _stripManagedMcpBlock(String content) {
if (content.isEmpty) {
return content;
}
var remaining = content;
while (true) {
final start = remaining.indexOf(_managedMcpBlockStart);
if (start < 0) {
break;
}
final end = remaining.indexOf(_managedMcpBlockEnd, start);
if (end < 0) {
remaining = remaining.substring(0, start);
break;
}
remaining =
remaining.substring(0, start) +
remaining.substring(end + _managedMcpBlockEnd.length);
}
return remaining;
}
String _buildManagedMcpBlock(List<OpencodeMcpServer> servers) {
final buffer = StringBuffer()
..writeln(_managedMcpBlockStart)
..writeln('# Generated by XWorkmate - Managed MCP Server Configuration')
..writeln('# Last updated: ${DateTime.now().toIso8601String()}')
..writeln();
for (final server in servers) {
buffer.writeln('[mcp_servers.${server.name}]');
if (server.url.trim().isNotEmpty) {
buffer.writeln('url = "${server.url.trim()}"');
} else {
buffer.writeln('type = "stdio"');
buffer.writeln('command = "${server.command}"');
if (server.args.isNotEmpty) {
buffer.writeln('args = ${_formatTomlArray(server.args)}');
}
}
if (server.env.isNotEmpty) {
final entries = server.env.entries
.map((entry) => '${entry.key} = "${entry.value}"')
.join(', ');
buffer.writeln('env = { $entries }');
}
buffer.writeln();
}
buffer.writeln(_managedMcpBlockEnd);
return buffer.toString().trimRight();
}
String _formatTomlArray(List<String> items) {
if (items.isEmpty) {
return '[]';
}
return '[${items.map((item) => '"$item"').join(', ')}]';
}
}
class OpencodeMcpServer {
const OpencodeMcpServer({
required this.name,
this.command = '',
this.url = '',
this.args = const <String>[],
this.env = const <String, String>{},
});
final String name;
final String command;
final String url;
final List<String> args;
final Map<String, String> env;
}