Optimized the root .gitignore to exclude virtual environments, node modules, and temp folders to ensure clean and lightweight version tracking. Co-authored-by: Cursor <cursoragent@cursor.com>
214 lines
6.1 KiB
TypeScript
214 lines
6.1 KiB
TypeScript
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<TAgent extends string = string> {
|
|
version: 1;
|
|
configuredAgents: TAgent[];
|
|
activeAgent: TAgent;
|
|
workDir: string;
|
|
weixinOptions: Record<string, string>;
|
|
}
|
|
|
|
interface ParsedProjectBlock {
|
|
name: string | null;
|
|
agentType: string | null;
|
|
workDir: string | null;
|
|
weixinOptions: Record<string, string> | 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<string, string> {
|
|
const options: Record<string, string> = {};
|
|
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, string>): 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, string>): 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<TAgent extends string>(params: {
|
|
content: string;
|
|
supportedAgents: TAgent[];
|
|
blockedUserId: string;
|
|
}): TAgent | null {
|
|
const { content, supportedAgents, blockedUserId } = params;
|
|
const supportedAgentSet = new Set<string>(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<TAgent extends string>(params: {
|
|
content: string;
|
|
supportedAgents: TAgent[];
|
|
blockedUserId: string;
|
|
preferredActiveAgent?: TAgent | null;
|
|
recoveredAllowFrom?: string | null;
|
|
}): AxhubWeixinState<TAgent> | null {
|
|
const {
|
|
content,
|
|
supportedAgents,
|
|
blockedUserId,
|
|
preferredActiveAgent = null,
|
|
recoveredAllowFrom = null,
|
|
} = params;
|
|
|
|
const supportedAgentSet = new Set<string>(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<TAgent extends string>(params: {
|
|
agent: TAgent;
|
|
workDir: string;
|
|
weixinOptions: Record<string, string>;
|
|
}): 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<TAgent extends string>(params: {
|
|
agent: TAgent;
|
|
workDir: string;
|
|
}): string {
|
|
const { agent, workDir } = params;
|
|
return generateSingleAgentConfigToml({
|
|
agent,
|
|
workDir,
|
|
weixinOptions: {
|
|
token: '',
|
|
},
|
|
});
|
|
}
|