Files
PIT_Channel/src/config.ts

213 lines
5.4 KiB
TypeScript
Raw Normal View History

/**
*
* @module config
*/
import type {
PITBotAccountConfig,
ResolvedPITBotAccount
} from "./types.js";
import type { OpenClawConfig } from "openclaw/plugin-sdk";
import { createLogger } from "./utils/logger.js";
const MODULE = "config";
interface PITChannelConfig extends PITBotAccountConfig {
accounts?: Record<string, PITBotAccountConfig>;
}
/**
* ID
*/
export const DEFAULT_ACCOUNT_ID = "default";
/**
* PIT Bot ID
* @returns ID
*/
export function listPITBotAccountIds(): string[] {
return [DEFAULT_ACCOUNT_ID];
}
/**
* PIT Bot
* @param cfg OpenClaw
* @param accountId ID
* @returns
* @throws
*/
export function resolvePITBotAccount(
cfg: OpenClawConfig,
accountId: string
): ResolvedPITBotAccount {
const log = createLogger(MODULE);
const pit = cfg.channels?.["zhidui-channel"] as PITChannelConfig | undefined;
// 如果没有配置,返回默认值
if (!pit) {
log.warn(`No zhidui-channel configuration found, using defaults`);
return {
accountId,
name: undefined,
enabled: false,
routerUrl: "",
authToken: "",
secretSource: "none",
config: {
enabled: false,
reconnectInterval: 5000,
heartbeatInterval: 30000,
heartbeatTimeout: 10000,
ackTimeout: 30000,
maxQueueSize: 100,
},
};
}
// 获取全局默认配置
const defaults: PITBotAccountConfig = {
enabled: pit.enabled ?? true,
routerUrl: pit.routerUrl,
authToken: pit.authToken,
name: pit.name,
reconnectInterval: pit.reconnectInterval ?? 5000,
heartbeatInterval: pit.heartbeatInterval ?? 30000,
heartbeatTimeout: pit.heartbeatTimeout ?? 10000,
ackTimeout: pit.ackTimeout ?? 30000,
maxQueueSize: pit.maxQueueSize ?? 100,
};
// 获取特定账户配置
let accountConfig: PITBotAccountConfig = {};
if (pit.accounts && typeof pit.accounts === "object") {
const specificConfig = pit.accounts[accountId];
if (specificConfig && typeof specificConfig === "object") {
accountConfig = specificConfig;
}
}
// 合并配置(账户配置覆盖默认配置)
const mergedConfig: PITBotAccountConfig = {
...defaults,
...accountConfig,
};
// 解析认证 Token
const { authToken, secretSource } = resolveAuthToken(mergedConfig.authToken, accountId, log);
// 验证必需字段
const routerUrl = mergedConfig.routerUrl;
if (!routerUrl || typeof routerUrl !== "string") {
log.warn(`No routerUrl configured for account ${accountId}`);
}
return {
accountId,
name: mergedConfig.name,
enabled: mergedConfig.enabled ?? true,
routerUrl: routerUrl ?? "",
authToken: authToken ?? "",
secretSource,
config: mergedConfig,
};
}
/**
* PIT Bot
* @param cfg OpenClaw
* @param accountId ID
* @param config
*/
export function applyPITBotAccountConfig(
cfg: OpenClawConfig,
accountId: string,
config: Partial<PITBotAccountConfig>
): void {
// No-op for now - configuration is managed by OpenClaw
console.log(`[zhidui-channel] applyAccountConfig called for ${accountId}:`, config);
}
/**
* Token
*
*/
function resolveAuthToken(
configToken: string | undefined,
accountId: string,
log: { warn: (msg: string) => void }
): { authToken: string | null; secretSource: "config" | "env" | "none" } {
// 1. 如果配置中直接设置了 Token
if (configToken && typeof configToken === "string") {
// 检查是否是环境变量引用 ${VAR_NAME}
const envMatch = configToken.match(/^\$\{(.+)\}$/);
if (envMatch) {
const envVar = envMatch[1];
const token = process.env[envVar];
if (!token) {
log.warn(`Environment variable ${envVar} is not set for account ${accountId}`);
return { authToken: null, secretSource: "env" };
}
return { authToken: token, secretSource: "env" };
}
return { authToken: configToken, secretSource: "config" };
}
// 2. 尝试从标准环境变量名获取
const envVarName = accountId === DEFAULT_ACCOUNT_ID
? "PIT_ROUTER_TOKEN"
: `PIT_ROUTER_TOKEN_${accountId.toUpperCase()}`;
const envToken = process.env[envVarName];
if (envToken) {
return { authToken: envToken, secretSource: "env" };
}
// 3. 无 Token
return { authToken: null, secretSource: "none" };
}
/**
*
*/
export function validateConfig(config: PITBotAccountConfig | undefined): { valid: boolean; errors: string[] } {
const errors: string[] = [];
if (!config) {
errors.push("Configuration is required");
return { valid: false, errors };
}
if (!config.routerUrl) {
errors.push("routerUrl is required");
} else if (!isValidUrl(config.routerUrl)) {
errors.push(`routerUrl is invalid: ${config.routerUrl}`);
}
if (config.reconnectInterval !== undefined && config.reconnectInterval < 1000) {
errors.push("reconnectInterval must be at least 1000ms");
}
if (config.heartbeatInterval !== undefined && config.heartbeatInterval < 1000) {
errors.push("heartbeatInterval must be at least 1000ms");
}
return {
valid: errors.length === 0,
errors,
};
}
/**
* URL
*/
function isValidUrl(url: string): boolean {
try {
new URL(url);
return true;
} catch {
return false;
}
}