fix(sync): auto-trigger auth flow on unauthenticated sync actions (#120)

Problem: running a sync action (e.g. `:Pending gtasks push`) without
being authenticated would silently abort with a warning, requiring
the user to manually run `:Pending auth` first.

Solution: `oauth.with_token()` now auto-triggers the browser auth flow
when no token exists (for non-bundled credentials) and resumes the
original action on success. `auth()` and `_exchange_code()` now call
`on_complete(ok)` on all exit paths. S3 backends run
`aws sts get-caller-identity` before every sync action, auto-triggering
SSO login on expired sessions.
This commit is contained in:
Barrett Ruth 2026-03-10 11:36:31 -04:00
parent 422f8f9b05
commit 149f2dac2e
5 changed files with 256 additions and 5 deletions

View file

@ -66,6 +66,35 @@ local function ensure_sync_id(task)
return sync_id
end
---@return boolean
local function ensure_credentials()
local cmd = base_cmd()
vim.list_extend(cmd, { 'sts', 'get-caller-identity', '--output', 'json' })
local result = util.system(cmd, { text = true })
if result.code == 0 then
return true
end
local stderr = result.stderr or ''
if stderr:find('SSO') or stderr:find('sso') then
log.info('S3: SSO session expired — running login...')
local login_cmd = base_cmd()
vim.list_extend(login_cmd, { 'sso', 'login' })
local login_result = util.system(login_cmd, { text = true })
if login_result.code == 0 then
log.info('S3: SSO login successful')
return true
end
log.error('S3: SSO login failed — ' .. (login_result.stderr or ''))
return false
end
if stderr:find('Unable to locate credentials') or stderr:find('NoCredentialProviders') then
log.error('S3: no AWS credentials configured. See :h pending-s3')
else
log.error('S3: credential check failed — ' .. stderr)
end
return false
end
local function create_bucket()
local name = util.input({ prompt = 'S3 bucket name (pending.nvim): ' })
if not name then
@ -177,6 +206,9 @@ end
function M.push()
util.async(function()
util.with_guard('S3', function()
if not ensure_credentials() then
return
end
local s3cfg = get_config()
if not s3cfg or not s3cfg.bucket then
log.error('S3: bucket is required. Set sync.s3.bucket in config.')
@ -231,6 +263,9 @@ end
function M.pull()
util.async(function()
util.with_guard('S3', function()
if not ensure_credentials() then
return
end
local s3cfg = get_config()
if not s3cfg or not s3cfg.bucket then
log.error('S3: bucket is required. Set sync.s3.bucket in config.')
@ -330,6 +365,9 @@ end
function M.sync()
util.async(function()
util.with_guard('S3', function()
if not ensure_credentials() then
return
end
local s3cfg = get_config()
if not s3cfg or not s3cfg.bucket then
log.error('S3: bucket is required. Set sync.s3.bucket in config.')
@ -466,5 +504,6 @@ function M.health()
end
M._ensure_sync_id = ensure_sync_id
M._ensure_credentials = ensure_credentials
return M