Rename ARIS bridge to go core
This commit is contained in:
parent
293278961e
commit
bd76a91130
@ -47,12 +47,12 @@
|
||||
- Assistant 任务线程升级为持续会话:支持流式回复、继续追问、线程归档和重启恢复。
|
||||
- 任务列表按 `单机智能体 / 本地 OpenClaw Gateway / 远程 OpenClaw Gateway` 分组,保持极简列表布局。
|
||||
- Multi-Agent 协作正式升级为 `Architect / Engineer / Tester`,并可选 `ARIS` 作为最强协作框架。
|
||||
- ARIS bundle 作为只读资产内嵌进 App,`skills/` 直接复用 upstream,`llm-chat` 与 `claude-review` 切到 Go bridge。
|
||||
- `Ollama Cloud` 文案与默认地址统一,打包后的 `.app` 会随同分发 `xworkmate-aris-bridge` helper。
|
||||
- ARIS bundle 作为只读资产内嵌进 App,`skills/` 直接复用 upstream,`llm-chat` 与 `claude-review` 切到 Go core。
|
||||
- `Ollama Cloud` 文案与默认地址统一,打包后的 `.app` 会随同分发 `xworkmate-go-core` helper。
|
||||
|
||||
### Current Delivery Scope
|
||||
- 已交付:Single Agent streaming threads、OpenClaw 本地/远程任务线程、手动归档与持续会话恢复。
|
||||
- 已交付:Multi-Agent managed runtime、ARIS framework preset、本地优先 Ollama 回退、Go bridge runtime 和打包分发。
|
||||
- 已交付:Multi-Agent managed runtime、ARIS framework preset、本地优先 Ollama 回退、Go core runtime 和打包分发。
|
||||
- 已交付:Settings / Assistant 里的 ARIS 轻量状态展示、任务分组、Ollama Cloud 设置迁移。
|
||||
- 保持 truth-first:Scheduled Tasks 仍是 `cron.list` 只读视图;Memory 仍是 `memory/sync` 同步能力,不宣传 CRUD。
|
||||
|
||||
|
||||
6
Makefile
6
Makefile
@ -8,7 +8,7 @@ DART ?= dart
|
||||
DEVICE ?= macos
|
||||
APP_STORE_DART_DEFINE ?= --dart-define=XWORKMATE_APP_STORE=true
|
||||
|
||||
.PHONY: help deps analyze test check format run build-linux build-macos build-ios-sim package-deb package-rpm package-linux package-mac install-mac clean build-aris-bridge render-release-docs
|
||||
.PHONY: help deps analyze test check format run build-linux build-macos build-ios-sim package-deb package-rpm package-linux package-mac install-mac clean build-go-core render-release-docs
|
||||
|
||||
help: ## Show available targets
|
||||
@grep -E '^[a-zA-Z0-9_.-]+:.*?## ' Makefile | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "%-18s %s\n", $$1, $$2}'
|
||||
@ -42,8 +42,8 @@ build-macos: ## Build the macOS app in release mode
|
||||
build-ios-sim: ## Build the iOS app for the simulator
|
||||
$(FLUTTER) build ios --simulator $(APP_STORE_DART_DEFINE)
|
||||
|
||||
build-aris-bridge: ## Build the ARIS Go bridge helper
|
||||
bash scripts/build-aris-bridge.sh
|
||||
build-go-core: ## Build the Go core helper
|
||||
bash scripts/build-go-core.sh
|
||||
|
||||
package-deb: ## Create the Linux .deb package
|
||||
bash scripts/package-linux-deb.sh
|
||||
|
||||
@ -1,14 +1,14 @@
|
||||
# XWorkmate
|
||||
|
||||
XWorkmate is an AI workspace shell built with Flutter.
|
||||
`v0.5` ships persistent assistant task threads, optional ARIS-powered multi-agent collaboration, and a bundled Go bridge runtime that travels with the macOS app.
|
||||
`v0.5` ships persistent assistant task threads, optional ARIS-powered multi-agent collaboration, and a bundled Go core runtime that travels with the macOS app.
|
||||
|
||||
## v0.5 Highlights
|
||||
|
||||
- Assistant 任务线程支持流式回复、继续追问和手动归档,不再是一问一答即结束。
|
||||
- 任务列表按 `单机智能体 / 本地 OpenClaw Gateway / 远程 OpenClaw Gateway` 分组显示。
|
||||
- Multi-Agent 协作支持 `Architect / Engineer / Tester`,并可切换 `Native / ARIS` 框架。
|
||||
- ARIS `skills/` 直接随 App 内置,`llm-chat` 与 `claude-review` 统一由 Go bridge 驱动。
|
||||
- ARIS `skills/` 直接随 App 内置,`llm-chat` 与 `claude-review` 统一由 Go core 驱动。
|
||||
- `Ollama Cloud` 设置、ARIS helper bundling、macOS DMG 打包与安装链路已打通。
|
||||
|
||||
## Current Scope
|
||||
@ -17,7 +17,7 @@ XWorkmate is an AI workspace shell built with Flutter.
|
||||
- Single Agent streaming assistant threads
|
||||
- OpenClaw local/remote task threads with persistent context
|
||||
- Multi-Agent orchestration with optional ARIS preset
|
||||
- Bundled ARIS skills, Go bridge helper, `llm-chat` reviewer, and `claude-review`
|
||||
- Bundled ARIS skills, Go core helper, `llm-chat` reviewer, and `claude-review`
|
||||
- Ollama Cloud settings, task grouping, and macOS packaged delivery
|
||||
- Flutter Web shell with `Assistant` + `Settings` only, supporting `Single Agent` and `Relay OpenClaw Gateway`
|
||||
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
- `远程 OpenClaw Gateway`
|
||||
- `ARIS + 本地 Ollama`
|
||||
- `Architect / Engineer / Tester`
|
||||
- `Go bridge reviewer`
|
||||
- `Go core reviewer`
|
||||
- `外部 Agent CLI / JSON-RPC session`
|
||||
|
||||
## 推荐验证顺序
|
||||
@ -29,7 +29,7 @@
|
||||
- 本地 Ollama 可用时,即便缺失部分云端 CLI,也应能退化运行
|
||||
- 线程应可继续追问,不是一答即结束
|
||||
- 任务列表仍保持极简,只显示名称、时间、归档
|
||||
- `llm-chat` 和 `claude-review` 由 Go bridge 驱动,不依赖 `go run`
|
||||
- `llm-chat` 和 `claude-review` 由 Go core 驱动,不依赖 `go run`
|
||||
|
||||
## 建议记录项
|
||||
|
||||
|
||||
@ -2,13 +2,13 @@
|
||||
|
||||
## 目标
|
||||
|
||||
验证 `Go bridge` 驱动的 reviewer / CLI 会话是持续的 session,而不是一次 prompt 一次退出。
|
||||
验证 `Go core` 驱动的 reviewer / CLI 会话是持续的 session,而不是一次 prompt 一次退出。
|
||||
|
||||
## 推荐配置
|
||||
|
||||
- 框架:`ARIS`
|
||||
- 本地 Ollama 可用
|
||||
- `llm-chat` / `claude-review` 走 Go bridge
|
||||
- `llm-chat` / `claude-review` 走 Go core
|
||||
- Assistant 使用现有线程,不切新页面
|
||||
|
||||
## 建议任务
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
module xworkmate/aris_bridge
|
||||
module xworkmate/go_core
|
||||
|
||||
go 1.25.0
|
||||
|
||||
@ -203,7 +203,7 @@ func handleToolBridgeRequest(request rpcRequest) map[string]any {
|
||||
"tools": map[string]any{},
|
||||
},
|
||||
"serverInfo": map[string]any{
|
||||
"name": "xworkmate-aris-bridge",
|
||||
"name": "xworkmate-go-core",
|
||||
"version": "0.2.0",
|
||||
},
|
||||
},
|
||||
@ -12,7 +12,7 @@ import '../i18n/app_language.dart';
|
||||
import '../models/app_models.dart';
|
||||
import '../runtime/device_identity_store.dart';
|
||||
import '../runtime/aris_bundle.dart';
|
||||
import '../runtime/aris_bridge.dart';
|
||||
import '../runtime/go_core.dart';
|
||||
import '../runtime/runtime_bootstrap.dart';
|
||||
import '../runtime/desktop_platform_service.dart';
|
||||
import '../runtime/gateway_runtime.dart';
|
||||
@ -151,14 +151,14 @@ class AppController extends ChangeNotifier {
|
||||
_availableSingleAgentProvidersOverride =
|
||||
availableSingleAgentProvidersOverride;
|
||||
_arisBundleRepository = ArisBundleRepository();
|
||||
_arisBridgeLocator = ArisBridgeLocator();
|
||||
_goCoreLocator = GoCoreLocator();
|
||||
_singleAgentRunner =
|
||||
singleAgentRunner ??
|
||||
DefaultSingleAgentRunner(appServerClient: _singleAgentAppServerClient);
|
||||
_multiAgentOrchestrator = MultiAgentOrchestrator(
|
||||
config: _resolveMultiAgentConfig(_settingsController.snapshot),
|
||||
arisBundleRepository: _arisBundleRepository,
|
||||
arisBridgeLocator: _arisBridgeLocator,
|
||||
goCoreLocator: _goCoreLocator,
|
||||
);
|
||||
|
||||
_attachChildListeners();
|
||||
@ -189,7 +189,7 @@ class AppController extends ChangeNotifier {
|
||||
late final DirectSingleAgentAppServerClient _singleAgentAppServerClient;
|
||||
late final List<SingleAgentProvider>? _availableSingleAgentProvidersOverride;
|
||||
late final ArisBundleRepository _arisBundleRepository;
|
||||
late final ArisBridgeLocator _arisBridgeLocator;
|
||||
late final GoCoreLocator _goCoreLocator;
|
||||
late final SingleAgentRunner _singleAgentRunner;
|
||||
late final MultiAgentOrchestrator _multiAgentOrchestrator;
|
||||
DirectSingleAgentCapabilities _singleAgentCapabilities =
|
||||
|
||||
@ -2463,8 +2463,8 @@ class _SettingsPageState extends State<SettingsPage> {
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
appText(
|
||||
'ARIS 模式会把内嵌 skills 与 Go bridge reviewer 作为本地 Ollama 协作增强层,不会覆盖你原有的 CLI 全局配置。',
|
||||
'ARIS mode injects embedded skills and the Go bridge reviewer for local Ollama collaboration without overwriting your existing CLI global config.',
|
||||
'ARIS 模式会把内嵌 skills 与 Go core reviewer 作为本地 Ollama 协作增强层,不会覆盖你原有的 CLI 全局配置。',
|
||||
'ARIS mode injects embedded skills and the Go core reviewer for local Ollama collaboration without overwriting your existing CLI global config.',
|
||||
),
|
||||
style: theme.textTheme.bodySmall,
|
||||
),
|
||||
|
||||
@ -3,7 +3,7 @@ import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import '../app/app_store_policy.dart';
|
||||
import 'aris_bridge.dart';
|
||||
import 'go_core.dart';
|
||||
|
||||
typedef ArisProcessStarter =
|
||||
Future<Process> Function(
|
||||
@ -16,7 +16,7 @@ typedef ArisProcessStarter =
|
||||
class ArisLlmChatClient {
|
||||
ArisLlmChatClient({
|
||||
ArisProcessStarter? processStarter,
|
||||
ArisBridgeLocator? bridgeLocator,
|
||||
GoCoreLocator? bridgeLocator,
|
||||
Duration rpcTimeout = const Duration(minutes: 2),
|
||||
}) : _processStarter =
|
||||
processStarter ??
|
||||
@ -28,11 +28,11 @@ class ArisLlmChatClient {
|
||||
workingDirectory: workingDirectory,
|
||||
);
|
||||
}),
|
||||
_bridgeLocator = bridgeLocator ?? ArisBridgeLocator(),
|
||||
_bridgeLocator = bridgeLocator ?? GoCoreLocator(),
|
||||
_rpcTimeout = rpcTimeout;
|
||||
|
||||
final ArisProcessStarter _processStarter;
|
||||
final ArisBridgeLocator _bridgeLocator;
|
||||
final GoCoreLocator _bridgeLocator;
|
||||
final Duration _rpcTimeout;
|
||||
|
||||
Future<String> chat({
|
||||
@ -92,12 +92,12 @@ class ArisLlmChatClient {
|
||||
isAppleHost: Platform.isIOS || Platform.isMacOS,
|
||||
)) {
|
||||
throw UnsupportedError(
|
||||
'App Store builds do not allow launching the bundled ARIS bridge process.',
|
||||
'App Store builds do not allow launching the bundled Go core process.',
|
||||
);
|
||||
}
|
||||
final launch = await _bridgeLocator.locate();
|
||||
if (launch == null) {
|
||||
throw StateError('ARIS Go bridge is unavailable.');
|
||||
throw StateError('Go core is unavailable.');
|
||||
}
|
||||
|
||||
final process = await _processStarter(
|
||||
@ -126,7 +126,7 @@ class ArisLlmChatClient {
|
||||
} catch (error) {
|
||||
if (!responseCompleter.isCompleted) {
|
||||
responseCompleter.completeError(
|
||||
StateError('ARIS bridge returned invalid JSON: $error'),
|
||||
StateError('Go core returned invalid JSON: $error'),
|
||||
);
|
||||
}
|
||||
return;
|
||||
@ -149,7 +149,7 @@ class ArisLlmChatClient {
|
||||
!responseCompleter.isCompleted) {
|
||||
final error = (message['error'] as Map).cast<String, dynamic>();
|
||||
responseCompleter.completeError(
|
||||
StateError(error['message']?.toString() ?? 'ARIS bridge error'),
|
||||
StateError(error['message']?.toString() ?? 'Go core error'),
|
||||
);
|
||||
}
|
||||
});
|
||||
@ -168,7 +168,7 @@ class ArisLlmChatClient {
|
||||
StateError(
|
||||
stderrText.isNotEmpty
|
||||
? stderrText
|
||||
: 'ARIS bridge exited with code $exitCode',
|
||||
: 'Go core exited with code $exitCode',
|
||||
),
|
||||
);
|
||||
return;
|
||||
@ -177,7 +177,7 @@ class ArisLlmChatClient {
|
||||
StateError(
|
||||
stderrText.isNotEmpty
|
||||
? stderrText
|
||||
: 'ARIS bridge closed without returning a tool result.',
|
||||
: 'Go core closed without returning a tool result.',
|
||||
),
|
||||
);
|
||||
});
|
||||
@ -209,7 +209,7 @@ class ArisLlmChatClient {
|
||||
return await responseCompleter.future.timeout(
|
||||
_rpcTimeout,
|
||||
onTimeout: () => throw TimeoutException(
|
||||
'ARIS bridge timed out after ${_rpcTimeout.inSeconds}s',
|
||||
'Go core timed out after ${_rpcTimeout.inSeconds}s',
|
||||
_rpcTimeout,
|
||||
),
|
||||
);
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import 'dart:io';
|
||||
|
||||
class ArisBridgeLaunch {
|
||||
const ArisBridgeLaunch({
|
||||
class GoCoreLaunch {
|
||||
const GoCoreLaunch({
|
||||
required this.executable,
|
||||
this.arguments = const <String>[],
|
||||
this.workingDirectory,
|
||||
@ -12,57 +12,57 @@ class ArisBridgeLaunch {
|
||||
final String? workingDirectory;
|
||||
}
|
||||
|
||||
typedef ArisBinaryExistsResolver = Future<bool> Function(String command);
|
||||
typedef GoCoreBinaryExistsResolver = Future<bool> Function(String command);
|
||||
|
||||
class ArisBridgeLocator {
|
||||
ArisBridgeLocator({
|
||||
ArisBinaryExistsResolver? binaryExistsResolver,
|
||||
class GoCoreLocator {
|
||||
GoCoreLocator({
|
||||
GoCoreBinaryExistsResolver? binaryExistsResolver,
|
||||
String? workspaceRoot,
|
||||
String Function()? resolvedExecutableResolver,
|
||||
}) : _binaryExistsResolver = binaryExistsResolver,
|
||||
_workspaceRoot = workspaceRoot,
|
||||
_resolvedExecutableResolver = resolvedExecutableResolver;
|
||||
|
||||
final ArisBinaryExistsResolver? _binaryExistsResolver;
|
||||
final GoCoreBinaryExistsResolver? _binaryExistsResolver;
|
||||
final String? _workspaceRoot;
|
||||
final String Function()? _resolvedExecutableResolver;
|
||||
|
||||
Future<ArisBridgeLaunch?> locate() async {
|
||||
Future<GoCoreLaunch?> locate() async {
|
||||
final bundled = await _bundledHelper();
|
||||
if (bundled != null) {
|
||||
return bundled;
|
||||
}
|
||||
|
||||
final override =
|
||||
(Platform.environment['XWORKMATE_ARIS_BRIDGE_BIN'] ??
|
||||
Platform.environment['ARIS_BRIDGE_BIN'] ??
|
||||
(Platform.environment['XWORKMATE_GO_CORE_BIN'] ??
|
||||
Platform.environment['GO_CORE_BIN'] ??
|
||||
'')
|
||||
.trim();
|
||||
if (override.isNotEmpty && await _binaryExists(override)) {
|
||||
return ArisBridgeLaunch(executable: override);
|
||||
return GoCoreLaunch(executable: override);
|
||||
}
|
||||
|
||||
for (final candidate in <String>['xworkmate-aris-bridge', 'aris-bridge']) {
|
||||
for (final candidate in <String>['xworkmate-go-core', 'go-core']) {
|
||||
if (await _binaryExists(candidate)) {
|
||||
return ArisBridgeLaunch(executable: candidate);
|
||||
return GoCoreLaunch(executable: candidate);
|
||||
}
|
||||
}
|
||||
|
||||
final root = (_workspaceRoot ?? Directory.current.path).trim();
|
||||
if (root.isNotEmpty) {
|
||||
for (final path in <String>[
|
||||
'$root/go/bin/xworkmate-aris-bridge',
|
||||
'$root/go/bin/aris-bridge',
|
||||
'$root/build/bin/xworkmate-aris-bridge',
|
||||
'$root/go/bin/xworkmate-go-core',
|
||||
'$root/go/bin/go-core',
|
||||
'$root/build/bin/xworkmate-go-core',
|
||||
]) {
|
||||
if (await File(path).exists()) {
|
||||
return ArisBridgeLaunch(executable: path);
|
||||
return GoCoreLaunch(executable: path);
|
||||
}
|
||||
}
|
||||
|
||||
final packageDirectory = Directory('$root/go/aris_bridge');
|
||||
final packageDirectory = Directory('$root/go/go_core');
|
||||
if (await packageDirectory.exists() && await _binaryExists('go')) {
|
||||
return ArisBridgeLaunch(
|
||||
return GoCoreLaunch(
|
||||
executable: 'go',
|
||||
arguments: const <String>['run', '.'],
|
||||
workingDirectory: packageDirectory.path,
|
||||
@ -74,7 +74,7 @@ class ArisBridgeLocator {
|
||||
|
||||
Future<bool> isAvailable() async => await locate() != null;
|
||||
|
||||
Future<ArisBridgeLaunch?> _bundledHelper() async {
|
||||
Future<GoCoreLaunch?> _bundledHelper() async {
|
||||
final resolvedExecutable =
|
||||
(_resolvedExecutableResolver?.call() ?? Platform.resolvedExecutable)
|
||||
.trim();
|
||||
@ -93,10 +93,9 @@ class ArisBridgeLocator {
|
||||
if (macOsDirectoryName != 'MacOS' || contentsDirectoryName != 'Contents') {
|
||||
return null;
|
||||
}
|
||||
final bundledPath =
|
||||
'${contentsDirectory.path}/Helpers/xworkmate-aris-bridge';
|
||||
final bundledPath = '${contentsDirectory.path}/Helpers/xworkmate-go-core';
|
||||
if (await File(bundledPath).exists()) {
|
||||
return ArisBridgeLaunch(executable: bundledPath);
|
||||
return GoCoreLaunch(executable: bundledPath);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@ -2,7 +2,7 @@ import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'aris_bundle.dart';
|
||||
import 'aris_bridge.dart';
|
||||
import 'go_core.dart';
|
||||
import 'codex_config_bridge.dart';
|
||||
import 'opencode_config_bridge.dart';
|
||||
import 'runtime_models.dart';
|
||||
@ -12,11 +12,11 @@ class MultiAgentMountManager {
|
||||
CodexConfigBridge? codexConfigBridge,
|
||||
OpencodeConfigBridge? opencodeConfigBridge,
|
||||
ArisBundleRepository? arisBundleRepository,
|
||||
ArisBridgeLocator? arisBridgeLocator,
|
||||
GoCoreLocator? goCoreLocator,
|
||||
}) : this._(
|
||||
arisAdapter: ArisMountAdapter(
|
||||
arisBundleRepository ?? ArisBundleRepository(),
|
||||
arisBridgeLocator ?? ArisBridgeLocator(),
|
||||
goCoreLocator ?? GoCoreLocator(),
|
||||
),
|
||||
codexConfigBridge: codexConfigBridge ?? CodexConfigBridge(),
|
||||
opencodeConfigBridge: opencodeConfigBridge ?? OpencodeConfigBridge(),
|
||||
@ -154,10 +154,10 @@ abstract class CliMountAdapter {
|
||||
}
|
||||
|
||||
class ArisMountAdapter extends CliMountAdapter {
|
||||
ArisMountAdapter(this._bundleRepository, this._bridgeLocator);
|
||||
ArisMountAdapter(this._bundleRepository, this._goCoreLocator);
|
||||
|
||||
final ArisBundleRepository _bundleRepository;
|
||||
final ArisBridgeLocator _bridgeLocator;
|
||||
final GoCoreLocator _goCoreLocator;
|
||||
String _lastBundleVersion = '';
|
||||
|
||||
String get lastBundleVersion => _lastBundleVersion;
|
||||
@ -197,7 +197,7 @@ class ArisMountAdapter extends CliMountAdapter {
|
||||
final bundle = await _bundleRepository.ensureReady();
|
||||
_lastBundleVersion = bundle.manifest.bundleVersion;
|
||||
final skillCount = await _bundleRepository.countSkillFiles();
|
||||
final bridgeAvailable = await _bridgeLocator.isAvailable();
|
||||
final bridgeAvailable = await _goCoreLocator.isAvailable();
|
||||
final llmChatEntry = bundle.manifest.llmChatServerPath.trim();
|
||||
final llmChatReady = llmChatEntry.isNotEmpty;
|
||||
return ManagedMountTargetState.placeholder(
|
||||
@ -219,8 +219,8 @@ class ArisMountAdapter extends CliMountAdapter {
|
||||
: 0,
|
||||
detail: llmChatReady
|
||||
? bridgeAvailable
|
||||
? 'Embedded bundle ${bundle.manifest.bundleVersion} ready; XWorkmate Go bridge manages llm-chat and claude-review.'
|
||||
: 'Embedded bundle is ready, but the XWorkmate Go bridge is not available yet.'
|
||||
? 'Embedded bundle ${bundle.manifest.bundleVersion} ready; XWorkmate Go core manages llm-chat and claude-review.'
|
||||
: 'Embedded bundle is ready, but the XWorkmate Go core is not available yet.'
|
||||
: 'Embedded bundle extracted, but llm-chat metadata is missing.',
|
||||
);
|
||||
} catch (error) {
|
||||
|
||||
@ -6,7 +6,7 @@ import 'package:flutter/foundation.dart';
|
||||
|
||||
import '../app/app_store_policy.dart';
|
||||
import 'aris_bundle.dart';
|
||||
import 'aris_bridge.dart';
|
||||
import 'go_core.dart';
|
||||
import 'aris_llm_chat_client.dart';
|
||||
import 'multi_agent_frameworks.dart';
|
||||
import 'runtime_models.dart';
|
||||
@ -32,16 +32,16 @@ class MultiAgentOrchestrator extends ChangeNotifier {
|
||||
MultiAgentOrchestrator({
|
||||
required MultiAgentConfig config,
|
||||
ArisBundleRepository? arisBundleRepository,
|
||||
ArisBridgeLocator? arisBridgeLocator,
|
||||
GoCoreLocator? goCoreLocator,
|
||||
Future<bool> Function(String command)? binaryExistsResolver,
|
||||
HttpClient Function()? httpClientFactory,
|
||||
ArisLlmChatClient? arisLlmChatClient,
|
||||
CliProcessStarter? processStarter,
|
||||
}) : _config = config,
|
||||
_arisBundleRepository = arisBundleRepository ?? ArisBundleRepository(),
|
||||
_arisBridgeLocator =
|
||||
arisBridgeLocator ??
|
||||
ArisBridgeLocator(binaryExistsResolver: binaryExistsResolver),
|
||||
_goCoreLocator =
|
||||
goCoreLocator ??
|
||||
GoCoreLocator(binaryExistsResolver: binaryExistsResolver),
|
||||
_binaryExistsResolver = binaryExistsResolver,
|
||||
_httpClientFactory = httpClientFactory ?? HttpClient.new,
|
||||
_processStarter =
|
||||
@ -58,15 +58,15 @@ class MultiAgentOrchestrator extends ChangeNotifier {
|
||||
arisLlmChatClient ??
|
||||
ArisLlmChatClient(
|
||||
bridgeLocator:
|
||||
arisBridgeLocator ??
|
||||
ArisBridgeLocator(binaryExistsResolver: binaryExistsResolver),
|
||||
goCoreLocator ??
|
||||
GoCoreLocator(binaryExistsResolver: binaryExistsResolver),
|
||||
);
|
||||
|
||||
/// 当前配置
|
||||
MultiAgentConfig _config;
|
||||
MultiAgentConfig get config => _config;
|
||||
final ArisBundleRepository _arisBundleRepository;
|
||||
final ArisBridgeLocator _arisBridgeLocator;
|
||||
final GoCoreLocator _goCoreLocator;
|
||||
final Future<bool> Function(String command)? _binaryExistsResolver;
|
||||
final HttpClient Function() _httpClientFactory;
|
||||
final CliProcessStarter _processStarter;
|
||||
@ -1051,10 +1051,10 @@ ${selectedSkills.isEmpty ? '- 无' : selectedSkills.map((item) => '- $item').joi
|
||||
required String aiGatewayApiKey,
|
||||
}) async {
|
||||
try {
|
||||
if (!await _arisBridgeLocator.isAvailable()) {
|
||||
if (!await _goCoreLocator.isAvailable()) {
|
||||
return const CliResult(
|
||||
output: '',
|
||||
error: 'ARIS Go bridge is unavailable for llm-chat',
|
||||
error: 'Go core is unavailable for llm-chat',
|
||||
exitCode: -1,
|
||||
);
|
||||
}
|
||||
@ -1081,10 +1081,10 @@ ${selectedSkills.isEmpty ? '- 无' : selectedSkills.map((item) => '- $item').joi
|
||||
required String prompt,
|
||||
}) async {
|
||||
try {
|
||||
if (!await _arisBridgeLocator.isAvailable()) {
|
||||
if (!await _goCoreLocator.isAvailable()) {
|
||||
return const CliResult(
|
||||
output: '',
|
||||
error: 'ARIS Go bridge is unavailable for claude-review',
|
||||
error: 'Go core is unavailable for claude-review',
|
||||
exitCode: -1,
|
||||
);
|
||||
}
|
||||
@ -1217,10 +1217,7 @@ ${selectedSkills.isEmpty ? '- 无' : selectedSkills.map((item) => '- $item').joi
|
||||
};
|
||||
}
|
||||
|
||||
bool _prefersOllamaLaunch({
|
||||
required String tool,
|
||||
required String model,
|
||||
}) {
|
||||
bool _prefersOllamaLaunch({required String tool, required String model}) {
|
||||
final normalizedTool = tool.trim().toLowerCase();
|
||||
final normalizedModel = model.trim();
|
||||
if (normalizedModel.isEmpty) {
|
||||
|
||||
@ -11,20 +11,20 @@
|
||||
- 持续 Assistant 任务线程与流式 AI Gateway 对话
|
||||
- `单机智能体 / 本地 OpenClaw Gateway / 远程 OpenClaw Gateway` 三模式统一
|
||||
- `Architect / Engineer / Tester` 多 Agent 协作
|
||||
- 可选 `ARIS` 框架、内嵌 skills、Go bridge runtime
|
||||
- 可选 `ARIS` 框架、内嵌 skills、Go core runtime
|
||||
- `Ollama Cloud` 文案和默认地址统一
|
||||
|
||||
## Bundled Runtime
|
||||
|
||||
- `assets/aris/skills` 继续直接复用 upstream `skills/`
|
||||
- `llm-chat` 与 `claude-review` 统一由 `xworkmate-aris-bridge` 提供
|
||||
- macOS `.app` 会把 helper 打进 `Contents/Helpers/xworkmate-aris-bridge`
|
||||
- `llm-chat` 与 `claude-review` 统一由 `xworkmate-go-core` 提供
|
||||
- macOS `.app` 会把 helper 打进 `Contents/Helpers/xworkmate-go-core`
|
||||
|
||||
## Validation
|
||||
|
||||
- `flutter analyze`
|
||||
- `flutter test`
|
||||
- `cd go/aris_bridge && go test ./...`
|
||||
- `cd go/go_core && go test ./...`
|
||||
- `flutter test integration_test/desktop_navigation_flow_test.dart -d macos`
|
||||
- `flutter test integration_test/desktop_settings_flow_test.dart -d macos`
|
||||
- `flutter build macos`
|
||||
|
||||
@ -2,9 +2,9 @@
|
||||
set -euo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
BRIDGE_DIR="$ROOT_DIR/go/aris_bridge"
|
||||
BRIDGE_DIR="$ROOT_DIR/go/go_core"
|
||||
OUTPUT_DIR="${OUTPUT_DIR:-$ROOT_DIR/build/bin}"
|
||||
OUTPUT_PATH="${OUTPUT_PATH:-$OUTPUT_DIR/xworkmate-aris-bridge}"
|
||||
OUTPUT_PATH="${OUTPUT_PATH:-$OUTPUT_DIR/xworkmate-go-core}"
|
||||
|
||||
if [[ ! -f "$BRIDGE_DIR/go.mod" ]]; then
|
||||
echo "Missing go.mod in $BRIDGE_DIR" >&2
|
||||
@ -12,13 +12,13 @@ if [[ ! -f "$BRIDGE_DIR/go.mod" ]]; then
|
||||
fi
|
||||
|
||||
if ! command -v go >/dev/null 2>&1; then
|
||||
echo "Go toolchain is required to build xworkmate-aris-bridge" >&2
|
||||
echo "Go toolchain is required to build xworkmate-go-core" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p "$OUTPUT_DIR"
|
||||
|
||||
echo "Building xworkmate-aris-bridge..."
|
||||
echo "Building xworkmate-go-core..."
|
||||
(
|
||||
cd "$BRIDGE_DIR"
|
||||
GO111MODULE=on go build -o "$OUTPUT_PATH" .
|
||||
@ -9,7 +9,7 @@ APP_NAME="${APP_NAME:-XWorkmate}"
|
||||
BUILD_MODE="${BUILD_MODE:-release}"
|
||||
APP_STORE_DEFINE="${APP_STORE_DEFINE:---dart-define=XWORKMATE_APP_STORE=${XWORKMATE_APP_STORE:-true}}"
|
||||
PRODUCTS_DIR_NAME="$(tr '[:lower:]' '[:upper:]' <<< "${BUILD_MODE:0:1}")${BUILD_MODE:1}"
|
||||
BRIDGE_BINARY_NAME="${BRIDGE_BINARY_NAME:-xworkmate-aris-bridge}"
|
||||
BRIDGE_BINARY_NAME="${BRIDGE_BINARY_NAME:-xworkmate-go-core}"
|
||||
BRIDGE_BUILD_PATH="${ROOT_DIR}/build/bin/${BRIDGE_BINARY_NAME}"
|
||||
|
||||
if [[ ! -f "$PUBSPEC_PATH" ]]; then
|
||||
@ -37,8 +37,8 @@ HELPER_PATH="$HELPERS_DIR/$BRIDGE_BINARY_NAME"
|
||||
|
||||
mkdir -p "$DIST_DIR"
|
||||
|
||||
echo "Building bundled ARIS bridge..."
|
||||
bash "$ROOT_DIR/scripts/build-aris-bridge.sh"
|
||||
echo "Building bundled Go core..."
|
||||
bash "$ROOT_DIR/scripts/build-go-core.sh"
|
||||
|
||||
echo "Building $APP_NAME $APP_VERSION ($APP_BUILD) for macOS..."
|
||||
BUILD_ARGS=(
|
||||
|
||||
@ -6,7 +6,7 @@ import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:xworkmate/runtime/aris_bridge.dart';
|
||||
import 'package:xworkmate/runtime/go_core.dart';
|
||||
import 'package:xworkmate/runtime/aris_llm_chat_client.dart';
|
||||
|
||||
void main() {
|
||||
@ -112,8 +112,8 @@ void main() {
|
||||
});
|
||||
}
|
||||
|
||||
ArisBridgeLocator _fixedLocator() {
|
||||
return ArisBridgeLocator(
|
||||
GoCoreLocator _fixedLocator() {
|
||||
return GoCoreLocator(
|
||||
binaryExistsResolver: (_) async => true,
|
||||
workspaceRoot: Directory.systemTemp.path,
|
||||
resolvedExecutableResolver: () =>
|
||||
|
||||
@ -4,14 +4,14 @@ library;
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:xworkmate/runtime/aris_bridge.dart';
|
||||
import 'package:xworkmate/runtime/go_core.dart';
|
||||
|
||||
void main() {
|
||||
test(
|
||||
'ArisBridgeLocator prefers bundled helper inside macOS app bundle',
|
||||
'GoCoreLocator prefers bundled helper inside macOS app bundle',
|
||||
() async {
|
||||
final tempDirectory = await Directory.systemTemp.createTemp(
|
||||
'xworkmate-aris-bridge-bundle-',
|
||||
'xworkmate-go-core-bundle-',
|
||||
);
|
||||
addTearDown(() async {
|
||||
if (await tempDirectory.exists()) {
|
||||
@ -22,11 +22,11 @@ void main() {
|
||||
'${tempDirectory.path}/XWorkmate.app/Contents/Helpers',
|
||||
);
|
||||
await helpersDir.create(recursive: true);
|
||||
final helperFile = File('${helpersDir.path}/xworkmate-aris-bridge');
|
||||
final helperFile = File('${helpersDir.path}/xworkmate-go-core');
|
||||
await helperFile.writeAsString('#!/bin/sh\nexit 0\n');
|
||||
await Process.run('chmod', <String>['+x', helperFile.path]);
|
||||
|
||||
final locator = ArisBridgeLocator(
|
||||
final locator = GoCoreLocator(
|
||||
workspaceRoot: tempDirectory.path,
|
||||
binaryExistsResolver: (_) async => true,
|
||||
resolvedExecutableResolver: () =>
|
||||
@ -43,10 +43,10 @@ void main() {
|
||||
);
|
||||
|
||||
test(
|
||||
'ArisBridgeLocator falls back to go run in the local bridge package',
|
||||
'GoCoreLocator falls back to go run in the local bridge package',
|
||||
() async {
|
||||
final tempDirectory = await Directory.systemTemp.createTemp(
|
||||
'xworkmate-aris-bridge-',
|
||||
'xworkmate-go-core-',
|
||||
);
|
||||
addTearDown(() async {
|
||||
if (await tempDirectory.exists()) {
|
||||
@ -54,10 +54,10 @@ void main() {
|
||||
}
|
||||
});
|
||||
await Directory(
|
||||
'${tempDirectory.path}/go/aris_bridge',
|
||||
'${tempDirectory.path}/go/go_core',
|
||||
).create(recursive: true);
|
||||
|
||||
final locator = ArisBridgeLocator(
|
||||
final locator = GoCoreLocator(
|
||||
workspaceRoot: tempDirectory.path,
|
||||
binaryExistsResolver: (command) async => command == 'go',
|
||||
);
|
||||
@ -67,7 +67,7 @@ void main() {
|
||||
expect(launch, isNotNull);
|
||||
expect(launch!.executable, 'go');
|
||||
expect(launch.arguments, const <String>['run', '.']);
|
||||
expect(launch.workingDirectory, '${tempDirectory.path}/go/aris_bridge');
|
||||
expect(launch.workingDirectory, '${tempDirectory.path}/go/go_core');
|
||||
},
|
||||
);
|
||||
}
|
||||
@ -1,5 +1,5 @@
|
||||
import '../test_suite_stub.dart'
|
||||
if (dart.library.io) 'aris_bridge_suite.dart'
|
||||
if (dart.library.io) 'go_core_suite.dart'
|
||||
as suite;
|
||||
|
||||
void main() {
|
||||
@ -5,7 +5,7 @@ import 'dart:io';
|
||||
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:xworkmate/runtime/aris_bundle.dart';
|
||||
import 'package:xworkmate/runtime/aris_bridge.dart';
|
||||
import 'package:xworkmate/runtime/go_core.dart';
|
||||
import 'package:xworkmate/runtime/codex_config_bridge.dart';
|
||||
import 'package:xworkmate/runtime/multi_agent_mounts.dart';
|
||||
import 'package:xworkmate/runtime/runtime_models.dart';
|
||||
@ -14,7 +14,7 @@ void main() {
|
||||
test('ArisMountAdapter reports error when bundle is unavailable', () async {
|
||||
final adapter = ArisMountAdapter(
|
||||
_ThrowingArisBundleRepository(),
|
||||
ArisBridgeLocator(binaryExistsResolver: (_) async => false),
|
||||
GoCoreLocator(binaryExistsResolver: (_) async => false),
|
||||
);
|
||||
|
||||
final state = await adapter.reconcile(
|
||||
@ -44,7 +44,7 @@ void main() {
|
||||
final bundle = await _writeFakeBundle(tempDir);
|
||||
final adapter = ArisMountAdapter(
|
||||
_FixedArisBundleRepository(bundle),
|
||||
ArisBridgeLocator(
|
||||
GoCoreLocator(
|
||||
workspaceRoot: tempDir.path,
|
||||
binaryExistsResolver: (_) async => false,
|
||||
),
|
||||
@ -63,7 +63,7 @@ void main() {
|
||||
expect(state.syncState, 'embedded');
|
||||
expect(state.discoveredMcpCount, 1);
|
||||
expect(state.managedMcpCount, 0);
|
||||
expect(state.detail, contains('bridge is not available'));
|
||||
expect(state.detail, contains('Go core is not available'));
|
||||
},
|
||||
);
|
||||
|
||||
@ -83,10 +83,10 @@ void main() {
|
||||
'${tempDir.path}/XWorkmate.app/Contents/Helpers',
|
||||
);
|
||||
await helperDir.create(recursive: true);
|
||||
final helper = File('${helperDir.path}/xworkmate-aris-bridge');
|
||||
final helper = File('${helperDir.path}/xworkmate-go-core');
|
||||
await helper.writeAsString('#!/bin/sh\nexit 0\n');
|
||||
await Process.run('chmod', <String>['+x', helper.path]);
|
||||
final locator = ArisBridgeLocator(
|
||||
final locator = GoCoreLocator(
|
||||
workspaceRoot: tempDir.path,
|
||||
binaryExistsResolver: (_) async => false,
|
||||
resolvedExecutableResolver: () =>
|
||||
|
||||
@ -13,11 +13,11 @@ import 'package:xworkmate/runtime/runtime_models.dart';
|
||||
|
||||
void main() {
|
||||
test(
|
||||
'MultiAgentOrchestrator falls back to local Ollama + ARIS Go chat bridge',
|
||||
'MultiAgentOrchestrator falls back to local Ollama + ARIS Go core chat runtime',
|
||||
() async {
|
||||
final fakeOllama = await _FakeOllamaServer.start();
|
||||
addTearDown(fakeOllama.close);
|
||||
final bridgeClient = _FakeArisBridgeClient();
|
||||
final bridgeClient = _FakeGoCoreClient();
|
||||
final orchestrator = MultiAgentOrchestrator(
|
||||
config: MultiAgentConfig.defaults().copyWith(
|
||||
enabled: true,
|
||||
@ -68,11 +68,11 @@ void main() {
|
||||
);
|
||||
|
||||
test(
|
||||
'MultiAgentOrchestrator routes tester claude reviews through the same Go bridge',
|
||||
'MultiAgentOrchestrator routes tester claude reviews through the same Go core runtime',
|
||||
() async {
|
||||
final fakeOllama = await _FakeOllamaServer.start();
|
||||
addTearDown(fakeOllama.close);
|
||||
final bridgeClient = _FakeArisBridgeClient();
|
||||
final bridgeClient = _FakeGoCoreClient();
|
||||
final orchestrator = MultiAgentOrchestrator(
|
||||
config: MultiAgentConfig.defaults().copyWith(
|
||||
enabled: true,
|
||||
@ -117,8 +117,8 @@ void main() {
|
||||
);
|
||||
}
|
||||
|
||||
class _FakeArisBridgeClient extends ArisLlmChatClient {
|
||||
_FakeArisBridgeClient();
|
||||
class _FakeGoCoreClient extends ArisLlmChatClient {
|
||||
_FakeGoCoreClient();
|
||||
|
||||
int chatCallCount = 0;
|
||||
int claudeReviewCallCount = 0;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user