import type { ComponentChildren } from "preact"; import { useEffect, useRef, useState } from "preact/hooks"; import { t } from "../../../i18n"; import { publicText } from "../../../features/scrub/scrub"; import { api, apiErrorText, ApiError } from "../../../features/customize/api"; import { custTab } from "../../../features/customize/actions"; import { asList, asString, confirmAction, hint, loadModels, refreshKeyBanner, } from "../../../features/customize/host"; import { openVolcengineAuthorization, startVolcengineKeyPolling, volcApiKeyUrl, volcPercent, volcQuotaValue, VOLC_CHECK_FAILED_STATES, VOLC_CONFIGURE_REFRESH_CODES, } from "../../../features/customize/volcengine"; import { useAlive, useTimerLease } from "../use-timer-lease"; import { Icon } from "../icons"; type VolcState = Record; function rec(value: unknown): Record { return value && typeof value === "object" && !Array.isArray(value) ? (value as Record) : {}; } function VolcBtn({ label, icon, class: cls = "outline-btn small", disabled, spinning, onClick, }: { label: string; icon: string; class?: string; disabled?: boolean; spinning?: boolean; onClick: () => void; }) { return ( ); } function VolcNotice({ tone, title, body, }: { tone?: string; title: string; body?: string; }) { const icon = tone === "ok" ? "check" : tone === "warn" ? "alert-triangle" : "terminal"; return (
{title}
{body ?
{body}
: null}
); } function VolcExternal({ label, url, icon = "globe", onOpen, }: { label: string; url: string; icon?: string; onOpen?: () => void; }) { return ( { openVolcengineAuthorization(url); if (onOpen) onOpen(); }} /> ); } export function VolcenginePanel() { const alive = useAlive(); const lease = useTimerLease(); const [state, setState] = useState({}); const [busy, setBusy] = useState(false); const [planKey, setPlanKey] = useState(""); const [keyChoice, setKeyChoice] = useState(""); const [endpointChoice, setEndpointChoice] = useState(""); const [refreshMessage, setRefreshMessage] = useState(""); const [polling, setPolling] = useState(false); const pollStop = useRef<(() => void) | null>(null); const configuring = useRef(false); const stopPoll = () => { pollStop.current?.(); pollStop.current = null; setPolling(false); }; useEffect(() => { return () => stopPoll(); }, []); const applyState = (next: VolcState) => { if (!alive()) return; setState(next); }; const refresh = async (opts?: { autoConfigure?: boolean; announce?: boolean; }): Promise => { const autoConfigure = opts?.autoConfigure !== false; const next = await api("/volcengine/refresh", { method: "POST" }); const plans = asList(next.plans).filter( (plan) => plan && rec(plan).available !== false, ); const accessState = asString(rec(next.access).state); const checkFailed = VOLC_CHECK_FAILED_STATES.has(accessState); if (alive()) { setRefreshMessage(opts?.announce && !checkFailed ? t("cust.volc.rechecked") : ""); applyState(next); } if ( autoConfigure && !next.configured && !next.linked && plans.length === 1 && accessState === "ready" ) { await configure(next, asString(rec(plans[0]).key)); } else if ( autoConfigure && !next.configured && !next.linked && accessState === "platform_ready" ) { await configure(next, "platform", "", asString(rec(next.access).endpoint_choice)); } return next; }; const configure = async ( current: VolcState, nextPlanKey: string, apiKeyChoice = "", nextEndpoint = "", ) => { if (configuring.current) return; configuring.current = true; setBusy(true); try { const result = await api("/volcengine/configure", { method: "POST", body: JSON.stringify({ plan_key: nextPlanKey, api_key_choice: apiKeyChoice || undefined, endpoint_choice: nextEndpoint || undefined, }), }); await loadModels(); await refreshKeyBanner(); applyState(rec(result.connection) || current); custTab("models"); } catch (error) { setBusy(false); const code = error instanceof ApiError ? error.code : ""; if (VOLC_CONFIGURE_REFRESH_CODES.has(code)) { try { await refresh({ autoConfigure: false }); return; } catch { /* Fall through. */ } } applyState({ ...current, _error: t("cust.volc.configureFailed", apiErrorText(error)), }); } finally { configuring.current = false; } }; const startKeyPoll = () => { stopPoll(); setPolling(true); const handle = startVolcengineKeyPolling(lease, { isAlive: alive, refresh: () => refresh({ autoConfigure: true }), onExhausted: () => { setPolling(false); applyState({ ...state }); }, }); pollStop.current = () => { handle.stop(); setPolling(false); }; }; const startLogin = async () => { let authWindow: Window | null = null; try { authWindow = window.open("about:blank", "_blank"); } catch { /* Use the fallback button. */ } try { if (authWindow) authWindow.opener = null; } catch { /* Best effort. */ } try { const login = await api("/volcengine/login", { method: "POST", body: JSON.stringify({ mode: "device" }), }); authWindow = openVolcengineAuthorization( asString(login.authorize_url), authWindow, ); applyState({ ...state, login }); } catch (error) { try { if (authWindow && !authWindow.closed) authWindow.close(); } catch { /* Ignore blocked popups. */ } applyState({ ...state, _error: apiErrorText(error) }); } }; useEffect(() => { void (async () => { try { const result = await api("/volcengine/connection"); applyState(result); } catch (error) { applyState({ state: "error", _error: apiErrorText(error) }); } })(); }, [alive]); const login = rec(state.login); const identity = rec(state.identity); const access = rec(state.access); const plans = asList(state.plans).filter( (plan) => plan && rec(plan).available !== false, ) as Record[]; let statusText = t("cust.volc.disconnected"); let statusClass = ""; if (state.state === "connected") { statusText = t("cust.volc.connected"); statusClass = " ok"; } else if (state.state === "expired") statusText = t("cust.volc.expired"); else if (state.state === "not_installed") statusText = t("cust.volc.notInstalled"); const identityDetail = identity.name ? [identity.name, identity.project_name ? t("cust.volc.project", identity.project_name) : ""] .filter(Boolean) .join(" / ") : ""; let selected = planKey || asString(state.configured_plan_key) || asString(access.plan_key); if (plans.length) { if (!plans.some((plan) => plan.key === selected)) selected = asString(plans[0]?.key); } const selectedPlan = plans.find((plan) => plan.key === selected) || null; const accessState = access.state === "plan_choice_required" && selectedPlan ? asString(selectedPlan.key_state) : asString(access.state) || (plans.length ? "key_check_failed" : "no_plan"); const configuredForSelection = !!( state.configured && state.configured_plan_key === (selected || access.plan_key) ); const resourceCheckFailed = VOLC_CHECK_FAILED_STATES.has(accessState); const body = (() => { if (state.state === "not_installed") { return (
{ window.open("https://github.com/volcengine/ark-cli", "_blank", "noopener"); }} />
); } if (login.state === "connecting") { return ( <>
{t("cust.volc.projectHint")}
{ try { const next = await api("/volcengine/login/cancel", { method: "POST" }); applyState({ ...state, login: next }); } catch (error) { hint(apiErrorText(error), true); } }} />
); } if (login.state === "awaiting_code") { return ( ); } if (login.state === "failed") { return ( ); } if (state.state !== "connected") { const prepKey = identity.project_name ? "cust.volc.reconnectPrep" : "cust.volc.loginPrep"; return ( <> {state._error ? (
{publicText(state._error, 240)}
) : null}
void startLogin()} />
); } const actions: ComponentChildren[] = []; const usageItems = asList(rec(state.usage).items).filter( (item) => !rec(item).product || rec(item).product === selected, ); const periods = usageItems.flatMap((item) => Array.isArray(rec(item).periods) ? rec(item).periods : [], ); if (accessState === "no_plan") { actions.push( , , polling ? ( {t("cust.volc.keyWaiting")} ) : ( ), ); } else if (accessState === "key_missing") { actions.push( , polling ? ( {t("cust.volc.keyWaiting")} ) : ( ), ); } else if (accessState === "key_choice_required" && configuredForSelection) { actions.push( {t("cust.volc.ready")} , ); } else if (accessState === "key_choice_required") { const choices = asList( selectedPlan && selectedPlan.key_choices ? selectedPlan.key_choices : access.key_choices, ) as Record[]; let currentKey = keyChoice; if (!choices.some((c) => c.id === currentKey)) currentKey = asString(choices[0]?.id); const endpointChoices = asList(access.endpoint_choices) as Record[]; let currentEp = endpointChoice; if (endpointChoices.length && !endpointChoices.some((c) => c.id === currentEp)) currentEp = asString(endpointChoices[0]?.id); actions.push( ,
, endpointChoices.length ? (
) : null, void configure( state, selected || asString(access.plan_key), currentKey, endpointChoices.length ? currentEp : "", ) } />, ); } else if (["profile_missing", "profile_ambiguous"].includes(accessState)) { actions.push( , void startLogin()} />, ); } else if (accessState === "plan_inactive") { actions.push( , , ); } else if (accessState === "seat_required") { actions.push( , , ); } else if (accessState === "quota_exhausted") { actions.push( , , ); } else if (configuredForSelection && resourceCheckFailed) { actions.push( , {t("cust.volc.ready")} , ); } else if (configuredForSelection) { actions.push( {t("cust.volc.ready")} , ); } else if (accessState === "platform_ready") { actions.push( , void configure(state, "platform", "", asString(access.endpoint_choice)) } />, ); } else if (accessState === "endpoint_choice_required") { const choices = asList(access.endpoint_choices) as Record[]; let currentEp = endpointChoice; if (!choices.some((c) => c.id === currentEp)) currentEp = asString(choices[0]?.id); actions.push( ,
, void configure(state, "platform", "", currentEp)} />, ); } else if (accessState === "platform_endpoint_required") { actions.push( , , ); } else if (resourceCheckFailed) { actions.push( , ); } else if (plans.length) { actions.push( void configure(state, selected)} />, ); } return ( <> {state._error ? (
{publicText(state._error, 240)}
) : null} {plans.length > 1 ? ( <>
) : null} {periods.length ? ( <>
{t("cust.volc.quota")}
{periods.map((period, i) => { const p = rec(period); const parsed = p.reset_at ? new Date(String(p.reset_at)) : null; const reset = parsed && !Number.isNaN(parsed.getTime()) ? parsed.toLocaleString() : p.reset_at; return (
{publicText(p.label, 24)} {volcQuotaValue(period)}
{reset ? (
{t("cust.volc.reset", publicText(reset, 80))}
) : null}
); })}
) : null}
{actions} void startLogin()} /> {state.linked ? ( { if (!confirmAction(t("cust.volc.disconnectConfirm"))) return; try { await api("/volcengine/disconnect", { method: "POST", body: JSON.stringify({ confirm: true }), }); await loadModels(); await refreshKeyBanner(); custTab("models"); } catch (error) { hint(apiErrorText(error), true); } }} /> ) : null}
); })(); return (
{t("cust.volc.title")}
{identityDetail ?
{identityDetail}
: null}
{statusText}
{body}
); } function RecheckButton({ refresh, applyState, state, refreshMessage, setRefreshMessage, }: { refresh: (opts?: { announce?: boolean }) => Promise; applyState: (s: VolcState) => void; state: VolcState; refreshMessage: string; setRefreshMessage: (s: string) => void; }) { const [spin, setSpin] = useState(false); return ( <> { setSpin(true); setRefreshMessage(""); try { await refresh({ announce: true }); } catch (error) { applyState({ ...state, _error: t("cust.volc.refreshFailed", apiErrorText(error)), }); } finally { setSpin(false); } }} /> {refreshMessage ? {refreshMessage} : null} ); } function AwaitingCode({ state, login, applyState, refresh, }: { state: VolcState; login: Record; applyState: (s: VolcState) => void; refresh: () => Promise; }) { const [code, setCode] = useState(""); const [busy, setBusy] = useState(false); return ( <> {state._error ? (
{publicText(state._error, 240)}
) : null}
{t("cust.volc.projectHint")}
openVolcengineAuthorization(asString(login.authorize_url))} /> setCode((e.target as HTMLInputElement).value)} /> { const value = code.trim(); if (!value) return; setBusy(true); try { await api("/volcengine/login/complete", { method: "POST", body: JSON.stringify({ code: value }), }); setCode(""); await refresh(); } catch (error) { setBusy(false); try { const next = await api("/volcengine/connection"); applyState({ ...next, _error: apiErrorText(error) }); } catch { hint(apiErrorText(error), true); } } }} /> { try { const next = await api("/volcengine/login/cancel", { method: "POST" }); applyState({ ...state, login: next }); } catch (error) { hint(apiErrorText(error), true); } }} />
); } function FailedLogin({ state, login, startLogin, refresh, }: { state: VolcState; login: Record; startLogin: () => Promise; refresh: () => Promise; }) { const code = asString(login.error_code); const detail = asString(login.error_detail || state._error || code); let notice: ComponentChildren; if (code === "project_selection_required") { notice = ( ); } else if (code === "interactive_terminal_unavailable") { notice = ( ); } else { notice = ; } return ( <> {notice}
void startLogin()} /> {state.state !== "connected" ? ( { try { await refresh(); } catch (error) { hint(t("cust.volc.refreshFailed", apiErrorText(error)), true); } }} /> ) : null}
); }