Fix remote thread status fallback

This commit is contained in:
Haitao Pan 2026-03-22 14:58:14 +08:00
parent 23e8cdff75
commit 95ae87578d
2 changed files with 79 additions and 4 deletions

View File

@ -3577,12 +3577,17 @@ class AppController extends ChangeNotifier {
RuntimeConnectionMode _modeFromHost(String host) {
final trimmed = host.trim().toLowerCase();
if (trimmed == '127.0.0.1' || trimmed == 'localhost') {
if (_isLoopbackHost(trimmed)) {
return RuntimeConnectionMode.local;
}
return RuntimeConnectionMode.remote;
}
bool _isLoopbackHost(String host) {
final trimmed = host.trim().toLowerCase();
return trimmed == '127.0.0.1' || trimmed == 'localhost';
}
AssistantExecutionTarget _assistantExecutionTargetForMode(
RuntimeConnectionMode mode,
) {
@ -3628,10 +3633,14 @@ class AppController extends ChangeNotifier {
}
final defaults = GatewayConnectionProfile.defaults();
final savedHost = savedProfile.host.trim().isEmpty
final useDefaultRemoteEndpoint =
savedProfile.host.trim().isEmpty ||
_isLoopbackHost(savedProfile.host) ||
savedProfile.port <= 0;
final savedHost = useDefaultRemoteEndpoint
? defaults.host
: savedProfile.host.trim();
final savedPort = savedProfile.port <= 0
final savedPort = useDefaultRemoteEndpoint
? defaults.port
: savedProfile.port;
return savedProfile.copyWith(
@ -3640,7 +3649,7 @@ class AppController extends ChangeNotifier {
setupCode: '',
host: savedHost,
port: savedPort,
tls: savedProfile.tls,
tls: useDefaultRemoteEndpoint ? defaults.tls : savedProfile.tls,
);
}
}

View File

@ -376,6 +376,10 @@ void main() {
controller.assistantExecutionTarget,
AssistantExecutionTarget.remote,
);
expect(
controller.assistantConnectionTargetLabel,
'gateway.example.com:9443',
);
expect(completed, isFalse);
connectGate.complete();
@ -389,6 +393,68 @@ void main() {
},
);
test(
'AppController does not leak the local endpoint into remote thread status while reconnecting',
() async {
SharedPreferences.setMockInitialValues(<String, Object>{});
final tempDirectory = await Directory.systemTemp.createTemp(
'xworkmate-execution-target-remote-fallback-',
);
addTearDown(() async {
await _deleteDirectoryWithRetry(tempDirectory);
});
final store = SecureConfigStore(
enableSecureStorage: false,
databasePathResolver: () async => '${tempDirectory.path}/settings.db',
fallbackDirectoryPathResolver: () async => tempDirectory.path,
);
final gateway = _FakeGatewayRuntime(store: store);
final controller = AppController(
store: store,
runtimeCoordinator: RuntimeCoordinator(
gateway: gateway,
codex: _FakeCodexRuntime(),
),
);
addTearDown(controller.dispose);
await _waitFor(() => !controller.initializing);
await controller.saveSettings(
controller.settings.copyWith(
gateway: controller.settings.gateway.copyWith(
mode: RuntimeConnectionMode.local,
host: '127.0.0.1',
port: 18789,
tls: false,
),
),
refreshAfterSave: false,
);
final connectGate = Completer<void>();
gateway.holdNextConnect(connectGate);
final switchFuture = controller.setAssistantExecutionTarget(
AssistantExecutionTarget.remote,
);
await Future<void>.delayed(Duration.zero);
expect(
controller.assistantExecutionTarget,
AssistantExecutionTarget.remote,
);
expect(controller.assistantConnectionStatusLabel, '离线');
expect(
controller.assistantConnectionTargetLabel,
'openclaw.svc.plus:443',
);
connectGate.complete();
await switchFuture;
},
);
test(
'AppController notifies aiGatewayOnly target changes before disconnect completes',
() async {