Skill: kkzz1225/senseaudio-floating-audio-assistant (version 1.0.0)

--- name: senseaudio-floating-audio-assistant description: Use when the user wants to open, stop, configure, debug, or package the SenseAudio floating audio assistant in AudioClaw, including system-audio subtitles, bilingual ASR/translation, recent-project organization, copied-text TTS, music generation, or macOS audio routing. metadata: {"openclaw":{"emoji":"🎧","primaryEnv":"SENSEAUDIO_API_KEY","requires":{"bins":["bash","python3","swiftc","SwitchAudioSource"],"env":["SENSEAUDIO_API_KEY","AUDIOCLAW_ASR_API_KEY"]},"install":[{"kind":"brew","formula":"switchaudio-osx","bins":["SwitchAudioSource"]}],"os":["macos"],"skillKey":[REDACTED_SECRET]}} --- # SenseAudio 浮窗音频助手 使用这个 skill 来启动和维护 SenseAudio 浮窗音频助手。 高优先级触发说法： - 打开实时字幕 - 开启系统音频字幕 - 打开双语同传 - 启动字幕浮窗 - 打开听视频字幕助手 - 打开浮窗音频助手 - 打开最近项目 - 打开音乐工坊 - 用 `$senseaudio-floating-audio-assistant` ## Default Behavior 默认启动 macOS 原生悬浮窗： - 采集系统播放音频，不依赖麦克风 - 自动进入字幕音频路由模式，并在停止时恢复原音频输出和音量 - 使用本地 `sherpa-onnx` 输出低延迟快速字幕 - 使用 SenseAudio 输出更准确的最终 ASR - 可在 ASR 面板选择快速识别语言和 SenseAudio 目标翻译语言 - 可显示并区分“快速 ASR / SenseAudio ASR / SenseAudio 翻译” - 保存最近项目，支持重命名、查看原文、多模板整理、自定义模板编辑和模板文件导入 - 复制文本后支持 SenseAudio TTS 朗读和音色选择 - 音乐工坊使用 SenseAudio 音乐生成接口，保留生成历史并支持播放/暂停/重命名 ## Main Commands 启动浮窗： ```bash bash "{baseDir}/scripts/start-senseaudio-floating-audio-assistant.sh" ``` 停止浮窗并恢复系统音频： ```bash bash "{baseDir}/scripts/stop-senseaudio-floating-audio-assistant.sh" ``` 查看运行状态： ```bash bash "{baseDir}/scripts/status-senseaudio-floating-audio-assistant.sh" ``` 检查系统音频链路和基础依赖： ```bash bash "{baseDir}/scripts/check-senseaudio-floating-audio-assistant-setup.sh" ``` 打开最近项目/历史记录目录： ```bash bash "{baseDir}/scripts/open-senseaudio-floating-audio-assistant-runs.sh" ``` 打开 Audio MIDI Setup： ```bash bash "{baseDir}/scripts/open_audio_midi_setup.sh" ``` 运行自检： ```bash bash "{baseDir}/scripts/doctor-senseaudio-floating-audio-assistant.sh" python3 "{baseDir}/scripts/senseaudio_api_smoke.py" ``` 如果需要真实调用 SenseAudio 做一条极小 TTS 探针，再运行： ```bash python3 "{baseDir}/scripts/senseaudio_api_smoke.py" --live-tts ``` ## Action Flow 如果用户说“打开实时字幕”“开启双语同传”“启动浮窗字幕”“打开音乐工坊”“查看最近项目”，不要只返回命令，也不要假装状态已经切换。优先直接执行启动脚本： ```bash bash "{baseDir}/scripts/start-senseaudio-floating-audio-assistant.sh" ``` 如果用户说“关掉字幕”“停止实时字幕”“恢复电脑声音”，优先执行： ```bash bash "{baseDir}/scripts/stop-senseaudio-floating-audio-assistant.sh" ``` ## Requirements 这个 skill 伴随 AudioClaw 使用，默认当前环境已经有可调用的 `audioclaw agent`。实时字幕、翻译、TTS 和音乐生成使用 SenseAudio；最近项目的“整理/关键信息提取/模板处理”必须交给 AudioClaw agent，不要用本地假结果兜底。 必需设备和系统依赖： - macOS 桌面环境，原生浮窗由 Swift/AppKit 实现。 - `BlackHole 2ch`：作为系统输出音频的虚拟采集设备。 - `Multi-Output Device`：在 Audio MIDI Setup 中创建，包含真实扬声器/耳机和 `BlackHole 2ch`。 - `SwitchAudioSource`：来自 `switchaudio-osx`，用于启动时切换到多输出设备、停止时恢复原输出。 - `swiftc`：来自 Xcode Command Line Tools，用于编译原生浮窗。 - `python3` 和 `bash`：用于实时 ASR runner、诊断脚本和包装脚本。 - 可访问 SenseAudio API 的网络环境。 常用安装和打开方式： ```bash xcode-select --install brew install switchaudio-osx bash "{baseDir}/scripts/open_audio_midi_setup.sh" ``` 如果没有 Homebrew，手动安装 `switchaudio-osx` 后确保 `SwitchAudioSource` 在 `PATH` 中即可。 ## Configuration 凭据由当前 AudioClaw workspace 或运行环境提供，上传包内不携带任何本地凭据、密钥值或环境变量样板。 运行时需要可用的 SenseAudio API key；`SENSEAUDIO_API_KEY` 是首选名称，`AUDIOCLAW_ASR_API_KEY` 作为兼容名称保留。不要把 API key 打印到聊天里；检查脚本只报告是否存在。 音频路由配置： - 在 Audio MIDI Setup 里创建 `Multi-Output Device`。 - 设备名可以是系统默认的 `Multi-Output Device` 或中文 `多输出设备`，启动脚本会优先查找这两个名称。 - 勾选当前实际听音设备，例如耳机、显示器音频或扬声器。 - 同时勾选 `BlackHole 2ch`。 - 启动脚本会尝试切换到多输出设备；停止脚本会恢复原输出和音量。 外部参数和运行态输入： - 快速 ASR 的源语言。 - SenseAudio ASR/翻译的目标语言。 - 复制文本朗读的 TTS 音色。 - 音乐工坊的提示词、风格参数、历史歌曲名称和播放状态。 - 最近项目整理模板，可选择预设模板、编辑自定义模板或导入模板文件。 - AudioClaw agent 接收所选模板和保存的 SenseAudio ASR 原文，返回整理结果；skill 本身不保存额外 agent 密钥。 ## Health Check 为了便于维护和排障，这个 skill 内置了轻量诊断入口和可复用预设： - `scripts/senseaudio_api_smoke.py`: 输出当前 SenseAudio API 配置摘要，包含 ASR WebSocket、TTS 和音乐生成入口；默认不消耗额度，`--live-tts` 才真实调用。 - `scripts/doctor-senseaudio-floating-audio-assistant.sh`: 本地健康检查入口，用来确认 manifest、启动脚本、诊断 JSON 和预设文件仍然可用。 - `presets/`: 整理模板和音乐生成参数样板，不包含真实密钥或环境变量内容。 - `references/quickstart.md`: 精简启动流程。 - `references/operator_notes.md`: 面向使用者的安装、配置和启动摘要。 - `references/senseaudio_integration.md`: SenseAudio 接入说明。 - `references/troubleshooting.md`: 常见失败路径。 ## Troubleshooting 没有字幕时，按顺序检查： 1. 运行 `check-senseaudio-floating-audio-assistant-setup.sh` 2. 确认 macOS 当前输出是 `多输出设备` / `Multi-Output Device` 3. 播放一段真实音频后重新开始 ASR 4. 如果 SenseAudio 连接失败，先确认 AudioClaw 运行环境是否注入了 SenseAudio 凭据，再看实时并发配额 5. 如果停止后电脑没声音，运行停止脚本恢复音频路由 “整理”功能依赖 `audioclaw agent` 的文本模型配置。若 AudioClaw 后端模型不返回，实时字幕、ASR、翻译、TTS 和音乐工坊仍可用，但整理会等待真实 AudioClaw 结果；不要伪造本地兜底结果。

