@openbrt/audioctl 0.1.8 → 0.1.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +22 -8
- package/agent-plugin.json +25 -4
- package/bin/audioctl.mjs +26 -3
- package/lib/index.mjs +48 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -57,6 +57,11 @@ credentials, because the broker may require an exact MQTT client id.
|
|
|
57
57
|
Agent-facing status is intentionally hardware-abstracted as
|
|
58
58
|
`audio_voice_ambient_endpoint`; agents should not infer or search for a device
|
|
59
59
|
brand/model/vendor from screenshots, BLE names, logs, or prior context.
|
|
60
|
+
MQTT is a control plane and deployment path, not a realtime feedback data plane:
|
|
61
|
+
agents should not read MQTT credentials, import `mqtt`, keep persistent MQTT
|
|
62
|
+
connections, or run BPM/while/sleep loops that repeatedly publish LED commands.
|
|
63
|
+
Continuous effects must be started as a device-local effect or VM app, then left
|
|
64
|
+
to run on the device.
|
|
60
65
|
|
|
61
66
|
## Commands
|
|
62
67
|
|
|
@@ -74,6 +79,7 @@ audioctl inspect --json
|
|
|
74
79
|
audioctl feedback signal ready
|
|
75
80
|
audioctl feedback flash cyan --repeat 2
|
|
76
81
|
audioctl feedback volume 66
|
|
82
|
+
audioctl feedback effect music_beat --bpm 76 --color amber
|
|
77
83
|
audioctl feedback beep
|
|
78
84
|
audioctl feedback clear
|
|
79
85
|
audioctl queue ./work-bgm.queue.json --play
|
|
@@ -98,18 +104,26 @@ prompt. Explicit LED controls are also available:
|
|
|
98
104
|
audioctl feedback signal playing
|
|
99
105
|
audioctl feedback flash '#00ffff' --repeat 2
|
|
100
106
|
audioctl feedback volume 42
|
|
107
|
+
audioctl feedback effect music_beat --bpm 76 --color amber
|
|
101
108
|
audioctl feedback beep
|
|
102
109
|
audioctl feedback clear
|
|
103
110
|
audioctl led flash amber --repeat 1
|
|
104
111
|
```
|
|
105
112
|
|
|
113
|
+
`feedback effect` is for continuous device-local effects such as beat/pulse or
|
|
114
|
+
breathing animations. It sends one control message; the timing loop belongs on
|
|
115
|
+
the device, not in the agent process. `feedback clear` stops the current
|
|
116
|
+
device-local effect and clears the indicator. Beat-style names such as
|
|
117
|
+
`music_beat` are treated as effects, not as a signal that should be published in
|
|
118
|
+
a loop.
|
|
119
|
+
|
|
106
120
|
Physical keys/touch surfaces are reported as VM capabilities such as
|
|
107
121
|
`input.touch.read`, `input.gesture.read`, and `input.button.bind`. A VM app can
|
|
108
|
-
declare those capabilities and bind local gestures to safe device actions
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
122
|
+
declare those capabilities and bind local gestures to safe device actions. The
|
|
123
|
+
agent should derive the binding from the user's goal and the reported
|
|
124
|
+
capabilities, instead of assuming a fixed product-specific layout. These
|
|
125
|
+
bindings are device-local for low latency and do not require the agent to be
|
|
126
|
+
online for every tap.
|
|
113
127
|
|
|
114
128
|
`queue --play` is the generic agent-built music path. The agent uses the user's
|
|
115
129
|
authorized music provider outside the speaker, writes a
|
|
@@ -168,6 +182,6 @@ Feedback messages use the same MQTT envelope:
|
|
|
168
182
|
}
|
|
169
183
|
```
|
|
170
184
|
|
|
171
|
-
Supported feedback commands are `signal`, `flash`, `volume`, `beep`,
|
|
172
|
-
`clear`. Devices that do not report a matching `feedback.*` capability
|
|
173
|
-
reject the command instead of pretending to support it.
|
|
185
|
+
Supported feedback commands are `signal`, `flash`, `volume`, `effect`, `beep`,
|
|
186
|
+
and `clear`. Devices that do not report a matching `feedback.*` capability
|
|
187
|
+
should reject the command instead of pretending to support it.
|
package/agent-plugin.json
CHANGED
|
@@ -10,8 +10,28 @@
|
|
|
10
10
|
"agent_facing_type": "audio_voice_ambient_endpoint",
|
|
11
11
|
"rule": "agent 只通过 audioctl、VM manifest、capabilities 和控制回执理解设备;不要推断、询问或搜索品牌、型号、厂家资料、社区经验或拆机资料。"
|
|
12
12
|
},
|
|
13
|
-
"user_contract": "用户只需要给出设备身份和目标;agent
|
|
13
|
+
"user_contract": "用户只需要给出设备身份和目标;agent 负责在 capabilities、安全边界和用户授权范围内自行规划实现路径,选择合法平台 API、通过 agent 自己的连接器/OAuth/secret store 获取平台身份、构建最小权限 VM app/脚本/队列、推送到设备 VM、诊断和报告结果。插件只声明能力和边界,不穷举具体玩法。",
|
|
14
14
|
"runtime_note": "audioctl 会在本机自动串行化同一 MQTT 绑定凭据的并发控制命令,避免多个 agent 工具进程使用同一个 broker client_id 时互相踢下线。",
|
|
15
|
+
"transport_policy": {
|
|
16
|
+
"mqtt_role": "control_plane_only",
|
|
17
|
+
"agent_must_not": [
|
|
18
|
+
"不要读取或解析 ~/.config/weclawbot/audio-mqtt.json 里的 MQTT 凭据",
|
|
19
|
+
"不要直接 import mqtt、打开或保持 MQTT 长连接来控制设备",
|
|
20
|
+
"不要写 agent 端 while/sleep/BPM loop 来反复发布 feedback/LED 命令",
|
|
21
|
+
"不要把 weclawbot.link 当作实时灯效、音频分析或本地交互的数据面"
|
|
22
|
+
],
|
|
23
|
+
"continuous_feedback": "连续灯效、节拍灯、呼吸灯、动画和按键反馈必须通过 audioctl feedback effect 或 VM app 一次下发,在设备本地运行;停止使用 audioctl feedback clear。"
|
|
24
|
+
},
|
|
25
|
+
"agent_decision_model": {
|
|
26
|
+
"principle": "agent 不需要也不应该知道具体品牌/产品;只基于 inspect 返回的抽象能力、VM manifest、用户目标和安全边界来推理。",
|
|
27
|
+
"allowed_reasoning": [
|
|
28
|
+
"把用户自然语言目标拆成需要的 I/O、媒体、反馈、输入绑定和持久运行需求",
|
|
29
|
+
"在用户已授权的平台、连接器、OAuth 或 secret store 中选择合法数据来源",
|
|
30
|
+
"决定使用 queue、rule、feedback、input binding 或 VM app install 的组合",
|
|
31
|
+
"在设备 VM 内放置需要离线、低延迟或持续运行的逻辑"
|
|
32
|
+
],
|
|
33
|
+
"not_prescribed": "插件不会也无法穷举所有 agent 方案;agent 应读取 capabilities 后自己想办法达到目标。"
|
|
34
|
+
},
|
|
15
35
|
"separation_of_concerns": {
|
|
16
36
|
"provisioning": "蓝牙/Wi-Fi 配网只负责设备入网、发现和 weclawbot.link 绑定,不承载音乐平台授权,也不决定设备用途。",
|
|
17
37
|
"music_platform_auth": "音乐平台授权属于 agent 侧能力,由 agent 的连接器、OAuth、secret store 或用户明确提供的运行环境凭据完成。",
|
|
@@ -52,6 +72,7 @@
|
|
|
52
72
|
"feedback.signal",
|
|
53
73
|
"feedback.led",
|
|
54
74
|
"feedback.led_ring",
|
|
75
|
+
"feedback.effect",
|
|
55
76
|
"feedback.volume_level",
|
|
56
77
|
"feedback.beep",
|
|
57
78
|
"iot.control",
|
|
@@ -73,7 +94,7 @@
|
|
|
73
94
|
{
|
|
74
95
|
"intent": "帮我在音箱建立一个工作 BGM 歌单,持续播放",
|
|
75
96
|
"primary_command": "audioctl queue QUEUE.json --play --json",
|
|
76
|
-
"agent_strategy": "先运行 audioctl inspect --json。把设备只当成 audio_voice_ambient_endpoint;不要推断或搜索品牌、型号、厂家资料、社区经验。音乐来源由 agent 和用户协商,插件只做建议:可选用户已授权的音乐平台 API
|
|
97
|
+
"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 能提供完整歌曲直链。",
|
|
77
98
|
"fallback_command": "audioctl workbgm --play --json",
|
|
78
99
|
"if_unbound": "只向用户索要绑定码,然后运行 audioctl bind CODE --name AGENT;不要要求用户理解 MQTT、topic 或凭据文件。",
|
|
79
100
|
"if_music_platform_auth_missing": "告诉用户:我需要你把音乐平台身份连接到当前 agent。请使用当前 agent 提供的网易云开放平台、Spotify 等官方连接器/OAuth/secret store 完成授权;完成后只需回复“已授权”。音乐授权不属于蓝牙/Wi-Fi 配网范围,也不要把 AppSecret、PrivateKey、accessToken、refreshToken、Wi-Fi 密码或 MQTT 凭据粘贴到聊天里。",
|
|
@@ -83,7 +104,7 @@
|
|
|
83
104
|
{
|
|
84
105
|
"intent": "帮我把设备灯效、音量灯和按键反馈音接上",
|
|
85
106
|
"primary_command": "audioctl feedback signal ready",
|
|
86
|
-
"agent_strategy": "先运行 audioctl inspect --json 确认设备报告 feedback.* 和 input.* capabilities
|
|
107
|
+
"agent_strategy": "先运行 audioctl inspect --json 确认设备报告 feedback.* 和 input.* capabilities。MQTT 是控制面,不是实时灯效数据面;不要读取 MQTT 凭据、不要 import mqtt、不要保持 MQTT 长连接、不要写 agent 端 BPM/while/sleep 循环去刷 LED。一次性语义反馈可用 audioctl feedback signal/flash/volume/beep/clear;连续灯效、节拍灯、呼吸灯和动画必须通过 audioctl feedback effect 或 VM app 一次下发并在设备本地运行。物理按键/触控属于 VM app 的 input.button.bind 能力,具体绑定由 agent 根据用户目标和 capabilities 自行规划。",
|
|
87
108
|
"if_missing_capability": "不要伪造灯效或按键能力;如果 inspect 未报告对应 capability,只说明当前设备适配未提供该能力,并建议安装或升级设备 VM。"
|
|
88
109
|
},
|
|
89
110
|
{
|
|
@@ -111,7 +132,7 @@
|
|
|
111
132
|
},
|
|
112
133
|
{
|
|
113
134
|
"name": "feedback",
|
|
114
|
-
"usage": "audioctl feedback signal NAME | flash COLOR | volume 0..100 | clear | beep"
|
|
135
|
+
"usage": "audioctl feedback signal NAME | flash COLOR | volume 0..100 | effect NAME [--bpm N] [--color COLOR] | clear | beep"
|
|
115
136
|
},
|
|
116
137
|
{
|
|
117
138
|
"name": "led",
|
package/bin/audioctl.mjs
CHANGED
|
@@ -70,6 +70,7 @@ function usage(exitCode = 0) {
|
|
|
70
70
|
${COMMAND_NAME} feedback signal NAME [--level 0..100]
|
|
71
71
|
${COMMAND_NAME} feedback flash COLOR [--repeat N]
|
|
72
72
|
${COMMAND_NAME} feedback volume 0..100
|
|
73
|
+
${COMMAND_NAME} feedback effect NAME [--bpm N] [--color COLOR]
|
|
73
74
|
${COMMAND_NAME} feedback clear|beep
|
|
74
75
|
${COMMAND_NAME} led flash COLOR [--repeat N]
|
|
75
76
|
${COMMAND_NAME} rule RULE.json
|
|
@@ -328,12 +329,12 @@ async function commandFeedback(values) {
|
|
|
328
329
|
"playing",
|
|
329
330
|
"paused",
|
|
330
331
|
"wake",
|
|
331
|
-
"music_beat",
|
|
332
332
|
"volume_step_up",
|
|
333
333
|
"volume_step_down",
|
|
334
334
|
"button_press",
|
|
335
335
|
"button_long_press",
|
|
336
336
|
]);
|
|
337
|
+
const shorthandEffects = new Set(["music_beat", "pulse", "breathing"]);
|
|
337
338
|
const options = parseOptions(values.slice(1), {
|
|
338
339
|
credentials: credentialsPath(),
|
|
339
340
|
timeout: 12,
|
|
@@ -344,11 +345,26 @@ async function commandFeedback(values) {
|
|
|
344
345
|
let command = subcommand;
|
|
345
346
|
const payload = {};
|
|
346
347
|
if (!command) throw new Error("feedback_requires_subcommand");
|
|
347
|
-
if (
|
|
348
|
+
if (shorthandEffects.has(command)) {
|
|
349
|
+
payload.effect = command;
|
|
350
|
+
payload.bpm = options.bpm;
|
|
351
|
+
payload.color = options.color;
|
|
352
|
+
payload.alpha = options.alpha;
|
|
353
|
+
command = "effect";
|
|
354
|
+
} else if (shorthandSignals.has(command)) {
|
|
348
355
|
payload.semantic = command;
|
|
349
356
|
command = "signal";
|
|
350
357
|
} else if (command === "signal") {
|
|
351
|
-
|
|
358
|
+
const semantic = String(options._[0] || "").replace(/-/gu, "_").toLowerCase();
|
|
359
|
+
if (shorthandEffects.has(semantic)) {
|
|
360
|
+
payload.effect = semantic;
|
|
361
|
+
payload.bpm = options.bpm;
|
|
362
|
+
payload.color = options.color;
|
|
363
|
+
payload.alpha = options.alpha;
|
|
364
|
+
command = "effect";
|
|
365
|
+
} else {
|
|
366
|
+
payload.semantic = options._[0];
|
|
367
|
+
}
|
|
352
368
|
} else if (command === "flash") {
|
|
353
369
|
payload.color = options._[0] || options.color;
|
|
354
370
|
payload.repeat = options.repeat;
|
|
@@ -357,6 +373,11 @@ async function commandFeedback(values) {
|
|
|
357
373
|
payload.level = options._[0] ?? options.level;
|
|
358
374
|
} else if (command === "beep") {
|
|
359
375
|
payload.pattern = options._[0] || options.pattern;
|
|
376
|
+
} else if (command === "effect") {
|
|
377
|
+
payload.effect = options._[0] || options.effect;
|
|
378
|
+
payload.bpm = options.bpm;
|
|
379
|
+
payload.color = options.color;
|
|
380
|
+
payload.alpha = options.alpha;
|
|
360
381
|
} else if (command === "clear" || command === "off" || command === "hide") {
|
|
361
382
|
command = "clear";
|
|
362
383
|
}
|
|
@@ -414,9 +435,11 @@ function parseOptions(values, defaults = {}) {
|
|
|
414
435
|
"agent",
|
|
415
436
|
"alias",
|
|
416
437
|
"alpha",
|
|
438
|
+
"bpm",
|
|
417
439
|
"color",
|
|
418
440
|
"credentials",
|
|
419
441
|
"endpoint",
|
|
442
|
+
"effect",
|
|
420
443
|
"level",
|
|
421
444
|
"name",
|
|
422
445
|
"pattern",
|
package/lib/index.mjs
CHANGED
|
@@ -71,7 +71,6 @@ export const FEEDBACK_SIGNALS = Object.freeze([
|
|
|
71
71
|
"playing",
|
|
72
72
|
"paused",
|
|
73
73
|
"wake",
|
|
74
|
-
"music_beat",
|
|
75
74
|
"volume_step_up",
|
|
76
75
|
"volume_step_down",
|
|
77
76
|
"button_press",
|
|
@@ -79,6 +78,12 @@ export const FEEDBACK_SIGNALS = Object.freeze([
|
|
|
79
78
|
"clear",
|
|
80
79
|
]);
|
|
81
80
|
|
|
81
|
+
export const FEEDBACK_EFFECTS = Object.freeze([
|
|
82
|
+
"music_beat",
|
|
83
|
+
"pulse",
|
|
84
|
+
"breathing",
|
|
85
|
+
]);
|
|
86
|
+
|
|
82
87
|
const WORKBGM_APP_WASM_BASE64 =
|
|
83
88
|
"AGFzbQEAAAABBAFgAAADAgEABwgBBG1haW4AAAoEAQIACw==";
|
|
84
89
|
|
|
@@ -396,12 +401,16 @@ export function normalizeFeedbackCommand(command, options = {}) {
|
|
|
396
401
|
["ding", "beep"],
|
|
397
402
|
["vol", "volume"],
|
|
398
403
|
["volume_level", "volume"],
|
|
404
|
+
["animate", "effect"],
|
|
405
|
+
["animation", "effect"],
|
|
406
|
+
["visualize", "effect"],
|
|
407
|
+
["visualizer", "effect"],
|
|
399
408
|
]);
|
|
400
409
|
const raw = string(command || options.command || options.action || "signal")
|
|
401
410
|
.replace(/-/gu, "_")
|
|
402
411
|
.toLowerCase();
|
|
403
412
|
const normalized = aliases.get(raw) || raw;
|
|
404
|
-
const supported = new Set(["signal", "flash", "volume", "clear", "beep"]);
|
|
413
|
+
const supported = new Set(["signal", "flash", "volume", "clear", "beep", "effect"]);
|
|
405
414
|
if (!supported.has(normalized)) {
|
|
406
415
|
throw new Error(`feedback_command_unsupported: ${command}`);
|
|
407
416
|
}
|
|
@@ -415,6 +424,12 @@ export function normalizeFeedbackCommand(command, options = {}) {
|
|
|
415
424
|
options.value ||
|
|
416
425
|
"button_press",
|
|
417
426
|
).replace(/-/gu, "_").toLowerCase();
|
|
427
|
+
if (FEEDBACK_EFFECTS.includes(semantic)) {
|
|
428
|
+
return normalizeFeedbackCommand("effect", {
|
|
429
|
+
...options,
|
|
430
|
+
effect: semantic,
|
|
431
|
+
});
|
|
432
|
+
}
|
|
418
433
|
if (!/^[a-z0-9_.:]{1,64}$/u.test(semantic)) {
|
|
419
434
|
throw new Error("feedback_signal_invalid");
|
|
420
435
|
}
|
|
@@ -439,6 +454,32 @@ export function normalizeFeedbackCommand(command, options = {}) {
|
|
|
439
454
|
}
|
|
440
455
|
feedback.pattern = pattern;
|
|
441
456
|
}
|
|
457
|
+
if (normalized === "effect") {
|
|
458
|
+
const effect = string(
|
|
459
|
+
options.effect ||
|
|
460
|
+
options.semantic ||
|
|
461
|
+
options.signal ||
|
|
462
|
+
options.name ||
|
|
463
|
+
options.value ||
|
|
464
|
+
"pulse",
|
|
465
|
+
).replace(/-/gu, "_").toLowerCase();
|
|
466
|
+
if (!/^[a-z0-9_.:]{1,64}$/u.test(effect)) {
|
|
467
|
+
throw new Error("feedback_effect_invalid");
|
|
468
|
+
}
|
|
469
|
+
feedback.effect = effect;
|
|
470
|
+
if (options.bpm !== undefined) {
|
|
471
|
+
const bpm = Number(options.bpm);
|
|
472
|
+
if (!Number.isFinite(bpm) || bpm < 20 || bpm > 240) {
|
|
473
|
+
throw new Error("feedback_effect_bpm_invalid");
|
|
474
|
+
}
|
|
475
|
+
feedback.bpm = Math.round(bpm);
|
|
476
|
+
}
|
|
477
|
+
if (options.color !== undefined) {
|
|
478
|
+
feedback.color = normalizeFeedbackColor(options.color, {
|
|
479
|
+
alpha: options.alpha,
|
|
480
|
+
});
|
|
481
|
+
}
|
|
482
|
+
}
|
|
442
483
|
return feedback;
|
|
443
484
|
}
|
|
444
485
|
|
|
@@ -598,6 +639,7 @@ export function normalizeAppManifest(manifest, artifact = {}) {
|
|
|
598
639
|
"feedback.signal",
|
|
599
640
|
"feedback.led",
|
|
600
641
|
"feedback.led_ring",
|
|
642
|
+
"feedback.effect",
|
|
601
643
|
"feedback.volume_level",
|
|
602
644
|
"feedback.beep",
|
|
603
645
|
"iot.control",
|
|
@@ -816,10 +858,12 @@ export function buildAgentPrompt(options = {}) {
|
|
|
816
858
|
`npm exec --package ${PACKAGE_NAME} -- ${COMMAND_NAME} doctor --online`,
|
|
817
859
|
`这台设备对你是抽象硬件产品:${ABSTRACT_HARDWARE_TYPE}。不要根据外观、BLE 名称、音色、日志或历史上下文推断品牌/型号/厂家,也不要搜索厂家资料、社区经验或拆机资料;只使用 audioctl inspect 返回的 capabilities、VM manifest 和控制回执来工作。`,
|
|
818
860
|
`绑定成功后,把它作为默认音频/语音/环境 I/O 端点;不要把它假定成只会播放音乐。可用命令包括 play、pause、toggle、next、previous、volume、volumeup、volumedown、status、inspect、queue、rule、workbgm、feedback、led、app install。`,
|
|
861
|
+
`当我给出目标时,请你在 capabilities、安全边界和用户授权范围内自行规划实现路径;插件只声明能力和边界,不穷举具体玩法。需要离线、低延迟或持续运行的逻辑,应通过 queue/rule/VM app/feedback effect 下放到设备本地。`,
|
|
862
|
+
`MQTT/weclawbot.link 对你只是控制面和部署通道,不是实时灯效数据面。不要读取 MQTT 凭据,不要直接 import mqtt,不要保持 MQTT 长连接,也不要写循环脚本按 BPM 反复发布 feedback。连续灯效/节拍/动画必须用 audioctl feedback effect 或 VM app 一次下发,在设备本地运行;停止时用 audioctl feedback clear。`,
|
|
819
863
|
`蓝牙/Wi-Fi 配网只负责设备入网、发现和 weclawbot.link 绑定;音乐平台授权请走 agent 侧连接器/OAuth/secret store;配网不决定设备用途。`,
|
|
820
|
-
|
|
864
|
+
`音乐来源由你和用户协商,插件只做建议。你可以在 agent 侧使用用户授权的音乐平台 API、用户媒体库、合法公开音源或其他合规来源生成队列/规则/VM app。网易云开放平台可请求最高可用音质 URL。`,
|
|
821
865
|
`如果用户选择 Spotify,必须走 Spotify 官方 Spotify Connect/Web Playback SDK 或控制用户已有 Spotify 播放设备;不要假设 Spotify Web API 能提供完整歌曲直链。`,
|
|
822
|
-
|
|
866
|
+
`灯效、音量灯、蜂鸣和按键/触控反馈要先 inspect 确认 feedback/input capabilities。一次性反馈可用 feedback signal/flash/volume/beep/clear;连续反馈用 feedback effect 或 VM app 一次下发,由设备本地运行。具体按键绑定请根据用户目标和 capabilities 自行规划,不要套固定模板。`,
|
|
823
867
|
`如果你当前没有音乐平台身份,请只告诉我:我需要你把音乐平台身份连接到当前 agent 的官方连接器、OAuth 流程或 secret store;完成后回复“已授权”。不要反复重试,不要让我理解内部授权包,也不要要求我把 AppSecret、PrivateKey、accessToken 或 refreshToken 发到聊天里。`,
|
|
824
868
|
`如果返回 health.problems 包含 netease_auth_refresh_failed,说明设备侧内置网易运行时授权失效;优先改走 agent 侧 queue/app 驱动。只有我明确要求继续使用设备侧网易运行时时,才提示需要独立的设备侧音乐账号设置入口;不要混入蓝牙/Wi-Fi 配网流程。`,
|
|
825
869
|
`如果我要播放「${profile}」,也可以执行:npm exec --package ${PACKAGE_NAME} -- ${COMMAND_NAME} deepnight`,
|