Remove bundled app helper binaries
This commit is contained in:
parent
55a80d2891
commit
5ab79c7398
@ -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 本地/远程任务线程、手动归档与持续会话恢复。
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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.',
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -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;
|
||||
);
|
||||
}
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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 */
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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"
|
||||
@ -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"
|
||||
|
||||
31
test/runtime/embedded_agent_launch_policy_test.dart
Normal file
31
test/runtime/embedded_agent_launch_policy_test.dart
Normal 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,
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
25
test/runtime/go_core_test.dart
Normal file
25
test/runtime/go_core_test.dart
Normal 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);
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user