path: references/troubleshooting.md
content: # Troubleshooting

If the overlay does not start:

- Run `bash "{baseDir}/scripts/check-senseaudio-floating-audio-assistant-setup.sh"`.
- Confirm `swiftc`, `python3`, and a SenseAudio key are available.
- Confirm `SwitchAudioSource` is available if using system-audio capture.

If there is no subtitle output:

- Confirm macOS output is a Multi-Output Device containing BlackHole 2ch.
- Play real system audio after starting ASR.
- If local fast captions appear but SenseAudio does not, check realtime quota and API key validity.

If computer audio changes after use:

- Run `bash "{baseDir}/scripts/stop-senseaudio-floating-audio-assistant.sh"`.
- The stop script restores the previous output device and volume from workspace state.

If AudioClaw organization waits forever:

- Realtime ASR/TTS/music may still work.
- Organization depends on the configured `audioclaw agent` text model returning a real result.

If the device chain is not configured:

- Open Audio MIDI Setup with `bash "{baseDir}/scripts/open_audio_midi_setup.sh"`.
- Create a Multi-Output Device.
- Select both the real output device and `BlackHole 2ch`.
- Re-run the check script before starting ASR again.


path: references/senseaudio_integration.md
content: # SenseAudio Integration

This skill is a thin product wrapper over the realtime interpreter toolchain. The skill directory also includes `scripts/senseaudio_api_smoke.py` for quick local diagnostics.

