--- jasper/auth.lua --- Token management, shared with the jasper_waybar.py Python script. --- Token cache: $XDG_CONFIG_HOME/jasper/token.json (same path as Python) local M = {} local JASPER_URL = "https://jasper.4sigma.it" --- @return string path to the token cache file local function token_file_path() local xdg = os.getenv("XDG_CONFIG_HOME") or (os.getenv("HOME") .. "/.config") return xdg .. "/jasper/token.json" end --- Read the cached auth token from disk. --- @return string|nil function M.get_token() local path = token_file_path() if vim.fn.filereadable(path) ~= 1 then return nil end local lines = vim.fn.readfile(path) if not lines or #lines == 0 then return nil end local ok, data = pcall(vim.fn.json_decode, table.concat(lines, "\n")) if ok and type(data) == "table" and data.auth_token then return data.auth_token end return nil end --- Delete the cached token (e.g. on expiry). function M.delete_token() local path = token_file_path() if vim.fn.filereadable(path) == 1 then vim.fn.delete(path) end end --- Perform a login request and cache the resulting token. --- @param username string --- @param password string --- @return string|nil token --- @return string|nil error message function M.login(username, password) local result = vim.system({ "curl", "-s", "--data-urlencode", "username=" .. username, "--data-urlencode", "password=" .. password, JASPER_URL .. "/api/v1/token/login/", }, { text = true }):wait() if result.code ~= 0 then return nil, "curl error (exit " .. result.code .. ")" end local ok, data = pcall(vim.fn.json_decode, result.stdout) if not ok or type(data) ~= "table" or not data.auth_token then return nil, "login failed (bad credentials or unexpected response)" end -- Save to cache (same format as Python script) local path = token_file_path() local dir = vim.fn.fnamemodify(path, ":h") vim.fn.mkdir(dir, "p") vim.fn.writefile({ vim.fn.json_encode(data) }, path) return data.auth_token, nil end --- Interactively ask the user for credentials using vim.ui.input, --- then call login(). Calls `callback(token)` on success, `callback(nil)` on failure. --- @param callback fun(token: string|nil) function M.prompt_login(callback) vim.ui.input({ prompt = "Jasper username: " }, function(username) if not username or username == "" then vim.notify("[Jasper] Login cancelled", vim.log.levels.WARN) callback(nil) return end vim.ui.input({ prompt = "Jasper password: ", secret = true }, function(password) if not password or password == "" then vim.notify("[Jasper] Login cancelled", vim.log.levels.WARN) callback(nil) return end local token, err = M.login(username, password) if err or not token then vim.notify("[Jasper] " .. (err or "Login failed"), vim.log.levels.ERROR) callback(nil) return end vim.notify("[Jasper] Login successful", vim.log.levels.INFO) callback(token) end) end) end return M