diff --git a/Makefile b/Makefile index ece3966c..bfedcfbc 100644 --- a/Makefile +++ b/Makefile @@ -20,7 +20,7 @@ APP_DART_DEFINE_BUILD ?= --dart-define=XWORKMATE_BUILD_NUMBER=$(APP_BUILD_NUMBER APP_DART_DEFINE_BUILD_DATE ?= --dart-define=XWORKMATE_BUILD_DATE=$(APP_BUILD_DATE) APP_DART_DEFINE_BUILD_COMMIT ?= --dart-define=XWORKMATE_BUILD_COMMIT=$(APP_BUILD_COMMIT) -.PHONY: help deps analyze test test-all test-flutter test-golden test-integration test-integration-macos test-patrol test-go test-ci check format run open-macos-xcode sync-version build-linux build-macos build-ios-sim ios-pods ios-pods-check build-ios-release-no-codesign verify-ios-release package-deb package-rpm package-linux package-mac install-mac clean build-go-core render-release-docs docs-public-api check-export-compliance test-real-env-login-chain inspect-xworkmate-bridge-service test-api-contract test-api-scenario-contract check-api-external +.PHONY: help deps analyze test test-all test-flutter test-golden test-integration test-integration-macos test-patrol test-go test-ci check format run open-macos-xcode sync-version build-linux build-macos build-ios-sim ios-pods ios-pods-check build-ios-release-no-codesign verify-ios-release package-deb package-rpm package-linux package-mac install-mac clean render-release-docs docs-public-api check-export-compliance test-real-env-login-chain inspect-xworkmate-bridge-service test-api-contract test-api-scenario-contract check-api-external help: ## Show available targets @grep -E '^[a-zA-Z0-9_.-]+:.*?## ' Makefile | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "%-18s %s\n", $$1, $$2}' @@ -119,9 +119,6 @@ build-ios-release-no-codesign: ios-pods-check ## Build the iOS device app in rel verify-ios-release: ios-pods-check build-ios-release-no-codesign analyze ## Regenerate pods, build iOS release without codesigning, and run static analysis -build-go-core: ## Build the external ACP bridge helper from xworkmate-bridge - bash scripts/build-go-core.sh - package-deb: ## Create the Linux .deb package bash scripts/package-linux-deb.sh @@ -131,7 +128,7 @@ package-rpm: ## Create the Linux .rpm package package-linux: ## Create both Linux packages bash scripts/package-linux.sh -package-mac: build-go-core ## Create the macOS .app and DMG +package-mac: ## Create the macOS .app and DMG XWORKMATE_APP_STORE=true bash scripts/package-flutter-mac-app.sh install-mac: package-mac ## Package and install the macOS app into /Applications diff --git a/docs/architecture/xworkmate-bridge-migration.md b/docs/architecture/xworkmate-bridge-migration.md index ee138708..62dddad9 100644 --- a/docs/architecture/xworkmate-bridge-migration.md +++ b/docs/architecture/xworkmate-bridge-migration.md @@ -41,13 +41,13 @@ Last Updated: 2026-04-13 ## Build Contract -`xworkmate-app` 仍然消费名为 `xworkmate-go-core` 的 helper artifact。 +`xworkmate-app` 不再构建、嵌入或启动本地 ACP bridge helper。 这表示: -- helper 从 `xworkmate-bridge` 构建 -- app 负责定位与调用 helper -- helper 内部的 bridge/runtime 行为以 bridge repo 为准,不再在 app repo 内保留并列设计文档 +- macOS 打包不再从 `xworkmate-bridge` sibling repo 构建 `xworkmate-go-core` +- app bundle 不再携带 `xworkmate-go-core` 或本地 ACP bridge 启动脚本 +- app 端只消费托管 `xworkmate-bridge` capability、routing、gateway runtime 合同 ## Operational Note diff --git a/lib/runtime/gateway_runtime_core.dart b/lib/runtime/gateway_runtime_core.dart index 65424900..16e9e9ce 100644 --- a/lib/runtime/gateway_runtime_core.dart +++ b/lib/runtime/gateway_runtime_core.dart @@ -548,9 +548,9 @@ class GatewayRuntime extends ChangeNotifier with GatewayRuntimeHelpersInternal { await connectProfile( GatewayConnectionProfile.defaultsGateway().copyWith( mode: RuntimeConnectionMode.remote, - host: '127.0.0.1', - port: 18789, - tls: false, + host: Uri.parse(kManagedBridgeServerUrl).host, + port: 443, + tls: true, selectedAgentId: selectedAgentId, ), ); diff --git a/pubspec.yaml b/pubspec.yaml index 23b16939..134073bb 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -3,8 +3,8 @@ description: "XWorkmate desktop-first AI workspace shell." publish_to: 'none' version: 1.1.3+1 -build-date: 2026-05-27 -build-id: 8cb97a0 +build-date: 2026-05-28 +build-id: 82d46f5 environment: sdk: ^3.11.0 diff --git a/scripts/build-go-core.sh b/scripts/build-go-core.sh deleted file mode 100644 index 1c32af55..00000000 --- a/scripts/build-go-core.sh +++ /dev/null @@ -1,42 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -ROOT_DIR="$(cd "$(dirname "$0")/.." && pwd)" -DEFAULT_BRIDGE_DIR="" -for candidate in "$ROOT_DIR/../xworkmate-bridge" "$ROOT_DIR/../../xworkmate-bridge" -do - if [[ -f "$candidate/go.mod" ]]; then - DEFAULT_BRIDGE_DIR="$candidate" - break - fi -done -BRIDGE_DIR="${XWORKMATE_BRIDGE_DIR:-$DEFAULT_BRIDGE_DIR}" -OUTPUT_DIR="${OUTPUT_DIR:-$ROOT_DIR/build/bin}" -OUTPUT_PATH_BASE="${OUTPUT_DIR}/xworkmate-go-core" - -if [[ "$(uname -s)" == *MINGW* || "$(uname -s)" == *MSYS* || "$(uname -s)" == *CYGWIN* ]]; then - OUTPUT_PATH="${OUTPUT_PATH:-${OUTPUT_PATH_BASE}.exe}" -else - OUTPUT_PATH="${OUTPUT_PATH:-${OUTPUT_PATH_BASE}}" -fi - -if [[ ! -f "$BRIDGE_DIR/go.mod" ]]; then - echo "Missing xworkmate-bridge repo or go.mod in $BRIDGE_DIR" >&2 - exit 1 -fi - -if ! command -v go >/dev/null 2>&1; then - echo "Go toolchain is required to build xworkmate-go-core" >&2 - exit 1 -fi - -mkdir -p "$OUTPUT_DIR" - -echo "Building xworkmate-go-core from xworkmate-bridge..." -( - cd "$BRIDGE_DIR" - GO111MODULE=on go build -o "$OUTPUT_PATH" . -) - -chmod +x "$OUTPUT_PATH" -echo "Built: $OUTPUT_PATH" diff --git a/scripts/package-flutter-mac-app.sh b/scripts/package-flutter-mac-app.sh index 5537ee35..de0ea78e 100755 --- a/scripts/package-flutter-mac-app.sh +++ b/scripts/package-flutter-mac-app.sh @@ -104,18 +104,6 @@ if [[ ! -d "$BUILD_APP_PATH" ]]; then exit 1 fi -# Embed xworkmate-go-core for local/non-App-Store builds if available -if [[ "${XWORKMATE_APP_STORE:-}" != "true" ]]; then - SOURCE_GO_CORE="$ROOT_DIR/build/bin/xworkmate-go-core" - # lib/runtime/go_core.dart expects it under build/bin relative to one of the roots - TARGET_GO_CORE="$BUILD_APP_PATH/Contents/MacOS/build/bin/xworkmate-go-core" - if [[ -f "$SOURCE_GO_CORE" ]]; then - echo "Embedding xworkmate-go-core into app bundle..." - mkdir -p "$(dirname "$TARGET_GO_CORE")" - cp "$SOURCE_GO_CORE" "$TARGET_GO_CORE" - fi -fi - verify_bundle_signature() { local app_path="$1" echo "Verifying code signature: $app_path" @@ -147,17 +135,11 @@ find "$DIST_APP_PATH/Contents/Frameworks" -name "*.framework" -type d | while re codesign --force --sign "${SIGN_IDENTITY:--}" --timestamp=none "$framework" done -# 2. Sign our manually added binaries if any -if [[ -f "$DIST_APP_PATH/Contents/MacOS/build/bin/xworkmate-go-core" ]]; then - echo "Signing embedded helper: xworkmate-go-core" - codesign --force --sign "${SIGN_IDENTITY:--}" --timestamp=none "$DIST_APP_PATH/Contents/MacOS/build/bin/xworkmate-go-core" -fi - -# 3. Sign the main executable +# 2. Sign the main executable echo "Signing main executable..." codesign --force --sign "${SIGN_IDENTITY:--}" --timestamp=none "$DIST_APP_PATH/Contents/MacOS/$APP_NAME" -# 4. Finally sign the app bundle itself +# 3. Finally sign the app bundle itself echo "Signing app bundle..." codesign --force --sign "${SIGN_IDENTITY:--}" --timestamp=none "$DIST_APP_PATH" diff --git a/test/runtime/bridge_runtime_cleanup_test.dart b/test/runtime/bridge_runtime_cleanup_test.dart index 2cc09115..62799322 100644 --- a/test/runtime/bridge_runtime_cleanup_test.dart +++ b/test/runtime/bridge_runtime_cleanup_test.dart @@ -1,7 +1,11 @@ +import 'dart:async'; import 'dart:io'; import 'package:flutter_test/flutter_test.dart'; import 'package:xworkmate/app/app_controller.dart'; +import 'package:xworkmate/runtime/device_identity_store.dart'; +import 'package:xworkmate/runtime/gateway_runtime.dart'; +import 'package:xworkmate/runtime/gateway_runtime_session_client.dart'; import 'package:xworkmate/runtime/mode_switcher.dart'; import 'package:xworkmate/runtime/runtime_models.dart'; import 'package:xworkmate/runtime/secure_config_store.dart'; @@ -178,5 +182,95 @@ void main() { ); }, ); + + test( + 'session client bootstrap uses the managed bridge instead of local ACP', + () async { + final storeRoot = await Directory.systemTemp.createTemp( + 'xworkmate-bridge-session-bootstrap-', + ); + addTearDown(() async { + if (await storeRoot.exists()) { + try { + await storeRoot.delete(recursive: true); + } on FileSystemException { + // Temp cleanup is best effort while Flutter test teardown releases IO. + } + } + }); + + final store = SecureConfigStore( + secretRootPathResolver: () async => '${storeRoot.path}/secrets', + appDataRootPathResolver: () async => '${storeRoot.path}/app-data', + supportRootPathResolver: () async => '${storeRoot.path}/support', + enableSecureStorage: false, + ); + await store.initialize(); + final sessionClient = _CapturingGatewayRuntimeSessionClient(); + final runtime = GatewayRuntime( + store: store, + identityStore: DeviceIdentityStore(store), + sessionClient: sessionClient, + runtimeId: 'runtime-session-bootstrap-test', + ); + addTearDown(runtime.dispose); + await runtime.initialize(); + + await runtime.ensureBridgeSessionConnected(selectedAgentId: 'codex'); + + final request = sessionClient.lastConnectRequest; + expect(request, isNotNull); + expect(request!.mode, RuntimeConnectionMode.remote); + expect(request.host, Uri.parse(kManagedBridgeServerUrl).host); + expect(request.port, 443); + expect(request.tls, isTrue); + expect(request.host, isNot(anyOf('127.0.0.1', 'localhost'))); + }, + ); }); } + +class _CapturingGatewayRuntimeSessionClient + implements GatewayRuntimeSessionClient { + final StreamController _updates = + StreamController.broadcast(); + + GatewayRuntimeSessionConnectRequest? lastConnectRequest; + + @override + Stream get updates => _updates.stream; + + @override + Future connect( + GatewayRuntimeSessionConnectRequest request, + ) async { + lastConnectRequest = request; + return GatewayRuntimeSessionConnectResult( + snapshot: GatewayConnectionSnapshot.initial(mode: request.mode).copyWith( + status: RuntimeConnectionStatus.connected, + statusText: 'Connected', + remoteAddress: '${request.host}:${request.port}', + deviceId: request.identity.deviceId, + ), + auth: const {'role': 'operator'}, + returnedDeviceToken: '', + raw: const {}, + ); + } + + @override + Future disconnect({required String runtimeId}) async {} + + @override + Future request({ + required String runtimeId, + required String method, + Map? params, + Duration timeout = const Duration(seconds: 15), + }) { + throw UnimplementedError('request is not used by this cleanup test'); + } + + @override + Future dispose() => _updates.close(); +}