fix: allow device-paired clients to retrieve TTS API keys (#14613)

* refactor: add config.get to READ_METHODS set

* refactor(gateway): scope talk secrets via talk.config

* fix: resolve rebase conflicts for talk scope refactor

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
This commit is contained in:
Sk Akram
2026-02-13 21:37:49 +05:30
committed by GitHub
parent c2f7b66d22
commit 4c86821aca
14 changed files with 264 additions and 6 deletions

View File

@@ -119,7 +119,7 @@ class ConnectionManager(
fun buildOperatorConnectOptions(): GatewayConnectOptions {
return GatewayConnectOptions(
role = "operator",
scopes = emptyList(),
scopes = listOf("operator.read", "operator.write", "operator.talk.secrets"),
caps = emptyList(),
commands = emptyList(),
permissions = emptyMap(),

View File

@@ -814,7 +814,7 @@ class TalkModeManager(
val sagVoice = System.getenv("SAG_VOICE_ID")?.trim()
val envKey = System.getenv("ELEVENLABS_API_KEY")?.trim()
try {
val res = session.request("config.get", "{}")
val res = session.request("talk.config", """{"includeSecrets":true}""")
val root = json.parseToJsonElement(res).asObjectOrNull()
val config = root?.get("config").asObjectOrNull()
val talk = config?.get("talk").asObjectOrNull()

View File

@@ -1750,7 +1750,7 @@ private extension NodeAppModel {
func makeOperatorConnectOptions(clientId: String, displayName: String?) -> GatewayConnectOptions {
GatewayConnectOptions(
role: "operator",
scopes: ["operator.read", "operator.write", "operator.admin"],
scopes: ["operator.read", "operator.write", "operator.talk.secrets"],
caps: [],
commands: [],
permissions: [:],

View File

@@ -1671,7 +1671,7 @@ extension TalkModeManager {
func reloadConfig() async {
guard let gateway else { return }
do {
let res = try await gateway.request(method: "config.get", paramsJSON: "{}", timeoutSeconds: 8)
let res = try await gateway.request(method: "talk.config", paramsJSON: "{\"includeSecrets\":true}", timeoutSeconds: 8)
guard let json = try JSONSerialization.jsonObject(with: res) as? [String: Any] else { return }
guard let config = json["config"] as? [String: Any] else { return }
let talk = config["talk"] as? [String: Any]

View File

@@ -64,6 +64,7 @@ actor GatewayConnection {
case wizardNext = "wizard.next"
case wizardCancel = "wizard.cancel"
case wizardStatus = "wizard.status"
case talkConfig = "talk.config"
case talkMode = "talk.mode"
case webLoginStart = "web.login.start"
case webLoginWait = "web.login.wait"

View File

@@ -800,8 +800,8 @@ extension TalkModeRuntime {
do {
let snap: ConfigSnapshot = try await GatewayConnection.shared.requestDecoded(
method: .configGet,
params: nil,
method: .talkConfig,
params: ["includeSecrets": AnyCodable(true)],
timeoutMs: 8000)
let talk = snap.config?["talk"]?.dictionaryValue
let ui = snap.config?["ui"]?.dictionaryValue