fix(update): avoid SSH auth for passive official checks

This commit is contained in:
helix4u
2026-06-11 12:45:07 +05:30
committed by kshitijk4poor
parent 4829f8d2c5
commit cedd9b6d47
3 changed files with 159 additions and 2 deletions
+73 -1
View File
@@ -278,6 +278,8 @@ const PROFILE_NAME_RE = /^[a-z0-9][a-z0-9_-]{0,63}$/
// tracks main. User can also override at runtime via
// hermesDesktop.updates.setBranch().
const DEFAULT_UPDATE_BRANCH = 'main'
const OFFICIAL_REPO_HTTPS_URL = 'https://github.com/NousResearch/hermes-agent.git'
const OFFICIAL_REPO_CANONICAL = 'github.com/nousresearch/hermes-agent'
// desktop.log lives under HERMES_HOME/logs/ so it sits next to agent.log,
// errors.log, gateway.log produced by hermes_logging.setup_logging — one log
// directory per user, regardless of which UI surface produced the line.
@@ -1312,6 +1314,40 @@ function runGit(args, options = {}) {
const firstLine = text => (text || '').split('\n').find(Boolean) || ''
function canonicalGitHubRemote(url) {
if (!url) return ''
let value = String(url).trim()
if (value.startsWith('git@github.com:')) {
value = `github.com/${value.slice('git@github.com:'.length)}`
} else if (value.startsWith('ssh://git@github.com/')) {
value = `github.com/${value.slice('ssh://git@github.com/'.length)}`
} else {
try {
const parsed = new URL(value)
if (parsed.hostname && parsed.pathname) value = `${parsed.hostname}${parsed.pathname}`
} catch {
// Leave non-URL forms unchanged.
}
}
value = value.trim().replace(/\/+$/, '')
if (value.endsWith('.git')) value = value.slice(0, -4)
return value.toLowerCase()
}
function isSshRemote(url) {
const value = String(url || '').trim().toLowerCase()
return value.startsWith('git@') || value.startsWith('ssh://')
}
function isOfficialSshRemote(url) {
return isSshRemote(url) && canonicalGitHubRemote(url) === OFFICIAL_REPO_CANONICAL
}
async function getOriginUrl(updateRoot) {
const origin = await runGit(['remote', 'get-url', 'origin'], { cwd: updateRoot })
return origin.code === 0 ? origin.stdout.trim() : ''
}
function emitUpdateProgress(payload) {
const merged = { stage: 'idle', message: '', percent: null, error: null, ...payload, at: Date.now() }
rememberLog(`[updates] ${merged.stage}: ${merged.message || merged.error || ''}`)
@@ -1331,7 +1367,9 @@ async function resolveHealedBranch(updateRoot, branch) {
return branch || 'main'
}
const probe = await runGit(['ls-remote', '--exit-code', '--heads', 'origin', branch], { cwd: updateRoot })
const originUrl = await getOriginUrl(updateRoot)
const remote = isOfficialSshRemote(originUrl) ? OFFICIAL_REPO_HTTPS_URL : 'origin'
const probe = await runGit(['ls-remote', '--exit-code', '--heads', remote, branch], { cwd: updateRoot })
if (probe.code !== 2) {
return branch
}
@@ -1359,6 +1397,40 @@ async function checkUpdates() {
}
branch = await resolveHealedBranch(updateRoot, branch)
const originUrl = await getOriginUrl(updateRoot)
if (isOfficialSshRemote(originUrl)) {
const git = args => runGit(args, { cwd: updateRoot }).then(r => r.stdout.trim())
const [currentSha, target, dirtyStr, currentBranch] = await Promise.all([
git(['rev-parse', 'HEAD']),
runGit(['ls-remote', OFFICIAL_REPO_HTTPS_URL, `refs/heads/${branch}`], { cwd: updateRoot }),
git(['status', '--porcelain']),
git(['rev-parse', '--abbrev-ref', 'HEAD'])
])
const targetSha = firstLine(target.stdout).split(/\s+/)[0] || ''
if (target.code !== 0 || !targetSha) {
return {
supported: true,
branch,
error: 'fetch-failed',
message: firstLine(target.stderr) || 'git ls-remote failed.',
hermesRoot: updateRoot,
fetchedAt: Date.now()
}
}
return {
supported: true,
branch,
currentBranch,
behind: currentSha && currentSha === targetSha ? 0 : 1,
currentSha,
targetSha,
commits: [],
dirty: dirtyStr.length > 0,
hermesRoot: updateRoot,
fetchedAt: Date.now()
}
}
const fetched = await runGit(['fetch', '--quiet', 'origin', branch], { cwd: updateRoot })
if (fetched.code !== 0) {
return {