const PROJECT_BLOCK_SPLIT_REGEX = /(?=\[\[projects\]\])/; const PROJECT_NAME_REGEX = /name\s*=\s*"([^"]+)"/; const PROJECT_AGENT_TYPE_REGEX = /\[projects\.agent\]\s*\ntype\s*=\s*"([^"]+)"/; const PROJECT_WORK_DIR_REGEX = /\[projects\.agent\.options\][\s\S]*?work_dir\s*=\s*"([^"]*)"/; const WEIXIN_OPTIONS_SECTION_REGEX = /(\[\[projects\.platforms\]\]\s*\ntype\s*=\s*"weixin"\s*\n\s*\n\[projects\.platforms\.options\]\s*\n)([\s\S]*?)(?=\n\[\[projects\]\]|$)/; const OPTION_LINE_REGEX = /^([a-zA-Z0-9_]+)\s*=\s*"((?:[^"\\]|\\.)*)"$/gm; export interface AxhubWeixinState { version: 1; configuredAgents: TAgent[]; activeAgent: TAgent; workDir: string; weixinOptions: Record; } interface ParsedProjectBlock { name: string | null; agentType: string | null; workDir: string | null; weixinOptions: Record | null; } function unescapeTomlString(value: string): string { return value .replace(/\\\\/g, '\\') .replace(/\\"/g, '"') .replace(/\\n/g, '\n') .replace(/\\r/g, '\r') .replace(/\\t/g, '\t'); } function escapeTomlString(value: string): string { return value .replace(/\\/g, '\\\\') .replace(/"/g, '\\"') .replace(/\n/g, '\\n') .replace(/\r/g, '\\r') .replace(/\t/g, '\\t'); } function parseTomlOptionsBody(body: string): Record { const options: Record = {}; for (const match of body.matchAll(OPTION_LINE_REGEX)) { options[match[1]] = unescapeTomlString(match[2]); } return options; } function parseProjectBlocks(content: string): ParsedProjectBlock[] { return content .split(PROJECT_BLOCK_SPLIT_REGEX) .filter((block) => block.includes('[[projects]]')) .map((block) => { const weixinMatch = block.match(WEIXIN_OPTIONS_SECTION_REGEX); return { name: block.match(PROJECT_NAME_REGEX)?.[1] || null, agentType: block.match(PROJECT_AGENT_TYPE_REGEX)?.[1] || null, workDir: block.match(PROJECT_WORK_DIR_REGEX)?.[1] || null, weixinOptions: weixinMatch ? parseTomlOptionsBody(weixinMatch[2]) : null, }; }); } function getPreferredOptionKeys(options: Record): string[] { const preferred = ['token', 'base_url', 'account_id', 'allow_from', 'admin_from']; const remaining = Object.keys(options) .filter((key) => !preferred.includes(key)) .sort(); return [...preferred.filter((key) => key in options), ...remaining]; } function serializeTomlOptions(options: Record): string { return getPreferredOptionKeys(options) .map((key) => `${key} = "${escapeTomlString(options[key])}"`) .join('\n'); } function buildHeader(): string { return `# Auto-generated by Axhub Make — cc-connect WeChat integration # language = "zh" [log] level = "info" `; } export function findActiveAgentFromConfig(params: { content: string; supportedAgents: TAgent[]; blockedUserId: string; }): TAgent | null { const { content, supportedAgents, blockedUserId } = params; const supportedAgentSet = new Set(supportedAgents); for (const project of parseProjectBlocks(content)) { if (!project.agentType || !supportedAgentSet.has(project.agentType)) continue; const allowFrom = project.weixinOptions?.allow_from; if (allowFrom && allowFrom !== blockedUserId && allowFrom.includes('@im.wechat')) { return project.agentType as TAgent; } } return null; } export function createAxhubWeixinStateFromConfig(params: { content: string; supportedAgents: TAgent[]; blockedUserId: string; preferredActiveAgent?: TAgent | null; recoveredAllowFrom?: string | null; }): AxhubWeixinState | null { const { content, supportedAgents, blockedUserId, preferredActiveAgent = null, recoveredAllowFrom = null, } = params; const supportedAgentSet = new Set(supportedAgents); const projects = parseProjectBlocks(content).filter((project) => { return ( !!project.agentType && supportedAgentSet.has(project.agentType) && !!project.weixinOptions?.token ); }); if (projects.length === 0) { return null; } const configuredAgents = supportedAgents.filter((agent) => { return projects.some((project) => project.agentType === agent); }); if (configuredAgents.length === 0) { return null; } const activeAgentFromConfig = findActiveAgentFromConfig({ content, supportedAgents: configuredAgents, blockedUserId, }); const activeAgent = activeAgentFromConfig || (preferredActiveAgent && configuredAgents.includes(preferredActiveAgent) ? preferredActiveAgent : null) || configuredAgents[0]; const activeProject = projects.find((project) => project.agentType === activeAgent) || projects[0]; const allowFrom = activeProject.weixinOptions?.allow_from && activeProject.weixinOptions.allow_from !== blockedUserId && activeProject.weixinOptions.allow_from.includes('@im.wechat') ? activeProject.weixinOptions.allow_from : recoveredAllowFrom; if (!allowFrom) { return null; } return { version: 1, configuredAgents, activeAgent, workDir: activeProject.workDir || '', weixinOptions: { ...(activeProject.weixinOptions || {}), allow_from: allowFrom, }, }; } export function generateSingleAgentConfigToml(params: { agent: TAgent; workDir: string; weixinOptions: Record; }): string { const { agent, workDir, weixinOptions } = params; return `${buildHeader()}[[projects]] name = "axhub-${escapeTomlString(agent)}" [projects.agent] type = "${escapeTomlString(agent)}" [projects.agent.options] work_dir = "${escapeTomlString(workDir)}" mode = "yolo" [[projects.platforms]] type = "weixin" [projects.platforms.options] ${serializeTomlOptions(weixinOptions)} `; } export function generateWeixinSetupConfigToml(params: { agent: TAgent; workDir: string; }): string { const { agent, workDir } = params; return generateSingleAgentConfigToml({ agent, workDir, weixinOptions: { token: '', }, }); }