Diagnostic commands:

```bash
python3 "{baseDir}/scripts/senseaudio_api_smoke.py"
python3 "{baseDir}/scripts/senseaudio_api_smoke.py" --live-tts
```

The smoke script reports the API surfaces used by the project:

- ASR WebSocket: `wss://api.senseaudio.cn/ws/v1/audio/transcriptions`
- TTS HTTP/SSE: `https://api.senseaudio.cn/v1/t2a_v2`
- Music generation: `https://api.senseaudio.cn/v1/music/song/create`

Runtime capabilities:

- Realtime ASR and optional translation are handled by the long-lived websocket runner.
- Copied-text reading uses SenseAudio TTS.
- Music workshop uses SenseAudio music generation and stores generated track metadata.
- Recent-project organization uses `audioclaw agent` with the selected template and the saved SenseAudio ASR text.

Runtime configuration inputs:

- SenseAudio credential: provided by the AudioClaw runtime environment; no credential file or key value is bundled in this upload package.
- ASR UI: fast local ASR source language and SenseAudio translation target language.
- TTS UI: copied text and selected voice.
- Music UI: prompt, style controls, generation history, track rename, play/pause.
- Organization UI: selected built-in or imported template plus the saved project transcript.

Safety:

- The smoke script reports whether an API key exists, but never prints the key.
- `--live-tts` is optional because it consumes API quota.


path: references/quickstart.md
content: # Quickstart

Use this skill when the user wants the SenseAudio floating audio assistant, system-audio ASR, bilingual captions, copied-text TTS, recent projects, or music workshop.

Primary commands:

```bash
bash "{baseDir}/scripts/start-senseaudio-floating-audio-assistant.sh"
bash "{baseDir}/scripts/stop-senseaudio-floating-audio-assistant.sh"
bash "{baseDir}/scripts/status-senseaudio-floating-audio-assistant.sh"
bash "{baseDir}/scripts/doctor-senseaudio-floating-audio-assistant.sh"
```

Required setup:

- Provide a SenseAudio API key through the AudioClaw runtime environment before launching.
- Install `BlackHole 2ch`, then create a Multi-Output Device that includes the physical output device and `BlackHole 2ch`.
- Name the route `Multi-Output Device` or `多输出设备`; the launcher searches those names before starting capture.
- Make sure `SwitchAudioSource`, `swiftc`, `python3`, and `bash` are available.
- Keep `audioclaw agent` configured for recent-project organization; do not fake organization output if the agent is unavailable.

Expected runtime flow:

