Remove bundled app helper binaries

This commit is contained in:
Haitao Pan 2026-04-11 14:45:50 +08:00
parent 55a80d2891
commit 5ab79c7398
12 changed files with 74 additions and 111 deletions

View File

@ -70,7 +70,7 @@
- 任务列表按 `单机智能体 / 本地 OpenClaw Gateway / 远程 OpenClaw Gateway` 分组,保持极简列表布局。
- Multi-Agent 协作正式升级为 `Architect / Engineer / Tester`,并可选 `ARIS` 作为最强协作框架。
- ARIS bundle 作为只读资产内嵌进 App`skills/` 直接复用 upstream`llm-chat` 与 `claude-review` 切到 Go core。
- `Ollama Cloud` 文案与默认地址统一,打包后的 `.app` 会随同分发 `xworkmate-go-core` helper
- `Ollama Cloud` 文案与默认地址统一,Go core 保持为包外开发态能力,不再内嵌进 `.app`
### Current Delivery Scope
- 已交付Single Agent streaming threads、OpenClaw 本地/远程任务线程、手动归档与持续会话恢复。

View File

@ -29,13 +29,12 @@ The following app-side concerns remain in `xworkmate-app`:
- Flutter UI and settings pages
- ACP Bridge client-side configuration and secure-storage handling
- Dart runtime launch/locator logic for the helper binary
- packaging logic that embeds the helper into the app bundle
## Build Contract
`xworkmate-app` expects the helper artifact named `xworkmate-go-core`.
This is the current cross-repo runtime contract, not a legacy compatibility shim. The helper is built from `xworkmate-bridge` and consumed by `xworkmate-app`.
This is the current cross-repo runtime contract for local development flows, not a legacy compatibility shim. The helper is built from `xworkmate-bridge` and consumed by `xworkmate-app` outside the shipped app bundle.
## App Repository Changes

View File

@ -97,7 +97,7 @@ class ArisLlmChatClient {
isAppleHost: Platform.isIOS || Platform.isMacOS,
)) {
throw UnsupportedError(
'App Store builds only allow the bundled Go core helper inside the app bundle.',
'App Store builds do not allow launching local Go core processes.',
);
}

View File

