dsh-im-ops/plugin-src/client/channels/shared/token-api.js
iamyaojie 3161816cc4 feat: 按机器人选择 Agent Preset
在九个渠道的设置页卡片上选择 Host 已有预设;未选则跟随默认,只影响之后新建的会话。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-21 19:19:45 +08:00

99 lines
3.7 KiB
JavaScript

import { normalizeAgentPresetCatalog, normalizeAgentPresetId, SET_AGENT_PRESET_ENDPOINT } from '../../agent-preset.js';
const ACCOUNT_STATES = new Set(['connected', 'connecting', 'offline', 'error']);
function isRecord(value) {
return value !== null && typeof value === 'object' && !Array.isArray(value);
}
function text(value, fallback, max = 240) {
return typeof value === 'string' && value.trim() ? value.trim().slice(0, max) : fallback;
}
function id(value) {
const result = text(value, '', 128);
return /^[a-z\d_-]+$/i.test(result) ? result : undefined;
}
function timestamp(value) {
if (typeof value === 'number' && Number.isFinite(value)) return value;
const parsed = typeof value === 'string' ? Date.parse(value) : Number.NaN;
return Number.isNaN(parsed) ? undefined : parsed;
}
export const TOKEN_BOT_ENDPOINTS = Object.freeze({
status: 'connection.status',
bindCredentials: 'bot.bind-credentials',
reconnectBot: 'bot.reconnect',
deleteBot: 'bot.delete',
setWorkspace: 'bot.workspace.set',
setAgentPreset: SET_AGENT_PRESET_ENDPOINT,
});
export function createTokenChannelApi(channel, connectionSummary, {
normalizeBotExtension = () => ({}),
} = {}) {
const unwrapRpcResult = (result) => {
if (!isRecord(result) || typeof result.ok !== 'boolean') {
throw new Error(`${channel} 服务返回了无法识别的响应`);
}
if (!result.ok) {
const error = new Error(text(result.error?.message, `${channel} 操作失败`));
error.code = text(result.error?.code, `${channel.toUpperCase()}_RPC_ERROR`, 80);
throw error;
}
return result.value;
};
const normalizeBot = (value) => {
if (!isRecord(value) || !id(value.botId)) return undefined;
const connected = value.connected === true;
const state = ACCOUNT_STATES.has(value.state) ? value.state : 'offline';
const extension = normalizeBotExtension(value);
return {
botId: id(value.botId),
connected,
state: connected ? 'connected' : state,
workspace: text(value.workspace, '', 4_096),
agentPreset: normalizeAgentPresetId(value.agentPreset),
bot: {
name: text(value.bot?.name, `${channel}机器人`, 100),
username: text(value.bot?.username, '', 100),
idMasked: text(value.bot?.idMasked, '机器人标识已安全保存', 140),
},
health: {
summary: text(
value.health?.summary,
connected ? `${channel}${connectionSummary}运行正常` : `${channel}连接尚未就绪`,
),
lastCheckedAt: timestamp(value.health?.lastCheckedAt),
},
error: isRecord(value.error) ? {
code: text(value.error.code, `${channel.toUpperCase()}_ACCOUNT_ERROR`, 80),
message: text(value.error.message, `${channel}连接尚未就绪`),
} : null,
...(isRecord(extension) ? extension : {}),
};
};
const normalizeSnapshot = (value) => {
const source = isRecord(value?.snapshot) ? value.snapshot : value;
if (!isRecord(source) || !Array.isArray(source.bots)) {
throw new Error(`${channel} 服务没有返回有效的机器人列表`);
}
const bots = source.bots.map(normalizeBot).filter(Boolean);
return {
revision: Number.isSafeInteger(source.revision) ? source.revision : 0,
bots,
totals: { configured: bots.length, connected: bots.filter((bot) => bot.connected).length },
agentPresetCatalog: normalizeAgentPresetCatalog(source.agentPresetCatalog),
};
};
const presentError = (error) => ({
code: text(error?.code, `${channel.toUpperCase()}_ERROR`, 80),
message: text(error?.message, `${channel}操作失败,请稍后重试`),
});
return Object.freeze({ unwrapRpcResult, normalizeSnapshot, presentError });
}