1. The start script resolves the workspace relative to the skill directory.
2. It launches the existing realtime interpreter toolchain.
3. The toolchain switches macOS output to a Multi-Output Device containing BlackHole 2ch.
4. The native overlay captures system audio, streams ASR to SenseAudio, and writes project runs under workspace state.
5. The stop script restores the previous macOS audio route and volume.


path: references/operator_notes.md
content: # Operator Notes

This skill opens and manages the AudioClaw SenseAudio floating audio assistant on macOS. It captures system output audio through BlackHole, displays fast local ASR plus SenseAudio ASR/translation, and keeps recent projects for AudioClaw agent organization.

## Requirements

- macOS desktop with Xcode Command Line Tools (`swiftc`).
- `python3`, `bash`, and `SwitchAudioSource` from `switchaudio-osx`.
- `BlackHole 2ch`.
- A Multi-Output Device named `Multi-Output Device` or `多输出设备`, containing both the real speaker/headphones and `BlackHole 2ch`.
- AudioClaw runtime with `audioclaw agent` available for organizing recent-project transcripts.
- SenseAudio API key supplied by the AudioClaw runtime environment.

## Configure

Install system audio dependencies, create the Multi-Output Device, and provide the SenseAudio credential through AudioClaw's normal runtime configuration. This upload package intentionally does not include credential files, key values, or environment-variable samples.

Open Audio MIDI Setup when the device route needs to be created or repaired:

```bash
bash "{baseDir}/scripts/open_audio_midi_setup.sh"
```

## Use

```bash
bash "{baseDir}/scripts/start-senseaudio-floating-audio-assistant.sh"
bash "{baseDir}/scripts/status-senseaudio-floating-audio-assistant.sh"
bash "{baseDir}/scripts/stop-senseaudio-floating-audio-assistant.sh"
```

Run diagnostics without consuming SenseAudio quota:

```bash
bash "{baseDir}/scripts/doctor-senseaudio-floating-audio-assistant.sh"
python3 "{baseDir}/scripts/senseaudio_api_smoke.py"
```

Use `--live-tts` only when a small real SenseAudio TTS call is acceptable.


path: agents/openai.yaml
content: interface:
  display_name: "SenseAudio Floating Audio Assistant"
  short_description: "Open and manage the AudioClaw SenseAudio floating audio assistant, including subtitles, translation, TTS, music, and recent-project templates"
  default_prompt: "Use $senseaudio-floating-audio-assistant when the user wants to open, stop, configure, debug, or package the floating audio assistant. Prefer the named wrapper scripts, check BlackHole/Multi-Output routing when system audio is silent, use AudioClaw agent for organization, and never print SenseAudio API keys."


path: README.md
content: # SenseAudio Floating Audio Assistant

SenseAudio Floating Audio Assistant is an AudioClaw skill for desktop audio understanding. It combines SenseAudio ASR, translation, TTS, and music generation with AudioClaw agent organization so users can capture system audio, view floating subtitles, preserve recent transcripts, and turn captured speech into structured notes.

## What It Provides

- System-audio subtitle overlay for videos, meetings, classes, and courseware.
- Fast local captions plus SenseAudio final ASR and optional translation.
- Recent-project transcript management with AudioClaw agent organization templates.
- Copied-text reading through SenseAudio TTS.
- Music workshop presets for SenseAudio music generation.
- Health-check scripts for launchers, references, presets, and SenseAudio API surface discovery.

## Runtime Requirements

- macOS desktop environment for the native floating window.
- BlackHole 2ch and a Multi-Output Device that includes the real speaker or headphones.
- Xcode Command Line Tools for Swift compilation.
- Python 3 and Bash for wrapper scripts and diagnostics.
- SwitchAudioSource from switchaudio-osx for audio route switching.
- AudioClaw agent for transcript organization.
- SenseAudio credentials supplied by the host AudioClaw runtime.

## Main Commands

```bash
bash "{baseDir}/scripts/start-senseaudio-floating-audio-assistant.sh"
bash "{baseDir}/scripts/status-senseaudio-floating-audio-assistant.sh"
bash "{baseDir}/scripts/stop-senseaudio-floating-audio-assistant.sh"
```

