UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

243 lines (242 loc) • 10.6 kB
import { o as normalizeLowercaseStringOrEmpty } from "./string-coerce-1VuiLaaR.mjs"; import { n as isProviderLoginPatchPersisted, t as decideProviderLoginSessionAdoption } from "./auth-profile-override-provenance-Jztnei-C.mjs"; import { t as escapeHtml } from "./html-escape-BMD_QFeA.mjs"; import { f as formatProviderLoginCommand } from "./oauth-refresh-failure-B2gKOXl5.mjs"; import { t as danger } from "./globals-DyT306Z_.mjs"; import { i as resolveCommandAuthorization } from "./command-auth-CUBIvgy7.mjs"; import "./string-coerce-runtime-50qKeWeL.mjs"; import "./text-utility-runtime-BDs7x8Fk.mjs"; import { f as resolveStorePath, s as patchSessionEntry } from "./session-store-runtime-DSst5RB7.mjs"; import { n as formatProviderLoginCompletion, r as formatProviderLoginFailure } from "./provider-login-recovery-gHCWblhB.mjs"; import "./runtime-env-C0XPA9NZ.mjs"; import "./command-auth-native-BNTFtG_H.mjs"; import { a as createDeferred } from "./extension-shared-GoQu59iC.mjs"; import { a as offerProviderLoginModelAccess, c as releaseProviderLoginFlow, d as runProviderChannelLoginFlow, i as createProviderLoginFlowRegistry, l as reserveProviderLoginFlow, o as prepareProviderChannelLogin, r as cancelProviderLoginFlow, s as refreshProviderLoginAuthState, t as answerProviderLoginModelAccess } from "./provider-auth-login-flow-runtime-DmjA6luv.mjs"; import { s as buildTelegramRoutingTarget } from "./helpers-8klSsO8Z.mjs"; import { t as withTelegramApiErrorLogging } from "./api-logging-CaOm1ysK.mjs"; import { t as defaultTelegramNativeCommandDeps } from "./bot-native-command-deps.runtime.js"; //#region extensions/telegram/src/bot-native-command-login.ts const activeTelegramProviderLoginFlows = createProviderLoginFlowRegistry(); function formatTelegramLoginDeviceCode(params) { return [ `<b>${escapeHtml(params.title)}</b>`, "", ...params.message ? [escapeHtml(params.message)] : [], `Code: <code>${escapeHtml(params.code)}</code>`, ...params.expiresInMinutes ? [`Code expires in ${params.expiresInMinutes} minutes. Never share it.`] : [] ].join("\n"); } function buildTelegramProviderLoginFlowKey(dispatch) { const threadKey = dispatch.threadSpec.id == null ? dispatch.threadSpec.scope : `${dispatch.threadSpec.scope}:${dispatch.threadSpec.id}`; return [ "telegram", dispatch.route.accountId, String(dispatch.chatId), threadKey, dispatch.route.agentId ].join(":"); } async function executeTelegramLoginCommand(params) { const { dispatch } = params; const sendLoginMessage = async (text, parseMode) => { await withTelegramApiErrorLogging({ operation: "sendMessage", runtime: dispatch.runtime, fn: () => dispatch.bot.api.sendMessage(dispatch.chatId, text, { ...dispatch.threadParams, ...parseMode ? { parse_mode: parseMode } : {} }) }); }; const sendLoginResultMessage = async (text) => { await dispatch.telegramDeps.sendMessageTelegram(buildTelegramRoutingTarget(dispatch.chatId, dispatch.threadSpec), text, { cfg: dispatch.runtimeCfg, token: dispatch.opts.token, accountId: dispatch.route.accountId }); }; const assertCurrent = (config = dispatch.telegramDeps.getRuntimeConfig()) => { dispatch.assertOwnerCurrent?.(); const authorization = resolveCommandAuthorization({ cfg: config, ctx: dispatch.ownerContext, commandAuthorized: dispatch.commandAuthorized }); if (!dispatch.senderIsOwner || !authorization.senderIsOwner || !authorization.isAuthorizedSender) throw new Error("Provider login authority is no longer active."); }; const sendLoginReply = async (reply) => { const { deliverReplies } = await dispatch.loadDeliveryRuntime(); return (await deliverReplies({ replies: [reply], ...dispatch.buildDeliveryBaseOptions({ sessionKeyForInternalHooks: dispatch.targetSessionKey, policySessionKey: dispatch.targetSessionKey }) })).delivered; }; const prepared = await prepareProviderChannelLogin({ commandText: params.commandText, commandAuthorized: dispatch.commandAuthorized, senderIsOwner: dispatch.senderIsOwner, isPrivateChat: dispatch.msg.chat.type === "private", config: dispatch.runtimeCfg, agentId: dispatch.route.agentId, signal: dispatch.opts.accountAbortSignal, refreshAuth: () => refreshProviderLoginAuthState({ agentId: dispatch.route.agentId, readConfig: dispatch.telegramDeps.getRuntimeConfig, assertCurrent: (config) => { dispatch.opts.accountAbortSignal?.throwIfAborted(); assertCurrent(config); } }), cancelLogin: () => cancelProviderLoginFlow({ flows: activeTelegramProviderLoginFlows, flowKey: buildTelegramProviderLoginFlowKey(dispatch) }), answerChoice: (command) => answerProviderLoginModelAccess({ flows: activeTelegramProviderLoginFlows, flowKey: buildTelegramProviderLoginFlowKey(dispatch), command, agentId: dispatch.route.agentId, readConfig: dispatch.telegramDeps.getRuntimeConfig, runtime: dispatch.runtime, signal: dispatch.opts.accountAbortSignal, assertCurrent }) }); if (!prepared) return false; if (prepared.status !== "ready") { if (!prepared.reply.presentation) { await sendLoginMessage(prepared.reply.text); return prepared.status === "reply"; } return await sendLoginReply(prepared.reply) && prepared.status === "reply"; } const loginChoice = prepared.choice; const flowKey = buildTelegramProviderLoginFlowKey(dispatch); const reservation = reserveProviderLoginFlow({ flows: activeTelegramProviderLoginFlows, flowKey, providerLabel: loginChoice.providerLabel, signal: dispatch.opts.accountAbortSignal }); if (reservation.status === "active") { await sendLoginMessage(`${reservation.providerLabel} sign-in is already in progress. Finish it, or send /login cancel to cancel.`); return true; } const flowSignal = reservation.record.signal; const signInActionDelivered = createDeferred(); let signInActionWasDelivered = false; const sendLoginAction = async (reply) => { flowSignal.throwIfAborted(); if (!await sendLoginReply(reply)) throw new Error("Provider sign-in action could not be delivered."); flowSignal.throwIfAborted(); signInActionWasDelivered = true; signInActionDelivered.resolve(); }; const completion = (async () => { let terminalMessage; let modelAccess; try { const targetSessionEntryAtStart = dispatch.nativeCommandRuntime.getSessionEntry({ agentId: dispatch.route.agentId, sessionKey: dispatch.targetSessionKey }); const loginResult = await runProviderChannelLoginFlow({ runLoginFlow: dispatch.telegramDeps.runModelsAuthLoginFlow ?? defaultTelegramNativeCommandDeps.runModelsAuthLoginFlow, choice: loginChoice, agentId: dispatch.route.agentId, config: dispatch.runtimeCfg, readConfig: dispatch.telegramDeps.getRuntimeConfig, runtime: dispatch.runtime, signal: flowSignal, assertCurrent, sendMessage: sendLoginMessage, sendReply: sendLoginAction, onModelAccessRequested: (request) => { modelAccess = request; }, sendDeviceCode: async (deviceCode) => { flowSignal.throwIfAborted(); await sendLoginMessage(formatTelegramLoginDeviceCode(deviceCode), "HTML"); flowSignal.throwIfAborted(); signInActionWasDelivered = true; signInActionDelivered.resolve(); }, unsupportedPromptMessage: "This provider needs input that Telegram cannot collect. Open Control UI → Models and choose Sign in." }); flowSignal.throwIfAborted(); const nextProfileId = loginResult.profiles.find((profile) => normalizeLowercaseStringOrEmpty(profile.provider) === normalizeLowercaseStringOrEmpty(loginChoice.providerId))?.profileId; let sessionSwitchFailed = !nextProfileId; if (nextProfileId && normalizeLowercaseStringOrEmpty(params.currentProvider) === normalizeLowercaseStringOrEmpty(loginChoice.providerId)) { const storePath = resolveStorePath(dispatch.runtimeCfg.session?.store, { agentId: dispatch.route.agentId }); let entryObserved = false; let adoptionDecision; try { const persisted = await patchSessionEntry({ sessionKey: dispatch.targetSessionKey, storePath, requireWriteSuccess: true, skipMaintenance: true, assertCommitAllowed: () => { flowSignal.throwIfAborted(); assertCurrent(); }, update: (entry) => { entryObserved = true; adoptionDecision = decideProviderLoginSessionAdoption({ currentModelProvider: params.currentProvider, loginProvider: loginChoice.providerId, nextProfileId, snapshot: targetSessionEntryAtStart, current: entry }); return adoptionDecision.status === "patch" ? adoptionDecision.patch : null; } }); flowSignal.throwIfAborted(); if (entryObserved && (adoptionDecision?.status === "rejected" || !persisted || adoptionDecision?.status === "patch" && !isProviderLoginPatchPersisted(persisted, nextProfileId))) sessionSwitchFailed = true; } catch (error) { flowSignal.throwIfAborted(); dispatch.runtime.error?.(danger(`telegram ${formatProviderLoginCommand(loginChoice.command)} completed but failed to update session auth profile: ${String(error)}`)); sessionSwitchFailed = true; } } const sessionModel = targetSessionEntryAtStart?.modelOverride ?? targetSessionEntryAtStart?.model; terminalMessage = formatProviderLoginCompletion(loginChoice, loginResult.authRefresh, sessionSwitchFailed, nextProfileId && sessionModel ? { model: sessionModel, profileId: nextProfileId } : void 0); } catch (error) { modelAccess = void 0; if (flowSignal.aborted) return; dispatch.runtime.error?.(danger(`telegram ${formatProviderLoginCommand(loginChoice.command)} failed: ${String(error)}`)); terminalMessage = formatProviderLoginFailure(loginChoice, error); } if (flowSignal.aborted) return; try { if (modelAccess) { const reply = offerProviderLoginModelAccess({ flows: activeTelegramProviderLoginFlows, flowKey, prepared: modelAccess, terminalMessage }); await sendLoginAction(reply); } else await sendLoginResultMessage(terminalMessage); } catch (error) { dispatch.runtime.error?.(danger(`telegram ${formatProviderLoginCommand(loginChoice.command)} result notification failed: ${String(error)}`)); } })().finally(() => { releaseProviderLoginFlow({ flows: activeTelegramProviderLoginFlows, flowKey, record: reservation.record }); }); await Promise.race([signInActionDelivered.promise, completion]); return signInActionWasDelivered; } //#endregion export { executeTelegramLoginCommand };