fix: 修复 serve 模式模型接口返回空数据
多层问题修复: - setup.ts: CoStrict 模型预取从 fire-and-forget 改为 await,确保缓存就绪 - sessionHandle.ts: 修复子进程 spawn 参数丢失 -d/--feature 的问题, 修复 models 数组被当 Record 处理的类型错误,始终传 --verbose - sessionManager.ts: 新增 probe session 预热机制和 initData 缓存, provider 路由不再依赖活跃 session - provider.ts: 输出格式匹配消费端 ProviderCapabilitiesResponse 类型, models 从数组转为 Record<string, ModelInfo> - session.ts: 修复 scriptArgs 计算逻辑,避免传入 serve 子命令
This commit is contained in:
parent
954c218085
commit
d5cfe0aff0
17
src/main.tsx
17
src/main.tsx
|
|
@ -5960,9 +5960,10 @@ async function run(): Promise<CommanderCommand> {
|
||||||
await import("./server/sessionManager.js");
|
await import("./server/sessionManager.js");
|
||||||
const { printBanner } =
|
const { printBanner } =
|
||||||
await import("./server/serverBanner.js");
|
await import("./server/serverBanner.js");
|
||||||
const { createServerLogger } =
|
const { getScriptArgsForChild, saveChildSpawnPrefix } =
|
||||||
await import("./server/serverLog.js");
|
await import("./server/sessionHandle.js");
|
||||||
|
|
||||||
|
await saveChildSpawnPrefix();
|
||||||
const eventBus = new EventBus();
|
const eventBus = new EventBus();
|
||||||
const config = {
|
const config = {
|
||||||
port: parseInt(opts.port, 10),
|
port: parseInt(opts.port, 10),
|
||||||
|
|
@ -5981,23 +5982,29 @@ async function run(): Promise<CommanderCommand> {
|
||||||
workspace: config.workspace,
|
workspace: config.workspace,
|
||||||
});
|
});
|
||||||
await sessionManager.init();
|
await sessionManager.init();
|
||||||
const logger = createServerLogger();
|
|
||||||
|
|
||||||
const server = startServer(config, sessionManager);
|
const server = startServer(config, sessionManager);
|
||||||
const actualPort = server.port ?? config.port;
|
const actualPort = server.port ?? config.port;
|
||||||
printBanner(config, undefined, actualPort);
|
printBanner(config, undefined, actualPort);
|
||||||
|
|
||||||
|
sessionManager.startProbeSession({
|
||||||
|
cwd: config.workspace || process.cwd(),
|
||||||
|
execPath: process.execPath,
|
||||||
|
scriptArgs: getScriptArgsForChild(),
|
||||||
|
});
|
||||||
|
|
||||||
let shuttingDown = false;
|
let shuttingDown = false;
|
||||||
const shutdown = async () => {
|
const shutdown = async () => {
|
||||||
if (shuttingDown) return;
|
if (shuttingDown) return;
|
||||||
shuttingDown = true;
|
shuttingDown = true;
|
||||||
|
sessionManager.killProbe();
|
||||||
server.stop(true);
|
server.stop(true);
|
||||||
eventBus.destroy();
|
eventBus.destroy();
|
||||||
await sessionManager.destroyAll();
|
await sessionManager.destroyAll();
|
||||||
process.exit(0);
|
process.exit(0);
|
||||||
};
|
};
|
||||||
process.once("SIGINT", () => void shutdown());
|
process.on("SIGINT", () => void shutdown());
|
||||||
process.once("SIGTERM", () => void shutdown());
|
process.on("SIGTERM", () => void shutdown());
|
||||||
};
|
};
|
||||||
|
|
||||||
program
|
program
|
||||||
|
|
|
||||||
|
|
@ -2,64 +2,91 @@ import { Hono } from 'hono'
|
||||||
import type { SessionManager } from '../sessionManager.js'
|
import type { SessionManager } from '../sessionManager.js'
|
||||||
import type { InitData } from '../sessionHandle.js'
|
import type { InitData } from '../sessionHandle.js'
|
||||||
|
|
||||||
|
type ModelInfo = {
|
||||||
|
value: string
|
||||||
|
displayName: string
|
||||||
|
description: string
|
||||||
|
supportsEffort?: boolean
|
||||||
|
supportedEffortLevels?: string[]
|
||||||
|
supportsAdaptiveThinking?: boolean
|
||||||
|
supportsFastMode?: boolean
|
||||||
|
supportsAutoMode?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
function getModels(initData: InitData | null): ModelInfo[] {
|
||||||
|
const raw = initData?.models
|
||||||
|
if (Array.isArray(raw)) return raw as ModelInfo[]
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
function getProviderId(initData: InitData | null): string {
|
||||||
|
return initData?.account?.apiProvider ?? 'anthropic'
|
||||||
|
}
|
||||||
|
|
||||||
|
function getProviderName(initData: InitData | null): string {
|
||||||
|
const p = initData?.account?.apiProvider
|
||||||
|
if (p === 'firstParty') return 'Anthropic'
|
||||||
|
if (p === 'costrict') return 'CoStrict'
|
||||||
|
return p ?? 'Anthropic'
|
||||||
|
}
|
||||||
|
|
||||||
|
function toCapabilities(initData: InitData | null) {
|
||||||
|
const models = getModels(initData)
|
||||||
|
const providerId = getProviderId(initData)
|
||||||
|
const providerName = getProviderName(initData)
|
||||||
|
const defaultModel = models[0]?.value ?? ''
|
||||||
|
|
||||||
|
const modelsRecord: Record<string, unknown> = {}
|
||||||
|
for (const m of models) {
|
||||||
|
modelsRecord[m.value] = {
|
||||||
|
id: m.value,
|
||||||
|
name: m.displayName,
|
||||||
|
limit: {
|
||||||
|
context: 200000,
|
||||||
|
output: 8192,
|
||||||
|
},
|
||||||
|
capabilities: {
|
||||||
|
temperature: false,
|
||||||
|
reasoning: true,
|
||||||
|
attachment: true,
|
||||||
|
toolcall: true,
|
||||||
|
input: { text: true, audio: false, image: true, video: false, pdf: false },
|
||||||
|
output: { text: true, audio: false, image: false, video: false, pdf: false },
|
||||||
|
interleaved: false,
|
||||||
|
},
|
||||||
|
status: 'active' as const,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
connected: [{
|
||||||
|
id: providerId,
|
||||||
|
name: providerName,
|
||||||
|
source: 'config' as const,
|
||||||
|
default_model: defaultModel,
|
||||||
|
models: modelsRecord,
|
||||||
|
}],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function createProviderRoutes(sessionManager: SessionManager): Hono {
|
export function createProviderRoutes(sessionManager: SessionManager): Hono {
|
||||||
return new Hono()
|
return new Hono()
|
||||||
.get('/provider', c => {
|
.get('/provider', async c => {
|
||||||
const initData = getFirstInitData(sessionManager)
|
let initData = sessionManager.getCachedInitData()
|
||||||
const models = initData?.models ?? {}
|
if (!initData) {
|
||||||
const modelValues = Object.values(models as Record<string, string>)
|
await sessionManager.waitForInitData()
|
||||||
const defaultModel = modelValues[0] ?? ''
|
initData = sessionManager.getCachedInitData()
|
||||||
|
|
||||||
return c.json({
|
|
||||||
connected: initData?.account?.apiProvider
|
|
||||||
? [initData.account.apiProvider]
|
|
||||||
: ['anthropic'],
|
|
||||||
default_model: defaultModel,
|
|
||||||
providers: [
|
|
||||||
{
|
|
||||||
id: initData?.account?.apiProvider ?? 'anthropic',
|
|
||||||
name:
|
|
||||||
initData?.account?.apiProvider === 'firstParty'
|
|
||||||
? 'Anthropic'
|
|
||||||
: initData?.account?.apiProvider ?? 'Anthropic',
|
|
||||||
connected: true,
|
|
||||||
models: Object.entries(models as Record<string, string>).map(
|
|
||||||
([key, id]) => ({
|
|
||||||
id,
|
|
||||||
name: key,
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.get('/provider/capabilities', c => {
|
|
||||||
const initData = getFirstInitData(sessionManager)
|
|
||||||
const models = initData?.models ?? {}
|
|
||||||
|
|
||||||
return c.json({
|
|
||||||
connected: [
|
|
||||||
{
|
|
||||||
provider_id: initData?.account?.apiProvider ?? 'anthropic',
|
|
||||||
provider_name:
|
|
||||||
initData?.account?.apiProvider === 'firstParty'
|
|
||||||
? 'Anthropic'
|
|
||||||
: initData?.account?.apiProvider ?? 'Anthropic',
|
|
||||||
models: Object.entries(models as Record<string, string>).map(
|
|
||||||
([key, id]) => ({
|
|
||||||
model_id: id,
|
|
||||||
model_name: key,
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getFirstInitData(sessionManager: SessionManager): InitData | null {
|
return c.json(toCapabilities(initData))
|
||||||
for (const handle of sessionManager.getAllSessions()) {
|
})
|
||||||
if (handle.initData) return handle.initData
|
.get('/provider/capabilities', async c => {
|
||||||
|
let initData = sessionManager.getCachedInitData()
|
||||||
|
if (!initData) {
|
||||||
|
await sessionManager.waitForInitData()
|
||||||
|
initData = sessionManager.getCachedInitData()
|
||||||
}
|
}
|
||||||
return null
|
|
||||||
|
return c.json(toCapabilities(initData))
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import { Hono } from 'hono'
|
import { Hono } from 'hono'
|
||||||
import { streamSSE } from 'hono/streaming'
|
import { streamSSE } from 'hono/streaming'
|
||||||
import type { SessionManager } from '../sessionManager.js'
|
import type { SessionManager } from '../sessionManager.js'
|
||||||
|
import { getScriptArgsForChild } from '../sessionHandle.js'
|
||||||
import {
|
import {
|
||||||
badRequest,
|
badRequest,
|
||||||
notFound,
|
notFound,
|
||||||
|
|
@ -125,7 +126,7 @@ export function createSessionRoutes(
|
||||||
systemPrompt: body.system_prompt,
|
systemPrompt: body.system_prompt,
|
||||||
resumeSessionId: body.resume_session_id,
|
resumeSessionId: body.resume_session_id,
|
||||||
execPath: process.execPath,
|
execPath: process.execPath,
|
||||||
scriptArgs: process.argv[1] ? [process.argv[1]] : [],
|
scriptArgs: getScriptArgsForChild(),
|
||||||
})
|
})
|
||||||
|
|
||||||
const initData = handle.initData
|
const initData = handle.initData
|
||||||
|
|
@ -138,7 +139,7 @@ export function createSessionRoutes(
|
||||||
created_at: handle.getInfo().created_at,
|
created_at: handle.getInfo().created_at,
|
||||||
commands: initData?.commands ?? [],
|
commands: initData?.commands ?? [],
|
||||||
agents: initData?.agents ?? [],
|
agents: initData?.agents ?? [],
|
||||||
models: initData?.models ?? {},
|
models: initData?.models ?? [],
|
||||||
account: initData?.account ?? {},
|
account: initData?.account ?? {},
|
||||||
},
|
},
|
||||||
201,
|
201,
|
||||||
|
|
|
||||||
|
|
@ -61,6 +61,52 @@ export type SessionHandleOptions = {
|
||||||
verbose?: boolean
|
verbose?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getScriptArgsForChild(): string[] {
|
||||||
|
const argv1 = process.argv[1]
|
||||||
|
if (!argv1) return []
|
||||||
|
if (argv1.endsWith('.ts') || argv1.endsWith('.tsx') || argv1.includes('/') || argv1.includes('\\')) {
|
||||||
|
return [argv1]
|
||||||
|
}
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getChildSpawnArgs(): { execPath: string; scriptArgs: string[] } {
|
||||||
|
const execPath = process.execPath
|
||||||
|
const scriptArgs = getScriptArgsForChild()
|
||||||
|
return { execPath, scriptArgs }
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function saveChildSpawnPrefix(): Promise<void> {
|
||||||
|
if (process.env._CSC_CHILD_SPAWN_PREFIX) return
|
||||||
|
const { execPath, scriptArgs } = getChildSpawnArgs()
|
||||||
|
let defineArgs: string[] = []
|
||||||
|
let featureArgs: string[] = []
|
||||||
|
try {
|
||||||
|
const definesMod = await import('../../scripts/defines.js') as { getMacroDefines: () => Record<string, string>; DEFAULT_BUILD_FEATURES: readonly string[] }
|
||||||
|
const defines = definesMod.getMacroDefines()
|
||||||
|
defineArgs = Object.entries(defines).flatMap(([k, v]) => ['-d', `${k}:${v}`])
|
||||||
|
const features = definesMod.DEFAULT_BUILD_FEATURES
|
||||||
|
featureArgs = features.flatMap((f: string) => ['--feature', f])
|
||||||
|
} catch {}
|
||||||
|
const envFeatures = Object.entries(process.env)
|
||||||
|
.filter(([k]) => k.startsWith('FEATURE_') && k.slice(8))
|
||||||
|
.map(([k]) => ['--feature', k.slice(8)] as [string, string])
|
||||||
|
.flat()
|
||||||
|
const allFeatureArgs = [...featureArgs, ...envFeatures]
|
||||||
|
const prefix = JSON.stringify({ execPath, scriptArgs, defineArgs, featureArgs: allFeatureArgs })
|
||||||
|
process.env._CSC_CHILD_SPAWN_PREFIX = prefix
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadChildSpawnPrefix(): { execPath: string; scriptArgs: string[]; defineArgs?: string[]; featureArgs?: string[] } | null {
|
||||||
|
const raw = process.env._CSC_CHILD_SPAWN_PREFIX
|
||||||
|
if (!raw) return null
|
||||||
|
try {
|
||||||
|
return JSON.parse(raw) as { execPath: string; scriptArgs: string[]; defineArgs?: string[]; featureArgs?: string[] }
|
||||||
|
} catch {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export class SessionHandle {
|
export class SessionHandle {
|
||||||
readonly sessionId: string
|
readonly sessionId: string
|
||||||
readonly cwd: string
|
readonly cwd: string
|
||||||
|
|
@ -138,8 +184,7 @@ export class SessionHandle {
|
||||||
}
|
}
|
||||||
|
|
||||||
async start(): Promise<void> {
|
async start(): Promise<void> {
|
||||||
const args = [
|
const printArgs = [
|
||||||
...this.opts.scriptArgs,
|
|
||||||
'--print',
|
'--print',
|
||||||
'--input-format',
|
'--input-format',
|
||||||
'stream-json',
|
'stream-json',
|
||||||
|
|
@ -154,7 +199,7 @@ export class SessionHandle {
|
||||||
...(this.opts.resumeSessionId
|
...(this.opts.resumeSessionId
|
||||||
? ['--resume', this.opts.resumeSessionId]
|
? ['--resume', this.opts.resumeSessionId]
|
||||||
: []),
|
: []),
|
||||||
...(this.opts.verbose ? ['--verbose'] : []),
|
'--verbose',
|
||||||
]
|
]
|
||||||
|
|
||||||
const env: NodeJS.ProcessEnv = {
|
const env: NodeJS.ProcessEnv = {
|
||||||
|
|
@ -166,7 +211,12 @@ export class SessionHandle {
|
||||||
env.CLAUDE_CODE_SYSTEM_PROMPT = this.opts.systemPrompt
|
env.CLAUDE_CODE_SYSTEM_PROMPT = this.opts.systemPrompt
|
||||||
}
|
}
|
||||||
|
|
||||||
this.child = spawn(this.opts.execPath, args, {
|
const saved = loadChildSpawnPrefix()
|
||||||
|
const defineArgs = saved?.defineArgs ?? []
|
||||||
|
const featureArgs = saved?.featureArgs ?? []
|
||||||
|
const spawnArgs = [...defineArgs, ...featureArgs, ...this.opts.scriptArgs, ...printArgs]
|
||||||
|
|
||||||
|
this.child = spawn(this.opts.execPath, spawnArgs, {
|
||||||
cwd: this.opts.cwd,
|
cwd: this.opts.cwd,
|
||||||
stdio: ['pipe', 'pipe', 'pipe'],
|
stdio: ['pipe', 'pipe', 'pipe'],
|
||||||
env,
|
env,
|
||||||
|
|
@ -293,10 +343,9 @@ export class SessionHandle {
|
||||||
if (response?.subtype === 'success' && response?.request_id === this.initRequestId) {
|
if (response?.subtype === 'success' && response?.request_id === this.initRequestId) {
|
||||||
const initData = (response.response ?? {}) as InitData
|
const initData = (response.response ?? {}) as InitData
|
||||||
this._initData = initData
|
this._initData = initData
|
||||||
if (initData.models) {
|
if (Array.isArray(initData.models) && initData.models.length > 0 && !this._model) {
|
||||||
const models = initData.models as Record<string, string>
|
const first = (initData.models as Array<Record<string, string>>)[0]
|
||||||
const vals = Object.values(models)
|
this._model = first?.value ?? first?.name
|
||||||
if (vals.length > 0 && !this._model) this._model = vals[0]
|
|
||||||
}
|
}
|
||||||
this.initResolve(initData)
|
this.initResolve(initData)
|
||||||
this.initResolve = null
|
this.initResolve = null
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import { join } from 'path'
|
||||||
import { getClaudeConfigHomeDir } from '../utils/envUtils.js'
|
import { getClaudeConfigHomeDir } from '../utils/envUtils.js'
|
||||||
import { logError } from '../utils/log.js'
|
import { logError } from '../utils/log.js'
|
||||||
import type { EventBus } from './eventBus.js'
|
import type { EventBus } from './eventBus.js'
|
||||||
import { SessionHandle } from './sessionHandle.js'
|
import { SessionHandle, type InitData } from './sessionHandle.js'
|
||||||
import type { SessionIndex, SessionIndexEntry, SessionState } from './types.js'
|
import type { SessionIndex, SessionIndexEntry, SessionState } from './types.js'
|
||||||
|
|
||||||
const INDEX_FILE = 'server-sessions.json'
|
const INDEX_FILE = 'server-sessions.json'
|
||||||
|
|
@ -17,6 +17,10 @@ export class SessionManager {
|
||||||
private defaultWorkspace?: string
|
private defaultWorkspace?: string
|
||||||
private idleCheckInterval: ReturnType<typeof setInterval> | null = null
|
private idleCheckInterval: ReturnType<typeof setInterval> | null = null
|
||||||
private indexDirty = false
|
private indexDirty = false
|
||||||
|
private _cachedInitData: InitData | null = null
|
||||||
|
private _initDataReady: Promise<void> | null = null
|
||||||
|
private _resolveInitDataReady: (() => void) | null = null
|
||||||
|
private _probeHandle: SessionHandle | null = null
|
||||||
|
|
||||||
constructor(opts: {
|
constructor(opts: {
|
||||||
eventBus: EventBus
|
eventBus: EventBus
|
||||||
|
|
@ -182,9 +186,61 @@ export class SessionManager {
|
||||||
throw err
|
throw err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (handle.initData) {
|
||||||
|
this._cachedInitData = handle.initData
|
||||||
|
}
|
||||||
|
|
||||||
return handle
|
return handle
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getCachedInitData(): InitData | null {
|
||||||
|
if (this._cachedInitData) return this._cachedInitData
|
||||||
|
for (const handle of this.sessions) {
|
||||||
|
if (handle[1].initData) return handle[1].initData
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
waitForInitData(timeoutMs = 30000): Promise<void> {
|
||||||
|
if (this._cachedInitData) return Promise.resolve()
|
||||||
|
return this._initDataReady ?? Promise.resolve()
|
||||||
|
}
|
||||||
|
|
||||||
|
startProbeSession(opts: {
|
||||||
|
cwd?: string
|
||||||
|
execPath: string
|
||||||
|
scriptArgs: string[]
|
||||||
|
}): void {
|
||||||
|
this._initDataReady = new Promise(resolve => {
|
||||||
|
this._resolveInitDataReady = resolve
|
||||||
|
})
|
||||||
|
|
||||||
|
void (async () => {
|
||||||
|
try {
|
||||||
|
const probe = await this.createSession({
|
||||||
|
cwd: opts.cwd,
|
||||||
|
execPath: opts.execPath,
|
||||||
|
scriptArgs: opts.scriptArgs,
|
||||||
|
})
|
||||||
|
this._probeHandle = probe
|
||||||
|
this._probeHandle = null
|
||||||
|
await this.deleteSession(probe.sessionId)
|
||||||
|
} catch (err) {
|
||||||
|
this._probeHandle?.forceKill()
|
||||||
|
this._probeHandle = null
|
||||||
|
}
|
||||||
|
this._resolveInitDataReady?.()
|
||||||
|
})()
|
||||||
|
}
|
||||||
|
|
||||||
|
killProbe(): void {
|
||||||
|
if (this._probeHandle) {
|
||||||
|
this._probeHandle.forceKill()
|
||||||
|
this._probeHandle = null
|
||||||
|
}
|
||||||
|
this._resolveInitDataReady?.()
|
||||||
|
}
|
||||||
|
|
||||||
async deleteSession(id: string): Promise<boolean> {
|
async deleteSession(id: string): Promise<boolean> {
|
||||||
const handle = this.sessions.get(id)
|
const handle = this.sessions.get(id)
|
||||||
if (!handle) return false
|
if (!handle) return false
|
||||||
|
|
|
||||||
30
src/setup.ts
30
src/setup.ts
|
|
@ -377,8 +377,8 @@ export async function setup(
|
||||||
profileCheckpoint('setup_after_prefetch')
|
profileCheckpoint('setup_after_prefetch')
|
||||||
|
|
||||||
// CoStrict provider: 启动时恢复凭证并预取模型列表
|
// CoStrict provider: 启动时恢复凭证并预取模型列表
|
||||||
|
// 必须同步 await,否则 serve/SDK 模式下 getModelOptions() 读取缓存时还为空
|
||||||
if (getAPIProvider() === 'costrict') {
|
if (getAPIProvider() === 'costrict') {
|
||||||
void (async () => {
|
|
||||||
try {
|
try {
|
||||||
const { loadCoStrictCredentials, saveCoStrictCredentials } = await import('./costrict/provider/credentials.js')
|
const { loadCoStrictCredentials, saveCoStrictCredentials } = await import('./costrict/provider/credentials.js')
|
||||||
const { isCoStrictTokenValid, refreshCoStrictToken, extractExpiryFromJWT } = await import('./costrict/provider/token.js')
|
const { isCoStrictTokenValid, refreshCoStrictToken, extractExpiryFromJWT } = await import('./costrict/provider/token.js')
|
||||||
|
|
@ -387,16 +387,13 @@ export async function setup(
|
||||||
|
|
||||||
const creds = await loadCoStrictCredentials()
|
const creds = await loadCoStrictCredentials()
|
||||||
if (!creds) {
|
if (!creds) {
|
||||||
// 没有凭证,清除 modelType 让下次启动回到登录界面
|
|
||||||
const { updateSettingsForSource } = await import('./utils/settings/settings.js')
|
const { updateSettingsForSource } = await import('./utils/settings/settings.js')
|
||||||
updateSettingsForSource('userSettings', { modelType: undefined } as any)
|
updateSettingsForSource('userSettings', { modelType: undefined } as any)
|
||||||
return
|
} else {
|
||||||
}
|
|
||||||
|
|
||||||
// 验证 / 刷新 token(注意 refreshCoStrictToken 接收 RefreshTokenParams,字段名与 CoStrictCredentials 不同)
|
|
||||||
let activeCreds = creds
|
let activeCreds = creds
|
||||||
if (!isCoStrictTokenValid(creds)) {
|
let tokenReady = isCoStrictTokenValid(creds)
|
||||||
if (creds.refresh_token) {
|
|
||||||
|
if (!tokenReady && creds.refresh_token) {
|
||||||
try {
|
try {
|
||||||
const refreshed = await refreshCoStrictToken({
|
const refreshed = await refreshCoStrictToken({
|
||||||
baseUrl: getCoStrictBaseURL(creds.base_url),
|
baseUrl: getCoStrictBaseURL(creds.base_url),
|
||||||
|
|
@ -411,30 +408,25 @@ export async function setup(
|
||||||
updated_at: new Date().toISOString(),
|
updated_at: new Date().toISOString(),
|
||||||
}
|
}
|
||||||
await saveCoStrictCredentials(activeCreds)
|
await saveCoStrictCredentials(activeCreds)
|
||||||
|
tokenReady = true
|
||||||
} catch {
|
} catch {
|
||||||
// 刷新失败,清除 modelType 提示重新登录
|
|
||||||
const { updateSettingsForSource } = await import('./utils/settings/settings.js')
|
const { updateSettingsForSource } = await import('./utils/settings/settings.js')
|
||||||
updateSettingsForSource('userSettings', { modelType: undefined } as any)
|
updateSettingsForSource('userSettings', { modelType: undefined } as any)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
} else {
|
} else if (!tokenReady) {
|
||||||
// 无 refresh_token,清除 modelType
|
|
||||||
const { updateSettingsForSource } = await import('./utils/settings/settings.js')
|
const { updateSettingsForSource } = await import('./utils/settings/settings.js')
|
||||||
updateSettingsForSource('userSettings', { modelType: undefined } as any)
|
updateSettingsForSource('userSettings', { modelType: undefined } as any)
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 预取模型列表,填充同步缓存
|
if (tokenReady && activeCreds.access_token) {
|
||||||
const baseUrl = getCoStrictBaseURL(activeCreds.base_url)
|
const baseUrl = getCoStrictBaseURL(activeCreds.base_url)
|
||||||
const models = await fetchCoStrictModels(baseUrl, activeCreds.access_token)
|
await fetchCoStrictModels(baseUrl, activeCreds.access_token)
|
||||||
// 模型列表已预取并缓存,不再自动设置 COSTRICT_MODEL 环境变量
|
|
||||||
// resolveCoStrictModel() 会直接透传用户配置的模型名
|
|
||||||
process.env.CLAUDE_CODE_USE_COSTRICT = '1'
|
process.env.CLAUDE_CODE_USE_COSTRICT = '1'
|
||||||
|
}
|
||||||
|
}
|
||||||
} catch {
|
} catch {
|
||||||
// 初始化失败不阻断启动
|
// 初始化失败不阻断启动
|
||||||
}
|
}
|
||||||
})()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pre-fetch data for Logo v2 - await to ensure it's ready before logo renders.
|
// Pre-fetch data for Logo v2 - await to ensure it's ready before logo renders.
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user