@ -12,15 +12,12 @@ bool shouldBlockEmbeddedAgentLaunch({
}
bool shouldBlockGoCoreLaunch(
GoCoreLaunch launch, {
GoCoreLaunch _, {
required bool isAppleHost,
bool? enabled,
}) {
if (!shouldApplyAppleAppStorePolicy(
return shouldBlockEmbeddedAgentLaunch(
isAppleHost: isAppleHost,
enabled: enabled,
)) {
return false;
}
return launch.source != GoCoreLaunchSource.bundledHelper;
);
}

View File

@ -154,7 +154,7 @@ class GoAcpStdioBridge {
isAppleHost: Platform.isIOS || Platform.isMacOS,
)) {
throw UnsupportedError(
'App Store builds only allow the bundled Go core helper inside the app bundle.',
'App Store builds do not allow launching local Go core processes.',
);
}
final process = await _processStarter(
@ -183,7 +183,10 @@ class GoAcpStdioBridge {
);
}),
);
await request(method: 'acp.capabilities', params: const <String, dynamic>{});
await request(
method: 'acp.capabilities',
params: const <String, dynamic>{},
);
}
void _handleStdoutLine(String line) {

View File

@ -1,9 +1,6 @@
import 'dart:io';
enum GoCoreLaunchSource {
bundledHelper,
buildArtifact,
}
enum GoCoreLaunchSource { buildArtifact }
class GoCoreLaunch {
const GoCoreLaunch({
@ -35,11 +32,6 @@ class GoCoreLocator {
final String Function()? _resolvedExecutableResolver;
Future<GoCoreLaunch?> locate() async {
final bundled = await _bundledHelper();
if (bundled != null) {
return bundled;
}
for (final root in _candidateRoots()) {
final path = '$root/build/bin/xworkmate-go-core';
if (await _binaryExists(path)) {
@ -54,35 +46,6 @@ class GoCoreLocator {
Future<bool> isAvailable() async => await locate() != null;
Future<GoCoreLaunch?> _bundledHelper() async {
final resolvedExecutable =
(_resolvedExecutableResolver?.call() ?? Platform.resolvedExecutable)
.trim();
if (resolvedExecutable.isEmpty) {
return null;
}
final executableFile = File(resolvedExecutable);
final executableDirectory = executableFile.parent;
final contentsDirectory = executableDirectory.parent;
final macOsDirectoryName = executableDirectory.path
.split(Platform.pathSeparator)
.last;
final contentsDirectoryName = contentsDirectory.path
.split(Platform.pathSeparator)
.last;
if (macOsDirectoryName != 'MacOS' || contentsDirectoryName != 'Contents') {
return null;
}
final bundledPath = '${contentsDirectory.path}/Helpers/xworkmate-go-core';
if (await _binaryExists(bundledPath)) {
return GoCoreLaunch(
executable: bundledPath,
source: GoCoreLaunchSource.bundledHelper,
);
}
return null;
}
List<String> _candidateRoots() {
final roots = <String>{};
final explicitRoot = _workspaceRoot?.trim() ?? '';
@ -106,7 +69,9 @@ class GoCoreLocator {
roots.addAll(_ancestorPaths(executableDirectory));
}
return roots.where((path) => path.trim().isNotEmpty).toList(growable: false);
return roots
.where((path) => path.trim().isNotEmpty)
.toList(growable: false);
}
List<String> _ancestorPaths(Directory start) {

View File

@ -248,7 +248,6 @@
33CC10EB2044A3C60003C045 /* Resources */,
33CC110E2044A8840003C045 /* Bundle Framework */,
3399D490228B24CF009A79C7 /* ShellScript */,
A1B2C3084F0A000100000001 /* Embed Bundled Go Core Helper */,
93B26977D4D2EC7AFAB54C8E /* [CP] Embed Pods Frameworks */,
A1B2C3074F0A000100000001 /* Generate Missing Framework dSYMs */,
);
@ -454,26 +453,6 @@
shellScript = "/bin/bash \"${PROJECT_DIR}/../scripts/macos_generate_missing_dsyms.sh\"\n";
showEnvVarsInLog = 0;
};
A1B2C3084F0A000100000001 /* Embed Bundled Go Core Helper */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
);
name = "Embed Bundled Go Core Helper";
outputFileListPaths = (
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "/bin/bash \"${PROJECT_DIR}/../scripts/embed-go-core-helper.sh\" \"${TARGET_BUILD_DIR}/${WRAPPER_NAME}\"\n";
showEnvVarsInLog = 0;
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */

View File

@ -18,7 +18,7 @@
- `assets/aris/skills` 继续直接复用 upstream `skills/`
- `llm-chat``claude-review` 统一由 `xworkmate-go-core` 提供
- macOS `.app` 会把 helper 打进 `Contents/Helpers/xworkmate-go-core`
- macOS App Store 交付不再在 `.app` 内嵌 `xworkmate-go-core`
## Validation

View File

@ -1,35 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
APP_BUNDLE_PATH="${1:-${APP_BUNDLE_PATH:-}}"
BRIDGE_BINARY_NAME="${BRIDGE_BINARY_NAME:-xworkmate-go-core}"
BRIDGE_BUILD_PATH="${ROOT_DIR}/build/bin/${BRIDGE_BINARY_NAME}"
if [[ -z "$APP_BUNDLE_PATH" ]]; then
echo "Missing app bundle path for embedded go-core helper" >&2
exit 1
fi
if [[ ! -d "$APP_BUNDLE_PATH" ]]; then
echo "App bundle does not exist: $APP_BUNDLE_PATH" >&2
exit 1
fi
HELPERS_DIR="$APP_BUNDLE_PATH/Contents/Helpers"
HELPER_PATH="$HELPERS_DIR/$BRIDGE_BINARY_NAME"
bash "$ROOT_DIR/scripts/build-go-core.sh"
mkdir -p "$HELPERS_DIR"
ditto "$BRIDGE_BUILD_PATH" "$HELPER_PATH"
chmod +x "$HELPER_PATH"
SIGN_IDENTITY="${EXPANDED_CODE_SIGN_IDENTITY:-${CODE_SIGN_IDENTITY:--}}"
if [[ -n "$SIGN_IDENTITY" && "$SIGN_IDENTITY" != "-" ]]; then
codesign --force --sign "$SIGN_IDENTITY" --timestamp=none "$HELPER_PATH"
else
echo "Skipping helper codesign: no explicit signing identity provided."
fi
echo "Embedded go-core helper: $HELPER_PATH"

View File

@ -102,13 +102,12 @@ bash "$ROOT_DIR/scripts/check-apple-export-compliance.sh" "$BUILD_APP_PATH"
rm -rf "$DIST_APP_PATH" "$DIST_DMG_PATH"
ditto "$BUILD_APP_PATH" "$DIST_APP_PATH"
if [[ -n "$SIGN_IDENTITY" ]]; then
echo "Refreshing bundled helper and re-signing with explicit identity..."
XWORKMATE_SIGN_IDENTITY="$SIGN_IDENTITY" bash "$ROOT_DIR/scripts/embed-go-core-helper.sh" "$DIST_APP_PATH"
echo "Re-signing app bundle with explicit identity..."
codesign --force --deep --sign "$SIGN_IDENTITY" \
--preserve-metadata=entitlements,requirements,flags,runtime \
--timestamp=none "$DIST_APP_PATH"
else
echo "Preserving Flutter build output signature and embedded helper."
echo "Preserving Flutter build output signature."
fi
verify_bundle_signature "$DIST_APP_PATH"

View File

@ -0,0 +1,31 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:xworkmate/runtime/embedded_agent_launch_policy.dart';
import 'package:xworkmate/runtime/go_core.dart';
void main() {
group('embedded agent launch policy', () {
test('blocks Go core launch for App Store policy on Apple hosts', () {
const launch = GoCoreLaunch(
executable: '/tmp/build/bin/xworkmate-go-core',
source: GoCoreLaunchSource.buildArtifact,
);
expect(
shouldBlockGoCoreLaunch(launch, isAppleHost: true, enabled: true),
isTrue,
);
});
test('allows Go core launch when App Store policy is disabled', () {
const launch = GoCoreLaunch(
executable: '/tmp/build/bin/xworkmate-go-core',
source: GoCoreLaunchSource.buildArtifact,
);
expect(
shouldBlockGoCoreLaunch(launch, isAppleHost: true, enabled: false),
isFalse,
);
});
});
}

View File

@ -0,0 +1,25 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:xworkmate/runtime/go_core.dart';
void main() {
group('GoCoreLocator', () {
test(
'finds workspace build artifact and never depends on app bundle helpers',
() async {
final locator = GoCoreLocator(
workspaceRoot: '/repo/app',
resolvedExecutableResolver: () =>
'/repo/app/build/macos/Build/Products/Release/XWorkmate.app/Contents/MacOS/XWorkmate',
binaryExistsResolver: (path) async =>
path == '/repo/app/build/bin/xworkmate-go-core',
);
final launch = await locator.locate();
expect(launch, isNotNull);
expect(launch!.executable, '/repo/app/build/bin/xworkmate-go-core');
expect(launch.source, GoCoreLaunchSource.buildArtifact);
},
);
});
}