fix(portable): Gemini keys, Telegram pairing UI, and config durability

Improve first-run and channel setup for non-technical users: detect newer
Gemini key formats, pin Node 22.22.3, add Control Panel Telegram approve
flow, and keep channels/models when config is rewritten. Persist uclaw
wizard state via uclaw-meta.json so restarts skip language/persona prompts.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-08-18 18:45:03 +08:00
parent 0be8c3e3fe
commit dd8e0f55c3
35 changed files with 2673 additions and 34 deletions

View File

@@ -5,7 +5,7 @@ import vm from 'node:vm';
import test from 'node:test';
import assert from 'node:assert/strict';
import { detectProvider, classifyFailure } from '../portable/lib/provider-detect.mjs';
import { detectProvider, classifyFailure, modelsToTry } from '../portable/lib/provider-detect.mjs';
const repoRoot = fileURLToPath(new URL('..', import.meta.url));
const read = (...parts) => readFileSync(join(repoRoot, ...parts), 'utf8');
@@ -103,6 +103,7 @@ test('API keys are identified by prefix so nobody has to know what a Base URL is
['sk-ant-api03-abc', 'anthropic'],
['sk-or-v1-abc', 'openrouter'],
['AIzaSyABC', 'google'],
['AQ.Ab8CExampleKey', 'google'],
['gsk_abc', 'groq'],
['sk-proj-abc', 'openai'],
['sk-plain', 'openai'],
@@ -116,6 +117,13 @@ test('API keys are identified by prefix so nobody has to know what a Base URL is
assert.equal(detectProvider('sk-ant-x').id, 'anthropic');
});
test('Gemini model checks fall back when the first model 404s for this account', () => {
const google = detectProvider('AQ.AbExample');
const tries = modelsToTry(google, 'gemini-2.5-flash');
assert.ok(tries.includes('gemini-2.0-flash'), 'should try 2.0-flash after a 404 on 2.5');
assert.ok(tries[0] === 'gemini-2.5-flash', 'requested model should be tried first');
});
test('every key-check failure maps to a message that says what to do next', () => {
const outcomes = [
{ status: 401 }, { status: 403 }, { status: 429 }, { status: 404 }, { status: 503 },