fix: preserve legacy macOS key capture

This commit is contained in:
wuyouMaster
2026-09-14 18:44:15 +08:00
parent 488966d771
commit c437be349e
5 changed files with 90 additions and 14 deletions
Binary file not shown.
BIN
View File
Binary file not shown.
@@ -1 +1 @@
f1bbd88584075c0e6d3487357a06424949de26d7e6dcad803d210aab83b0cbda xkey_helper
f1bbd88584075c0e6d3487357a06424949de26d7e6dcad803d210aab83b0cbda xkey_helper_4_1_13
+48 -11
View File
@@ -6,6 +6,7 @@ import { promisify } from 'util'
import { isValidDatabaseKey } from './database-key-store'
import crypto from 'crypto'
import { findResource, getResourceCandidates } from './resource-paths'
import { detectWechatVersion } from './services/connection-diagnostics'
const execFileAsync = promisify(execFile)
@@ -24,10 +25,42 @@ export interface ImageKeyResult {
error?: string
}
export type XkeyHelperMode = 'legacy' | 'wechat-4.1.13'
export function resolveXkeyHelperMode(wechatVersion: string): XkeyHelperMode {
return /^4\.1\.13(?:\.|$)/.test(wechatVersion.trim()) ? 'wechat-4.1.13' : 'legacy'
}
export function buildXkeyHelperArguments(pid: number, timeoutMs: number): string[] {
return [String(pid), String(timeoutMs), '--profile', 'wechat-4.1.13', '--account']
}
export function buildAppleSiliconXkeyInvocation(
wechatVersion: string,
pid: number,
timeoutMs: number
): {
mode: XkeyHelperMode
resourceName: string
args: string[]
waitMs: number
timeoutSeconds: number
execTimeoutMs: number
} {
const mode = resolveXkeyHelperMode(wechatVersion)
const isWechat413 = mode === 'wechat-4.1.13'
const waitMs = Math.max(isWechat413 ? 120_000 : 30_000, timeoutMs)
const timeoutSeconds = Math.ceil(waitMs / 1000) + (isWechat413 ? 10 : 30)
return {
mode,
resourceName: isWechat413 ? 'xkey_helper_4_1_13' : 'xkey_helper',
args: isWechat413 ? buildXkeyHelperArguments(pid, waitMs) : [String(pid), String(waitMs)],
waitMs,
timeoutSeconds,
execTimeoutMs: isWechat413 ? timeoutSeconds * 1000 + 5_000 : waitMs + 20_000
}
}
export function mapXkeyHelperFailure(
rawError: string,
fallbackCode = 'HELPER_RESULT_INVALID'
@@ -260,11 +293,11 @@ export class KeyServiceMac {
}
}
private getHelperPath(): string {
const helperPath = findResource('xkey_helper')
private getHelperPath(resourceName = 'xkey_helper'): string {
const helperPath = findResource(resourceName)
if (!helperPath) {
throw new Error(
`找不到 xkey_helper(已检查:${getResourceCandidates('xkey_helper').join(';')})`
`找不到 ${resourceName}(已检查:${getResourceCandidates(resourceName).join(';')})`
)
}
return helperPath
@@ -326,17 +359,17 @@ export class KeyServiceMac {
onStatus?.(result.success ? '密钥获取成功' : '密钥获取失败')
return result
}
const wechatVersion = await detectWechatVersion()
onStatus?.('正在查找微信进程...')
const pid = await this.getWeChatPid()
const helperPath = this.getHelperPath()
const waitMs = Math.max(120_000, timeoutMs)
const timeoutSeconds = Math.ceil(waitMs / 1000) + 10
const helperArguments = buildXkeyHelperArguments(pid, waitMs)
const invocation = buildAppleSiliconXkeyInvocation(wechatVersion, pid, timeoutMs)
const isWechat413 = invocation.mode === 'wechat-4.1.13'
const helperPath = this.getHelperPath(invocation.resourceName)
onStatus?.('正在请求管理员授权...')
const scriptLines = [
`set helperPath to ${JSON.stringify(helperPath)}`,
`set cmd to quoted form of helperPath & " ${helperArguments.join(' ')}"`,
`set timeoutSec to ${timeoutSeconds}`,
`set cmd to quoted form of helperPath & " ${invocation.args.join(' ')}"`,
`set timeoutSec to ${invocation.timeoutSeconds}`,
'try',
'with timeout of timeoutSec seconds',
'set outText to do shell script cmd with administrator privileges',
@@ -346,11 +379,15 @@ export class KeyServiceMac {
'return "ERR::" & errNum & "::" & errMsg',
'end try'
]
onStatus?.('授权后请在微信登录界面点击“登录”,已有登录凭据时通常不需要扫码')
onStatus?.(
isWechat413
? '授权后请在微信登录界面点击“登录”,已有登录凭据时通常不需要扫码'
: '授权后 需要在微信登录界面 点击登录微信'
)
const { stdout } = await execFileAsync(
'/usr/bin/osascript',
scriptLines.flatMap((line) => ['-e', line]),
{ timeout: timeoutSeconds * 1000 + 5_000 }
{ timeout: invocation.execTimeoutMs }
)
const output = String(stdout).trim()
if (output.startsWith('ERR::-128')) {
+41 -2
View File
@@ -7,9 +7,11 @@ vi.mock('electron', () => ({
}))
import {
buildAppleSiliconXkeyInvocation,
buildXkeyHelperArguments,
mapXkeyHelperFailure,
parseXkeyHelperOutput
parseXkeyHelperOutput,
resolveXkeyHelperMode
} from '../../src/main/key-service-mac'
describe('parseXkeyHelperOutput', () => {
@@ -71,7 +73,19 @@ describe('parseXkeyHelperOutput', () => {
})
})
it('uses the verified 4.1.13 account-key capture contract', () => {
it.each([
['4.1.8', 'legacy'],
['4.1.9.57', 'legacy'],
['4.1.10', 'legacy'],
['4.1.13', 'wechat-4.1.13'],
['4.1.13.91', 'wechat-4.1.13'],
['4.1.130', 'legacy'],
['未检测到', 'legacy']
])('selects the helper mode for WeChat %s', (version, mode) => {
expect(resolveXkeyHelperMode(version)).toBe(mode)
})
it('builds the verified 4.1.13 account-key capture contract', () => {
expect(buildXkeyHelperArguments(60037, 120_000)).toEqual([
'60037',
'120000',
@@ -81,6 +95,31 @@ describe('parseXkeyHelperOutput', () => {
])
})
it('uses the new helper only for WeChat 4.1.13', () => {
expect(buildAppleSiliconXkeyInvocation('4.1.13.91', 60037, 60_000)).toEqual({
mode: 'wechat-4.1.13',
resourceName: 'xkey_helper_4_1_13',
args: ['60037', '120000', '--profile', 'wechat-4.1.13', '--account'],
waitMs: 120_000,
timeoutSeconds: 130,
execTimeoutMs: 135_000
})
})
it.each(['4.1.8', '4.1.9.57', '4.1.10', '未检测到'])(
'preserves the legacy helper invocation for WeChat %s',
(version) => {
expect(buildAppleSiliconXkeyInvocation(version, 60037, 60_000)).toEqual({
mode: 'legacy',
resourceName: 'xkey_helper',
args: ['60037', '60000'],
waitMs: 60_000,
timeoutSeconds: 90,
execTimeoutMs: 80_000
})
}
)
it('extracts a helper error embedded in AppleScript diagnostics', () => {
expect(
mapXkeyHelperFailure(