keyringstorage: properly handle keyring fetching (#2108)

- if auto lock enabled, don't do anything and wait for lock password
- not try to overwrite and don't consider loaded when unlocking fails
- retry unlock and re fetch on demand (ai request)
This commit is contained in:
end-4
2025-11-02 23:20:01 +01:00
parent 525108dd95
commit 1ee08fca51
6 changed files with 66 additions and 25 deletions
@@ -13,10 +13,16 @@ import Quickshell.Hyprland
Scope { Scope {
id: root id: root
Process {
id: unlockKeyringProc
onExited: (exitCode, exitStatus) => {
KeyringStorage.fetchKeyringData();
}
}
function unlockKeyring() { function unlockKeyring() {
Quickshell.execDetached({ unlockKeyringProc.exec({
environment: ({ environment: ({
UNLOCK_PASSWORD: root.currentText "UNLOCK_PASSWORD": lockContext.currentText
}), }),
command: ["bash", "-c", Quickshell.shellPath("scripts/keyring/unlock.sh")] command: ["bash", "-c", Quickshell.shellPath("scripts/keyring/unlock.sh")]
}) })
@@ -24,7 +30,7 @@ Scope {
property var windowData: [] property var windowData: []
function saveWindowPositionAndTile() { function saveWindowPositionAndTile() {
Hyprland.dispatch(`keyword dwindle:pseudotile true`) Quickshell.execDetached(["hyprctl", "keyword", "dwindle:pseudotile", "true"])
root.windowData = HyprlandData.windowList.filter(w => (w.floating && w.workspace.id === HyprlandData.activeWorkspace.id)) root.windowData = HyprlandData.windowList.filter(w => (w.floating && w.workspace.id === HyprlandData.activeWorkspace.id))
root.windowData.forEach(w => { root.windowData.forEach(w => {
Hyprland.dispatch(`pseudo address:${w.address}`) Hyprland.dispatch(`pseudo address:${w.address}`)
@@ -38,7 +44,7 @@ Scope {
Hyprland.dispatch(`movewindowpixel exact ${w.at[0]} ${w.at[1]}, address:${w.address}`) Hyprland.dispatch(`movewindowpixel exact ${w.at[0]} ${w.at[1]}, address:${w.address}`)
Hyprland.dispatch(`pseudo address:${w.address}`) Hyprland.dispatch(`pseudo address:${w.address}`)
}) })
Hyprland.dispatch(`keyword dwindle:pseudotile false`) Quickshell.execDetached(["hyprctl", "keyword", "dwindle:pseudotile", "false"])
} }
// This stores all the information shared between the lock surfaces on each screen. // This stores all the information shared between the lock surfaces on each screen.
@@ -156,20 +162,24 @@ Scope {
} }
} }
function initIfReady() {
if (!Config.ready || !Persistent.ready) return;
if (Config.options.lock.launchOnStartup && Persistent.isNewHyprlandInstance) {
Hyprland.dispatch("global quickshell:lock")
} else {
KeyringStorage.fetchKeyringData();
}
}
Connections { Connections {
target: Config target: Config
function onReadyChanged() { function onReadyChanged() {
if (Config.options.lock.launchOnStartup && Config.ready && Persistent.ready && Persistent.isNewHyprlandInstance) { root.initIfReady();
Hyprland.dispatch("global quickshell:lock")
}
} }
} }
Connections { Connections {
target: Persistent target: Persistent
function onReadyChanged() { function onReadyChanged() {
if (Config.options.lock.launchOnStartup && Config.ready && Persistent.ready && Persistent.isNewHyprlandInstance) { root.initIfReady();
Hyprland.dispatch("global quickshell:lock")
}
} }
} }
} }
+11
View File
@@ -0,0 +1,11 @@
#!/usr/bin/env bash
locked_state=$(busctl --user get-property org.freedesktop.secrets \
/org/freedesktop/secrets/collection/login \
org.freedesktop.Secret.Collection Locked)
if [[ "${locked_state}" == "b false" ]]; then
echo 'Keyring is unlocked' >&2
exit 0
else
echo 'Keyring is locked' >&2
exit 1
fi
+15
View File
@@ -0,0 +1,15 @@
#!/usr/bin/env bash
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
data=$(secret-tool lookup 'application' 'illogical-impulse')
if [[ -z "$data" ]]; then
if "${SCRIPT_DIR}/is_unlocked.sh"; then
echo 'not found'
exit 1
else
echo 'locked'
exit 2
fi
fi
echo "$data"
@@ -1,12 +1,10 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# Based on https://unix.stackexchange.com/a/602935 # Based on https://unix.stackexchange.com/a/602935
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# Skip if already unlocked # Skip if already unlocked
locked_state=$(busctl --user get-property org.freedesktop.secrets \ if "${SCRIPT_DIR}/is_unlocked.sh"; then
/org/freedesktop/secrets/collection/login \
org.freedesktop.Secret.Collection Locked)
if [[ "${locked_state}" == "b false" ]]; then
echo 'Keyring is already unlocked.' >&2
exit 1 exit 1
fi fi
+4 -2
View File
@@ -532,8 +532,6 @@ Singleton {
modelId = modelId.toLowerCase() modelId = modelId.toLowerCase()
if (modelList.indexOf(modelId) !== -1) { if (modelList.indexOf(modelId) !== -1) {
const model = models[modelId] const model = models[modelId]
// Fetch API keys if needed
if (model?.requires_key) KeyringStorage.fetchKeyringData();
// See if policy prevents online models // See if policy prevents online models
if (Config.options.policies.ai === 2 && !model.endpoint.includes("localhost")) { if (Config.options.policies.ai === 2 && !model.endpoint.includes("localhost")) {
root.addMessage( root.addMessage(
@@ -641,6 +639,10 @@ Singleton {
function makeRequest() { function makeRequest() {
const model = models[currentModelId]; const model = models[currentModelId];
// Fetch API keys if needed
if (model?.requires_key && !KeyringStorage.loaded) KeyringStorage.fetchKeyringData();
requester.currentStrategy = root.currentApiStrategy; requester.currentStrategy = root.currentApiStrategy;
requester.currentStrategy.reset(); // Reset strategy state requester.currentStrategy.reset(); // Reset strategy state
@@ -1,6 +1,7 @@
pragma Singleton pragma Singleton
pragma ComponentBehavior: Bound pragma ComponentBehavior: Bound
import qs
import qs.modules.common import qs.modules.common
import qs.modules.common.functions import qs.modules.common.functions
import Quickshell; import Quickshell;
@@ -89,11 +90,13 @@ Singleton {
Process { Process {
id: getData id: getData
command: [ // We need to use echo for a newline so splitparser does parse command: [ // We need to use echo for a newline so splitparser does parse
"bash", "-c", `echo $(secret-tool lookup 'application' 'illogical-impulse')`, "bash", "-c", `${Directories.scriptPath}/keyring/try_lookup.sh 2> /dev/null`,
] ]
stdout: SplitParser { stdout: StdioCollector {
onRead: data => { id: keyringDataOutputCollector
if(data.length === 0) return; onStreamFinished: {
const data = keyringDataOutputCollector.text;
if (data.length === 0 || !data.startsWith("{")) return;
try { try {
root.keyringData = JSON.parse(data); root.keyringData = JSON.parse(data);
// console.log("[KeyringStorage] Keyring data fetched:", JSON.stringify(root.keyringData)); // console.log("[KeyringStorage] Keyring data fetched:", JSON.stringify(root.keyringData));
@@ -105,13 +108,15 @@ Singleton {
} }
} }
onExited: (exitCode, exitStatus) => { onExited: (exitCode, exitStatus) => {
// console.log("[KeyringStorage] Keyring data fetch process exited with code:", exitCode); console.log("[KeyringStorage] Keyring data fetch process exited with code:", exitCode);
if (exitCode !== 0) { if (exitCode === 1) {
console.error("[KeyringStorage] Failed to get keyring data, reinitializing."); console.error("[KeyringStorage] Entry not found, initializing.");
root.keyringData = {}; root.keyringData = {};
saveKeyringData() saveKeyringData()
} }
root.loaded = true; if (exitCode !== 2) {
root.loaded = true;
}
} }
} }