Remove bundled app helper binaries
This commit is contained in:
parent
55a80d2891
commit
5ab79c7398
@ -70,7 +70,7 @@
|
|||||||
- 任务列表按 `单机智能体 / 本地 OpenClaw Gateway / 远程 OpenClaw Gateway` 分组,保持极简列表布局。
|
- 任务列表按 `单机智能体 / 本地 OpenClaw Gateway / 远程 OpenClaw Gateway` 分组,保持极简列表布局。
|
||||||
- Multi-Agent 协作正式升级为 `Architect / Engineer / Tester`,并可选 `ARIS` 作为最强协作框架。
|
- Multi-Agent 协作正式升级为 `Architect / Engineer / Tester`,并可选 `ARIS` 作为最强协作框架。
|
||||||
- ARIS bundle 作为只读资产内嵌进 App,`skills/` 直接复用 upstream,`llm-chat` 与 `claude-review` 切到 Go core。
|
- 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
|
### Current Delivery Scope
|
||||||
- 已交付:Single Agent streaming threads、OpenClaw 本地/远程任务线程、手动归档与持续会话恢复。
|
- 已交付:Single Agent streaming threads、OpenClaw 本地/远程任务线程、手动归档与持续会话恢复。
|
||||||
|
|||||||
@ -29,13 +29,12 @@ The following app-side concerns remain in `xworkmate-app`:
|
|||||||
- Flutter UI and settings pages
|
- Flutter UI and settings pages
|
||||||
- ACP Bridge client-side configuration and secure-storage handling
|
- ACP Bridge client-side configuration and secure-storage handling
|
||||||
- Dart runtime launch/locator logic for the helper binary
|
- Dart runtime launch/locator logic for the helper binary
|
||||||
- packaging logic that embeds the helper into the app bundle
|
|
||||||
|
|
||||||
## Build Contract
|
## Build Contract
|
||||||
|
|
||||||
`xworkmate-app` expects the helper artifact named `xworkmate-go-core`.
|
`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
|
## App Repository Changes
|
||||||
|
|
||||||
|
|||||||
@ -97,7 +97,7 @@ class ArisLlmChatClient {
|
|||||||
isAppleHost: Platform.isIOS || Platform.isMacOS,
|
isAppleHost: Platform.isIOS || Platform.isMacOS,
|
||||||
)) {
|
)) {
|
||||||
throw UnsupportedError(
|
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(
|
bool shouldBlockGoCoreLaunch(
|
||||||
GoCoreLaunch launch, {
|
GoCoreLaunch _, {
|
||||||
required bool isAppleHost,
|
required bool isAppleHost,
|
||||||
bool? enabled,
|
bool? enabled,
|
||||||
}) {
|
}) {
|
||||||
if (!shouldApplyAppleAppStorePolicy(
|
return shouldBlockEmbeddedAgentLaunch(
|
||||||
isAppleHost: isAppleHost,
|
isAppleHost: isAppleHost,
|
||||||
enabled: enabled,
|
enabled: enabled,
|
||||||
)) {
|
);
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return launch.source != GoCoreLaunchSource.bundledHelper;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -154,7 +154,7 @@ class GoAcpStdioBridge {
|
|||||||
isAppleHost: Platform.isIOS || Platform.isMacOS,
|
isAppleHost: Platform.isIOS || Platform.isMacOS,
|
||||||
)) {
|
)) {
|
||||||
throw UnsupportedError(
|
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(
|
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) {
|
void _handleStdoutLine(String line) {
|
||||||
|
|||||||
@ -1,9 +1,6 @@
|
|||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
enum GoCoreLaunchSource {
|
enum GoCoreLaunchSource { buildArtifact }
|
||||||
bundledHelper,
|
|
||||||
buildArtifact,
|
|
||||||
}
|
|
||||||
|
|
||||||
class GoCoreLaunch {
|
class GoCoreLaunch {
|
||||||
const GoCoreLaunch({
|
const GoCoreLaunch({
|
||||||
@ -35,11 +32,6 @@ class GoCoreLocator {
|
|||||||
final String Function()? _resolvedExecutableResolver;
|
final String Function()? _resolvedExecutableResolver;
|
||||||
|
|
||||||
Future<GoCoreLaunch?> locate() async {
|
Future<GoCoreLaunch?> locate() async {
|
||||||
final bundled = await _bundledHelper();
|
|
||||||
if (bundled != null) {
|
|
||||||
return bundled;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (final root in _candidateRoots()) {
|
for (final root in _candidateRoots()) {
|
||||||
final path = '$root/build/bin/xworkmate-go-core';
|
final path = '$root/build/bin/xworkmate-go-core';
|
||||||
if (await _binaryExists(path)) {
|
if (await _binaryExists(path)) {
|
||||||
@ -54,35 +46,6 @@ class GoCoreLocator {
|
|||||||
|
|
||||||
Future<bool> isAvailable() async => await locate() != null;
|
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() {
|
List<String> _candidateRoots() {
|
||||||
final roots = <String>{};
|
final roots = <String>{};
|
||||||
final explicitRoot = _workspaceRoot?.trim() ?? '';
|
final explicitRoot = _workspaceRoot?.trim() ?? '';
|
||||||
@ -106,7 +69,9 @@ class GoCoreLocator {
|
|||||||
roots.addAll(_ancestorPaths(executableDirectory));
|
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) {
|
List<String> _ancestorPaths(Directory start) {
|
||||||
|
|||||||
@ -248,7 +248,6 @@
|
|||||||
33CC10EB2044A3C60003C045 /* Resources */,
|
33CC10EB2044A3C60003C045 /* Resources */,
|
||||||
33CC110E2044A8840003C045 /* Bundle Framework */,
|
33CC110E2044A8840003C045 /* Bundle Framework */,
|
||||||
3399D490228B24CF009A79C7 /* ShellScript */,
|
3399D490228B24CF009A79C7 /* ShellScript */,
|
||||||
A1B2C3084F0A000100000001 /* Embed Bundled Go Core Helper */,
|
|
||||||
93B26977D4D2EC7AFAB54C8E /* [CP] Embed Pods Frameworks */,
|
93B26977D4D2EC7AFAB54C8E /* [CP] Embed Pods Frameworks */,
|
||||||
A1B2C3074F0A000100000001 /* Generate Missing Framework dSYMs */,
|
A1B2C3074F0A000100000001 /* Generate Missing Framework dSYMs */,
|
||||||
);
|
);
|
||||||
@ -454,26 +453,6 @@
|
|||||||
shellScript = "/bin/bash \"${PROJECT_DIR}/../scripts/macos_generate_missing_dsyms.sh\"\n";
|
shellScript = "/bin/bash \"${PROJECT_DIR}/../scripts/macos_generate_missing_dsyms.sh\"\n";
|
||||||
showEnvVarsInLog = 0;
|
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 */
|
/* End PBXShellScriptBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXSourcesBuildPhase section */
|
/* Begin PBXSourcesBuildPhase section */
|
||||||
|
|||||||
@ -18,7 +18,7 @@
|
|||||||
|
|
||||||
- `assets/aris/skills` 继续直接复用 upstream `skills/`
|
- `assets/aris/skills` 继续直接复用 upstream `skills/`
|
||||||
- `llm-chat` 与 `claude-review` 统一由 `xworkmate-go-core` 提供
|
- `llm-chat` 与 `claude-review` 统一由 `xworkmate-go-core` 提供
|
||||||
- macOS `.app` 会把 helper 打进 `Contents/Helpers/xworkmate-go-core`
|
- macOS App Store 交付不再在 `.app` 内嵌 `xworkmate-go-core`
|
||||||
|
|
||||||
## Validation
|
## 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"
|
rm -rf "$DIST_APP_PATH" "$DIST_DMG_PATH"
|
||||||
ditto "$BUILD_APP_PATH" "$DIST_APP_PATH"
|
ditto "$BUILD_APP_PATH" "$DIST_APP_PATH"
|
||||||
if [[ -n "$SIGN_IDENTITY" ]]; then
|
if [[ -n "$SIGN_IDENTITY" ]]; then
|
||||||
echo "Refreshing bundled helper and re-signing with explicit identity..."
|
echo "Re-signing app bundle with explicit identity..."
|
||||||
XWORKMATE_SIGN_IDENTITY="$SIGN_IDENTITY" bash "$ROOT_DIR/scripts/embed-go-core-helper.sh" "$DIST_APP_PATH"
|
|
||||||
codesign --force --deep --sign "$SIGN_IDENTITY" \
|
codesign --force --deep --sign "$SIGN_IDENTITY" \
|
||||||
--preserve-metadata=entitlements,requirements,flags,runtime \
|
--preserve-metadata=entitlements,requirements,flags,runtime \
|
||||||
--timestamp=none "$DIST_APP_PATH"
|
--timestamp=none "$DIST_APP_PATH"
|
||||||
else
|
else
|
||||||
echo "Preserving Flutter build output signature and embedded helper."
|
echo "Preserving Flutter build output signature."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
verify_bundle_signature "$DIST_APP_PATH"
|
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