Run a local health check:

```bash
bash "{baseDir}/scripts/doctor-senseaudio-floating-audio-assistant.sh"
```

Inspect SenseAudio API wiring without making a live request:

```bash
python3 "{baseDir}/scripts/senseaudio_api_smoke.py"
```

## Notes

This upload package intentionally does not include local credentials, local machine paths, credential files, or environment-variable assignment examples. Configure credentials through the host AudioClaw runtime before launching SenseAudio-backed features.


path: scripts/common.sh
content: #!/bin/bash
set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
SKILL_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
WORKSPACE_DIR="$(cd "$SKILL_DIR/../.." && pwd)"
TOOL_DIR="$WORKSPACE_DIR/tools/realtime_interpreter"
STATE_DIR="$WORKSPACE_DIR/state/realtime_interpreter"


path: scripts/check-senseaudio-floating-audio-assistant-setup.sh
content: #!/bin/bash
set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck source=common.sh
source "$SCRIPT_DIR/common.sh"
CHECK_SCRIPT="$TOOL_DIR/check_system_audio_setup.sh"
ENV_FILE="$WORKSPACE_DIR/.env"

echo "Checking SenseAudio floating audio assistant setup..."
echo

for bin in bash python3 swiftc; do
  if command -v "$bin" >/dev/null 2>&1; then
    echo "$bin: ok ($(command -v "$bin"))"
  else
    echo "$bin: missing"
  fi
done

if command -v SwitchAudioSource >/dev/null 2>&1; then
  echo "SwitchAudioSource: ok ($(command -v SwitchAudioSource))"
else
  echo "SwitchAudioSource: missing (install switchaudio-osx or put SwitchAudioSource in PATH)"
fi

if command -v audioclaw >/dev/null 2>&1; then
  echo "AudioClaw agent: CLI available ($(command -v audioclaw))"
else
  echo "AudioClaw agent: expected from host AudioClaw runtime; CLI is not on PATH"
fi

echo
if [ -f "$ENV_FILE" ]; then
  if grep -Eq '^(SENSEAUDIO_API_KEY|AUDIOCLAW_ASR_API_KEY)=' "$ENV_FILE"; then
    echo "SenseAudio key: configured in $ENV_FILE"
  else
    echo "SenseAudio key: missing in $ENV_FILE"
  fi
else
  echo "SenseAudio key: $ENV_FILE not found"
fi

echo
if [ -f "$CHECK_SCRIPT" ]; then
  bash "$CHECK_SCRIPT"
else
  echo "Audio setup checker not found: $CHECK_SCRIPT" >&2
  exit 1
fi


path: scripts/doctor-senseaudio-floating-audio-assistant.sh
content: #!/bin/bash
set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
SKILL_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"

python3 - <<'PY' "$SKILL_DIR/SKILL.md"
from pathlib import Path
import sys

path = Path(sys.argv[1])
text = path.read_text(encoding="utf-8")
assert text.startswith("---\n"), "SKILL.md must start with frontmatter"
assert "\n---\n" in text[4:], "SKILL.md frontmatter must close"
for needle in ["name: senseaudio-floating-audio-assistant", "description:", "metadata:"]:
    assert needle in text, f"missing {needle}"
for needle in [
    "start-senseaudio-floating-audio-assistant.sh",
    "stop-senseaudio-floating-audio-assistant.sh",
    "check-senseaudio-floating-audio-assistant-setup.sh",
    "AudioClaw agent",
    "BlackHole 2ch",
    "Multi-Output Device",
    "SenseAudio API key",
]:
    assert needle in text, f"missing launch contract item: {needle}"
print("manifest ok")
PY

for required in \
  start-senseaudio-floating-audio-assistant.sh \
  stop-senseaudio-floating-audio-assistant.sh \
  status-senseaudio-floating-audio-assistant.sh \
  check-senseaudio-floating-audio-assistant-setup.sh \
  open-senseaudio-floating-audio-assistant-runs.sh; do
  test -f "$SKILL_DIR/scripts/$required"
done
echo "launcher wrappers ok"

