Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
517a8ea942 | ||
|
|
4589f4abc3 | ||
|
|
e67659b78a |
+12
-1
@@ -1638,7 +1638,18 @@ META_TOOLS = [
|
||||
"host": {"type": "string", "description": "Host-ID/Name (z.B. 'Fire-TV Schlafzimmer')."},
|
||||
"text": {"type": "string", "description": "Der anzuzeigende Text."},
|
||||
"title": {"type": "string", "description": "Ueberschrift (Default 'ARIA')."},
|
||||
"seconds": {"type": "integer", "description": "Anzeigedauer (Default 12)."},
|
||||
"seconds": {
|
||||
"type": "integer",
|
||||
"description": (
|
||||
"Anzeigedauer in Sekunden (Default 12). "
|
||||
"0 = stehen lassen, bis Stefan selbst wegdrueckt "
|
||||
"(Fernbedienung: beliebige Taste, Handy: daneben "
|
||||
"tippen). Nimm 0 fuer alles zum LESEN — Wetter, "
|
||||
"Listen, Zahlen: eine feste Dauer ist da immer "
|
||||
"geraten und meist zu kurz. Kurze Hinweise waehrend "
|
||||
"eines Films ruhig mit Dauer."
|
||||
),
|
||||
},
|
||||
"mode": {"type": "string", "description": "overlay | speech | both"},
|
||||
"focus": {"type": "string", "description": "Bei Sprachausgabe: 'duck' (laufende Medien werden leiser, Standard) | 'pause' (Film/Musik anhalten) | 'none'."},
|
||||
"image_path": {
|
||||
|
||||
+46
-2
@@ -602,6 +602,24 @@
|
||||
selbst (Skills + skill_create Meta-Tool). Es gibt keine granulare
|
||||
Permission-Maske, Brain weiss zur Laufzeit welche Tools es hat. -->
|
||||
|
||||
<!-- Warnung: Proxy laeuft mit veralteten Patches.
|
||||
Der Proxy kopiert proxy-patches/ beim START in den npm-Code. Wer nach
|
||||
einem git pull nur neu startet statt force-recreate — oder vor dem
|
||||
Pull neu startet — laeuft weiter mit der alten Fassung, ohne dass es
|
||||
irgendwo auffaellt. Das hat schon zweimal Zeit gekostet: einmal wirkte
|
||||
ein Fix nicht, einmal blieb die Modell-Liste alt. -->
|
||||
<div id="proxy-patch-warning" style="display:none;margin-bottom:16px;padding:12px 14px;
|
||||
background:#2A1A08;border:1px solid #FF9F0A;border-radius:8px;">
|
||||
<div style="color:#FF9F0A;font-size:13px;font-weight:600;margin-bottom:6px;">
|
||||
⚠ Der Proxy läuft mit veralteten Patches
|
||||
</div>
|
||||
<div id="proxy-patch-detail" style="color:#C8A878;font-size:11px;line-height:1.6;"></div>
|
||||
<div style="margin-top:8px;">
|
||||
<code style="background:#12121E;padding:4px 8px;border-radius:4px;color:#E0E0F0;font-size:11px;display:inline-block;">
|
||||
docker compose up -d --force-recreate proxy</code>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Brain-Model-Einstellung -->
|
||||
<div class="settings-section">
|
||||
<h2>Sprachmodell (Brain)</h2>
|
||||
@@ -659,8 +677,13 @@
|
||||
<div class="card" style="max-width:620px;">
|
||||
<div style="font-size:11px;color:#8888AA;margin-bottom:12px;line-height:1.55;">
|
||||
Drei Kanäle, über die Claude Code <strong>zusätzlich</strong> zur eigentlichen
|
||||
Anfrage Daten verschickt. Gesetzt wird in <code>~/.claude/settings.json</code> —
|
||||
greift bei jeder neuen Anfrage, <strong>kein Neustart nötig</strong>.
|
||||
Anfrage Daten verschickt.
|
||||
<br><br>
|
||||
<span style="color:#3FFF9F;">✓ Kein Neustart nötig.</span>
|
||||
Gesetzt wird in <code>~/.claude/settings.json</code>, und die Claude-CLI liest
|
||||
die Datei bei <em>jedem</em> Aufruf neu. Speichern genügt — die nächste Anfrage
|
||||
nutzt die neue Einstellung. Nur ein <em>gerade laufender</em> Turn arbeitet noch
|
||||
mit der alten, weil sein Prozess schon gestartet ist.
|
||||
<br><br>
|
||||
Davon unabhängig ist, ob Anthropic mit deinen Unterhaltungen Modelle
|
||||
trainiert — das steht in deinem Konto unter
|
||||
@@ -2748,6 +2771,26 @@
|
||||
return;
|
||||
}
|
||||
|
||||
if (msg.type === 'proxy_patch_state') {
|
||||
const box = document.getElementById('proxy-patch-warning');
|
||||
const det = document.getElementById('proxy-patch-detail');
|
||||
if (box) {
|
||||
const stale = msg.stale || [];
|
||||
box.style.display = stale.length ? 'block' : 'none';
|
||||
if (stale.length && det) {
|
||||
const fmt = (ms) => new Date(ms).toLocaleString('de-DE',
|
||||
{ day: '2-digit', month: '2-digit', hour: '2-digit', minute: '2-digit' });
|
||||
det.innerHTML =
|
||||
'Diese Dateien wurden nach dem letzten Proxy-Start geändert — der Proxy '
|
||||
+ 'arbeitet noch mit der alten Fassung:<br>'
|
||||
+ stale.map(s => '• <code>' + escapeHtml(s.file) + '</code> — geändert '
|
||||
+ fmt(s.srcAt) + ', eingespielt ' + fmt(s.activeAt)).join('<br>')
|
||||
+ '<br><br>Der Proxy kopiert die Patches nur beim <strong>Start</strong>. '
|
||||
+ 'Ein <code>--build</code> reicht nicht:';
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (msg.type === 'privacy_flags') { renderPrivacyFlags(msg); return; }
|
||||
if (msg.type === 'privacy_flags_saved') {
|
||||
const st = document.getElementById('privacy-flags-status');
|
||||
@@ -4754,6 +4797,7 @@
|
||||
refreshVoiceIdStatus();
|
||||
send({ action: 'get_attachment_limit' });
|
||||
send({ action: 'get_privacy_flags' });
|
||||
send({ action: 'check_proxy_patches' });
|
||||
} else if (tab === 'brain') {
|
||||
loadBrainStatus();
|
||||
loadBrainMemoryList();
|
||||
|
||||
@@ -3217,6 +3217,8 @@ wss.on("connection", (ws) => {
|
||||
handleSetModel(ws, msg.model);
|
||||
} else if (msg.action === "test_model") {
|
||||
handleTestModel(ws, msg.model);
|
||||
} else if (msg.action === "check_proxy_patches") {
|
||||
handleCheckProxyPatches(ws);
|
||||
} else if (msg.action === "get_privacy_flags") {
|
||||
handleGetPrivacyFlags(ws);
|
||||
} else if (msg.action === "set_privacy_flags") {
|
||||
@@ -3647,6 +3649,50 @@ function handleSetModel(clientWs, model) {
|
||||
return;
|
||||
}
|
||||
|
||||
// ── Laufen im Proxy die aktuellen Patches? ──────────────────────────
|
||||
//
|
||||
// Der Proxy kopiert proxy-patches/*.js beim START ueber den npm-Code (siehe
|
||||
// docker-compose.yml, command:). Wer nach einem git pull nur `restart` macht —
|
||||
// oder neu startet, BEVOR der Pull durch ist — laeuft still mit der alten
|
||||
// Fassung weiter. Das faellt sonst nirgends auf: der Container ist "Up", die
|
||||
// Oberflaeche sieht normal aus, nur wirkt die Aenderung nicht.
|
||||
//
|
||||
// Darum: Zeitstempel der Quelldateien gegen die eingespielten vergleichen.
|
||||
const PROXY_PATCH_MAP = [
|
||||
["routes.js", "server/routes.js"],
|
||||
["manager.js", "subprocess/manager.js"],
|
||||
["openai-to-cli.js", "adapter/openai-to-cli.js"],
|
||||
["cli-to-openai.js", "adapter/cli-to-openai.js"],
|
||||
];
|
||||
|
||||
async function handleCheckProxyPatches(clientWs) {
|
||||
try {
|
||||
const dist = "/usr/local/lib/node_modules/claude-max-api-proxy/dist";
|
||||
const pairs = PROXY_PATCH_MAP
|
||||
.map(([src, dst]) => `/proxy-patches/${src} ${dist}/${dst}`).join(" ");
|
||||
// %Y = mtime als Unix-Zeit, %n = Dateiname
|
||||
const out = await dockerExec("aria-proxy", `stat -c '%Y %n' ${pairs} 2>/dev/null || true`);
|
||||
const mtime = {};
|
||||
for (const line of String(out || "").split("\n")) {
|
||||
const m = line.trim().match(/^(\d+)\s+(.+)$/);
|
||||
if (m) mtime[m[2]] = parseInt(m[1], 10);
|
||||
}
|
||||
const stale = [];
|
||||
for (const [src, dst] of PROXY_PATCH_MAP) {
|
||||
const a = mtime[`/proxy-patches/${src}`];
|
||||
const b = mtime[`${dist}/${dst}`];
|
||||
if (a == null || b == null) continue;
|
||||
// Toleranz 5s: Quelle und Kopie entstehen beim Start kurz nacheinander.
|
||||
if (a > b + 5) stale.push({ file: src, srcAt: a * 1000, activeAt: b * 1000 });
|
||||
}
|
||||
clientWs.send(JSON.stringify({ type: "proxy_patch_state", stale }));
|
||||
} catch (err) {
|
||||
// Kein Alarm bei einem Pruef-Fehler — sonst steht da eine Warnung, die
|
||||
// gar nichts ueber die Patches aussagt.
|
||||
clientWs.send(JSON.stringify({ type: "proxy_patch_state", stale: [], error: String((err && err.message) || err) }));
|
||||
}
|
||||
}
|
||||
|
||||
// ── Einstellungen: Datenschutz-Schalter von Claude Code ─────────────
|
||||
//
|
||||
// Drei Kanaele, ueber die Claude Code von sich aus Daten an Anthropic bzw. an
|
||||
|
||||
@@ -11,8 +11,8 @@ android {
|
||||
applicationId 'de.hackersoft.ariaagent'
|
||||
minSdk 26
|
||||
targetSdk 33 // 33 vermeidet die Foreground-Service-Typ-Pflicht von 34
|
||||
versionCode 104
|
||||
versionName '0.0.1.4'
|
||||
versionCode 105
|
||||
versionName '0.0.1.5'
|
||||
}
|
||||
|
||||
// Fester Signaturschlüssel: jeder Build signiert mit DEMSELBEN Key, damit
|
||||
|
||||
+23
@@ -44,6 +44,29 @@ class AriaAccessibilityService : AccessibilityService() {
|
||||
override fun onAccessibilityEvent(event: AccessibilityEvent?) { /* passiv */ }
|
||||
override fun onInterrupt() { /* passiv */ }
|
||||
|
||||
/**
|
||||
* Steht eine Einblendung offen, raeumt sie der naechste Tastendruck weg.
|
||||
*
|
||||
* Der Trick: wir geben IMMER `false` zurueck. Damit laeuft die Taste ganz
|
||||
* normal weiter an den Film/die App — die Einblendung schluckt nichts. Sie
|
||||
* muss dafuer auch nicht fokussierbar sein, kann also weiterhin ueber einem
|
||||
* laufenden Video stehen, ohne die Fernbedienung zu blockieren.
|
||||
*
|
||||
* Deshalb dieser Weg und nicht "Overlay faengt Tasten selbst ab": ein
|
||||
* fokussierbares Fenster wuerde der App darunter JEDE Eingabe nehmen,
|
||||
* solange es steht.
|
||||
*
|
||||
* Eine feste Anzeigedauer ist naemlich immer falsch — mal zu kurz zum
|
||||
* Lesen, mal steht sie ewig im Bild.
|
||||
*/
|
||||
override fun onKeyEvent(event: android.view.KeyEvent?): Boolean {
|
||||
if (event != null && event.action == android.view.KeyEvent.ACTION_DOWN
|
||||
&& OverlayView.isShowing()) {
|
||||
OverlayView.hide(applicationContext)
|
||||
}
|
||||
return false // Taste NICHT verbrauchen
|
||||
}
|
||||
|
||||
/** Strukturierter Baum des aktiven Fensters. Shape: {ok, result:{package,count,nodes[]}}. */
|
||||
fun dump(): JSONObject {
|
||||
val root = rootInActiveWindow
|
||||
|
||||
@@ -25,13 +25,33 @@ import android.widget.TextView
|
||||
*
|
||||
* Nicht fokussierbar (FLAG_NOT_FOCUSABLE) — die Fernbedienung steuert weiter die
|
||||
* App darunter, die Einblendung schluckt keine Tastendruecke.
|
||||
*
|
||||
* WEGBEKOMMEN ohne feste Anzeigedauer (seconds = 0):
|
||||
* - Fernbedienung: der Accessibility-Dienst sieht den naechsten Tastendruck und
|
||||
* raeumt die Einblendung weg — er gibt die Taste trotzdem an den Film weiter
|
||||
* (siehe AriaAccessibilityService.onKeyEvent).
|
||||
* - Touch: FLAG_WATCH_OUTSIDE_TOUCH meldet uns eine Beruehrung AUSSERHALB der
|
||||
* Einblendung. Die Beruehrung geht gleichzeitig an die App darunter — es
|
||||
* geht also kein Tipp verloren.
|
||||
* - Notbremse: nach HARD_CAP_SEC verschwindet sie in jedem Fall. Ohne das
|
||||
* koennte eine vergessene Einblendung dauerhaft im Bild stehen.
|
||||
*
|
||||
* Eine feste Dauer ist naemlich immer falsch geraten — mal zu kurz zum Lesen,
|
||||
* mal steht sie laengst im Weg.
|
||||
*/
|
||||
object OverlayView {
|
||||
|
||||
/** Auch eine "bleibt stehen"-Einblendung geht spaetestens danach weg. */
|
||||
private const val HARD_CAP_SEC = 600
|
||||
|
||||
private val main = Handler(Looper.getMainLooper())
|
||||
private var root: LinearLayout? = null
|
||||
private var hideRunnable: Runnable? = null
|
||||
|
||||
/** Steht gerade eine Einblendung? Der Accessibility-Dienst fragt das, bevor
|
||||
* er auf einen Tastendruck hin aufraeumt. */
|
||||
fun isShowing(): Boolean = root != null
|
||||
|
||||
/** Hat der Nutzer "Ueber anderen Apps anzeigen" erlaubt? */
|
||||
fun canDraw(ctx: Context): Boolean =
|
||||
if (Build.VERSION.SDK_INT >= 23) Settings.canDrawOverlays(ctx) else true
|
||||
@@ -98,6 +118,10 @@ object OverlayView {
|
||||
type,
|
||||
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or
|
||||
WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE or
|
||||
// Meldet uns Beruehrungen AUSSERHALB, ohne sie zu
|
||||
// schlucken — damit raeumt ein Tipp aufs Video die
|
||||
// Einblendung weg und steuert trotzdem den Player.
|
||||
WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH or
|
||||
WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN,
|
||||
android.graphics.PixelFormat.TRANSLUCENT,
|
||||
).apply {
|
||||
@@ -107,13 +131,23 @@ object OverlayView {
|
||||
width = (ctx.resources.displayMetrics.widthPixels * 0.42f).toInt()
|
||||
}
|
||||
|
||||
// Beruehrung ausserhalb -> wegraeumen. Die Beruehrung selbst
|
||||
// geht dank FLAG_NOT_TOUCHABLE weiter an die App darunter.
|
||||
box.setOnTouchListener { _, ev ->
|
||||
if (ev.action == android.view.MotionEvent.ACTION_OUTSIDE) hideNow(ctx)
|
||||
false
|
||||
}
|
||||
|
||||
wm.addView(box, lp)
|
||||
root = box
|
||||
|
||||
if (seconds > 0) {
|
||||
hideRunnable = Runnable { hideNow(ctx) }
|
||||
main.postDelayed(hideRunnable!!, seconds * 1000L)
|
||||
}
|
||||
// seconds > 0 : feste Dauer wie gehabt.
|
||||
// seconds == 0: bleibt stehen, bis der Nutzer etwas tut
|
||||
// (Taste via Accessibility-Dienst / Tipp daneben)
|
||||
// — mit Notbremse, damit sie nie haengen bleibt.
|
||||
val cap = if (seconds > 0) seconds else HARD_CAP_SEC
|
||||
hideRunnable = Runnable { hideNow(ctx) }
|
||||
main.postDelayed(hideRunnable!!, cap * 1000L)
|
||||
} catch (_: Exception) {
|
||||
root = null
|
||||
}
|
||||
|
||||
@@ -2,8 +2,9 @@
|
||||
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:accessibilityEventTypes="typeWindowStateChanged|typeWindowContentChanged"
|
||||
android:accessibilityFeedbackType="feedbackGeneric"
|
||||
android:accessibilityFlags="flagRetrieveInteractiveWindows|flagReportViewIds"
|
||||
android:accessibilityFlags="flagRetrieveInteractiveWindows|flagReportViewIds|flagRequestFilterKeyEvents"
|
||||
android:canRetrieveWindowContent="true"
|
||||
android:canPerformGestures="true"
|
||||
android:canRequestFilterKeyEvents="true"
|
||||
android:notificationTimeout="100"
|
||||
android:description="@string/accessibility_desc" />
|
||||
|
||||
@@ -162,7 +162,7 @@ OUT_MAX_CHARS_HARD = int(os.environ.get("OUT_MAX_CHARS_HARD", "200000") or "2000
|
||||
FILE_MAX_BYTES = int(os.environ.get("FILE_MAX_BYTES", str(10 * 1024 * 1024)) or str(10 * 1024 * 1024))
|
||||
|
||||
# Version (wird von release_agent.sh beim Release gesetzt).
|
||||
AGENT_VERSION = "0.0.1.4"
|
||||
AGENT_VERSION = "0.0.1.5"
|
||||
|
||||
HEARTBEAT_SEC = 25
|
||||
|
||||
|
||||
Reference in New Issue
Block a user