{
  "schema": "weclawbot.agent_plugin.v1",
  "id": "audioctl",
  "name": "audioctl",
  "package": "@openbrt/audioctl",
  "bin": "audioctl",
  "description": "Bind and drive WeClawBot-compatible audio, voice, and ambient I/O devices from user-owned AI agents.",
  "transport": "weclawbot.link/mqtt",
  "hardware_abstraction": {
    "agent_facing_type": "audio_voice_ambient_endpoint",
    "rule": "agent 只通过 audioctl、VM manifest、capabilities 和控制回执理解设备；不要推断、询问或搜索品牌、型号、厂家资料、社区经验或拆机资料。"
  },
  "user_contract": "用户只需要给出设备身份和目标；agent 负责在 capabilities、安全边界和用户授权范围内自行规划实现路径，选择合法平台 API、通过 agent 自己的连接器/OAuth/secret store 获取平台身份、构建最小权限 VM app/脚本/队列、推送到设备 VM、诊断和报告结果。插件只声明能力和边界，不穷举具体玩法。",
  "runtime_note": "audioctl 会在本机自动串行化同一 MQTT 绑定凭据的并发控制命令，避免多个 agent 工具进程使用同一个 broker client_id 时互相踢下线。",
  "transport_policy": {
    "mqtt_role": "control_plane_only",
    "agent_must_not": [
      "不要读取或解析 audioctl 本地 MQTT 凭据文件",
      "不要直接 import mqtt、打开或保持 MQTT 长连接来控制设备",
      "不要写 agent 端 while/sleep/BPM loop 来反复发布 feedback/LED 命令",
      "不要把 weclawbot.link 当作实时灯效、音频分析或本地交互的数据面",
      "不要在未获得用户明确确认时执行固件更新；agent 只能先发现新版并说明变更、版本和来源"
    ],
    "continuous_feedback": "连续灯效、节拍灯、呼吸灯、动画和按键反馈必须通过 audioctl light timeline/bind、VM app 或兼容的 feedback effect 一次下发，在设备本地运行；停止使用 audioctl light clear 或 feedback clear。"
  },
  "agent_decision_model": {
    "principle": "agent 不需要也不应该知道具体品牌/产品；只基于 inspect 返回的抽象能力、VM manifest、用户目标和安全边界来推理。",
    "allowed_reasoning": [
      "把用户自然语言目标拆成需要的 I/O、媒体、反馈、输入绑定和持久运行需求",
      "在用户已授权的平台、连接器、OAuth 或 secret store 中选择合法数据来源",
      "决定使用 queue、rule、light timeline/bind、feedback、input binding 或 VM app install 的组合",
      "在设备 VM 内放置需要离线、低延迟或持续运行的逻辑",
      "运行 audioctl firmware check 发现官网固件新版，并在用户确认后运行 audioctl firmware update --yes"
    ],
    "not_prescribed": "插件不会也无法穷举所有 agent 方案；agent 应读取 capabilities 后自己想办法达到目标。"
  },
  "separation_of_concerns": {
    "provisioning": "蓝牙/Wi-Fi 配网只负责设备入网、发现和 weclawbot.link 绑定，不承载音乐平台授权，也不决定设备用途。",
    "music_platform_auth": "音乐平台授权属于 agent 侧能力，由 agent 的连接器、OAuth、secret store 或用户明确提供的运行环境凭据完成。",
    "device_purpose": "设备用途由 agent 安装的 VM app 和显式 capabilities 决定，不由配网流程决定。"
  },
  "agent_feedback_channel": {
    "purpose": "让用户 agent 在能力声明、控制回执、真实体验、文档或插件引导不一致时留下结构化反馈。",
    "primary_command": "audioctl report \"MESSAGE\" --kind issue --area other --json",
    "rules": [
      "用户的现场视觉、听觉、触控反馈优先于设备返回的 active=true、applied 或 capability 声明。",
      "如果用户说某个 surface 看不到、按键不灵、蜂鸣不明显或体验不符合目标，agent 不要继续用状态回执证明它应该可用；先降级到用户确认可见/可听/可控的方案。",
      "出现 capability mismatch 时运行 audioctl report \"MESSAGE\" --kind capability_mismatch --area light --include-inspect --json 生成本地脱敏报告。",
      "报告默认只保存到本机，不自动提交外部系统；外发前必须获得用户确认。",
      "报告不得包含 MQTT 凭据、音乐平台 token、Wi-Fi 密码、AppSecret、PrivateKey 或其他密钥。"
    ]
  },
  "device_classes": [
    "audio_io",
    "audio_output",
    "ambient_io",
    "voice_endpoint",
    "smart_home_endpoint",
    "music_player"
  ],
  "agents": [
    "Codex",
    "Claude Code",
    "OpenClaw",
    "Hermes",
    "WorkBuddy",
    "Gemini CLI",
    "OpenCode"
  ],
  "capabilities": [
    "audio.play",
    "audio.pause",
    "audio.toggle",
    "audio.next",
    "audio.previous",
    "audio.seek",
    "audio.set_volume",
    "audio.queue.write",
    "audio.tts",
    "voice.wakeword",
    "voice.capture",
    "input.touch.read",
    "input.gesture.read",
    "input.button.bind",
    "feedback.signal",
    "feedback.led",
    "feedback.led_ring",
    "feedback.effect",
    "feedback.volume_level",
    "feedback.beep",
    "light.describe",
    "light.set",
    "light.timeline",
    "light.bind",
    "light.clear",
    "voice.wakeword.configure",
    "voice.wakeword.bind",
    "voice.wakeword.trigger",
    "voice.event.bind",
    "event.bind",
    "iot.control",
    "device.inspect",
    "firmware.update",
    "music.autopilot",
    "music.set_rule",
    "music.set_queue",
    "music.work_bgm",
    "music.status",
    "app.install"
  ],
  "pairing": {
    "endpoint": "https://weclawbot.link/byoa",
    "endpoint_env": "WEC_BYOA_ENDPOINT",
    "credentials_env": "WEC_AUDIO_CREDENTIALS",
    "code_pattern": "^[A-Za-z0-9_-]{6,64}$"
  },
  "natural_language_tasks": [
    {
      "intent": "帮我在音箱建立一个工作 BGM 歌单，持续播放",
      "primary_command": "audioctl queue QUEUE.json --play --json",
      "agent_strategy": "先运行 audioctl inspect --json。把设备只当成 audio_voice_ambient_endpoint；不要推断或搜索品牌、型号、厂家资料、社区经验。音乐来源由 agent 和用户协商，插件只做建议：可选用户已授权的音乐平台 API、用户媒体库、合法公开音源或生成式环境声。agent 需自行判断使用 queue、rule 或 VM app install；持续播放、URL 刷新、离线恢复、按键交互等长期逻辑应下放到设备 VM。本地没有音乐平台身份时，只要求用户把平台身份连接到该 agent 的连接器/OAuth/secret store；蓝牙配网和设备入网不是音乐平台授权入口。网易云开放平台可请求最高可用音质播放 URL；Spotify 必须走官方 Spotify Connect/Web Playback SDK 或控制用户已有 Spotify 设备，不能假设 Web API 能提供完整歌曲直链。",
      "fallback_command": "audioctl workbgm --play --json",
      "if_unbound": "只向用户索要绑定码，然后运行 audioctl bind CODE --name AGENT；不要要求用户理解 MQTT、topic 或凭据文件。",
      "if_music_platform_auth_missing": "告诉用户：我需要你把音乐平台身份连接到当前 agent。请使用当前 agent 提供的网易云开放平台、Spotify 等官方连接器/OAuth/secret store 完成授权；完成后只需回复“已授权”。音乐授权不属于蓝牙/Wi-Fi 配网范围，也不要把 AppSecret、PrivateKey、accessToken、refreshToken、Wi-Fi 密码或 MQTT 凭据粘贴到聊天里。",
      "if_device_side_netease_auth_failed": "这是设备内置网易运行时的旧授权失效。优先不要让用户折腾设备侧音乐账号；改由 agent 侧使用用户授权的音乐平台 API 生成队列并推送。只有当用户明确要求继续使用设备侧网易运行时时，才提示需要一个独立的设备侧音乐账号设置入口；不要把它混入蓝牙/Wi-Fi 配网流程。",
      "after_user_confirms_authorized": "agent 使用已授权的平台 API 生成 QUEUE.json，运行 audioctl queue QUEUE.json --play --json，然后报告是否 active/degraded。"
    },
    {
      "intent": "帮我把设备灯效、音量灯和按键反馈音接上",
      "primary_command": "audioctl feedback signal ready",
      "agent_strategy": "先运行 audioctl inspect --json 或 audioctl light describe --json 读取 light.surfaces、events、limits 以及 feedback/input capabilities。MQTT 是控制面，不是实时灯效数据面；不要读取 MQTT 凭据、不要 import mqtt、不要保持 MQTT 长连接、不要写 agent 端 BPM/while/sleep 循环去刷 LED。一次性语义提示可用 audioctl feedback signal/flash/volume/beep/clear；复杂灯效优先使用 audioctl light timeline 和 audioctl light bind，把外圈、中心等 surface 的 keyframes 与 media/input 事件绑定到设备本地。feedback effect 只是兼容/demo 入口，不要把 music_beat/pulse/breathing 当成能力边界。物理按键/触控属于 VM app 的 input.button.bind 能力，具体绑定由 agent 根据用户目标和 capabilities 自行规划。用户的现场视觉/听觉/触控反馈优先于 active=true、applied 或 capability 声明；如果用户说 center LED 或任何 surface 肉眼不可见，不要继续堆亮度并声称成功，先降级到可见的 ring/all/声音反馈，再生成本地报告。",
      "if_missing_capability": "不要伪造灯效或按键能力；如果 inspect 未报告对应 capability，只说明当前设备适配未提供该能力，并建议安装或升级设备 VM。",
      "if_user_reports_mismatch": "运行 audioctl report \"User reports requested feedback is not physically observable despite applied/active status\" --kind capability_mismatch --area light --include-inspect --json；报告只保存本机，外发前必须问用户。"
    },
    {
      "intent": "反馈设备、插件或 VM app 的问题/建议",
      "primary_command": "audioctl report \"MESSAGE\" --kind issue --area other --json",
      "agent_strategy": "把用户反馈整理为 observed/expected/reproduction/recommendation。需要设备状态佐证时加 --include-inspect；报告会脱敏并默认保存到本机。不要把 MQTT 凭据、音乐平台 token、Wi-Fi 密码或私钥写进报告；外发到 issue、聊天、邮件或其他系统前必须获得用户明确确认。"
    },
    {
      "intent": "让音箱作为语音遥控、家庭智能或语音互动端点",
      "primary_command": "audioctl app install MANIFEST.json APP.wasm",
      "agent_strategy": "不要把设备假定为音乐播放器。先运行 audioctl inspect --json 读取设备能力，再生成声明最小 capabilities 的 VM app。语音、TTS、家居控制等能力必须通过 manifest capabilities 显式声明；设备侧只执行授权范围内的 I/O。"
    },
    {
      "intent": "配置唤醒词或语音短语来执行某个操作",
      "primary_command": "audioctl wakeword configure WAKEWORD.json --json",
      "agent_strategy": "先运行 audioctl inspect --json 读取 wakeword.configurable、wakeword.commands、voice.wakeword.* 和 voice.event.bind capabilities。把 wakeword 视为逻辑语音触发绑定层：它把设备已有唤醒事件、ASR 短语或 agent 注入的测试短语映射到 VM/app 事件或本地动作。不要搜索品牌/型号/厂家资料，不要尝试训练或替换底层声学唤醒模型。需要让 VM app 处理时，在 app manifest 里声明最小 voice.wakeword.bind / voice.event.bind 能力和 wakeword_bindings；需要只测试触发时用 audioctl wakeword trigger WORD --json。",
      "if_missing_capability": "如果 inspect 未报告 wakeword.configurable 或 voice.wakeword.*，先运行 audioctl firmware check --json；发现官网新版后向用户说明并请求确认升级。"
    },
    {
      "intent": "检查并升级设备固件",
      "primary_command": "audioctl firmware check --json",
      "agent_strategy": "先运行 audioctl firmware check --json，从 https://weclawbot.link/firmware/audio/manifest.json 发现最新官方固件和当前设备版本。只向用户展示版本、来源、release notes 和风险提示；不得自行执行更新。只有用户明确确认后，才运行 audioctl firmware update --yes --json。固件更新是设备 host/runtime 更新，不等同于 VM app install；更新过程中不要读取 MQTT 凭据或私钥。",
      "if_update_available": "向用户确认：官网发现新固件，是否现在更新？说明版本、预计会重启部分本地服务、不会清除 Wi-Fi/MQTT/音乐平台/VM app 配置。",
      "if_user_confirms": "运行 audioctl firmware update --yes --json，并在完成后运行 audioctl inspect --json 验证 firmware.version、health 和 vm 状态。"
    }
  ],
  "commands": [
    {
      "name": "bind",
      "usage": "audioctl bind CODE --name AGENT"
    },
    {
      "name": "doctor",
      "usage": "audioctl doctor --online"
    },
    {
      "name": "inspect",
      "usage": "audioctl inspect --json"
    },
    {
      "name": "control",
      "usage": "audioctl play|pause|toggle|next|previous|volumeup|volumedown|status"
    },
    {
      "name": "feedback",
      "usage": "audioctl feedback signal NAME | flash COLOR | volume 0..100 | effect NAME [--bpm N] [--color COLOR] | clear | beep"
    },
    {
      "name": "light",
      "usage": "audioctl light describe | set SURFACE --color COLOR [--brightness 0..1] | timeline TIMELINE.json | bind BINDINGS.json | clear"
    },
    {
      "name": "wakeword",
      "usage": "audioctl wakeword status | configure CONFIG.json | bind BINDINGS.json | trigger WORD [--source NAME] | clear"
    },
    {
      "name": "led",
      "usage": "audioctl led flash COLOR [--repeat N]"
    },
    {
      "name": "volume",
      "usage": "audioctl volume 0..100"
    },
    {
      "name": "rule",
      "usage": "audioctl rule RULE.json"
    },
    {
      "name": "queue",
      "usage": "audioctl queue QUEUE.json [--play]"
    },
    {
      "name": "app install",
      "usage": "audioctl app install MANIFEST.json APP.wasm"
    },
    {
      "name": "firmware",
      "usage": "audioctl firmware check [--manifest URL] | firmware update [--manifest URL] --yes"
    },
    {
      "name": "report",
      "usage": "audioctl report MESSAGE [--kind issue|suggestion|capability_mismatch|ux_feedback|docs_feedback] [--area light|audio|input|wakeword|firmware|vm|plugin|docs|other] [--include-inspect] --json"
    },
    {
      "name": "workbgm",
      "usage": "audioctl workbgm --play"
    }
  ]
}
