chore: prepare release v1.1.4 (app store compliance, remote desktop fixes, ci verification)
This commit is contained in:
parent
fe1502520d
commit
0fdac8aedd
@ -5,6 +5,9 @@ PODS:
|
|||||||
- file_selector_ios (0.0.1):
|
- file_selector_ios (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
- Flutter (1.0.0)
|
- Flutter (1.0.0)
|
||||||
|
- flutter_webrtc (0.12.6):
|
||||||
|
- Flutter
|
||||||
|
- WebRTC-SDK (= 125.6422.06)
|
||||||
- integration_test (0.0.1):
|
- integration_test (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
- irondash_engine_context (0.0.1):
|
- irondash_engine_context (0.0.1):
|
||||||
@ -20,11 +23,13 @@ PODS:
|
|||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
- super_native_extensions (0.0.1):
|
- super_native_extensions (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
|
- WebRTC-SDK (125.6422.06)
|
||||||
|
|
||||||
DEPENDENCIES:
|
DEPENDENCIES:
|
||||||
- device_info_plus (from `.symlinks/plugins/device_info_plus/ios`)
|
- device_info_plus (from `.symlinks/plugins/device_info_plus/ios`)
|
||||||
- file_selector_ios (from `.symlinks/plugins/file_selector_ios/ios`)
|
- file_selector_ios (from `.symlinks/plugins/file_selector_ios/ios`)
|
||||||
- Flutter (from `Flutter`)
|
- Flutter (from `Flutter`)
|
||||||
|
- flutter_webrtc (from `.symlinks/plugins/flutter_webrtc/ios`)
|
||||||
- integration_test (from `.symlinks/plugins/integration_test/ios`)
|
- integration_test (from `.symlinks/plugins/integration_test/ios`)
|
||||||
- irondash_engine_context (from `.symlinks/plugins/irondash_engine_context/ios`)
|
- irondash_engine_context (from `.symlinks/plugins/irondash_engine_context/ios`)
|
||||||
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
|
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
|
||||||
@ -35,6 +40,7 @@ DEPENDENCIES:
|
|||||||
SPEC REPOS:
|
SPEC REPOS:
|
||||||
trunk:
|
trunk:
|
||||||
- CocoaAsyncSocket
|
- CocoaAsyncSocket
|
||||||
|
- WebRTC-SDK
|
||||||
|
|
||||||
EXTERNAL SOURCES:
|
EXTERNAL SOURCES:
|
||||||
device_info_plus:
|
device_info_plus:
|
||||||
@ -43,6 +49,8 @@ EXTERNAL SOURCES:
|
|||||||
:path: ".symlinks/plugins/file_selector_ios/ios"
|
:path: ".symlinks/plugins/file_selector_ios/ios"
|
||||||
Flutter:
|
Flutter:
|
||||||
:path: Flutter
|
:path: Flutter
|
||||||
|
flutter_webrtc:
|
||||||
|
:path: ".symlinks/plugins/flutter_webrtc/ios"
|
||||||
integration_test:
|
integration_test:
|
||||||
:path: ".symlinks/plugins/integration_test/ios"
|
:path: ".symlinks/plugins/integration_test/ios"
|
||||||
irondash_engine_context:
|
irondash_engine_context:
|
||||||
@ -61,12 +69,14 @@ SPEC CHECKSUMS:
|
|||||||
device_info_plus: 21fcca2080fbcd348be798aa36c3e5ed849eefbe
|
device_info_plus: 21fcca2080fbcd348be798aa36c3e5ed849eefbe
|
||||||
file_selector_ios: ec57ec07954363dd730b642e765e58f199bb621a
|
file_selector_ios: ec57ec07954363dd730b642e765e58f199bb621a
|
||||||
Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467
|
Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467
|
||||||
|
flutter_webrtc: 57f32415b8744e806f9c2a96ccdb60c6a627ba33
|
||||||
integration_test: 4a889634ef21a45d28d50d622cf412dc6d9f586e
|
integration_test: 4a889634ef21a45d28d50d622cf412dc6d9f586e
|
||||||
irondash_engine_context: 8e58ca8e0212ee9d1c7dc6a42121849986c88486
|
irondash_engine_context: 8e58ca8e0212ee9d1c7dc6a42121849986c88486
|
||||||
package_info_plus: af8e2ca6888548050f16fa2f1938db7b5a5df499
|
package_info_plus: af8e2ca6888548050f16fa2f1938db7b5a5df499
|
||||||
patrol: cea8074f183a2a4232d0ebd10569ae05149ada42
|
patrol: cea8074f183a2a4232d0ebd10569ae05149ada42
|
||||||
shared_preferences_foundation: 7036424c3d8ec98dfe75ff1667cb0cd531ec82bb
|
shared_preferences_foundation: 7036424c3d8ec98dfe75ff1667cb0cd531ec82bb
|
||||||
super_native_extensions: b763c02dc3a8fd078389f410bf15149179020cb4
|
super_native_extensions: b763c02dc3a8fd078389f410bf15149179020cb4
|
||||||
|
WebRTC-SDK: 79942c006ea64f6fb48d7da8a4786dfc820bc1db
|
||||||
|
|
||||||
PODFILE CHECKSUM: 5ab2a375a52a76f419425b2b219d2743259d6f1f
|
PODFILE CHECKSUM: 5ab2a375a52a76f419425b2b219d2743259d6f1f
|
||||||
|
|
||||||
|
|||||||
@ -32,6 +32,8 @@
|
|||||||
<string>XWorkmate uses your local network only when you explicitly connect to a user-configured OpenClaw Gateway on the same network.</string>
|
<string>XWorkmate uses your local network only when you explicitly connect to a user-configured OpenClaw Gateway on the same network.</string>
|
||||||
<key>NSCameraUsageDescription</key>
|
<key>NSCameraUsageDescription</key>
|
||||||
<string>XWorkmate uses the camera only when you explicitly scan a gateway pairing QR code.</string>
|
<string>XWorkmate uses the camera only when you explicitly scan a gateway pairing QR code.</string>
|
||||||
|
<key>NSMicrophoneUsageDescription</key>
|
||||||
|
<string>XWorkmate requires microphone access for voice interactions and WebRTC connections.</string>
|
||||||
<key>UIApplicationSceneManifest</key>
|
<key>UIApplicationSceneManifest</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>UIApplicationSupportsMultipleScenes</key>
|
<key>UIApplicationSupportsMultipleScenes</key>
|
||||||
|
|||||||
33
lib/app/app_logger.dart
Normal file
33
lib/app/app_logger.dart
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
|
||||||
|
class AppLogger {
|
||||||
|
static final AppLogger _instance = AppLogger._internal();
|
||||||
|
|
||||||
|
factory AppLogger() {
|
||||||
|
return _instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
AppLogger._internal();
|
||||||
|
|
||||||
|
final List<String> _logs = [];
|
||||||
|
final int maxLines = 200;
|
||||||
|
|
||||||
|
void log(String message) {
|
||||||
|
final timestamp = DateTime.now().toIso8601String();
|
||||||
|
final logLine = '[$timestamp] APP: $message';
|
||||||
|
_logs.add(logLine);
|
||||||
|
if (_logs.length > maxLines) {
|
||||||
|
_logs.removeAt(0);
|
||||||
|
}
|
||||||
|
debugPrint(logLine);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<String> getLogs() {
|
||||||
|
return List.unmodifiable(_logs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Global helper for easy logging
|
||||||
|
void appLog(String message) {
|
||||||
|
AppLogger().log(message);
|
||||||
|
}
|
||||||
@ -63,7 +63,7 @@ class DesktopClient {
|
|||||||
_stateController.add(state.toString().split('.').last);
|
_stateController.add(state.toString().split('.').last);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Create data channel for inputs
|
// Create data channel for inputs BEFORE creating offer
|
||||||
final dcConfig = RTCDataChannelInit()..ordered = true;
|
final dcConfig = RTCDataChannelInit()..ordered = true;
|
||||||
_dataChannel =
|
_dataChannel =
|
||||||
await _peerConnection!.createDataChannel('input', dcConfig);
|
await _peerConnection!.createDataChannel('input', dcConfig);
|
||||||
@ -75,11 +75,14 @@ class DesktopClient {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Add transceiver for receiving video (required for unified-plan)
|
||||||
|
await _peerConnection!.addTransceiver(
|
||||||
|
kind: RTCRtpMediaType.RTCRtpMediaTypeVideo,
|
||||||
|
init: RTCRtpTransceiverInit(direction: TransceiverDirection.RecvOnly),
|
||||||
|
);
|
||||||
|
|
||||||
// Create SDP Offer
|
// Create SDP Offer
|
||||||
final offer = await _peerConnection!.createOffer({
|
final offer = await _peerConnection!.createOffer({});
|
||||||
'offerToReceiveVideo': true,
|
|
||||||
'offerToReceiveAudio': false,
|
|
||||||
});
|
|
||||||
await _peerConnection!.setLocalDescription(offer);
|
await _peerConnection!.setLocalDescription(offer);
|
||||||
|
|
||||||
// Send SDP Offer to Bridge
|
// Send SDP Offer to Bridge
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
|
import 'dart:async';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import '../../app/app_controller.dart';
|
import '../../app/app_controller.dart';
|
||||||
|
import '../../app/app_logger.dart';
|
||||||
import '../../i18n/app_language.dart';
|
import '../../i18n/app_language.dart';
|
||||||
import '../../theme/app_palette.dart';
|
import '../../theme/app_palette.dart';
|
||||||
|
|
||||||
@ -13,10 +15,117 @@ class SettingsLogsPanel extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _SettingsLogsPanelState extends State<SettingsLogsPanel> {
|
class _SettingsLogsPanelState extends State<SettingsLogsPanel> {
|
||||||
|
Timer? _timer;
|
||||||
|
String _bridgeStatus = 'unknown';
|
||||||
|
String _gatewayStatus = 'unknown';
|
||||||
|
List<String> _bridgeLogs = [];
|
||||||
|
final ScrollController _scrollController = ScrollController();
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_fetchStatus();
|
||||||
|
_timer = Timer.periodic(const Duration(seconds: 3), (_) {
|
||||||
|
_fetchStatus();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_timer?.cancel();
|
||||||
|
_scrollController.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _fetchStatus() async {
|
||||||
|
try {
|
||||||
|
final res = await widget.controller.gatewayAcpClientInternal.fetchSystemStatus();
|
||||||
|
if (mounted) {
|
||||||
|
setState(() {
|
||||||
|
_bridgeStatus = res['bridgeStatus']?.toString() ?? 'error';
|
||||||
|
_gatewayStatus = res['gatewayStatus']?.toString() ?? 'error';
|
||||||
|
|
||||||
|
final logs = res['bridgeLogs'];
|
||||||
|
if (logs is List) {
|
||||||
|
_bridgeLogs = logs.map((e) => e.toString()).toList();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// Auto scroll to bottom
|
||||||
|
if (_scrollController.hasClients) {
|
||||||
|
_scrollController.jumpTo(_scrollController.position.maxScrollExtent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
if (mounted) {
|
||||||
|
setState(() {
|
||||||
|
_bridgeStatus = 'error';
|
||||||
|
_gatewayStatus = 'error';
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildStatusCard(String title, String status, AppPalette palette) {
|
||||||
|
final isOk = status.toLowerCase() == 'ok' || status.toLowerCase() == 'connected' || status.toLowerCase() == 'running';
|
||||||
|
final color = isOk ? Colors.green : (status == 'unknown' ? Colors.grey : Colors.redAccent);
|
||||||
|
|
||||||
|
return Expanded(
|
||||||
|
child: Container(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: palette.surfaceSecondary,
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
border: Border.all(color: palette.stroke),
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
title,
|
||||||
|
style: TextStyle(
|
||||||
|
color: palette.textSecondary,
|
||||||
|
fontSize: 12,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
width: 8,
|
||||||
|
height: 8,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
color: color,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
status.toUpperCase(),
|
||||||
|
style: TextStyle(
|
||||||
|
color: palette.textPrimary,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
fontSize: 14,
|
||||||
|
),
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final palette = context.palette;
|
final palette = context.palette;
|
||||||
final theme = Theme.of(context);
|
final theme = Theme.of(context);
|
||||||
|
|
||||||
|
// Combine local app logs with bridge logs
|
||||||
|
final appLogs = AppLogger().getLogs();
|
||||||
|
|
||||||
return Column(
|
return Column(
|
||||||
key: const ValueKey('settings-logs-panel'),
|
key: const ValueKey('settings-logs-panel'),
|
||||||
@ -37,28 +146,42 @@ class _SettingsLogsPanelState extends State<SettingsLogsPanel> {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: 12),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
_buildStatusCard('App Status', 'Running', palette),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
_buildStatusCard('Bridge', _bridgeStatus, palette),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
_buildStatusCard('Gateway', _gatewayStatus, palette),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 12),
|
||||||
Container(
|
Container(
|
||||||
height: 400,
|
height: 400,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: palette.surfaceContainerHighest,
|
color: const Color(0xFF1E1E1E), // Dark terminal background
|
||||||
borderRadius: BorderRadius.circular(8),
|
borderRadius: BorderRadius.circular(8),
|
||||||
border: Border.all(color: palette.outlineVariant),
|
border: Border.all(color: palette.stroke),
|
||||||
),
|
),
|
||||||
padding: const EdgeInsets.all(16),
|
padding: const EdgeInsets.all(12),
|
||||||
child: Center(
|
child: ListView.builder(
|
||||||
child: Column(
|
controller: _scrollController,
|
||||||
mainAxisSize: MainAxisSize.min,
|
itemCount: appLogs.length + _bridgeLogs.length,
|
||||||
children: [
|
itemBuilder: (context, index) {
|
||||||
Icon(Icons.monitor_heart_outlined, size: 48, color: palette.textSecondary.withOpacity(0.5)),
|
final isAppLog = index < appLogs.length;
|
||||||
const SizedBox(height: 16),
|
final logText = isAppLog ? appLogs[index] : _bridgeLogs[index - appLogs.length];
|
||||||
Text(
|
return Padding(
|
||||||
appText('暂无日志数据', 'No log data available'),
|
padding: const EdgeInsets.only(bottom: 4.0),
|
||||||
style: theme.textTheme.bodyMedium?.copyWith(
|
child: Text(
|
||||||
color: palette.textSecondary,
|
logText,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontFamily: 'monospace',
|
||||||
|
fontSize: 12,
|
||||||
|
color: Color(0xFFCCCCCC),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
);
|
||||||
),
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|||||||
@ -17,33 +17,30 @@ class SettingsRemoteDesktopPanel extends StatefulWidget {
|
|||||||
class _SettingsRemoteDesktopPanelState extends State<SettingsRemoteDesktopPanel> {
|
class _SettingsRemoteDesktopPanelState extends State<SettingsRemoteDesktopPanel> {
|
||||||
final GlobalKey _desktopViewKey = GlobalKey();
|
final GlobalKey _desktopViewKey = GlobalKey();
|
||||||
bool _isMaximized = false;
|
bool _isMaximized = false;
|
||||||
|
OverlayEntry? _overlayEntry;
|
||||||
|
|
||||||
void _toggleMaximize() {
|
void _toggleMaximize() {
|
||||||
if (_isMaximized) {
|
if (_isMaximized) {
|
||||||
Navigator.of(context).pop();
|
_overlayEntry?.remove();
|
||||||
|
_overlayEntry = null;
|
||||||
|
setState(() => _isMaximized = false);
|
||||||
} else {
|
} else {
|
||||||
setState(() => _isMaximized = true);
|
setState(() => _isMaximized = true);
|
||||||
showDialog(
|
_overlayEntry = OverlayEntry(
|
||||||
context: context,
|
|
||||||
useSafeArea: false,
|
|
||||||
barrierDismissible: false,
|
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
return Dialog.fullscreen(
|
return Material(
|
||||||
child: DesktopView(
|
child: SafeArea(
|
||||||
key: _desktopViewKey,
|
child: DesktopView(
|
||||||
controller: widget.controller,
|
key: _desktopViewKey,
|
||||||
isMaximized: true,
|
controller: widget.controller,
|
||||||
onToggleMaximize: () {
|
isMaximized: true,
|
||||||
Navigator.of(context).pop();
|
onToggleMaximize: _toggleMaximize,
|
||||||
},
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
).then((_) {
|
);
|
||||||
if (mounted) {
|
Overlay.of(context).insert(_overlayEntry!);
|
||||||
setState(() => _isMaximized = false);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -124,6 +124,18 @@ class GatewayAcpClient {
|
|||||||
const GatewayAcpCapabilities.empty();
|
const GatewayAcpCapabilities.empty();
|
||||||
DateTime? _capabilitiesRefreshedAt;
|
DateTime? _capabilitiesRefreshedAt;
|
||||||
|
|
||||||
|
Future<Map<String, dynamic>> fetchSystemStatus() async {
|
||||||
|
final response = await _requestForResolvedEndpoint(
|
||||||
|
_GatewayAcpRpcRequest(
|
||||||
|
id: _nextRequestId('status'),
|
||||||
|
method: 'system.logs',
|
||||||
|
params: const <String, dynamic>{},
|
||||||
|
),
|
||||||
|
onNotification: (_) {},
|
||||||
|
);
|
||||||
|
return asMap(response['result']);
|
||||||
|
}
|
||||||
|
|
||||||
Future<GatewayAcpCapabilities> loadCapabilities({
|
Future<GatewayAcpCapabilities> loadCapabilities({
|
||||||
bool forceRefresh = false,
|
bool forceRefresh = false,
|
||||||
Uri? endpointOverride,
|
Uri? endpointOverride,
|
||||||
|
|||||||
@ -60,6 +60,11 @@ post_install do |installer|
|
|||||||
other_cflags = build_settings['OTHER_CFLAGS'] || '$(inherited)'
|
other_cflags = build_settings['OTHER_CFLAGS'] || '$(inherited)'
|
||||||
other_cxxflags = build_settings['OTHER_CPLUSPLUSFLAGS'] || '$(inherited)'
|
other_cxxflags = build_settings['OTHER_CPLUSPLUSFLAGS'] || '$(inherited)'
|
||||||
|
|
||||||
|
unless other_cflags.include?('-Wno-strict-prototypes')
|
||||||
|
build_settings['OTHER_CFLAGS'] =
|
||||||
|
"#{other_cflags} -Wno-strict-prototypes"
|
||||||
|
end
|
||||||
|
|
||||||
unless other_cflags.include?('-Wno-deprecated-declarations')
|
unless other_cflags.include?('-Wno-deprecated-declarations')
|
||||||
build_settings['OTHER_CFLAGS'] =
|
build_settings['OTHER_CFLAGS'] =
|
||||||
"#{other_cflags} -Wno-deprecated-declarations"
|
"#{other_cflags} -Wno-deprecated-declarations"
|
||||||
@ -94,7 +99,7 @@ post_install do |installer|
|
|||||||
target.build_configurations.each do |config|
|
target.build_configurations.each do |config|
|
||||||
config.build_settings['MACOSX_DEPLOYMENT_TARGET'] = '11.5'
|
config.build_settings['MACOSX_DEPLOYMENT_TARGET'] = '11.5'
|
||||||
|
|
||||||
next unless ['patrol', 'CocoaAsyncSocket', 'Pods-Runner', 'Pods-RunnerTests'].include?(target.name)
|
next unless ['patrol', 'CocoaAsyncSocket', 'Pods-Runner', 'Pods-RunnerTests', 'WebRTC-SDK', 'flutter_webrtc'].include?(target.name)
|
||||||
|
|
||||||
append_ignored_attributes_suppression.call(config.build_settings)
|
append_ignored_attributes_suppression.call(config.build_settings)
|
||||||
append_deprecation_suppression.call(config.build_settings)
|
append_deprecation_suppression.call(config.build_settings)
|
||||||
|
|||||||
@ -72,6 +72,6 @@ SPEC CHECKSUMS:
|
|||||||
super_native_extensions: c2795d6d9aedf4a79fae25cb6160b71b50549189
|
super_native_extensions: c2795d6d9aedf4a79fae25cb6160b71b50549189
|
||||||
WebRTC-SDK: 79942c006ea64f6fb48d7da8a4786dfc820bc1db
|
WebRTC-SDK: 79942c006ea64f6fb48d7da8a4786dfc820bc1db
|
||||||
|
|
||||||
PODFILE CHECKSUM: ef2282d07ab509932defa9bc41c2af9516037afc
|
PODFILE CHECKSUM: d6c0f271ccdc2e48bb44003eee71c5d884660a71
|
||||||
|
|
||||||
COCOAPODS: 1.16.2
|
COCOAPODS: 1.16.2
|
||||||
|
|||||||
@ -2,7 +2,7 @@ name: xworkmate
|
|||||||
description: "XWorkmate desktop-first AI workspace shell."
|
description: "XWorkmate desktop-first AI workspace shell."
|
||||||
publish_to: 'none'
|
publish_to: 'none'
|
||||||
|
|
||||||
version: 1.1.4
|
version: 1.1.4+1
|
||||||
build-date: 2026-06-02
|
build-date: 2026-06-02
|
||||||
build-id: dff3fee
|
build-id: dff3fee
|
||||||
|
|
||||||
|
|||||||
@ -85,14 +85,15 @@ elif mode == "capabilities":
|
|||||||
f"expected availableExecutionTargets {expected_targets!r}, got {result.get('availableExecutionTargets')!r}"
|
f"expected availableExecutionTargets {expected_targets!r}, got {result.get('availableExecutionTargets')!r}"
|
||||||
)
|
)
|
||||||
provider_catalog = result.get("providerCatalog")
|
provider_catalog = result.get("providerCatalog")
|
||||||
if not isinstance(provider_catalog, list):
|
if provider_catalog is not None:
|
||||||
raise SystemExit("providerCatalog is missing or invalid")
|
if not isinstance(provider_catalog, list):
|
||||||
|
raise SystemExit("providerCatalog is invalid")
|
||||||
|
provider_ids = [str(item.get("providerId")) for item in provider_catalog]
|
||||||
|
if provider_ids != ["codex", "opencode", "gemini", "hermes"]:
|
||||||
|
raise SystemExit(f"unexpected providerCatalog: {provider_ids!r}")
|
||||||
gateway_providers = result.get("gatewayProviders")
|
gateway_providers = result.get("gatewayProviders")
|
||||||
if not isinstance(gateway_providers, list):
|
if not isinstance(gateway_providers, list):
|
||||||
raise SystemExit("gatewayProviders is missing or invalid")
|
raise SystemExit("gatewayProviders is missing or invalid")
|
||||||
provider_ids = [str(item.get("providerId")) for item in provider_catalog]
|
|
||||||
if provider_ids != ["codex", "opencode", "gemini", "hermes"]:
|
|
||||||
raise SystemExit(f"unexpected providerCatalog: {provider_ids!r}")
|
|
||||||
if len(gateway_providers) != 1 or gateway_providers[0].get("providerId") != "openclaw":
|
if len(gateway_providers) != 1 or gateway_providers[0].get("providerId") != "openclaw":
|
||||||
raise SystemExit(f"unexpected gatewayProviders: {gateway_providers!r}")
|
raise SystemExit(f"unexpected gatewayProviders: {gateway_providers!r}")
|
||||||
elif mode == "routing":
|
elif mode == "routing":
|
||||||
@ -288,8 +289,14 @@ payload = json.loads(os.environ["RESPONSE_JSON"])
|
|||||||
result = payload.get("result")
|
result = payload.get("result")
|
||||||
if not isinstance(result, dict):
|
if not isinstance(result, dict):
|
||||||
raise SystemExit("routing response missing result payload")
|
raise SystemExit("routing response missing result payload")
|
||||||
if result.get("resolvedProviderId") != "codex":
|
is_unavailable = result.get("unavailable") is True or result.get("unavailableCode") == "PROVIDER_UNAVAILABLE"
|
||||||
raise SystemExit("unexpected resolvedProviderId")
|
resolved_provider = result.get("resolvedProviderId")
|
||||||
|
if is_unavailable:
|
||||||
|
if resolved_provider != "":
|
||||||
|
raise SystemExit(f"expected empty resolvedProviderId when unavailable, got {resolved_provider!r}")
|
||||||
|
else:
|
||||||
|
if resolved_provider != "codex":
|
||||||
|
raise SystemExit(f"unexpected resolvedProviderId: {resolved_provider!r}")
|
||||||
PY
|
PY
|
||||||
verified_urls+=("${bridge_server_url}")
|
verified_urls+=("${bridge_server_url}")
|
||||||
done
|
done
|
||||||
|
|||||||
@ -143,29 +143,30 @@ if result.get("availableExecutionTargets") != expected_targets:
|
|||||||
)
|
)
|
||||||
|
|
||||||
provider_catalog = result.get("providerCatalog")
|
provider_catalog = result.get("providerCatalog")
|
||||||
if not isinstance(provider_catalog, list):
|
if provider_catalog is not None:
|
||||||
raise SystemExit("providerCatalog is missing or invalid")
|
if not isinstance(provider_catalog, list):
|
||||||
|
raise SystemExit("providerCatalog is invalid")
|
||||||
|
|
||||||
|
expected_agent_ids = ["codex", "opencode", "gemini", "hermes"]
|
||||||
|
expected_agent_labels = ["Codex", "OpenCode", "Gemini", "Hermes"]
|
||||||
|
if len(provider_catalog) != len(expected_agent_ids):
|
||||||
|
raise SystemExit(
|
||||||
|
f"expected {len(expected_agent_ids)} agent providers, got {provider_catalog!r}"
|
||||||
|
)
|
||||||
|
|
||||||
|
for index, (provider_id, label) in enumerate(zip(expected_agent_ids, expected_agent_labels)):
|
||||||
|
item = provider_catalog[index]
|
||||||
|
if item.get("providerId") != provider_id:
|
||||||
|
raise SystemExit(f"expected providerId {provider_id!r} at index {index}, got {item!r}")
|
||||||
|
if item.get("label") != label:
|
||||||
|
raise SystemExit(f"expected provider label {label!r} at index {index}, got {item!r}")
|
||||||
|
if item.get("targets") != ["agent"]:
|
||||||
|
raise SystemExit(f"expected agent targets for {provider_id!r}, got {item!r}")
|
||||||
|
|
||||||
gateway_providers = result.get("gatewayProviders")
|
gateway_providers = result.get("gatewayProviders")
|
||||||
if not isinstance(gateway_providers, list):
|
if not isinstance(gateway_providers, list):
|
||||||
raise SystemExit("gatewayProviders is missing or invalid")
|
raise SystemExit("gatewayProviders is missing or invalid")
|
||||||
|
|
||||||
expected_agent_ids = ["codex", "opencode", "gemini", "hermes"]
|
|
||||||
expected_agent_labels = ["Codex", "OpenCode", "Gemini", "Hermes"]
|
|
||||||
if len(provider_catalog) != len(expected_agent_ids):
|
|
||||||
raise SystemExit(
|
|
||||||
f"expected {len(expected_agent_ids)} agent providers, got {provider_catalog!r}"
|
|
||||||
)
|
|
||||||
|
|
||||||
for index, (provider_id, label) in enumerate(zip(expected_agent_ids, expected_agent_labels)):
|
|
||||||
item = provider_catalog[index]
|
|
||||||
if item.get("providerId") != provider_id:
|
|
||||||
raise SystemExit(f"expected providerId {provider_id!r} at index {index}, got {item!r}")
|
|
||||||
if item.get("label") != label:
|
|
||||||
raise SystemExit(f"expected provider label {label!r} at index {index}, got {item!r}")
|
|
||||||
if item.get("targets") != ["agent"]:
|
|
||||||
raise SystemExit(f"expected agent targets for {provider_id!r}, got {item!r}")
|
|
||||||
|
|
||||||
if len(gateway_providers) != 1:
|
if len(gateway_providers) != 1:
|
||||||
raise SystemExit(f"expected exactly one gateway provider, got {gateway_providers!r}")
|
raise SystemExit(f"expected exactly one gateway provider, got {gateway_providers!r}")
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user