standalone-entry.js 4.25 KB
/**
 * Standalone Entry - 独立运行入口
 *
 * 三个职责:
 *   1. runMasterSession()  — Master CLI 会话(默认入口,工具编排)
 *   2. runOrchestration()  — 完整多 Agent 编排(向后兼容 + 直接执行)
 *   3. runSingleAgent()    — 单个 Agent 执行(给 Extension mas-run 命令用)
 *
 * 共享进程级 RunRegistry 单例,确保同一运行时的进程追踪一致。
 */

const path = require('path');
const fs = require('fs');
const { RunRegistry } = require('./engine');

// ─── 进程级单例 ───
let _sharedRegistry = null;
function getRegistry() {
  if (!_sharedRegistry) _sharedRegistry = new RunRegistry();
  return _sharedRegistry;
}

/**
 * 运行完整编排流程
 *
 * @param {string} task         - 任务描述
 * @param {object} [options]
 * @param {string} [options.workDir]       - 工作目录 (默认 cwd)
 * @param {number} [options.maxRepairs]    - 最大修复轮次 (默认 5)
 * @param {string[]} [options.agentFilter] - Agent 过滤列表
 * @param {object} [options.config]        - 自定义配置 (默认读 agents.json)
 * @returns {Promise<object>}
 */
async function runOrchestration(task, options = {}) {
  const { Orchestrator } = require('./core/orchestrator');
  const config = options.config || loadConfig(options.agentFilter);
  const workDir = options.workDir || process.cwd();
  const registry = getRegistry();

  const orchestrator = new Orchestrator(config, workDir, {
    runRegistry: registry,
  });

  try {
    return await orchestrator.execute(task, {
      maxIterations: options.maxRepairs || 5,
    });
  } finally {
    await orchestrator.shutdown();
  }
}

/**
 * 运行单个 Agent
 *
 * @param {string} agentName   - Agent 名称
 * @param {string} prompt      - 任务 prompt
 * @param {object} [options]
 * @param {string} [options.workDir] - 工作目录
 * @returns {Promise<object>}
 */
async function runSingleAgent(agentName, prompt, options = {}) {
  const AgentManager = require('./core/agent-manager');
  const config = loadConfig();
  const workDir = options.workDir || process.cwd();
  const registry = getRegistry();

  const manager = new AgentManager(config, workDir, { runRegistry: registry });
  try {
    return await manager.callAgent(agentName, prompt);
  } finally {
    await manager.closeAllSessions();
  }
}

/**
 * 加载配置
 * @param {string[]} [agentFilter] - 只保留指定 Agent (master 始终保留)
 */
function loadConfig(agentFilter) {
  const configDir = path.resolve(__dirname, '../config');
  const backends = JSON.parse(fs.readFileSync(path.join(configDir, 'backends.json'), 'utf-8'));
  const { loadConfigFromSkills } = require('./core/skill-config-loader');
  const skillsDir = path.resolve(__dirname, '../../skills');
  const config = loadConfigFromSkills(skillsDir, backends);

  if (agentFilter?.length > 0) {
    const filtered = {};
    for (const name of agentFilter) {
      if (config.agents[name]) filtered[name] = config.agents[name];
    }
    // master 始终保留
    if (config.agents.master) filtered.master = config.agents.master;
    config.agents = filtered;
  }

  return config;
}

/**
 * 运行 Master CLI 会话(默认入口)
 *
 * 启动一个 MasterSession,Master 可以直接对话或通过工具调用 Sub Agent。
 *
 * @param {string} message       - 用户消息
 * @param {object} [options]
 * @param {string} [options.workDir]       - 工作目录 (默认 cwd)
 * @param {object} [options.config]        - 自定义配置
 * @param {function} [options.onData]      - 流式数据回调
 * @param {function} [options.onToolCall]  - 工具调用通知
 * @returns {Promise<object>}
 */
async function runMasterSession(message, options = {}) {
  const { MasterSession } = require('./core/master-session');
  const config = options.config || loadConfig();
  const workDir = options.workDir || process.cwd();
  const registry = getRegistry();

  const session = new MasterSession(config, workDir, {
    runRegistry: registry,
  });

  try {
    return await session.handleMessage(message, {
      onData: options.onData,
      onToolCall: options.onToolCall,
      maxToolRounds: options.maxToolRounds || 10,
    });
  } finally {
    await session.shutdown();
  }
}

module.exports = { runMasterSession, runOrchestration, runSingleAgent, getRegistry, loadConfig };