diff --git a/packages/client-android/android/app/src/main/assets/index.html b/packages/client-android/android/app/src/main/assets/index.html index 6f41757..4bc6d98 100644 --- a/packages/client-android/android/app/src/main/assets/index.html +++ b/packages/client-android/android/app/src/main/assets/index.html @@ -11,48 +11,48 @@ h1{font-size:20px;color:#38bdf8;margin-bottom:4px} .btn{display:block;width:100%;padding:14px;margin:8px 0;border:none;border-radius:8px;font-size:15px;cursor:pointer;color:#fff} .btn-blue{background:#1e40af} .btn-green{background:#15803d} -.btn-red{background:#991b1b} .input{display:block;width:100%;padding:12px;margin:8px 0;border:none;border-radius:8px;font-size:14px;background:#0f172a;color:#e2e8f0} -.log{background:#0a0f1a;padding:12px;border-radius:8px;margin-top:8px;max-height:200px;overflow-y:auto;font-size:11px;white-space:pre-wrap;color:#22c55e;font-family:monospace} +.log{background:#0a0f1a;padding:12px;border-radius:8px;margin-top:8px;max-height:250px;overflow-y:auto;font-size:11px;white-space:pre-wrap;color:#22c55e;font-family:monospace} .tab{display:inline-block;padding:8px 16px;margin-right:4px;border-radius:6px 6px 0 0;background:#1e293b;color:#64748b;font-size:13px;cursor:pointer} .tab-on{background:#1e40af;color:#fff}

Yuzu GCA

-
Self-contained MCP Client
+
Self-contained — no Gateway needed
- Exec - Files - SysInfo + Exec + Files + SysInfo
-
Execute Command (Gateway)
- - - +
Shell Command (local)
+ +
diff --git a/packages/client-android/android/app/src/main/java/dev/yuzu/gca/MainActivity.kt b/packages/client-android/android/app/src/main/java/dev/yuzu/gca/MainActivity.kt index 06a3b6c..48b4ebe 100644 --- a/packages/client-android/android/app/src/main/java/dev/yuzu/gca/MainActivity.kt +++ b/packages/client-android/android/app/src/main/java/dev/yuzu/gca/MainActivity.kt @@ -1,8 +1,12 @@ package dev.yuzu.gca import android.os.Bundle +import android.util.Log import android.webkit.WebView import androidx.appcompat.app.AppCompatActivity +import java.io.File +import java.io.BufferedReader +import java.io.InputStreamReader class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { @@ -11,7 +15,44 @@ class MainActivity : AppCompatActivity() { settings.javaScriptEnabled = true settings.domStorageEnabled = true settings.allowUniversalAccessFromFileURLs = true + addJavascriptInterface(LocalBridge(), "Android") loadUrl("file:///android_asset/index.html") }.also { setContentView(it) } } + + inner class LocalBridge { + @android.webkit.JavascriptInterface + fun exec(cmd: String): String { + return try { + val p = Runtime.getRuntime().exec(arrayOf("sh", "-c", cmd)) + val out = BufferedReader(InputStreamReader(p.inputStream)).readText() + val err = BufferedReader(InputStreamReader(p.errorStream)).readText() + p.waitFor() + if (out.isNotEmpty()) out else err + } catch (e: Exception) { "ERR: ${e.message}" } + } + + @android.webkit.JavascriptInterface + fun listFiles(path: String): String { + return try { + val dir = File(path) + if (!dir.exists()) return "ERR: path not found" + if (!dir.isDirectory) return "ERR: not a directory" + dir.listFiles()?.joinToString("\n") { f -> + (if (f.isDirectory) "[DIR] " else "[FILE] ") + f.name + + (if (f.isFile) " (${f.length()}B)" else "") + } ?: "empty" + } catch (e: Exception) { "ERR: ${e.message}" } + } + + @android.webkit.JavascriptInterface + fun sysinfo(): String { + return """ + Device: ${android.os.Build.MODEL} + Android: ${android.os.Build.VERSION.RELEASE} (SDK ${android.os.Build.VERSION.SDK_INT}) + CPU: ${Runtime.getRuntime().availableProcessors()} cores + Memory: ${Runtime.getRuntime().maxMemory() / 1024 / 1024}MB max + """.trimIndent() + } + } } diff --git a/s.png b/s.png new file mode 100644 index 0000000..c3e8d85 Binary files /dev/null and b/s.png differ