Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions flutter_app/android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,17 @@ subprojects {

subprojects {
project.evaluationDependsOn(":app")

// Fix plugins that still reference the deprecated jcenter() repository (#69).
// Force all subprojects to use mavenCentral() instead.
project.afterEvaluate {
project.repositories {
mavenCentral()
}
project.repositories.removeIf { repo ->
repo instanceof MavenArtifactRepository && repo.url.toString().contains("jcenter")
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Read-only verification: locate lifecycle/repository hooks in Gradle files
rg -n -C3 --type-add 'gradle:*.gradle' --type gradle \
  'evaluationDependsOn|afterEvaluate|projectsEvaluated|repositories\.removeIf|jcenter|mavenCentral'

Repository: mithun50/openclaw-termux

Length of output: 1804


afterEvaluate registration is too late and breaks the Gradle build.

At line 28, project.afterEvaluate { ... } is registered inside the subprojects block after project.evaluationDependsOn(":app") has already forced :app evaluation. Once a project is evaluated, registering afterEvaluate callbacks fails with "Cannot run Project.afterEvaluate(Closure) when the project is already evaluated."

Proposed fix (use gradle.projectsEvaluated instead)
 subprojects {
     project.evaluationDependsOn(":app")
-
-    // Fix plugins that still reference the deprecated jcenter() repository (`#69`).
-    // Force all subprojects to use mavenCentral() instead.
-    project.afterEvaluate {
-        project.repositories {
-            mavenCentral()
-        }
-        project.repositories.removeIf { repo ->
-            repo instanceof MavenArtifactRepository && repo.url.toString().contains("jcenter")
-        }
-    }
 }
+
+// Fix plugins that still reference the deprecated jcenter() repository (`#69`).
+// Apply once after project evaluation phase has completed.
+gradle.projectsEvaluated {
+    subprojects { subproject ->
+        subproject.repositories {
+            mavenCentral()
+        }
+        subproject.repositories.removeIf { repo ->
+            repo instanceof MavenArtifactRepository && repo.url.toString().contains("jcenter")
+        }
+    }
+}
🧰 Tools
🪛 GitHub Actions: Build OpenClaw Apps

[error] 28-28: Gradle build failed while evaluating root project 'android': Cannot run Project.afterEvaluate(Closure) when the project is already evaluated.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@flutter_app/android/build.gradle` around lines 28 - 35, The
project.afterEvaluate callback is registered too late (after evaluationHasBeen
forced by evaluationDependsOn(":app")) which causes the "already evaluated"
error; replace the project.afterEvaluate usage by using gradle.projectsEvaluated
and run the repository adjustments for every subproject there: in the
gradle.projectsEvaluated block iterate the relevant projects/subprojects and
ensure each project.repositories contains mavenCentral() and remove jcenter
entries (detect via instanceof MavenArtifactRepository and
repo.url.toString().contains("jcenter")); this ensures the repository changes
run after all projects are evaluated without attempting to register
afterEvaluate on an already-evaluated project.

}

tasks.register("clean", Delete) {
Expand Down
2 changes: 1 addition & 1 deletion flutter_app/lib/constants.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
class AppConstants {
static const String appName = 'OpenClaw';
static const String version = '1.8.4';
static const String version = '1.8.5';
static const String packageName = 'com.nxg.openclawproot';

/// Matches ANSI escape sequences (e.g. color codes in terminal output).
Expand Down
3 changes: 2 additions & 1 deletion flutter_app/lib/models/gateway_state.dart
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,14 @@ class GatewayState {
DateTime? startedAt,
bool clearStartedAt = false,
String? dashboardUrl,
bool clearDashboardUrl = false,
}) {
return GatewayState(
status: status ?? this.status,
logs: logs ?? this.logs,
errorMessage: clearError ? null : (errorMessage ?? this.errorMessage),
startedAt: clearStartedAt ? null : (startedAt ?? this.startedAt),
dashboardUrl: dashboardUrl ?? this.dashboardUrl,
dashboardUrl: clearDashboardUrl ? null : (dashboardUrl ?? this.dashboardUrl),
);
}

Expand Down
2 changes: 1 addition & 1 deletion flutter_app/lib/screens/setup_wizard_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ class _SetupWizardScreenState extends State<SetupWizardScreen> {
stepNumber: num,
label: state.step == step ? state.message : label,
isActive: state.step == step,
isComplete: state.stepNumber > step.index + 1 || state.isComplete,
isComplete: state.stepNumber > step.index || state.isComplete,
hasError: state.hasError && state.step == step,
progress: state.step == step ? state.progress : null,
),
Expand Down
19 changes: 19 additions & 0 deletions flutter_app/lib/screens/splash_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,25 @@ class _SplashScreenState extends State<SplashScreen>
setupComplete = false;
}

// Auto-repair: if only bionic-bypass is missing, regenerate it
// instead of forcing full re-setup (#70, #73).
if (!setupComplete) {
try {
final status = await NativeBridge.getBootstrapStatus();
final rootfsOk = status['rootfsExists'] == true;
final bashOk = status['binBashExists'] == true;
final nodeOk = status['nodeInstalled'] == true;
final openclawOk = status['openclawInstalled'] == true;
final bypassOk = status['bypassInstalled'] == true;

if (rootfsOk && bashOk && nodeOk && openclawOk && !bypassOk) {
setState(() => _status = 'Repairing bionic bypass...');
await NativeBridge.installBionicBypass();
setupComplete = await NativeBridge.isBootstrapComplete();
}
} catch (_) {}
}

if (!mounted) return;

if (setupComplete) {
Expand Down
42 changes: 39 additions & 3 deletions flutter_app/lib/services/gateway_service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -71,10 +71,16 @@ class GatewayService {
// Write allowCommands config so the next gateway restart picks it up,
// and in case the running gateway supports config hot-reload.
await _writeNodeAllowConfig();
// Prefer token from config file over stale SharedPreferences value (#74, #82).
final configToken = await _readTokenFromConfig();
final effectiveUrl = configToken != null
? 'http://localhost:18789/#token=$configToken'
: savedUrl;
if (configToken != null) prefs.dashboardUrl = effectiveUrl;
_startingAt = DateTime.now();
_updateState(_state.copyWith(
status: GatewayStatus.starting,
dashboardUrl: savedUrl,
dashboardUrl: effectiveUrl,
logs: [..._state.logs, _ts('[INFO] Gateway process detected, reconnecting...')],
));

Expand Down Expand Up @@ -173,6 +179,19 @@ fs.writeFileSync(p, JSON.stringify(c, null, 2));
}
}

/// Read the actual gateway auth token from openclaw.json config file (#74, #82).
/// This is the source of truth — more reliable than regex-scraping stdout.
Future<String?> _readTokenFromConfig() async {
try {
final raw = await NativeBridge.readRootfsFile('root/.openclaw/openclaw.json');
if (raw == null) return null;
final config = jsonDecode(raw) as Map<String, dynamic>;
final token = config['gateway']?['auth']?['token'];
if (token is String && token.isNotEmpty) return token;
} catch (_) {}
return null;
}

/// Escape a string for use as a single-quoted shell argument.
static String _shellEscape(String s) {
return "'${s.replaceAll("'", "'\\''")}'";
Expand All @@ -183,15 +202,17 @@ fs.writeFileSync(p, JSON.stringify(c, null, 2));
if (_startInProgress) return;
_startInProgress = true;

// Clear any stale token from a previous session (#74, #82).
// The fresh token will be captured from gateway stdout once it starts.
final prefs = PreferencesService();
await prefs.init();
final savedUrl = prefs.dashboardUrl;
prefs.dashboardUrl = null;

_updateState(_state.copyWith(
status: GatewayStatus.starting,
clearError: true,
clearDashboardUrl: true,
logs: [..._state.logs, _ts('[INFO] Starting gateway...')],
dashboardUrl: savedUrl,
));

try {
Expand Down Expand Up @@ -280,9 +301,24 @@ fs.writeFileSync(p, JSON.stringify(c, null, 2));
.timeout(const Duration(seconds: 3));

if (response.statusCode < 500 && _state.status != GatewayStatus.running) {
// Read the actual token from openclaw.json — source of truth (#74, #82).
// This ensures the displayed token always matches the gateway's config,
// even if the stdout regex didn't capture it.
String? configUrl = _state.dashboardUrl;
try {
final token = await _readTokenFromConfig();
if (token != null) {
configUrl = 'http://localhost:18789/#token=$token';
final prefs = PreferencesService();
await prefs.init();
prefs.dashboardUrl = configUrl;
}
} catch (_) {}

_updateState(_state.copyWith(
status: GatewayStatus.running,
startedAt: DateTime.now(),
dashboardUrl: configUrl,
logs: [..._state.logs, _ts('[INFO] Gateway is healthy')],
));
}
Expand Down
21 changes: 18 additions & 3 deletions flutter_app/lib/widgets/progress_step.dart
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,16 @@ class ProgressStep extends StatelessWidget {
circleChild = const Icon(Icons.check, color: Colors.white, size: 16);
} else if (isActive) {
circleColor = theme.colorScheme.primary;
// Use indeterminate (spinning) when progress is 0 so the UI doesn't
// appear frozen during long-running steps (#83).
final effectiveProgress = (progress != null && progress! > 0.0) ? progress : null;
circleChild = SizedBox(
width: 16,
height: 16,
child: CircularProgressIndicator(
strokeWidth: 2,
color: Colors.white,
value: progress,
value: effectiveProgress,
),
);
} else {
Expand Down Expand Up @@ -83,15 +86,27 @@ class ProgressStep extends StatelessWidget {
: theme.colorScheme.onSurfaceVariant,
),
),
if (isActive && progress != null)
if (isActive && progress != null) ...[
Padding(
padding: const EdgeInsets.only(top: 4),
child: LinearProgressIndicator(
value: progress,
// Show indeterminate animation when progress is 0 (#83)
value: progress! > 0.0 ? progress : null,
minHeight: 4,
borderRadius: BorderRadius.circular(2),
),
),
if (progress! > 0.0)
Padding(
padding: const EdgeInsets.only(top: 2),
child: Text(
'${(progress! * 100).toInt()}%',
style: theme.textTheme.labelSmall?.copyWith(
color: theme.colorScheme.onSurfaceVariant,
),
),
),
],
],
),
),
Expand Down
2 changes: 1 addition & 1 deletion flutter_app/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name: openclaw
description: OpenClaw AI Gateway for Android - standalone, no Termux required.
publish_to: 'none'
version: 1.8.4+15
version: 1.8.5+16

environment:
sdk: '>=3.2.0 <4.0.0'
Expand Down
Loading