fix: improve remote gateway bootstrap prefill
This commit is contained in:
parent
c9c52de212
commit
70b6856adb
@ -507,7 +507,10 @@ class AppController extends ChangeNotifier {
|
||||
Future<void> _initialize() async {
|
||||
try {
|
||||
await _settingsController.initialize();
|
||||
final bootstrap = await RuntimeBootstrapConfig.load();
|
||||
final bootstrap = await RuntimeBootstrapConfig.load(
|
||||
workspacePathHint: settings.workspacePath,
|
||||
cliPathHint: settings.cliPath,
|
||||
);
|
||||
final seeded = bootstrap.mergeIntoSettings(settings);
|
||||
if (seeded.toJsonString() != settings.toJsonString()) {
|
||||
await _settingsController.saveSnapshot(seeded);
|
||||
|
||||
@ -409,7 +409,7 @@ class _SettingsPageState extends State<SettingsPage> {
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
'${controller.connection.status.label} · ${controller.connection.remoteAddress ?? settings.gateway.host}:${settings.gateway.port}',
|
||||
'${controller.connection.status.label} · ${controller.connection.remoteAddress ?? '${settings.gateway.host}:${settings.gateway.port}'}',
|
||||
style: Theme.of(context).textTheme.bodyLarge,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
|
||||
@ -17,10 +17,21 @@ class RuntimeBootstrapConfig {
|
||||
final GatewayBootstrapTarget? localGateway;
|
||||
final GatewayBootstrapTarget? remoteGateway;
|
||||
|
||||
static Future<RuntimeBootstrapConfig> load() async {
|
||||
final env = await _loadEnvFile();
|
||||
final workspaceRoot = _resolveWorkspaceRoot();
|
||||
final openClawRoot = _resolveOpenClawRoot(workspaceRoot);
|
||||
static Future<RuntimeBootstrapConfig> load({
|
||||
String? workspacePathHint,
|
||||
String? cliPathHint,
|
||||
}) async {
|
||||
final workspaceRoot = _resolveWorkspaceRoot(workspacePathHint);
|
||||
final openClawRoot = _resolveOpenClawRoot(
|
||||
workspaceRoot,
|
||||
cliPathHint: cliPathHint,
|
||||
);
|
||||
final env = await _loadEnvFile(
|
||||
workspacePathHint: workspacePathHint,
|
||||
cliPathHint: cliPathHint,
|
||||
workspaceRoot: workspaceRoot,
|
||||
openClawRoot: openClawRoot,
|
||||
);
|
||||
return RuntimeBootstrapConfig(
|
||||
workspacePath: workspaceRoot?.path,
|
||||
remoteProjectRoot: workspaceRoot?.path,
|
||||
@ -120,13 +131,27 @@ class GatewayBootstrapTarget {
|
||||
}
|
||||
}
|
||||
|
||||
Future<Map<String, String>> _loadEnvFile() async {
|
||||
final candidates = <File>{
|
||||
File('${Directory.current.path}/.env'),
|
||||
..._ancestorDirectories(
|
||||
Directory.current,
|
||||
).map((directory) => File('${directory.path}/.env')),
|
||||
}.toList(growable: false);
|
||||
Future<Map<String, String>> _loadEnvFile({
|
||||
String? workspacePathHint,
|
||||
String? cliPathHint,
|
||||
Directory? workspaceRoot,
|
||||
Directory? openClawRoot,
|
||||
}) async {
|
||||
final candidateDirectories = <Directory>{
|
||||
Directory.current,
|
||||
..._ancestorDirectories(Directory.current),
|
||||
..._pathCandidates(workspacePathHint),
|
||||
..._pathCandidates(
|
||||
cliPathHint == null ? null : File(cliPathHint).parent.path,
|
||||
),
|
||||
...?workspaceRoot == null ? null : <Directory>[workspaceRoot],
|
||||
...?workspaceRoot == null ? null : _ancestorDirectories(workspaceRoot),
|
||||
...?openClawRoot == null ? null : <Directory>[openClawRoot],
|
||||
...?openClawRoot == null ? null : _ancestorDirectories(openClawRoot),
|
||||
};
|
||||
final candidates = candidateDirectories
|
||||
.map((directory) => File('${directory.path}/.env'))
|
||||
.toList(growable: false);
|
||||
|
||||
for (final file in candidates) {
|
||||
if (!await file.exists()) {
|
||||
@ -155,11 +180,12 @@ Future<Map<String, String>> _loadEnvFile() async {
|
||||
return const <String, String>{};
|
||||
}
|
||||
|
||||
Directory? _resolveWorkspaceRoot() {
|
||||
final candidates = <Directory>[
|
||||
Directory? _resolveWorkspaceRoot(String? workspacePathHint) {
|
||||
final candidates = <Directory>{
|
||||
..._pathCandidates(workspacePathHint),
|
||||
Directory.current,
|
||||
..._ancestorDirectories(Directory.current),
|
||||
];
|
||||
}.toList(growable: false);
|
||||
for (final candidate in candidates) {
|
||||
if (File('${candidate.path}/pubspec.yaml').existsSync() &&
|
||||
File('${candidate.path}/lib/main.dart').existsSync()) {
|
||||
@ -169,7 +195,17 @@ Directory? _resolveWorkspaceRoot() {
|
||||
return null;
|
||||
}
|
||||
|
||||
Directory? _resolveOpenClawRoot(Directory? workspaceRoot) {
|
||||
Directory? _resolveOpenClawRoot(
|
||||
Directory? workspaceRoot, {
|
||||
String? cliPathHint,
|
||||
}) {
|
||||
final cliFile = cliPathHint == null ? null : File(cliPathHint);
|
||||
if (cliFile != null && cliFile.existsSync()) {
|
||||
final cliParent = cliFile.parent;
|
||||
if (File('${cliParent.path}/openclaw.mjs').existsSync()) {
|
||||
return cliParent;
|
||||
}
|
||||
}
|
||||
if (workspaceRoot == null) {
|
||||
return null;
|
||||
}
|
||||
@ -204,3 +240,20 @@ List<Directory> _ancestorDirectories(Directory start) {
|
||||
}
|
||||
return ancestors;
|
||||
}
|
||||
|
||||
List<Directory> _pathCandidates(String? rawPath) {
|
||||
final trimmed = rawPath?.trim() ?? '';
|
||||
if (trimmed.isEmpty) {
|
||||
return const <Directory>[];
|
||||
}
|
||||
final fileSystemEntityType = FileSystemEntity.typeSync(trimmed);
|
||||
final directory = switch (fileSystemEntityType) {
|
||||
FileSystemEntityType.directory => Directory(trimmed),
|
||||
FileSystemEntityType.file => File(trimmed).parent,
|
||||
_ => Directory(trimmed),
|
||||
};
|
||||
if (!directory.existsSync()) {
|
||||
return const <Directory>[];
|
||||
}
|
||||
return <Directory>[directory, ..._ancestorDirectories(directory)];
|
||||
}
|
||||
|
||||
@ -235,7 +235,10 @@ class _GatewayConnectDialogState extends State<GatewayConnectDialog> {
|
||||
}
|
||||
|
||||
Future<void> _loadBootstrapPrefill() async {
|
||||
final bootstrap = await RuntimeBootstrapConfig.load();
|
||||
final bootstrap = await RuntimeBootstrapConfig.load(
|
||||
workspacePathHint: widget.controller.settings.workspacePath,
|
||||
cliPathHint: widget.controller.settings.cliPath,
|
||||
);
|
||||
final preferred = bootstrap.preferredGatewayFor(_connectionMode);
|
||||
if (!mounted || preferred == null) {
|
||||
return;
|
||||
|
||||
@ -48,4 +48,44 @@ remote-token: remote-test-token
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
'RuntimeBootstrapConfig resolves .env from workspace path hints outside the repo cwd',
|
||||
() async {
|
||||
final tempDir = await Directory.systemTemp.createTemp(
|
||||
'xworkmate-bootstrap-hint-',
|
||||
);
|
||||
final outsideDir = await Directory.systemTemp.createTemp(
|
||||
'xworkmate-bootstrap-outside-',
|
||||
);
|
||||
addTearDown(() async {
|
||||
Directory.current = outsideDir.parent;
|
||||
await tempDir.delete(recursive: true);
|
||||
await outsideDir.delete(recursive: true);
|
||||
});
|
||||
|
||||
await File(
|
||||
'${tempDir.path}/pubspec.yaml',
|
||||
).writeAsString('name: xworkmate_test\n');
|
||||
await Directory('${tempDir.path}/lib').create(recursive: true);
|
||||
await File(
|
||||
'${tempDir.path}/lib/main.dart',
|
||||
).writeAsString('void main() {}\n');
|
||||
await File('${tempDir.path}/.env').writeAsString('''
|
||||
remote: wss://openclaw.example.com:443
|
||||
remote-token: remote-test-token
|
||||
''');
|
||||
|
||||
Directory.current = outsideDir;
|
||||
|
||||
final config = await RuntimeBootstrapConfig.load(
|
||||
workspacePathHint: tempDir.path,
|
||||
);
|
||||
|
||||
expect(config.remoteGateway, isNotNull);
|
||||
expect(config.remoteGateway!.host, 'openclaw.example.com');
|
||||
expect(config.remoteGateway!.token, 'remote-test-token');
|
||||
expect(config.workspacePath, tempDir.path);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user