Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 4 additions & 13 deletions src/account-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -445,16 +445,7 @@ export class AccountManager {
}

const providerScope = account?.provider
// DEBUG: Log provider scope and pool status
console.log(`[DEBUG handleRateLimit] accountId=${accountId}, providerScope=${providerScope}`)
if (providerScope) {
const pool = this.providerPools[providerScope] ?? []
console.log(`[DEBUG handleRateLimit] Pool for ${providerScope}: ${pool.length} accounts`)
for (const acc of pool) {
const state = this.state.accountStates[acc.id]
console.log(`[DEBUG handleRateLimit] - ${acc.id}: state=${state?.status}, rateLimited=${state?.rateLimitUntil}`)
}
}
// DEBUG: Log provider scope and pool status (removed console.log — TUI pollution)
const next = providerScope
? this.getNextAvailableAccount(accountId, providerScope)
: this.getNextAvailableAccount(accountId)
Expand Down Expand Up @@ -707,7 +698,7 @@ export class AccountManager {
const pool = this.providerPools[provider] ?? []
for (const acc of pool) {
if (!acc.rawEntry) continue
const logicalToken = (acc.rawEntry as any)?.access || (acc.rawEntry as any)?.apiKey
const logicalToken = (acc.rawEntry as any)?.key || (acc.rawEntry as any)?.access || (acc.rawEntry as any)?.apiKey
if (logicalToken === secret) return acc
}
return null
Expand Down Expand Up @@ -740,14 +731,14 @@ export class AccountManager {
if (!pool || pool.length === 0) continue

const physicalEntry = authData[provider] as any
const physicalToken = physicalEntry?.access || physicalEntry?.apiKey
const physicalToken = physicalEntry?.key || physicalEntry?.access || physicalEntry?.apiKey
if (!physicalToken) continue

const physicalAccount = this._findAccountBySecret(provider, physicalToken)
const best = this.getNextAvailableAccount(undefined, provider)
if (!best || !best.rawEntry) continue

const logicalToken = (best.rawEntry as any)?.access || (best.rawEntry as any)?.apiKey
const logicalToken = (best.rawEntry as any)?.key || (best.rawEntry as any)?.access || (best.rawEntry as any)?.apiKey
const isDifferent = physicalToken !== logicalToken

const accState = physicalAccount ? this.state.accountStates[physicalAccount.id] : null
Expand Down
2 changes: 1 addition & 1 deletion src/auth-json.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,6 @@ export function buildAuthJsonEntry(account: Account): AuthJsonEntry | null {
// Default: API key style
return {
type: "api",
access: primary,
key: primary,
} as any
}
31 changes: 24 additions & 7 deletions src/provider-credentials.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
import type { Account, AccountCredentials, AuthType } from "./types.js"
import { PROVIDER_ENV_VARS } from "./types.js"

export function resolveValueRef(value: string): string {
if (typeof value === "string" && value.startsWith("env:")) {
const varName = value.slice(4)
return process.env[varName] ?? value
}
return value
}

function inferDefaultEnvVar(provider: string): string {
const known = PROVIDER_ENV_VARS[provider]?.[0]
if (known) return known
Expand All @@ -19,11 +27,12 @@ export function normalizeAccount(account: Account): { account: Account; changed:

if (!account.credentials) {
const env: Record<string, string> = {}
if (account.apiKey && account.envVarName) {
env[account.envVarName] = account.apiKey
} else if (account.apiKey) {
const resolvedApiKey = account.apiKey ? resolveValueRef(account.apiKey) : undefined
if (resolvedApiKey && account.envVarName) {
env[account.envVarName] = resolvedApiKey
} else if (resolvedApiKey) {
const inferred = inferDefaultEnvVar(account.provider)
env[inferred] = account.apiKey
env[inferred] = resolvedApiKey
changed = true
}

Expand All @@ -47,16 +56,24 @@ export function normalizeAccount(account: Account): { account: Account; changed:
return { account, changed }
}

function resolveEnvVars(env: Record<string, string>): Record<string, string> {
const resolved: Record<string, string> = {}
for (const [k, v] of Object.entries(env)) {
resolved[k] = resolveValueRef(v)
}
return resolved
}

export function resolveAccountEnv(account: Account): Record<string, string> {
if (account.credentials?.env) return account.credentials.env
if (account.credentials?.env) return resolveEnvVars(account.credentials.env)

if (account.apiKey && account.envVarName) {
return { [account.envVarName]: account.apiKey }
return { [account.envVarName]: resolveValueRef(account.apiKey) }
}

if (account.apiKey) {
const inferred = inferDefaultEnvVar(account.provider)
return { [inferred]: account.apiKey }
return { [inferred]: resolveValueRef(account.apiKey) }
}

return {}
Expand Down
6 changes: 3 additions & 3 deletions src/storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,11 +148,11 @@ export function overwriteAuthJsonProvider(provider: string, rawEntry: unknown):
const raw = fs.readFileSync(activePath, "utf8")
const parsed = parseJson<AuthJsonMap>(raw)
if (typeof parsed === "object" && parsed !== null) {
// 🚨 PERFECT FORMAT FIX: Web UI butuh objek { type, access }, bukan string plain.
// opencode expects { type, key }, not { type, access }.
if (typeof rawEntry === "string") {
parsed[provider] = {
type: "api_key",
access: rawEntry
type: "api",
key: rawEntry
} as any
} else {
parsed[provider] = rawEntry as any
Expand Down