if find "$SKILL_DIR/scripts" -maxdepth 1 -type f -name '*realtime_subtitle*' | grep -q .; then
  echo "legacy realtime_subtitle wrapper names remain under scripts/" >&2
  exit 1
fi
echo "launcher namespace ok"

for script in "$SKILL_DIR"/scripts/*.sh; do
  bash -n "$script"
done
echo "shell entrypoints ok"

python3 -m py_compile "$SKILL_DIR/scripts/senseaudio_api_smoke.py"
diagnostics_json="$(mktemp)"
python3 "$SKILL_DIR/scripts/senseaudio_api_smoke.py" >"$diagnostics_json"
python3 -m json.tool "$diagnostics_json" >/dev/null
rm -f "$diagnostics_json"
find "$SKILL_DIR" -name '__pycache__' -type d -prune -exec rm -rf {} +
echo "SenseAudio probe ok"

test -f "$SKILL_DIR/README.md"
test -f "$SKILL_DIR/references/quickstart.md"
test -f "$SKILL_DIR/references/operator_notes.md"
test -f "$SKILL_DIR/references/senseaudio_integration.md"
test -f "$SKILL_DIR/references/troubleshooting.md"
echo "operator notes ok"

test -f "$SKILL_DIR/presets/meeting-digest-template.md"
test -f "$SKILL_DIR/presets/lofi-study-music.json"
home_path_marker="/$(printf 'Users')/"
workspace_marker="~$(printf '.')audioclaw"
env_assignment_marker="[A-Z][A-Z0-9_]{2,}="
if grep -R -E "sk-[A-Za-z0-9]{20,}|${home_path_marker}|${workspace_marker}|${env_assignment_marker}" "$SKILL_DIR/presets" "$SKILL_DIR/references" "$SKILL_DIR/SKILL.md" >/dev/null 2>&1; then
  echo "upload package contains a local path, key-like token, or environment assignment" >&2
  exit 1
fi
echo "presets ok"


path: scripts/open_audio_midi_setup.sh
content: #!/bin/bash
set -euo pipefail

open -a "Audio MIDI Setup"
echo "Opened Audio MIDI Setup."


path: scripts/stop-senseaudio-floating-audio-assistant.sh
content: #!/bin/bash
set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck source=common.sh
source "$SCRIPT_DIR/common.sh"
STOP_SCRIPT="$TOOL_DIR/stop_system_subtitle_overlay.sh"

if [ ! -f "$STOP_SCRIPT" ]; then
  echo "SenseAudio floating audio assistant stop script not found: $STOP_SCRIPT" >&2
  exit 1
fi

exec bash "$STOP_SCRIPT" "$@"


path: scripts/start-senseaudio-floating-audio-assistant.sh
content: #!/bin/bash
set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck source=common.sh
source "$SCRIPT_DIR/common.sh"
START_SCRIPT="$TOOL_DIR/start_system_subtitle_overlay.sh"
ENV_FILE="$WORKSPACE_DIR/.env"

if [ ! -f "$START_SCRIPT" ]; then
  echo "SenseAudio floating audio assistant launcher not found: $START_SCRIPT" >&2
  exit 1
fi

if ! command -v swiftc >/dev/null 2>&1; then
  echo "swiftc not found. Install Xcode Command Line Tools before launching the native overlay." >&2
  exit 1
fi

if [ ! -f "$ENV_FILE" ]; then
  echo "Warning: $ENV_FILE is missing. SenseAudio ASR/TTS/music features may not work until SENSEAUDIO_API_KEY is configured." >&2
fi

exec bash "$START_SCRIPT" "$@"


path: scripts/status-senseaudio-floating-audio-assistant.sh
content: #!/bin/bash
set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck source=common.sh
source "$SCRIPT_DIR/common.sh"
RUNS_DIR="$STATE_DIR/runs"
ENV_FILE="$WORKSPACE_DIR/.env"

echo "SenseAudio Floating Audio Assistant"
echo

if pgrep -fl "subtitle_overlay_app|AudioClawOverlay|runner.py|mic_pcm_stream" >/dev/null 2>&1; then
  echo "Process: running"
  pgrep -fl "subtitle_overlay_app|AudioClawOverlay|runner.py|mic_pcm_stream" | sed 's/^/  /'
else
  echo "Process: not running"
fi

echo
if [ -f "$ENV_FILE" ]; then
  if grep -Eq '^(SENSEAUDIO_API_KEY|AUDIOCLAW_ASR_API_KEY)=' "$ENV_FILE"; then
    echo "SenseAudio key: configured"
  else
    echo "SenseAudio key: .env exists, but no SENSEAUDIO_API_KEY/AUDIOCLAW_ASR_API_KEY entry was found"
  fi
else
  echo "SenseAudio key: workspace .env missing"
fi

echo
if [ -d "$RUNS_DIR" ]; then
  run_count="$(find "$RUNS_DIR" -mindepth 1 -maxdepth 1 -type d 2>/dev/null | wc -l | tr -d ' ')"
  latest_run="$(find "$RUNS_DIR" -mindepth 1 -maxdepth 1 -type d -print 2>/dev/null | sort | tail -n 1)"
  echo "Runs: $run_count"
  if [ -n "${latest_run:-}" ]; then
    echo "Latest: $latest_run"
  fi
else
  echo "Runs: none yet"
fi

echo
if [ -d "$TOOL_DIR" ]; then
  echo "Tool dir: $TOOL_DIR"
else
  echo "Tool dir missing: $TOOL_DIR"
fi


path: scripts/senseaudio_api_smoke.py
content: #!/usr/bin/env python3
from __future__ import annotations

import argparse
import json
import os
import time
import urllib.error
import urllib.request
from pathlib import Path


TTS_URL = "https://api.senseaudio.cn/v1/t2a_v2"
ASR_WS_URL = "wss://api.senseaudio.cn/ws/v1/audio/transcriptions"
MUSIC_CREATE_URL = "https://api.senseaudio.cn/v1/music/song/create"
DEFAULT_TTS_MODEL = "SenseAudio-TTS-1.0"
DEFAULT_ASR_MODEL = "senseaudio-asr-deepthink-1.5-260319"
DEFAULT_MUSIC_MODEL = "senseaudio-music-1.0-260319"


def parse_args() -> argparse.Namespace:
    parser = argparse.ArgumentParser(description="Diagnostic smoke test for SenseAudio integration used by this skill.")
    parser.add_argument("--env-file", default="", help="Optional .env file. Defaults to workspace/.env discovered from this skill.")
    parser.add_argument("--live-tts", action="store_true", help="Make a tiny real SenseAudio TTS request and report chunk metadata.")
    parser.add_argument("--voice-id", default="female_0006_a")
    parser.add_argument("--text", default="SenseAudio floating audio assistant smoke test.")
    return parser.parse_args()


def workspace_dir() -> Path:
    return Path(__file__).resolve().parents[3]


def load_dotenv(path: Path) -> None:
    if not path.exists():
        return
    for line in path.read_text(encoding="utf-8").splitlines():
        raw = line.strip()
        if not raw or raw.startswith("#") or "=" not in raw:
            continue
        key, value = raw.split("=", 1)
        os.environ.setdefault(key.strip(), value.strip().strip('"').strip("'"))


def api_key_available() -> bool:
    return bool(os.environ.get("SENSEAUDIO_API_KEY", "").strip() or os.environ.get("AUDIOCLAW_ASR_API_KEY", "").strip())


def resolve_api_key() -> str:
    for name in ("SENSEAUDIO_API_KEY", "AUDIOCLAW_ASR_API_KEY"):
        value = os.environ.get(name, "").strip()
        if value:
            return value
    raise SystemExit("Missing SENSEAUDIO_API_KEY or AUDIOCLAW_ASR_API_KEY.")


def parse_sse_tts(response) -> tuple[int, int, str]:
    chunk_count = 0
    audio_bytes = 0
    trace_id = ""
    for raw_line in response:
        line = raw_line.decode("utf-8", "replace").strip()
        if not line.startswith("data: "):
            continue
        chunk_count += 1
        payload = json.loads(line[6:])
        trace_id = payload.get("trace_id") or trace_id
        base_resp = payload.get("base_resp") or {}
        if base_resp.get("status_code") not in (None, 0):
            raise RuntimeError(base_resp.get("status_msg") or "SenseAudio returned an error.")
        data = payload.get("data") or {}
        audio_hex = data.get("audio") or ""
        audio_bytes += len(audio_hex) // 2
    return chunk_count, audio_bytes, trace_id


def live_tts_probe(api_key: str, text: str, voice_id: str) -> dict:
    payload = {
        "model": DEFAULT_TTS_MODEL,
        "text": text,
        "stream": True,
        "voice_setting": {"voice_id": voice_id},
        "audio_setting": {"format": "mp3", "sample_rate": 32000},
    }
    request = urllib.request.Request(
        TTS_URL,
        data=json.dumps(payload, ensure_ascii=False).encode("utf-8"),
        headers={
            "Authorization": f"Bearer {api_key}",
            "Content-Type": "application/json",
            "User-Agent": "SenseAudio-FloatingAudioAssistant/1.0",
        },
        method="POST",
    )
    started = time.time()
    try:
        with urllib.request.urlopen(request, timeout=60) as response:
            chunk_count, audio_bytes, trace_id = parse_sse_tts(response)
    except urllib.error.HTTPError as exc:
        body = exc.read().decode("utf-8", "replace")
        raise SystemExit(f"SenseAudio TTS HTTP {exc.code}: {body}") from exc
    return {
        "ok": audio_bytes > 0,
        "endpoint": TTS_URL,
        "model": DEFAULT_TTS_MODEL,
        "voice_id": voice_id,
        "chunk_count": chunk_count,
        "audio_bytes": audio_bytes,
        "trace_id": trace_id,
        "elapsed_ms": int((time.time() - started) * 1000),
    }


def main() -> int:
    args = parse_args()
    env_file = Path(args.env_file).expanduser().resolve() if args.env_file else workspace_dir() / ".env"
    load_dotenv(env_file)
    diagnostics = {
        "skill": "senseaudio-floating-audio-assistant",
        "integration_status": "api-backed",
        "api_key_configured": api_key_available(),
        "api_endpoints": {
            "asr_websocket": ASR_WS_URL,
            "tts_http_sse": TTS_URL,
            "music_create": MUSIC_CREATE_URL,
        },
        "models": {
            "asr": DEFAULT_ASR_MODEL,
            "tts": DEFAULT_TTS_MODEL,
            "music": DEFAULT_MUSIC_MODEL,
        },
        "live_probe": None,
    }
    if args.live_tts:
        diagnostics["live_probe"] = live_tts_probe(resolve_api_key(), args.text, args.voice_id)
    print(json.dumps(diagnostics, ensure_ascii=False, indent=2))
    return 0


if __name__ == "__main__":
    raise SystemExit(main())


path: scripts/open-senseaudio-floating-audio-assistant-runs.sh
content: #!/bin/bash
set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck source=common.sh
source "$SCRIPT_DIR/common.sh"
RUNS_DIR="$WORKSPACE_DIR/state/realtime_interpreter/runs"

mkdir -p "$RUNS_DIR"
open "$RUNS_DIR"
echo "Opened SenseAudio floating audio assistant runs: $RUNS_DIR"


path: presets/lofi-study-music.json
content: prompt: A warm lo-fi study track with soft piano, brushed drums, and a calm late-night classroom feeling.
style: lo-fi, piano, warm, instrumental
duration_seconds: 60
lyrics_mode: instrumental
title: Late Night Notes

path: presets/meeting-digest-template.md
content: # Meeting Digest Template

请基于转录文本输出：

- 会议主题：一句话总结。
- 关键结论：列出最重要的 3-5 条。
- 待办事项：包含负责人、动作、截止时间；没有明确负责人时标注“未指定”。
- 风险与争议：只记录文本中明确出现的内容。
- 原文引用：为每个关键结论保留一句支撑原文。


Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (HIGH)