VoiceWake: document escape path and reset stale forward command

This commit is contained in:
Peter Steinberger
2025-12-07 18:20:40 +01:00
parent 2a45455c80
commit 7efa152418
6 changed files with 81 additions and 16 deletions

View File

@@ -157,6 +157,7 @@ final class AppState: ObservableObject {
var storedForwardCommand = UserDefaults.standard
.string(forKey: voiceWakeForwardCommandKey) ?? defaultVoiceWakeForwardCommand
// Guard against older prefs missing flags; the forwarder depends on these for replies.
if !storedForwardCommand.contains("--deliver") || !storedForwardCommand.contains("--session") {
storedForwardCommand = defaultVoiceWakeForwardCommand
UserDefaults.standard.set(storedForwardCommand, forKey: voiceWakeForwardCommandKey)

View File

@@ -108,21 +108,23 @@ final class HealthStore: ObservableObject {
env: env,
timeout: 15)
guard response.ok, let data = response.payload, !data.isEmpty else {
self.lastError = response.message ?? "health probe failed"
if onDemand { self.snapshot = nil }
// Always try to decode JSON even when the CLI exits non-zero; it prints the
// failure snapshot before exiting so we can surface a useful message.
if let data = response.payload, !data.isEmpty,
let decoded = try? JSONDecoder().decode(HealthSnapshot.self, from: data)
{
self.snapshot = decoded
if response.ok {
self.lastSuccess = Date()
self.lastError = nil
} else {
self.lastError = self.describeFailure(from: decoded)
}
return
}
do {
let decoded = try JSONDecoder().decode(HealthSnapshot.self, from: data)
self.snapshot = decoded
self.lastSuccess = Date()
self.lastError = nil
} catch {
self.lastError = error.localizedDescription
if onDemand { self.snapshot = nil }
}
self.lastError = response.message ?? "health probe failed"
if onDemand { self.snapshot = nil }
}
var state: HealthState {
@@ -147,6 +149,22 @@ final class HealthStore: ObservableObject {
}
return "linked · auth \(auth) · socket ok"
}
private func describeFailure(from snap: HealthSnapshot) -> String {
if !snap.web.linked {
return "Not linked — run clawdis login"
}
if let connect = snap.web.connect, !connect.ok {
let code = connect.status.map { "status \($0)" } ?? "status unknown"
let elapsed = connect.elapsedMs.map { "\(Int($0))ms" } ?? "unknown duration"
let reason = connect.error ?? "connect failed"
return "\(reason) (\(code), \(elapsed))"
}
if !snap.ipc.exists {
return "IPC socket missing at \(snap.ipc.path)"
}
return "health probe failed"
}
}
func msToAge(_ ms: Double) -> String {

View File

@@ -1,3 +1,4 @@
onlyBuiltDependencies:
- '@whiskeysockets/baileys'
- esbuild
- sharp

View File

@@ -3,7 +3,7 @@ set -euo pipefail
APP_BUNDLE="${1:-dist/Clawdis.app}"
IDENTITY="${SIGN_IDENTITY:-}"
ENT_TMP=$(mktemp /tmp/clawdis-entitlements.XXXXXX.plist)
ENT_TMP=$(mktemp -t clawdis-entitlements)
if [ ! -d "$APP_BUNDLE" ]; then
echo "App bundle not found: $APP_BUNDLE" >&2
@@ -51,6 +51,8 @@ cat > "$ENT_TMP" <<'PLIST'
<dict>
<key>com.apple.security.hardened-runtime</key>
<true/>
<key>com.apple.security.cs.allow-jit</key>
<true/>
<key>com.apple.security.automation.apple-events</key>
<true/>
<key>com.apple.security.device.audio-input</key>
@@ -75,6 +77,13 @@ if [ -f "$APP_BUNDLE/Contents/MacOS/ClawdisCLI" ]; then
echo "Signing CLI helper"; sign_item "$APP_BUNDLE/Contents/MacOS/ClawdisCLI"
fi
# Sign bundled relay runtime bits (bun, native addons, libvips dylibs)
if [ -d "$APP_BUNDLE/Contents/Resources/Relay" ]; then
find "$APP_BUNDLE/Contents/Resources/Relay" -type f \( -name "bun" -o -name "*.node" -o -name "*.dylib" \) -print0 | while IFS= read -r -d '' f; do
echo "Signing relay payload: $f"; sign_item "$f"
done
fi
# Sign any embedded frameworks/dylibs if they ever appear
if [ -d "$APP_BUNDLE/Contents/Frameworks" ]; then
find "$APP_BUNDLE/Contents/Frameworks" \( -name "*.framework" -o -name "*.dylib" \) -print0 | while IFS= read -r -d '' f; do

View File

@@ -15,6 +15,9 @@ GIT_COMMIT=$(cd "$ROOT_DIR" && git rev-parse --short HEAD 2>/dev/null || echo "u
APP_VERSION="${APP_VERSION:-$PKG_VERSION}"
APP_BUILD="${APP_BUILD:-$PKG_VERSION}"
echo "📦 Building JS (pnpm build)"
(cd "$ROOT_DIR" && pnpm build)
cd "$ROOT_DIR/apps/macos"
echo "🔨 Building $PRODUCT (debug)"
@@ -26,6 +29,7 @@ echo "🧹 Cleaning old app bundle"
rm -rf "$APP_ROOT"
mkdir -p "$APP_ROOT/Contents/MacOS"
mkdir -p "$APP_ROOT/Contents/Resources"
mkdir -p "$APP_ROOT/Contents/Resources/Relay"
echo "📄 Writing Info.plist"
cat > "$APP_ROOT/Contents/Info.plist" <<PLIST
@@ -79,6 +83,36 @@ cp "$ROOT_DIR/apps/macos/Sources/Clawdis/Resources/Clawdis.icns" "$APP_ROOT/Cont
echo "📦 Copying WebChat resources"
rsync -a "$ROOT_DIR/apps/macos/Sources/Clawdis/Resources/WebChat" "$APP_ROOT/Contents/Resources/"
RELAY_DIR="$APP_ROOT/Contents/Resources/Relay"
BUN_SRC="${BUN_PATH:-$(command -v bun || true)}"
if [ -z "$BUN_SRC" ] || [ ! -x "$BUN_SRC" ]; then
echo "bun binary not found (set BUN_PATH to override)" >&2
exit 1
fi
echo "🧰 Staging relay runtime (bun + dist + node_modules)"
cp "$BUN_SRC" "$RELAY_DIR/bun"
chmod +x "$RELAY_DIR/bun"
rsync -a --delete --exclude "Clawdis.app" "$ROOT_DIR/dist/" "$RELAY_DIR/dist/"
cp "$ROOT_DIR/package.json" "$RELAY_DIR/"
cp "$ROOT_DIR/pnpm-lock.yaml" "$RELAY_DIR/"
if [ -f "$ROOT_DIR/.npmrc" ]; then
cp "$ROOT_DIR/.npmrc" "$RELAY_DIR/"
fi
echo "📦 Installing prod node_modules into bundle (hoisted, scripts enabled for sharp)"
PNPM_STORE_DIR="$RELAY_DIR/.pnpm-store" \
PNPM_HOME="$HOME/Library/pnpm" \
pnpm install \
--prod \
--frozen-lockfile \
--config.node-linker=hoisted \
--config.ignore-workspace-root-check=true \
--config.shared-workspace-lockfile=false \
--modules-dir "$RELAY_DIR/node_modules" \
--lockfile-dir "$ROOT_DIR" \
--dir "$RELAY_DIR"
if [ -f "$CLI_BIN" ]; then
echo "🔧 Copying CLI helper"
cp "$CLI_BIN" "$APP_ROOT/Contents/MacOS/ClawdisCLI"

View File

@@ -17,7 +17,7 @@ import {
setHeartbeatsEnabled,
type WebMonitorTuning,
} from "../provider-web.js";
import { defaultRuntime } from "../runtime.js";
import { defaultRuntime, type RuntimeEnv } from "../runtime.js";
import { VERSION } from "../version.js";
import {
resolveHeartbeatSeconds,
@@ -252,10 +252,12 @@ Examples:
}
const logs: string[] = [];
const runtime = {
const runtime: RuntimeEnv = {
log: (msg: string) => logs.push(String(msg)),
error: (msg: string) => logs.push(String(msg)),
exit: (_code: number) => {},
exit: (_code: number): never => {
throw new Error("agentCommand requested exit");
},
};
const opts: {