fix: honor MiMo native provider configuration when checking readiness

Recognize the selected custom Provider instead of treating official-account
whoami output as proof that a configured native model cannot run. Keep native
model and credential selection inside MiMo and isolate auth probes in tests.

Checks on the combined working tree: focused context/preferences/MiMo/preload
47/47 passed; readiness/WorkBuddy/catalog 71/71 passed. Real MiMo ACP first call,
native Session continuation, and production Electron group invocation passed
without forcing a model. git diff --cached --check passed.
This commit is contained in:
Ryder Sun committed 2026-10-20 14:31:41 +08:00
1 parent f77fd8b0c0
commit 7f9e95d86c
4 files changed
+69 -4

No files matched your search

@@ -0,0 +1,23 @@
const fs = require('node:fs')
const path = require('node:path')
const os = require('node:os')
function mimoConfiguredProvider(options = {}) {
const env = options.env || process.env
const root = env.XDG_CONFIG_HOME || path.join(options.home || os.homedir(), '.config')
try {
const file = path.join(root, 'mimocode', 'mimocode.json')
if (fs.statSync(file).size > 2 * 1024 * 1024) return false
const config = JSON.parse(fs.readFileSync(file, 'utf8'))
if (typeof config.model !== 'string' || !config.model.includes('/')) return false
const provider = config.provider?.[config.model.split('/')[0]]
let key = provider?.options?.apiKey
if (typeof key !== 'string' || !key.trim()) return false
const reference = key.match(/^\{env:([A-Za-z_][A-Za-z0-9_]*)\}$/)
if (reference) key = env[reference[1]]
else if (/^\{.*\}$/.test(key)) return false
return typeof key === 'string' && Boolean(key.trim())
} catch { return false }
}
module.exports = { mimoConfiguredProvider }
@@ -7,6 +7,7 @@ const { promisify } = require('node:util')
const { prepareCommand, searchPath } = require('./cli/cli-adapters.cjs')
const { isOpenClawSecretReference } = require('./cli/openclaw-runtime.cjs')
const { NETWORK_ENV_KEYS, networkEnvironment } = require('./cli/cli-network-environment.cjs')
const { mimoConfiguredProvider } = require('./cli/mimo-native-config.cjs')
const execFileAsync = promisify(execFile)
@@ -746,6 +747,9 @@ async function queryMimoAuthState(options = {}) {
async function resolveNativeCredentialState(kind, options = {}) {
const current = nativeCredentialState(kind, options)
if (kind === 'mimo') {
if (current.state === 'ready' || mimoConfiguredProvider(options)) {
return { state: 'ready', source: 'native-credential' }
}
return await queryMimoAuthState(options) || current
}
if (kind === 'openclaw') {
@@ -0,0 +1,29 @@
const test = require('node:test')
const assert = require('node:assert/strict')
const fs = require('node:fs')
const os = require('node:os')
const path = require('node:path')
const { resolveNativeCredentialState } = require('../../../src/agents/local-agent-readiness.cjs')
test('MiMo selected native provider remains usable without signing in to the built-in account', async t => {
const home = fs.mkdtempSync(path.join(os.tmpdir(), 'meldwork-mimo-selected-'))
t.after(() => fs.rmSync(home, { recursive: true, force: true }))
const root = path.join(home, '.config', 'mimocode')
fs.mkdirSync(root, { recursive: true })
const config = { model: 'private/text', provider: { private: { options: { apiKey: 'synthetic-key' } } } }
const file = path.join(root, 'mimocode.json')
fs.writeFileSync(file, JSON.stringify(config))
const probe = async () => ({ stdout: 'Not logged in. Run `mimo auth login` to log in.' })
const options = { home, env: {}, executable: '/tmp/mimo', execFileFn: probe }
const result = await resolveNativeCredentialState('mimo', options)
assert.deepEqual(result, { state: 'ready', source: 'native-credential' })
assert.equal(JSON.stringify(result).includes('synthetic-key'), false)
config.model = 'unconfigured/text'
fs.writeFileSync(file, JSON.stringify(config))
assert.equal((await resolveNativeCredentialState('mimo', options)).state, 'missing')
config.model = 'private/text'
config.provider.private.options.apiKey = '{env:PRIVATE_MODEL_KEY}'
fs.writeFileSync(file, JSON.stringify(config))
assert.equal((await resolveNativeCredentialState('mimo', options)).state, 'missing')
assert.equal((await resolveNativeCredentialState('mimo', { ...options, env: { PRIVATE_MODEL_KEY: 'native-env-value' } })).state, 'ready')
})
@@ -771,13 +771,16 @@ test('OpenClaw model status overrides a stale gateway token without exposing aut
}
})
test('MiMo readiness uses the installed CLI identity without forwarding private values', async () => {
test('MiMo readiness uses the installed CLI identity without forwarding private values', async (t) => {
const home = fs.mkdtempSync(path.join(os.tmpdir(), 'meldwork-mimo-auth-'))
t.after(() => fs.rmSync(home, { recursive: true, force: true }))
const calls = []
for (const stdout of [
'\u001b[32mProvider: MiMo\u001b[0m\nUser ID: user-123',
'Provider: MiMo\nType: OAuth',
]) {
const result = await resolveNativeCredentialState('mimo', {
home,
env: { PATH: '/usr/bin', MELDWORK_PRIVATE_VALUE: 'desktop-private-value' },
executable: '/tmp/mimo',
execFileFn: async (command, args, options) => {
@@ -795,8 +798,11 @@ test('MiMo readiness uses the installed CLI identity without forwarding private
assert.equal(calls.every(call => call.env.MELDWORK_PRIVATE_VALUE === undefined), true)
})
test('MiMo readiness recognizes ANSI-colored logged-out output even when the CLI exits zero', async () => {
test('MiMo readiness recognizes ANSI-colored logged-out output even when the CLI exits zero', async (t) => {
const home = fs.mkdtempSync(path.join(os.tmpdir(), 'meldwork-mimo-auth-'))
t.after(() => fs.rmSync(home, { recursive: true, force: true }))
const result = await resolveNativeCredentialState('mimo', {
home,
env: {},
executable: '/tmp/mimo',
execFileFn: async () => ({
@@ -807,8 +813,10 @@ test('MiMo readiness recognizes ANSI-colored logged-out output even when the CLI
assert.deepEqual(result, { state: 'missing', source: 'native-auth-status' })
})
test('MiMo readiness remains unknown for missing or malformed identity output', async () => {
assert.deepEqual(await resolveNativeCredentialState('mimo', {}), {
test('MiMo readiness remains unknown for missing or malformed identity output', async (t) => {
const home = fs.mkdtempSync(path.join(os.tmpdir(), 'meldwork-mimo-auth-'))
t.after(() => fs.rmSync(home, { recursive: true, force: true }))
assert.deepEqual(await resolveNativeCredentialState('mimo', { home, env: {} }), {
state: 'unknown', source: 'unverified',
})
@@ -820,6 +828,7 @@ test('MiMo readiness remains unknown for missing or malformed identity output',
'Authentication status unavailable',
]) {
const result = await resolveNativeCredentialState('mimo', {
home,
env: {},
executable: '/tmp/mimo',
execFileFn: async () => ({ stdout }),