@roll-agent/core 0.6.5 → 0.6.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- import{defineCommand as e}from"citty";import{resolve as t}from"node:path";import{existsSync as r,mkdirSync as i}from"node:fs";import{execFile as o}from"node:child_process";import{promisify as n}from"node:util";import{inspectAgentEnvRequirements as s}from"../../config/helpers.js";import{loadAgentsConfig as a}from"../../config/loader.js";import{discoverAgent as p}from"../../registry/discovery.js";import{writeRemoteSkillManifest as c}from"../../registry/manifest.js";import{AgentStore as d}from"../../registry/store.js";import{log as l}from"../utils/output.js";import{createInstallCommand as m,detectInstallCommand as g,formatPackageManagerCommand as f,formatPackageManagerError as u,runPackageManager as h}from"../utils/package-manager.js";const y=n(o);function $(e){return e.startsWith("https://")||e.startsWith("http://")||e.startsWith("git@")||e.endsWith(".git")}function v(e){return(e.split("/").pop()??e).replace(/\.git$/,"")}export default e({meta:{description:"注册一个 Agent(本地路径、Git URL 或远程 endpoint"},args:{path:{type:"positional",description:"Agent 本地路径或 Git URL",required:!1},remote:{type:"string",description:"远程 MCP endpoint(需配合 --name/--description"},name:{type:"string",description:"远程 Agent 名称"},description:{type:"string",description:"远程 Agent 描述"}},async run({args:e}){const{agentsConfig:o}=a();let n;if(e.remote){if(!e.name||!e.description)return l.error("远程注册需要同时提供 --name 和 --description"),void(process.exitCode=1);n=c({dataDir:o.dataDir,name:e.name,description:e.description,endpoint:e.remote})}else{if(!e.path)return l.error("请提供本地路径、Git URL,或使用 --remote <endpoint> 注册远程 Agent"),void(process.exitCode=1);if($(e.path)){const s=v(e.path),a=t(o.dataDir,"repos",s);if(r(a)){l.info(`仓库目录已存在,拉取最新代码: ${a}`);try{await y("git",["pull"],{cwd:a})}catch(e){return l.error(`git pull 失败: ${e instanceof Error?e.message:String(e)}`),void(process.exitCode=1)}}else{l.info(`克隆 ${e.path}...`);const n=t(o.dataDir,"repos");r(n)||i(n,{recursive:!0});try{await y("git",["clone",e.path,a]),l.success("克隆完成")}catch(e){return l.error(`git clone 失败: ${e instanceof Error?e.message:String(e)}`),void(process.exitCode=1)}}n=a}else if(n=t(e.path),!r(n))return l.error(`路径不存在: ${n}`),void(process.exitCode=1)}l.info("解析 SKILL.md...");const s=p(n);l.debug(`名称: ${s.skill.name}`),l.debug(`描述: ${s.skill.description}`),l.debug(`传输: ${s.transport.type}`);const k=t(n,"package.json"),A="1"===process.env.ROLL_SKIP_INSTALL;if(e.remote)l.info("远程 Agent 使用本地 manifest,无需安装依赖。");else if(r(k)&&!A){const e=g(n)??m("pnpm");l.info(`安装依赖 (${f(e)})...`);try{await h(e,{cwd:n}),l.success("依赖安装完成")}catch(t){return l.error(`依赖安装失败: ${u(e,t)}`),void(process.exitCode=1)}}else r(k)&&A&&l.warn("检测到 ROLL_SKIP_INSTALL=1,跳过依赖安装。");const j=e.remote?{type:"remote-manifest",endpoint:e.remote}:e.path&&$(e.path)?{type:"git",url:e.path}:{type:"local-path",path:n},w=new d(o.dataDir),S={skill:s.skill,transport:s.transport,runtime:s.runtime,installPath:n,registeredAt:(new Date).toISOString(),status:"idle",source:j,...s.skillBody.length>0?{skillBody:s.skillBody}:{}};try{w.add(S),l.success(`Agent "${s.skill.name}" 注册成功`),L(s.skill.name,s.skill.env,o.env)}catch(e){l.error(e instanceof Error?e.message:String(e)),process.exitCode=1}}});function L(e,t,r){const i=s(e,t,r);if(i)return i.missingRequired.length>0?(l.warn(`Agent "${e}" 仍缺少必填环境变量: ${i.missingRequired.map(e=>e.name).join(", ")}`),void l.info(`请在 roll.config.yaml 的 agents.env.${e} 中显式配置这些项。`)):void(i.processEnvOnlyRequired.length>0&&(l.warn(`Agent "${e}" 当前依赖 shell 环境变量: ${i.processEnvOnlyRequired.map(e=>e.name).join(", ")}`),l.info(`建议将这些项写入 roll.config.yaml 的 agents.env.${e}。`)))}
1
+ import{defineCommand as e}from"citty";import{resolve as t}from"node:path";import{existsSync as r,mkdirSync as i}from"node:fs";import{execFile as o}from"node:child_process";import{promisify as n}from"node:util";import{inspectAgentEnvRequirements as s}from"../../config/helpers.js";import{loadAgentsConfig as a}from"../../config/loader.js";import{discoverAgent as p}from"../../registry/discovery.js";import{writeRemoteSkillManifest as c}from"../../registry/manifest.js";import{AgentStore as d}from"../../registry/store.js";import{log as m}from"../utils/output.js";import{createInstallCommand as l,detectInstallCommand as g,formatPackageManagerCommand as f,formatPackageManagerError as u,runPackageManager as h}from"../utils/package-manager.js";const y=n(o);function $(e){return e.startsWith("https://")||e.startsWith("http://")||e.startsWith("git@")||e.endsWith(".git")}function v(e){return(e.split("/").pop()??e).replace(/\.git$/,"")}export default e({meta:{description:"注册本地目录、Git 仓库或远程 MCP endpoint"},args:{path:{type:"positional",description:"本地 Agent 目录或 Git 仓库 URL;远程 endpoint 请用 --remote",required:!1},remote:{type:"string",description:"远程 MCP endpoint;需要同时提供 --name 和 --description"},name:{type:"string",description:"远程 Agent 名称(仅 --remote 时使用)"},description:{type:"string",description:"远程 Agent 描述(仅 --remote 时使用)"}},async run({args:e}){const{agentsConfig:o}=a();let n;if(e.remote){if(!e.name||!e.description)return m.error("远程注册需要同时提供 --name 和 --description"),void(process.exitCode=1);n=c({dataDir:o.dataDir,name:e.name,description:e.description,endpoint:e.remote})}else{if(!e.path)return m.error("请提供本地路径、Git URL,或使用 --remote <endpoint> 注册远程 Agent"),void(process.exitCode=1);if($(e.path)){const s=v(e.path),a=t(o.dataDir,"repos",s);if(r(a)){m.info(`仓库目录已存在,拉取最新代码: ${a}`);try{await y("git",["pull"],{cwd:a})}catch(e){return m.error(`git pull 失败: ${e instanceof Error?e.message:String(e)}`),void(process.exitCode=1)}}else{m.info(`克隆 ${e.path}...`);const n=t(o.dataDir,"repos");r(n)||i(n,{recursive:!0});try{await y("git",["clone",e.path,a]),m.success("克隆完成")}catch(e){return m.error(`git clone 失败: ${e instanceof Error?e.message:String(e)}`),void(process.exitCode=1)}}n=a}else if(n=t(e.path),!r(n))return m.error(`路径不存在: ${n}`),void(process.exitCode=1)}m.info("解析 SKILL.md...");const s=p(n);m.debug(`名称: ${s.skill.name}`),m.debug(`描述: ${s.skill.description}`),m.debug(`传输: ${s.transport.type}`);const L=t(n,"package.json"),A="1"===process.env.ROLL_SKIP_INSTALL;if(e.remote)m.info("远程 Agent 使用本地 manifest,无需安装依赖。");else if(r(L)&&!A){const e=g(n)??l("pnpm");m.info(`安装依赖 (${f(e)})...`);try{await h(e,{cwd:n}),m.success("依赖安装完成")}catch(t){return m.error(`依赖安装失败: ${u(e,t)}`),void(process.exitCode=1)}}else r(L)&&A&&m.warn("检测到 ROLL_SKIP_INSTALL=1,跳过依赖安装。");const j=e.remote?{type:"remote-manifest",endpoint:e.remote}:e.path&&$(e.path)?{type:"git",url:e.path}:{type:"local-path",path:n},w=new d(o.dataDir),C={skill:s.skill,transport:s.transport,runtime:s.runtime,installPath:n,registeredAt:(new Date).toISOString(),status:"idle",source:j,...s.skillBody.length>0?{skillBody:s.skillBody}:{}};try{w.add(C),m.success(`Agent "${s.skill.name}" 注册成功`),k(s.skill.name,s.skill.env,o.env)}catch(e){m.error(e instanceof Error?e.message:String(e)),process.exitCode=1}}});function k(e,t,r){const i=s(e,t,r);if(i)return i.missingRequired.length>0?(m.warn(`Agent "${e}" 仍缺少必填环境变量: ${i.missingRequired.map(e=>e.name).join(", ")}`),void m.info(`请在 roll.config.yaml 的 agents.env.${e} 中显式配置这些项。`)):void(i.processEnvOnlyRequired.length>0&&(m.warn(`Agent "${e}" 当前依赖 shell 环境变量: ${i.processEnvOnlyRequired.map(e=>e.name).join(", ")}`),m.info(`建议将这些项写入 roll.config.yaml 的 agents.env.${e}。`)))}
@@ -1 +1 @@
1
- import{defineCommand as t}from"citty";import{loadAgentsConfig as e}from"../../config/loader.js";import{getAgentLogPath as r,getAgentPid as a,probeAgentEndpoint as n}from"../../registry/process-manager.js";import{AgentStore as s}from"../../registry/store.js";import{log as o}from"../utils/output.js";export default t({meta:{description:"检查 Agent 健康状态(兼容 on-demand / core-managed / external-managed)"},args:{restart:{type:"boolean",description:"兼容旧参数,stdio 按需模式下不生效",default:!1},json:{type:"boolean",description:"JSON 格式输出",default:!1}},async run({args:t}){const{agentsConfig:r}=e(),a=new s(r.dataDir),n=a.list();if(t.restart&&o.warn("`--restart` 仅为兼容保留参数;v1 不执行自动重启逻辑。"),0===n.length)return t.json?void console.log("[]"):void o.info("暂无已注册 Agent。");const l=[];for(const t of n)l.push(await i(t,a,r.dataDir));const p=l.filter(t=>!t.healthy);if(t.json)return console.log(JSON.stringify(l,null,2)),void(p.length>0&&(process.exitCode=1));for(const t of l)t.healthy?o.success(`${t.agentName} [${t.transport}]: ${t.message}`):o.error(`${t.agentName} [${t.transport}]: ${t.message}`);p.length>0&&(process.exitCode=1)}});async function i(t,e,r){switch(t.runtime.ownership){case"on-demand":return{agentName:t.skill.name,transport:t.transport.type,healthy:!0,message:"按需模式:无需常驻进程,由 run/ask 在调用时启动"};case"external-managed":return l(t,e);case"core-managed":return p(t,e,r)}}async function l(t,e){try{return await n(t,{timeoutMs:5e3}),e.updateStatus(t.skill.name,"online"),{agentName:t.skill.name,transport:t.transport.type,healthy:!0,message:"streamable-http"===t.transport.type?`外部服务可连接 (${t.transport.endpoint})`:"外部服务可连接"}}catch(r){return e.updateStatus(t.skill.name,"error"),{agentName:t.skill.name,transport:t.transport.type,healthy:!1,message:"streamable-http"===t.transport.type?`外部服务不可连接 (${t.transport.endpoint}): ${r instanceof Error?r.message:String(r)}`:`外部服务不可连接: ${r instanceof Error?r.message:String(r)}`}}}async function p(t,e,s){const o=a(s,t.skill.name);if(void 0===o)return e.updateStatus(t.skill.name,"stopped"),{agentName:t.skill.name,transport:t.transport.type,healthy:!1,message:`未运行(缺少活动 PID)。日志: ${r(s,t.skill.name)}`};try{return await n(t,{timeoutMs:5e3}),e.updateStatus(t.skill.name,"online"),{agentName:t.skill.name,transport:t.transport.type,healthy:!0,message:"streamable-http"===t.transport.type?`运行中 (PID: ${String(o)}),可连接 (${t.transport.endpoint})`:`运行中 (PID: ${String(o)})`}}catch(a){return e.updateStatus(t.skill.name,"error"),{agentName:t.skill.name,transport:t.transport.type,healthy:!1,message:"streamable-http"===t.transport.type?`进程存在但不可连接 (${t.transport.endpoint}): ${a instanceof Error?a.message:String(a)}。日志: ${r(s,t.skill.name)}`:`进程存在但不可连接: ${a instanceof Error?a.message:String(a)}。日志: ${r(s,t.skill.name)}`}}}
1
+ import{defineCommand as t}from"citty";import{loadAgentsConfig as e}from"../../config/loader.js";import{getAgentLogPath as r,getAgentPid as a,probeAgentEndpoint as n}from"../../registry/process-manager.js";import{AgentStore as s}from"../../registry/store.js";import{log as o}from"../utils/output.js";export default t({meta:{description:"检查 Agent 健康状态(兼容 on-demand / core-managed / external-managed)"},args:{restart:{type:"boolean",description:"兼容旧参数;当前不会执行自动重启",default:!1},json:{type:"boolean",description:"JSON 格式输出",default:!1}},async run({args:t}){const{agentsConfig:r}=e(),a=new s(r.dataDir),n=a.list();if(t.restart&&o.warn("`--restart` 仅为兼容保留参数;v1 不执行自动重启逻辑。"),0===n.length)return t.json?void console.log("[]"):void o.info("暂无已注册 Agent。");const l=[];for(const t of n)l.push(await i(t,a,r.dataDir));const p=l.filter(t=>!t.healthy);if(t.json)return console.log(JSON.stringify(l,null,2)),void(p.length>0&&(process.exitCode=1));for(const t of l)t.healthy?o.success(`${t.agentName} [${t.transport}]: ${t.message}`):o.error(`${t.agentName} [${t.transport}]: ${t.message}`);p.length>0&&(process.exitCode=1)}});async function i(t,e,r){switch(t.runtime.ownership){case"on-demand":return{agentName:t.skill.name,transport:t.transport.type,healthy:!0,message:"按需模式:无需常驻进程,由 run/ask 在调用时启动"};case"external-managed":return l(t,e);case"core-managed":return p(t,e,r)}}async function l(t,e){try{return await n(t,{timeoutMs:5e3}),e.updateStatus(t.skill.name,"online"),{agentName:t.skill.name,transport:t.transport.type,healthy:!0,message:"streamable-http"===t.transport.type?`外部服务可连接 (${t.transport.endpoint})`:"外部服务可连接"}}catch(r){return e.updateStatus(t.skill.name,"error"),{agentName:t.skill.name,transport:t.transport.type,healthy:!1,message:"streamable-http"===t.transport.type?`外部服务不可连接 (${t.transport.endpoint}): ${r instanceof Error?r.message:String(r)}`:`外部服务不可连接: ${r instanceof Error?r.message:String(r)}`}}}async function p(t,e,s){const o=a(s,t.skill.name);if(void 0===o)return e.updateStatus(t.skill.name,"stopped"),{agentName:t.skill.name,transport:t.transport.type,healthy:!1,message:`未运行(缺少活动 PID)。日志: ${r(s,t.skill.name)}`};try{return await n(t,{timeoutMs:5e3}),e.updateStatus(t.skill.name,"online"),{agentName:t.skill.name,transport:t.transport.type,healthy:!0,message:"streamable-http"===t.transport.type?`运行中 (PID: ${String(o)}),可连接 (${t.transport.endpoint})`:`运行中 (PID: ${String(o)})`}}catch(a){return e.updateStatus(t.skill.name,"error"),{agentName:t.skill.name,transport:t.transport.type,healthy:!1,message:"streamable-http"===t.transport.type?`进程存在但不可连接 (${t.transport.endpoint}): ${a instanceof Error?a.message:String(a)}。日志: ${r(s,t.skill.name)}`:`进程存在但不可连接: ${a instanceof Error?a.message:String(a)}。日志: ${r(s,t.skill.name)}`}}}
@@ -4,15 +4,15 @@ declare const _default: import("citty").CommandDef<{
4
4
  description: string;
5
5
  required: true;
6
6
  };
7
- skipBrowserSetup: {
7
+ "skip-browser-setup": {
8
8
  type: "boolean";
9
9
  description: string;
10
10
  default: false;
11
11
  };
12
- noStart: {
12
+ start: {
13
13
  type: "boolean";
14
14
  description: string;
15
- default: false;
15
+ default: true;
16
16
  };
17
17
  }>;
18
18
  export default _default;
@@ -1 +1 @@
1
- import{defineCommand as e}from"citty";import{existsSync as t,mkdirSync as r,statSync as i}from"node:fs";import{resolve as s}from"node:path";import{inspectAgentEnvRequirements as o}from"../../config/helpers.js";import{loadAgentsConfig as n}from"../../config/loader.js";import{discoverAgent as a}from"../../registry/discovery.js";import{startAgent as l,stopAgentGracefully as p,waitForAgentReady as m}from"../../registry/process-manager.js";import{runAgentSetup as c}from"../../registry/runtime-setup.js";import{AgentStore as d}from"../../registry/store.js";import{parsePackageName as g,readInstalledPackageManifest as u,resolveInstalledPackageRoot as f,sanitizeInstallId as k}from"../../registry/source.js";import{log as y}from"../utils/output.js";import{formatPackageManagerError as h,runPackageManager as w}from"../utils/package-manager.js";function v(e){return e.startsWith("git@")||e.startsWith("git+")||e.startsWith("github:")||e.startsWith("gitlab:")||e.startsWith("bitbucket:")||e.endsWith(".git")}export default e({meta:{description:"安装已编译的 Agent 包并注册到本地"},args:{package:{type:"positional",description:"npm package spec",required:!0},skipBrowserSetup:{type:"boolean",description:"跳过浏览器运行时安装",default:!1},noStart:{type:"boolean",description:"安装后不自动启动 core-managed Agent",default:!1}},async run({args:e}){const{agentsConfig:o}=n(),S=e.package;if(v(S))return y.error(`Git URL 请使用 \`roll agent add ${S}\` 注册,不要使用 \`roll agent install\``),void(process.exitCode=1);const j=s(S);if(t(j)&&i(j).isDirectory())return y.error(`本地源码目录请使用 \`roll agent add ${S}\` 注册,不要使用 \`roll agent install\``),void(process.exitCode=1);const C=g(S),x=s(o.dataDir,"installed",k(C));t(x)||r(x,{recursive:!0}),y.info(`安装 ${S}...`);const A={command:"npm",args:["install","--prefix",x,S]};try{await w(A,{timeout:12e4})}catch(e){return y.error(`安装失败: ${h(A,e)}`),void(process.exitCode=1)}const b=f(x,C);if(!t(b))return y.error(`安装完成但未找到包目录: ${b}`),void(process.exitCode=1);const B=u(b);y.info("解析已安装 Agent 的 SKILL.md...");const D=a(b),W=new d(o.dataDir),q={skill:D.skill,transport:D.transport,runtime:D.runtime,installPath:b,registeredAt:(new Date).toISOString(),status:"idle",source:{type:"installed-package",packageName:B?.name??C,packageSpec:S,installDir:x,...B?.version?{installedVersion:B.version}:{}},...D.skillBody.length>0?{skillBody:D.skillBody}:{}};"core-managed"===q.runtime.ownership&&q.runtime.setup?.playwright&&!e.skipBrowserSetup&&y.info(`即将安装浏览器运行时 (${q.runtime.setup.playwright.browsers.join(", ")}),这可能需要一些时间...`);const R=await c(q,{skipBrowserSetup:e.skipBrowserSetup});R.ok?R.skipped?y.info(R.message):y.success(R.message):(y.warn(`Agent setup 失败:${R.message}`),R.retryCommand&&y.info(`重试命令: ${R.retryCommand}`));const E=W.findByName(D.skill.name);try{const t="core-managed"===E?.runtime.ownership&&"online"===E.status;if("installed-package"===E?.source?.type?W.replace(E.skill.name,q):W.add(q),!R.ok)return W.updateStatus(D.skill.name,"error"),void(process.exitCode=1);if("core-managed"===q.runtime.ownership&&!e.noStart){t&&await p(o.dataDir,q.skill.name),W.updateStatus(q.skill.name,"starting");let e=!1;try{l(q,o.dataDir),e=!0,await m(q,{startupTimeoutMs:15e3,probeTimeoutMs:2e3}),W.updateStatus(q.skill.name,"online")}catch(t){return e&&await p(o.dataDir,q.skill.name).catch(()=>{}),W.updateStatus(q.skill.name,"error"),y.error(`Agent "${D.skill.name}" 已安装,但自动启动失败:${t instanceof Error?t.message:String(t)}`),void(process.exitCode=1)}}y.success(`Agent "${D.skill.name}" 安装并注册成功`),$(D.skill.name,D.skill.env,o.env)}catch(e){y.error(e instanceof Error?e.message:String(e)),process.exitCode=1}}});function $(e,t,r){const i=o(e,t,r);if(i)return i.missingRequired.length>0?(y.warn(`Agent "${e}" 仍缺少必填环境变量: ${i.missingRequired.map(e=>e.name).join(", ")}`),void y.info(`请在 roll.config.yaml 的 agents.env.${e} 中显式配置这些项。`)):void(i.processEnvOnlyRequired.length>0&&(y.warn(`Agent "${e}" 当前依赖 shell 环境变量: ${i.processEnvOnlyRequired.map(e=>e.name).join(", ")}`),y.info(`建议将这些项写入 roll.config.yaml 的 agents.env.${e}。`)))}
1
+ import{defineCommand as e}from"citty";import{existsSync as t,mkdirSync as r,statSync as s}from"node:fs";import{resolve as i}from"node:path";import{inspectAgentEnvRequirements as o}from"../../config/helpers.js";import{loadAgentsConfig as n}from"../../config/loader.js";import{discoverAgent as a}from"../../registry/discovery.js";import{startAgent as l,stopAgentGracefully as m,waitForAgentReady as p}from"../../registry/process-manager.js";import{runAgentSetup as c}from"../../registry/runtime-setup.js";import{AgentStore as d}from"../../registry/store.js";import{parsePackageName as g,readInstalledPackageManifest as u,resolveInstalledPackageRoot as f,sanitizeInstallId as k}from"../../registry/source.js";import{log as y}from"../utils/output.js";import{formatPackageManagerError as h,runPackageManager as w}from"../utils/package-manager.js";function v(e){return e.startsWith("git@")||e.startsWith("git+")||e.startsWith("github:")||e.startsWith("gitlab:")||e.startsWith("bitbucket:")||e.endsWith(".git")}export default e({meta:{description:"安装已发布的 Agent npm 包并注册到本地"},args:{package:{type:"positional",description:"npm 包名、版本范围或 .tgz 路径(源码目录请用 roll agent add)",required:!0},"skip-browser-setup":{type:"boolean",description:"跳过 Playwright 浏览器运行时安装/校验",default:!1},start:{type:"boolean",description:"安装后不自动启动 core-managed Agent",default:!0}},async run({args:e}){const{agentsConfig:o}=n(),j=e.package;if(v(j))return y.error(`Git URL 请使用 \`roll agent add ${j}\` 注册,不要使用 \`roll agent install\``),void(process.exitCode=1);const b=i(j);if(t(b)&&s(b).isDirectory())return y.error(`本地源码目录请使用 \`roll agent add ${j}\` 注册,不要使用 \`roll agent install\``),void(process.exitCode=1);const S=g(j),C=i(o.dataDir,"installed",k(S));t(C)||r(C,{recursive:!0}),y.info(`安装 ${j}...`);const x={command:"npm",args:["install","--prefix",C,j]};try{await w(x,{timeout:12e4})}catch(e){return y.error(`安装失败: ${h(x,e)}`),void(process.exitCode=1)}const A=f(C,S);if(!t(A))return y.error(`安装完成但未找到包目录: ${A}`),void(process.exitCode=1);const D=u(A);y.info("解析已安装 Agent 的 SKILL.md...");const W=a(A),q=new d(o.dataDir),B={skill:W.skill,transport:W.transport,runtime:W.runtime,installPath:A,registeredAt:(new Date).toISOString(),status:"idle",source:{type:"installed-package",packageName:D?.name??S,packageSpec:j,installDir:C,...D?.version?{installedVersion:D.version}:{}},...W.skillBody.length>0?{skillBody:W.skillBody}:{}};"core-managed"===B.runtime.ownership&&B.runtime.setup?.playwright&&!e["skip-browser-setup"]&&y.info(`即将安装浏览器运行时 (${B.runtime.setup.playwright.browsers.join(", ")}),这可能需要一些时间...`);const R=await c(B,{skipBrowserSetup:e["skip-browser-setup"]});R.ok?R.skipped?y.info(R.message):y.success(R.message):(y.warn(`Agent setup 失败:${R.message}`),R.retryCommand&&y.info(`重试命令: ${R.retryCommand}`));const E=q.findByName(W.skill.name);try{const t="core-managed"===E?.runtime.ownership&&"online"===E.status;if("installed-package"===E?.source?.type?q.replace(E.skill.name,B):q.add(B),!R.ok)return q.updateStatus(W.skill.name,"error"),void(process.exitCode=1);if("core-managed"===B.runtime.ownership&&e.start){t&&await m(o.dataDir,B.skill.name),q.updateStatus(B.skill.name,"starting");let e=!1;try{l(B,o.dataDir),e=!0,await p(B,{startupTimeoutMs:15e3,probeTimeoutMs:2e3}),q.updateStatus(B.skill.name,"online")}catch(t){return e&&await m(o.dataDir,B.skill.name).catch(()=>{}),q.updateStatus(B.skill.name,"error"),y.error(`Agent "${W.skill.name}" 已安装,但自动启动失败:${t instanceof Error?t.message:String(t)}`),void(process.exitCode=1)}}y.success(`Agent "${W.skill.name}" 安装并注册成功`),$(W.skill.name,W.skill.env,o.env)}catch(e){y.error(e instanceof Error?e.message:String(e)),process.exitCode=1}}});function $(e,t,r){const s=o(e,t,r);if(s)return s.missingRequired.length>0?(y.warn(`Agent "${e}" 仍缺少必填环境变量: ${s.missingRequired.map(e=>e.name).join(", ")}`),void y.info(`请在 roll.config.yaml 的 agents.env.${e} 中显式配置这些项。`)):void(s.processEnvOnlyRequired.length>0&&(y.warn(`Agent "${e}" 当前依赖 shell 环境变量: ${s.processEnvOnlyRequired.map(e=>e.name).join(", ")}`),y.info(`建议将这些项写入 roll.config.yaml 的 agents.env.${e}。`)))}
@@ -1 +1 @@
1
- import{defineCommand as t}from"citty";import{getAgentEnv as e}from"../../config/helpers.js";import{loadAgentsConfig as r,loadConfig as a}from"../../config/loader.js";import{getAgentLogPath as n,getAgentPid as i,probeAgentEndpoint as s,startAgent as o,stopAgentGracefully as m,waitForAgentReady as p}from"../../registry/process-manager.js";import{AgentStore as l}from"../../registry/store.js";import{log as d}from"../utils/output.js";export default t({meta:{description:"启动 Agent(core-managed HTTP 可由 Roll 托管)"},args:{name:{type:"positional",description:"Agent 名称",required:!0}},async run({args:t}){const{agentsConfig:c}=r(),u=new l(c.dataDir),g=u.findByName(t.name);if(!g)return d.error(`Agent "${t.name}" 未找到`),void(process.exitCode=1);switch(g.runtime.ownership){case"on-demand":return void d.success(`Agent "${t.name}" 为按需模式,无需手动启动。`);case"external-managed":return d.info(`Agent "${t.name}" 由外部服务管理,Roll 不负责启动。`),void("streamable-http"===g.transport.type&&d.info(`端点: ${g.transport.endpoint}`))}const f=i(c.dataDir,g.skill.name);if(void 0!==f)try{return await s(g,{timeoutMs:3e3}),u.updateStatus(g.skill.name,"online"),void d.success(`Agent "${t.name}" 已在运行 (PID: ${String(f)})\n 端点: ${"streamable-http"===g.transport.type?g.transport.endpoint:"n/a"}`)}catch(e){return u.updateStatus(g.skill.name,"error"),d.error(`Agent "${t.name}" 进程存在但不可连接:${e instanceof Error?e.message:String(e)}`),d.info(`日志: ${n(c.dataDir,g.skill.name)}`),void(process.exitCode=1)}let $;u.updateStatus(g.skill.name,"starting");try{const r=e(a().config,g.skill.name);$=o(g,c.dataDir,r),await p(g,{startupTimeoutMs:15e3,probeTimeoutMs:2e3}),u.updateStatus(g.skill.name,"online"),d.success(`Agent "${t.name}" 已启动 (PID: ${String($)})\n 端点: ${"streamable-http"===g.transport.type?g.transport.endpoint:"n/a"}\n 日志: ${n(c.dataDir,g.skill.name)}`)}catch(e){void 0!==$&&await m(c.dataDir,g.skill.name).catch(()=>{}),u.updateStatus(g.skill.name,"error"),d.error(`Agent "${t.name}" 启动失败:${e instanceof Error?e.message:String(e)}`),d.info(`日志: ${n(c.dataDir,g.skill.name)}`),process.exitCode=1}}});
1
+ import{defineCommand as t}from"citty";import{getAgentEnv as e}from"../../config/helpers.js";import{loadAgentsConfig as r,loadConfig as a}from"../../config/loader.js";import{getAgentLogPath as n,getAgentPid as i,probeAgentEndpoint as s,startAgent as o,stopAgentGracefully as m,waitForAgentReady as p}from"../../registry/process-manager.js";import{AgentStore as l}from"../../registry/store.js";import{log as d}from"../utils/output.js";export default t({meta:{description:"启动由 Roll 托管的 core-managed Agent"},args:{name:{type:"positional",description:"Agent 名称",required:!0}},async run({args:t}){const{agentsConfig:c}=r(),u=new l(c.dataDir),g=u.findByName(t.name);if(!g)return d.error(`Agent "${t.name}" 未找到`),void(process.exitCode=1);switch(g.runtime.ownership){case"on-demand":return void d.success(`Agent "${t.name}" 为按需模式,无需手动启动。`);case"external-managed":return d.info(`Agent "${t.name}" 由外部服务管理,Roll 不负责启动。`),void("streamable-http"===g.transport.type&&d.info(`端点: ${g.transport.endpoint}`))}const f=i(c.dataDir,g.skill.name);if(void 0!==f)try{return await s(g,{timeoutMs:3e3}),u.updateStatus(g.skill.name,"online"),void d.success(`Agent "${t.name}" 已在运行 (PID: ${String(f)})\n 端点: ${"streamable-http"===g.transport.type?g.transport.endpoint:"n/a"}`)}catch(e){return u.updateStatus(g.skill.name,"error"),d.error(`Agent "${t.name}" 进程存在但不可连接:${e instanceof Error?e.message:String(e)}`),d.info(`日志: ${n(c.dataDir,g.skill.name)}`),void(process.exitCode=1)}let $;u.updateStatus(g.skill.name,"starting");try{const r=e(a().config,g.skill.name);$=o(g,c.dataDir,r),await p(g,{startupTimeoutMs:15e3,probeTimeoutMs:2e3}),u.updateStatus(g.skill.name,"online"),d.success(`Agent "${t.name}" 已启动 (PID: ${String($)})\n 端点: ${"streamable-http"===g.transport.type?g.transport.endpoint:"n/a"}\n 日志: ${n(c.dataDir,g.skill.name)}`)}catch(e){void 0!==$&&await m(c.dataDir,g.skill.name).catch(()=>{}),u.updateStatus(g.skill.name,"error"),d.error(`Agent "${t.name}" 启动失败:${e instanceof Error?e.message:String(e)}`),d.info(`日志: ${n(c.dataDir,g.skill.name)}`),process.exitCode=1}}});
@@ -1 +1 @@
1
- import{defineCommand as e}from"citty";import{loadAgentsConfig as t}from"../../config/loader.js";import{stopAgentGracefully as r}from"../../registry/process-manager.js";import{AgentStore as n}from"../../registry/store.js";import{log as o}from"../utils/output.js";export default e({meta:{description:"停止 Agent(core-managed HTTP 可由 Roll 托管)"},args:{name:{type:"positional",description:"Agent 名称",required:!0}},async run({args:e}){const{agentsConfig:a}=t(),s=new n(a.dataDir),i=s.findByName(e.name);if(!i)return o.error(`Agent "${e.name}" 未找到`),void(process.exitCode=1);switch(i.runtime.ownership){case"on-demand":return void o.success(`Agent "${e.name}" 为按需模式,无需手动停止。`);case"external-managed":return o.info(`Agent "${e.name}" 由外部服务管理,请在外部停止。`),void("streamable-http"===i.transport.type&&o.info(`端点: ${i.transport.endpoint}`))}let m=!1;try{m=await r(a.dataDir,i.skill.name)}catch(t){return o.error(`停止 Agent "${e.name}" 失败:${t instanceof Error?t.message:String(t)}`),void(process.exitCode=1)}s.updateStatus(i.skill.name,"stopped"),m?o.success(`Agent "${e.name}" 已停止`):o.info(`Agent "${e.name}" 当前未运行`)}});
1
+ import{defineCommand as e}from"citty";import{loadAgentsConfig as t}from"../../config/loader.js";import{stopAgentGracefully as r}from"../../registry/process-manager.js";import{AgentStore as n}from"../../registry/store.js";import{log as o}from"../utils/output.js";export default e({meta:{description:"停止由 Roll 托管的 core-managed Agent"},args:{name:{type:"positional",description:"Agent 名称",required:!0}},async run({args:e}){const{agentsConfig:a}=t(),s=new n(a.dataDir),i=s.findByName(e.name);if(!i)return o.error(`Agent "${e.name}" 未找到`),void(process.exitCode=1);switch(i.runtime.ownership){case"on-demand":return void o.success(`Agent "${e.name}" 为按需模式,无需手动停止。`);case"external-managed":return o.info(`Agent "${e.name}" 由外部服务管理,请在外部停止。`),void("streamable-http"===i.transport.type&&o.info(`端点: ${i.transport.endpoint}`))}let m=!1;try{m=await r(a.dataDir,i.skill.name)}catch(t){return o.error(`停止 Agent "${e.name}" 失败:${t instanceof Error?t.message:String(t)}`),void(process.exitCode=1)}s.updateStatus(i.skill.name,"stopped"),m?o.success(`Agent "${e.name}" 已停止`):o.info(`Agent "${e.name}" 当前未运行`)}});
@@ -1 +1 @@
1
- import{defineCommand as o}from"citty";import{getAgentEnvFromAgentsConfig as t}from"../../config/helpers.js";import{loadAgentsConfig as e}from"../../config/loader.js";import{McpClientManager as n}from"../../mcp/client-manager.js";import{resolveTransportWithDevSpawnSpec as i}from"../../registry/dev-spawn.js";import{AgentStore as r}from"../../registry/store.js";import{normalizeListedTools as s}from"../utils/agent-tools.js";import{formatAgentToolsTextOutput as l}from"../utils/agent-tools-output.js";import{log as a}from"../utils/output.js";export default o({meta:{description:"查看 Agent 暴露的 MCP tools inputSchema"},args:{name:{type:"positional",description:"Agent 名称",required:!0},json:{type:"boolean",description:"JSON 格式输出",default:!1}},async run({args:o}){const{agentsConfig:m}=e(),c=new r(m.dataDir).findByName(o.name);if(!c)return a.error(`Agent "${o.name}" 未找到。使用 \`roll agent list\` 查看已注册 Agent。`),void(process.exitCode=1);const g=new n;try{a.info(`连接 Agent "${c.skill.name}" 并获取 MCP tools/list...`);const e=i(c),n=t(m,c.skill.name),r=await g.connect(c.skill.name,e,c.installPath,{...n?{env:n}:{}}),{tools:p}=await r.listTools(),f=s(p);if(o.json)return void console.log(JSON.stringify(f,null,2));if(0===f.length)return void console.log(`Agent "${c.skill.name}" 暂未暴露任何 tool。`);console.log(l(c.skill.name,f))}catch(o){a.error(o instanceof Error?o.message:String(o)),process.exitCode=1}finally{await g.disconnectAll()}}});
1
+ import{defineCommand as o}from"citty";import{getAgentEnvFromAgentsConfig as t}from"../../config/helpers.js";import{loadAgentsConfig as e}from"../../config/loader.js";import{McpClientManager as n}from"../../mcp/client-manager.js";import{resolveTransportWithDevSpawnSpec as i}from"../../registry/dev-spawn.js";import{AgentStore as r}from"../../registry/store.js";import{normalizeListedTools as s}from"../utils/agent-tools.js";import{formatAgentToolsTextOutput as l}from"../utils/agent-tools-output.js";import{log as a}from"../utils/output.js";export default o({meta:{description:"查看 Agent 暴露的 MCP tools 及输入 schema"},args:{name:{type:"positional",description:"Agent 名称",required:!0},json:{type:"boolean",description:"JSON 格式输出",default:!1}},async run({args:o}){const{agentsConfig:m}=e(),c=new r(m.dataDir).findByName(o.name);if(!c)return a.error(`Agent "${o.name}" 未找到。使用 \`roll agent list\` 查看已注册 Agent。`),void(process.exitCode=1);const g=new n;try{a.info(`连接 Agent "${c.skill.name}" 并获取 MCP tools/list...`);const e=i(c),n=t(m,c.skill.name),r=await g.connect(c.skill.name,e,c.installPath,{...n?{env:n}:{}}),{tools:p}=await r.listTools(),f=s(p);if(o.json)return void console.log(JSON.stringify(f,null,2));if(0===f.length)return void console.log(`Agent "${c.skill.name}" 暂未暴露任何 tool。`);console.log(l(c.skill.name,f))}catch(o){a.error(o instanceof Error?o.message:String(o)),process.exitCode=1}finally{await g.disconnectAll()}}});
@@ -1 +1 @@
1
- var t=this&&this.__rewriteRelativeImportExtension||function(t,e){return"string"==typeof t&&/^\.\.?\//.test(t)?t.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i,function(t,n,s,o,a){return n?e?".jsx":".js":!s||o&&a?s+o+"."+a.toLowerCase()+"js":t}):t};import{defineCommand as e}from"citty";const n=import.meta.url.endsWith(".ts")?"ts":"js";function s(e){const s=new URL(`./${e}.${n}`,import.meta.url).href;return import(t(s)).then(t=>t.default)}export default e({meta:{description:"管理 Agent(支持本地目录、已安装产物、远程服务)"},subCommands:{add:()=>s("agent-add"),install:()=>s("agent-install"),remove:()=>s("agent-remove"),list:()=>s("agent-list"),tools:()=>s("agent-tools"),start:()=>s("agent-start"),stop:()=>s("agent-stop"),info:()=>s("agent-info"),health:()=>s("agent-health")}});
1
+ var t=this&&this.__rewriteRelativeImportExtension||function(t,e){return"string"==typeof t&&/^\.\.?\//.test(t)?t.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i,function(t,n,s,o,a){return n?e?".jsx":".js":!s||o&&a?s+o+"."+a.toLowerCase()+"js":t}):t};import{defineCommand as e}from"citty";const n=import.meta.url.endsWith(".ts")?"ts":"js";function s(e){const s=new URL(`./${e}.${n}`,import.meta.url).href;return import(t(s)).then(t=>t.default)}export default e({meta:{description:"管理 Agent(本地目录、Git 仓库、已安装 npm 包、远程服务)"},subCommands:{add:()=>s("agent-add"),install:()=>s("agent-install"),remove:()=>s("agent-remove"),list:()=>s("agent-list"),tools:()=>s("agent-tools"),start:()=>s("agent-start"),stop:()=>s("agent-stop"),info:()=>s("agent-info"),health:()=>s("agent-health")}});
@@ -1 +1 @@
1
- import{defineCommand as e}from"citty";import{loadConfig as s}from"../../config/loader.js";import{getAgentEnv as t,getMissingAgentEnvRuntimeIssues as o,inspectAgentEnvRequirements as r}from"../../config/helpers.js";import{shouldSkipRuntimeReadinessForTool as n}from"../../config/runtime-env.js";import{createProviderModel as i,resolveLLMCall as a}from"../../llm/providers.js";import{McpClientManager as l}from"../../mcp/client-manager.js";import{AgentStore as m}from"../../registry/store.js";import{resolveTransportWithDevSpawnSpec as c}from"../../registry/dev-spawn.js";import{routeWithLLM as u}from"../../router/llm-router.js";import{extractToolInput as d}from"../../tool-runtime/argument-extractor.js";import{formatValidationIssuesMessage as g}from"../../tool-runtime/messages.js";import{preflightToolCall as f}from"../../tool-runtime/preflight.js";import{formatMissingToolMessage as p,normalizeListedTools as v}from"../utils/agent-tools.js";import{log as j,redactToolArgsForLog as y}from"../utils/output.js";import{extractTextContent as $,isToolErrorResult as N}from"../utils/tool-results.js";const k=.5;function x(e){console.log(JSON.stringify(e,null,2))}function w(e){const s=$("object"==typeof e.result&&null!==e.result&&"content"in e.result?e.result.content:void 0);for(const e of s)console.log(e)}export default e({meta:{description:"LLM 智能路由,自动选择 Agent 和 tool"},args:{message:{type:"positional",description:"自然语言消息",required:!0},json:{type:"boolean",description:"JSON 格式输出",default:!1},verbose:{type:"boolean",alias:"v",description:"输出调试日志",default:!1}},async run({args:e}){const{config:h}=s(),C=new m(h.agents.dataDir).list();if(0===C.length)return j.error("暂无已注册的 Agent。可使用 `roll agent add <path>`、`roll agent install <package>` 或 `roll agent add --remote <endpoint>`。"),void(process.exitCode=1);const L=s=>{if(e.json)x(s);else if("success"===s.status)w(s)},M=h.llm.defaultProvider,S=h.ask.llmModel??h.llm.defaultModel,b=h.llm.providers[M];if(!b)return j.error(`LLM provider "${M}" 未配置。请检查 roll.config.yaml`),void(process.exitCode=1);const{model:A,providerOptions:I}=a(M,S,b.apiKey,"structured-output",b.baseUrl);let O;j.info(`分析意图: "${e.message}"`);try{O=await u(e.message,C,A,I)}catch(e){const s={status:"failed",stage:"route",message:`LLM 路由失败: ${e instanceof Error?e.message:String(e)}`};return j.error(s.message),L(s),void(process.exitCode=1)}j.info(`路由决策: ${O.agentName}.${O.toolName} (置信度: ${String(O.confidence)})`);const T=h.ask.confirmThreshold??k;if(O.confidence<T){const e={status:"needs_confirmation",decision:O,message:`置信度 ${String(O.confidence)} 低于阈值 ${String(T)},跳过执行。 可使用 \`roll run ${O.agentName} ${O.toolName}\` 手动调用。`};return j.warn(e.message),L(e),void(process.exitCode=1)}const J=C.find(e=>e.skill.name===O.agentName);if(!J){const e={status:"failed",stage:"route",decision:O,message:`Agent "${O.agentName}" 未找到(LLM 返回了无效的 Agent 名称)`};return j.error(e.message),L(e),void(process.exitCode=1)}const E=i(M,h.llm.defaultModel,b.apiKey,b.baseUrl),K=new l;let P="connect";try{j.info(`连接 Agent "${J.skill.name}"...`);const s=t(h,J.skill.name),i=c(J),a=await K.connect(J.skill.name,i,J.installPath,{samplingModel:E,...s?{env:s}:{}}),l=v((await a.listTools()).tools),m=l.find(e=>e.name===O.toolName);if(!m){const e={status:"failed",stage:"route",decision:O,message:p(J.skill.name,O.toolName,l)};return j.error(e.message),L(e),void(process.exitCode=1)}P="execute";const u=await d(e.message,m,A,I),k={...O,input:u},x=r(J.skill.name,J.skill.env,h.agents.env),w=n(m.name)?[]:o(x),C=f(m,k.input,{runtimeIssues:w});if(!C.ok){const e={status:"needs_input",decision:k,validationIssues:C.issues,runtimeIssues:C.runtimeIssues,message:g(J.skill.name,O.toolName,C.issues,C.runtimeIssues)};return j.warn(e.message),L(e),void(process.exitCode=1)}j.info(`调用 ${J.skill.name}.${O.toolName}`),j.debug(`调用参数: ${JSON.stringify(y(k.input))}`);const M=await a.callTool({name:O.toolName,arguments:k.input});if(N(M)){const e={status:"failed",stage:"execute",decision:k,message:$(M.content).join("\n")||"Tool 调用失败"};return j.error(e.message),L(e),void(process.exitCode=1)}L({status:"success",decision:k,result:M}),j.success("调用完成")}catch(e){const s={status:"failed",stage:P,decision:O,message:e instanceof Error?e.message:String(e)};j.error(s.message),L(s),process.exitCode=1}finally{await K.disconnectAll()}}});
1
+ import{defineCommand as e}from"citty";import{loadConfig as s}from"../../config/loader.js";import{getAgentEnv as t,getMissingAgentEnvRuntimeIssues as o,inspectAgentEnvRequirements as r}from"../../config/helpers.js";import{shouldSkipRuntimeReadinessForTool as n}from"../../config/runtime-env.js";import{createProviderModel as i,resolveLLMCall as a}from"../../llm/providers.js";import{McpClientManager as l}from"../../mcp/client-manager.js";import{AgentStore as m}from"../../registry/store.js";import{resolveTransportWithDevSpawnSpec as c}from"../../registry/dev-spawn.js";import{routeWithLLM as u}from"../../router/llm-router.js";import{extractToolInput as d}from"../../tool-runtime/argument-extractor.js";import{formatValidationIssuesMessage as g}from"../../tool-runtime/messages.js";import{preflightToolCall as f}from"../../tool-runtime/preflight.js";import{formatMissingToolMessage as p,normalizeListedTools as v}from"../utils/agent-tools.js";import{log as j,redactToolArgsForLog as y}from"../utils/output.js";import{extractTextContent as $,isToolErrorResult as N}from"../utils/tool-results.js";const k=.5;function x(e){console.log(JSON.stringify(e,null,2))}function w(e){const s=$("object"==typeof e.result&&null!==e.result&&"content"in e.result?e.result.content:void 0);for(const e of s)console.log(e)}export default e({meta:{description:"LLM 从自然语言中选择 Agent 和 MCP tool"},args:{message:{type:"positional",description:"自然语言消息",required:!0},json:{type:"boolean",description:"JSON 格式输出",default:!1},verbose:{type:"boolean",alias:"v",description:"输出调试日志",default:!1}},async run({args:e}){const{config:C}=s(),h=new m(C.agents.dataDir).list();if(0===h.length)return j.error("暂无已注册的 Agent。可使用 `roll agent add <path>`、`roll agent install <package>` 或 `roll agent add --remote <endpoint>`。"),void(process.exitCode=1);const M=s=>{if(e.json)x(s);else if("success"===s.status)w(s)},L=C.llm.defaultProvider,S=C.ask.llmModel??C.llm.defaultModel,b=C.llm.providers[L];if(!b)return j.error(`LLM provider "${L}" 未配置。请检查 roll.config.yaml`),void(process.exitCode=1);const{model:A,providerOptions:I}=a(L,S,b.apiKey,"structured-output",b.baseUrl);let O;j.info(`分析意图: "${e.message}"`);try{O=await u(e.message,h,A,I)}catch(e){const s={status:"failed",stage:"route",message:`LLM 路由失败: ${e instanceof Error?e.message:String(e)}`};return j.error(s.message),M(s),void(process.exitCode=1)}j.info(`路由决策: ${O.agentName}.${O.toolName} (置信度: ${String(O.confidence)})`);const T=C.ask.confirmThreshold??k;if(O.confidence<T){const e={status:"needs_confirmation",decision:O,message:`置信度 ${String(O.confidence)} 低于阈值 ${String(T)},跳过执行。 可使用 \`roll run ${O.agentName} ${O.toolName}\` 手动调用。`};return j.warn(e.message),M(e),void(process.exitCode=1)}const J=h.find(e=>e.skill.name===O.agentName);if(!J){const e={status:"failed",stage:"route",decision:O,message:`Agent "${O.agentName}" 未找到(LLM 返回了无效的 Agent 名称)`};return j.error(e.message),M(e),void(process.exitCode=1)}const P=i(L,C.llm.defaultModel,b.apiKey,b.baseUrl),E=new l;let K="connect";try{j.info(`连接 Agent "${J.skill.name}"...`);const s=t(C,J.skill.name),i=c(J),a=await E.connect(J.skill.name,i,J.installPath,{samplingModel:P,...s?{env:s}:{}}),l=v((await a.listTools()).tools),m=l.find(e=>e.name===O.toolName);if(!m){const e={status:"failed",stage:"route",decision:O,message:p(J.skill.name,O.toolName,l)};return j.error(e.message),M(e),void(process.exitCode=1)}K="execute";const u=await d(e.message,m,A,I),k={...O,input:u},x=r(J.skill.name,J.skill.env,C.agents.env),w=n(m.name)?[]:o(x),h=f(m,k.input,{runtimeIssues:w});if(!h.ok){const e={status:"needs_input",decision:k,validationIssues:h.issues,runtimeIssues:h.runtimeIssues,message:g(J.skill.name,O.toolName,h.issues,h.runtimeIssues)};return j.warn(e.message),M(e),void(process.exitCode=1)}j.info(`调用 ${J.skill.name}.${O.toolName}`),j.debug(`调用参数: ${JSON.stringify(y(k.input))}`);const L=await a.callTool({name:O.toolName,arguments:k.input});if(N(L)){const e={status:"failed",stage:"execute",decision:k,message:$(L.content).join("\n")||"Tool 调用失败"};return j.error(e.message),M(e),void(process.exitCode=1)}M({status:"success",decision:k,result:L}),j.success("调用完成")}catch(e){const s={status:"failed",stage:K,decision:O,message:e instanceof Error?e.message:String(e)};j.error(s.message),M(s),process.exitCode=1}finally{await E.disconnectAll()}}});
@@ -1 +1 @@
1
- import{defineCommand as s}from"citty";import{log as e}from"../utils/output.js";const t="roll chat 仍处于 experimental 阶段,当前只提供命令骨架,不会执行会话编排、不会恢复 session,也不会隐式降级到 roll ask。";function o(){return{status:"unavailable",message:t}}function n(s){console.log(JSON.stringify(s,null,2))}export default s({meta:{description:"Experimental: 未来会话式统一入口(当前仅提供命令骨架)"},args:{message:{type:"positional",description:"起始消息",required:!1},session:{type:"string",description:"继续指定会话 ID"},json:{type:"boolean",description:"JSON 格式输出",default:!1}},async run({args:s}){const t=o();s.json?n(t):e.warn(t.message),process.exitCode=1}});
1
+ import{defineCommand as s}from"citty";import{log as e}from"../utils/output.js";const t="roll chat 仍处于 experimental 阶段,当前只提供命令骨架,不会执行会话编排、不会恢复 session,也不会隐式降级到 roll ask。";function o(){return{status:"unavailable",message:t}}function n(s){console.log(JSON.stringify(s,null,2))}export default s({meta:{description:"Experimental: 会话式入口骨架(当前不可用)"},args:{message:{type:"positional",description:"起始消息",required:!1},session:{type:"string",description:"继续已有会话的 session ID"},json:{type:"boolean",description:"JSON 格式输出",default:!1}},async run({args:s}){const t=o();s.json?n(t):e.warn(t.message),process.exitCode=1}});
@@ -1 +1 @@
1
- import{defineCommand as o}from"citty";import{readFileSync as t,writeFileSync as r,existsSync as e}from"node:fs";import{resolve as n}from"node:path";import{createInterface as i}from"node:readline/promises";import{stringify as s}from"yaml";import{inspectConfigFile as c,loadConfig as a,parseConfigDocument as l,validateConfigText as d}from"../../config/loader.js";import{encodePathToYaml as f,normalizeUserPath as u}from"../../config/key-codec.js";import{applyKnownConfigMigrations as g}from"../../config/migration.js";export default o({meta:{description:"管理全局配置"},args:{action:{type:"positional",description:"操作(init/get/set/migrate)",required:!0},key:{type:"positional",description:"配置键(get/set 时使用,点号分隔)",required:!1},value:{type:"positional",description:"配置值(set 时使用)",required:!1}},async run({args:o}){try{if("init"===o.action)return void await v();if("get"===o.action)return void w(o.key);if("set"===o.action)return void S(o.key,o.value);if("migrate"===o.action)return void $();console.error(`✗ 未知操作: ${o.action}。可用: init, get, set, migrate`),process.exitCode=1}catch(o){const t=o instanceof Error?o.message:String(o);console.error(`✗ ${t}`),process.exitCode=1}}});function p(o,t){const r=o?.trim();return r||t}function m({provider:o,model:t,apiKeyEnv:r}){return`llm:\n default-provider: ${o}\n default-model: ${t}\n providers:\n ${o}:\n api-key: \${${r}}\n\nask:\n confirm-threshold: 0.5\n\nagents:\n data-dir: ~/.roll-agent/agents\n`}async function h(){if(!process.stdin.isTTY){const[o,r,e]=t(0,"utf-8").split(/\r?\n/u);return{provider:p(o,"anthropic"),model:p(r,"claude-sonnet-4-20250514"),apiKeyEnv:p(e,"ANTHROPIC_API_KEY")}}const o=i({input:process.stdin,output:process.stderr}),r=p(await o.question("默认 LLM provider (anthropic/openai/qwen) [anthropic]: "),"anthropic"),e=p(await o.question("默认 model [claude-sonnet-4-20250514]: "),"claude-sonnet-4-20250514"),n=p(await o.question("API Key 环境变量名 [ANTHROPIC_API_KEY]: "),"ANTHROPIC_API_KEY");return o.close(),{provider:r,model:e,apiKeyEnv:n}}async function v(){const o=n(process.cwd(),"roll.config.yaml");if(e(o)){const t=c({configPath:o});switch(t.status){case"needs-migration":console.error(`⚠ 现有配置文件需要迁移: ${o}`),console.error(" 建议先运行 `roll config migrate`,再决定是否重新初始化。");break;case"invalid":console.error(`⚠ 现有配置文件存在问题:\n${t.error.message}`)}if(console.error(`⚠ 配置文件已存在: ${o}`),!process.stdin.isTTY)throw new Error("非交互模式下不会覆盖现有配置文件,请手动删除后重试。");const r=i({input:process.stdin,output:process.stderr}),e=await r.question("是否覆盖?(y/N) ");if(r.close(),"y"!==e.toLowerCase())return void console.error("已取消。")}const t=m(await h());d(t,o),r(o,t,"utf-8"),console.log(`✓ 配置文件已创建: ${o}`)}function y(o){const t=new Date;return`${o}.bak.${[t.getFullYear().toString().padStart(4,"0"),(t.getMonth()+1).toString().padStart(2,"0"),t.getDate().toString().padStart(2,"0"),"-",t.getHours().toString().padStart(2,"0"),t.getMinutes().toString().padStart(2,"0"),t.getSeconds().toString().padStart(2,"0")].join("")}`}function $(){const o=c();if("not-found"===o.status)throw new Error("未找到配置文件。请先运行 roll config init");if("valid"===o.status)return void console.log(`✓ 配置文件已是最新格式,无需迁移: ${o.configPath}`);if("invalid"===o.status)throw o.error;const t=l(o.raw,o.configPath),e=g(t);if(!e.ok){const o=e.issues.map(o=>` - ${o.message}`).join("\n");throw new Error(`配置无法自动迁移:\n${o}`)}if(!e.changed)return void console.log(`✓ 配置文件已是最新格式,无需迁移: ${o.configPath}`);const n=y(o.configPath);r(n,o.raw,"utf-8");const i=s(e.document,{lineWidth:0});d(i,o.configPath),r(o.configPath,i,"utf-8"),console.log(`✓ 配置文件已迁移: ${o.configPath}`),console.log(`✓ 已备份原文件: ${n}`);for(const o of e.summary)console.log(` - ${o}`)}function w(o){const{config:t,configPath:r}=a();if(!o)return console.log(JSON.stringify(t,null,2)),void(r&&console.error(`(来源: ${r})`));const e=u(o.split("."));let n=t;for(const t of e){if("object"!=typeof n||null===n)return console.error(`✗ 配置键 "${o}" 不存在`),void(process.exitCode=1);n=n[t]}if(void 0===n)return console.error(`✗ 配置键 "${o}" 不存在`),void(process.exitCode=1);console.log("object"==typeof n?JSON.stringify(n,null,2):String(n))}function S(o,e){if(!o||void 0===e)return console.error("✗ 用法: roll config set <key> <value>"),console.error(" 示例: roll config set ask.confirmThreshold 0.5"),void(process.exitCode=1);const{configPath:n}=a();if(!n)return console.error("✗ 未找到配置文件。请先运行 roll config init"),void(process.exitCode=1);const i=t(n,"utf-8"),c=l(i,n),u=f(o.split(".")),g=u[u.length-1];if(void 0===g)return console.error("✗ 配置键不能为空"),void(process.exitCode=1);let p=c;for(let o=0;o<u.length-1;o++){const t=u[o],r=p[t];("object"!=typeof r||null===r||Array.isArray(r))&&(p[t]={}),p=p[t]}let m=e;"true"===e?m=!0:"false"===e?m=!1:/^\d+(\.\d+)?$/.test(e)&&(m=Number(e)),p[g]=m;const h=s(c,{lineWidth:0});d(h,n),r(n,h,"utf-8"),console.log(`✓ ${o} = ${String(m)}`),console.error(` (已写入: ${n})`)}
1
+ import{defineCommand as o}from"citty";import{readFileSync as t,writeFileSync as r,existsSync as e}from"node:fs";import{resolve as n}from"node:path";import{createInterface as i}from"node:readline/promises";import{stringify as s}from"yaml";import{inspectConfigFile as c,loadConfig as a,parseConfigDocument as l,validateConfigText as d}from"../../config/loader.js";import{encodePathToYaml as f,normalizeUserPath as u}from"../../config/key-codec.js";import{applyKnownConfigMigrations as g}from"../../config/migration.js";export default o({meta:{description:"管理全局配置"},args:{action:{type:"positional",description:"操作(init/get/set/migrate)",required:!0},key:{type:"positional",description:"配置键(get/set 时使用,用英文句点 `.` 分隔,如 ask.confirm-threshold)",required:!1},value:{type:"positional",description:"配置值(set 时使用)",required:!1}},async run({args:o}){try{if("init"===o.action)return void await v();if("get"===o.action)return void w(o.key);if("set"===o.action)return void S(o.key,o.value);if("migrate"===o.action)return void $();console.error(`✗ 未知操作: ${o.action}。可用: init, get, set, migrate`),process.exitCode=1}catch(o){const t=o instanceof Error?o.message:String(o);console.error(`✗ ${t}`),process.exitCode=1}}});function p(o,t){const r=o?.trim();return r||t}function m({provider:o,model:t,apiKeyEnv:r}){return`llm:\n default-provider: ${o}\n default-model: ${t}\n providers:\n ${o}:\n api-key: \${${r}}\n\nask:\n confirm-threshold: 0.5\n\nagents:\n data-dir: ~/.roll-agent/agents\n`}async function h(){if(!process.stdin.isTTY){const[o,r,e]=t(0,"utf-8").split(/\r?\n/u);return{provider:p(o,"anthropic"),model:p(r,"claude-sonnet-4-20250514"),apiKeyEnv:p(e,"ANTHROPIC_API_KEY")}}const o=i({input:process.stdin,output:process.stderr}),r=p(await o.question("默认 LLM provider (anthropic/openai/qwen) [anthropic]: "),"anthropic"),e=p(await o.question("默认 model [claude-sonnet-4-20250514]: "),"claude-sonnet-4-20250514"),n=p(await o.question("API Key 环境变量名 [ANTHROPIC_API_KEY]: "),"ANTHROPIC_API_KEY");return o.close(),{provider:r,model:e,apiKeyEnv:n}}async function v(){const o=n(process.cwd(),"roll.config.yaml");if(e(o)){const t=c({configPath:o});switch(t.status){case"needs-migration":console.error(`⚠ 现有配置文件需要迁移: ${o}`),console.error(" 建议先运行 `roll config migrate`,再决定是否重新初始化。");break;case"invalid":console.error(`⚠ 现有配置文件存在问题:\n${t.error.message}`)}if(console.error(`⚠ 配置文件已存在: ${o}`),!process.stdin.isTTY)throw new Error("非交互模式下不会覆盖现有配置文件,请手动删除后重试。");const r=i({input:process.stdin,output:process.stderr}),e=await r.question("是否覆盖?(y/N) ");if(r.close(),"y"!==e.toLowerCase())return void console.error("已取消。")}const t=m(await h());d(t,o),r(o,t,"utf-8"),console.log(`✓ 配置文件已创建: ${o}`)}function y(o){const t=new Date;return`${o}.bak.${[t.getFullYear().toString().padStart(4,"0"),(t.getMonth()+1).toString().padStart(2,"0"),t.getDate().toString().padStart(2,"0"),"-",t.getHours().toString().padStart(2,"0"),t.getMinutes().toString().padStart(2,"0"),t.getSeconds().toString().padStart(2,"0")].join("")}`}function $(){const o=c();if("not-found"===o.status)throw new Error("未找到配置文件。请先运行 roll config init");if("valid"===o.status)return void console.log(`✓ 配置文件已是最新格式,无需迁移: ${o.configPath}`);if("invalid"===o.status)throw o.error;const t=l(o.raw,o.configPath),e=g(t);if(!e.ok){const o=e.issues.map(o=>` - ${o.message}`).join("\n");throw new Error(`配置无法自动迁移:\n${o}`)}if(!e.changed)return void console.log(`✓ 配置文件已是最新格式,无需迁移: ${o.configPath}`);const n=y(o.configPath);r(n,o.raw,"utf-8");const i=s(e.document,{lineWidth:0});d(i,o.configPath),r(o.configPath,i,"utf-8"),console.log(`✓ 配置文件已迁移: ${o.configPath}`),console.log(`✓ 已备份原文件: ${n}`);for(const o of e.summary)console.log(` - ${o}`)}function w(o){const{config:t,configPath:r}=a();if(!o)return console.log(JSON.stringify(t,null,2)),void(r&&console.error(`(来源: ${r})`));const e=u(o.split("."));let n=t;for(const t of e){if("object"!=typeof n||null===n)return console.error(`✗ 配置键 "${o}" 不存在`),void(process.exitCode=1);n=n[t]}if(void 0===n)return console.error(`✗ 配置键 "${o}" 不存在`),void(process.exitCode=1);console.log("object"==typeof n?JSON.stringify(n,null,2):String(n))}function S(o,e){if(!o||void 0===e)return console.error("✗ 用法: roll config set <key> <value>"),console.error(" 示例: roll config set ask.confirm-threshold 0.5"),void(process.exitCode=1);const{configPath:n}=a();if(!n)return console.error("✗ 未找到配置文件。请先运行 roll config init"),void(process.exitCode=1);const i=t(n,"utf-8"),c=l(i,n),u=f(o.split(".")),g=u[u.length-1];if(void 0===g)return console.error("✗ 配置键不能为空"),void(process.exitCode=1);let p=c;for(let o=0;o<u.length-1;o++){const t=u[o],r=p[t];("object"!=typeof r||null===r||Array.isArray(r))&&(p[t]={}),p=p[t]}let m=e;"true"===e?m=!0:"false"===e?m=!1:/^\d+(\.\d+)?$/.test(e)&&(m=Number(e)),p[g]=m;const h=s(c,{lineWidth:0});d(h,n),r(n,h,"utf-8"),console.log(`✓ ${o} = ${String(m)}`),console.error(` (已写入: ${n})`)}
@@ -1 +1 @@
1
- import{defineCommand as s}from"citty";import{existsSync as e}from"node:fs";import{getAgentEnvFromAgentsConfig as t,inspectAgentEnvRequirements as n}from"../../config/helpers.js";import{inspectConfigFile as o,loadAgentsConfig as a,loadConfig as r}from"../../config/loader.js";import{inspectAgentRuntimeEnvRequirements as i,summarizeAgentRuntimeEnvReport as m}from"../../config/runtime-env.js";import{inspectAgentRuntimeEnv as g}from"../../mcp/agent-diagnostics.js";import{AgentStore as c}from"../../registry/store.js";const l={ok:"✓",warn:"⚠",fail:"✗"},u={major:22,minor:6,patch:0};export function isNodeVersionSupported(s){const e=/^(\d+)\.(\d+)\.(\d+)/.exec(s);if(!e)return!1;const t=Number(e[1]),n=Number(e[2]),o=Number(e[3]);return t>u.major||!(t<u.major)&&(n>u.minor||!(n<u.minor)&&o>=u.patch)}export default s({meta:{description:"诊断系统状态"},args:{json:{type:"boolean",description:"JSON 格式输出",default:!1}},async run({args:s}){const u=[];let p;const f=process.versions.node;u.push(isNodeVersionSupported(f)?{name:"Node.js 版本",status:"ok",message:`v${f}`}:{name:"Node.js 版本",status:"fail",message:`v${f} (需要 ≥22.6.0)`});const d=o();let h;switch(d.status){case"not-found":u.push({name:"配置文件",status:"warn",message:"未找到,使用默认配置"}),p=r().config;break;case"valid":u.push({name:"配置文件",status:"ok",message:d.configPath}),p=d.config;break;case"needs-migration":u.push({name:"配置文件",status:"warn",message:`${d.configPath} (需要迁移,运行 roll config migrate)`});break;case"invalid":u.push({name:"配置文件",status:"fail",message:d.error.message})}try{h=a().agentsConfig}catch(s){u.push({name:"Agent 配置",status:"fail",message:s instanceof Error?s.message:String(s)})}if(p){const s=Object.keys(p.llm.providers);if(s.length>0){const e=s.filter(s=>{const e=p.llm.providers[s]?.apiKey??"";return e.startsWith("${")||0===e.length});e.length>0?u.push({name:"LLM Providers",status:"warn",message:`${s.join(", ")} (${e.join(", ")} API key 未设置)`}):u.push({name:"LLM Providers",status:"ok",message:s.join(", ")})}else u.push({name:"LLM Providers",status:"warn",message:"未配置任何 provider"})}else"needs-migration"===d.status&&u.push({name:"LLM Providers",status:"warn",message:"配置需要迁移,跳过完整 LLM 配置校验"});if(h){const s=h.dataDir;u.push(e(s)?{name:"Agent 数据目录",status:"ok",message:s}:{name:"Agent 数据目录",status:"warn",message:`${s} (不存在,首次 add 时创建)`});const o=new c(h.dataDir).list();u.push({name:"已注册 Agent",status:o.length>0?"ok":"warn",message:o.length>0?`${String(o.length)} 个 (${o.map(s=>s.skill.name).join(", ")})`:"无"});for(const s of o){const e=n(s.skill.name,s.skill.env,h.env);if(!e)continue;const o=await g(s,{agentsConfig:h}),a=i(e,t(h,s.skill.name),o),r=m(a);u.push({name:`Agent 环境变量 (${s.skill.name})`,status:r.status,message:r.message})}}const j=u.some(s=>"fail"===s.status);if(s.json)return console.log(JSON.stringify(u,null,2)),void(j&&(process.exitCode=1));console.log("Roll Agent 系统诊断\n");for(const s of u){const e=l[s.status];console.log(` ${e} ${s.name}: ${s.message}`)}console.log(j?"\n存在问题,请修复后重试。":"\n系统状态正常。"),j&&(process.exitCode=1)}});
1
+ import{defineCommand as s}from"citty";import{existsSync as e}from"node:fs";import{getAgentEnvFromAgentsConfig as t,inspectAgentEnvRequirements as n}from"../../config/helpers.js";import{inspectConfigFile as o,loadAgentsConfig as a,loadConfig as r}from"../../config/loader.js";import{inspectAgentRuntimeEnvRequirements as i,summarizeAgentRuntimeEnvReport as m}from"../../config/runtime-env.js";import{inspectAgentRuntimeEnv as g}from"../../mcp/agent-diagnostics.js";import{AgentStore as l}from"../../registry/store.js";const c={ok:"✓",warn:"⚠",fail:"✗"},u={major:22,minor:6,patch:0};export function isNodeVersionSupported(s){const e=/^(\d+)\.(\d+)\.(\d+)/.exec(s);if(!e)return!1;const t=Number(e[1]),n=Number(e[2]),o=Number(e[3]);return t>u.major||!(t<u.major)&&(n>u.minor||!(n<u.minor)&&o>=u.patch)}export default s({meta:{description:"诊断 Roll 配置、Agent 注册表和运行时状态"},args:{json:{type:"boolean",description:"JSON 格式输出",default:!1}},async run({args:s}){const u=[];let p;const f=process.versions.node;u.push(isNodeVersionSupported(f)?{name:"Node.js 版本",status:"ok",message:`v${f}`}:{name:"Node.js 版本",status:"fail",message:`v${f} (需要 ≥22.6.0)`});const d=o();let h;switch(d.status){case"not-found":u.push({name:"配置文件",status:"warn",message:"未找到,使用默认配置"}),p=r().config;break;case"valid":u.push({name:"配置文件",status:"ok",message:d.configPath}),p=d.config;break;case"needs-migration":u.push({name:"配置文件",status:"warn",message:`${d.configPath} (需要迁移,运行 roll config migrate)`});break;case"invalid":u.push({name:"配置文件",status:"fail",message:d.error.message})}try{h=a().agentsConfig}catch(s){u.push({name:"Agent 配置",status:"fail",message:s instanceof Error?s.message:String(s)})}if(p){const s=Object.keys(p.llm.providers);if(s.length>0){const e=s.filter(s=>{const e=p.llm.providers[s]?.apiKey??"";return e.startsWith("${")||0===e.length});e.length>0?u.push({name:"LLM Providers",status:"warn",message:`${s.join(", ")} (${e.join(", ")} API key 未设置)`}):u.push({name:"LLM Providers",status:"ok",message:s.join(", ")})}else u.push({name:"LLM Providers",status:"warn",message:"未配置任何 provider"})}else"needs-migration"===d.status&&u.push({name:"LLM Providers",status:"warn",message:"配置需要迁移,跳过完整 LLM 配置校验"});if(h){const s=h.dataDir;u.push(e(s)?{name:"Agent 数据目录",status:"ok",message:s}:{name:"Agent 数据目录",status:"warn",message:`${s} (不存在,首次 add 时创建)`});const o=new l(h.dataDir).list();u.push({name:"已注册 Agent",status:o.length>0?"ok":"warn",message:o.length>0?`${String(o.length)} 个 (${o.map(s=>s.skill.name).join(", ")})`:"无"});for(const s of o){const e=n(s.skill.name,s.skill.env,h.env);if(!e)continue;const o=await g(s,{agentsConfig:h}),a=i(e,t(h,s.skill.name),o),r=m(a);u.push({name:`Agent 环境变量 (${s.skill.name})`,status:r.status,message:r.message})}}const j=u.some(s=>"fail"===s.status);if(s.json)return console.log(JSON.stringify(u,null,2)),void(j&&(process.exitCode=1));console.log("Roll Agent 系统诊断\n");for(const s of u){const e=c[s.status];console.log(` ${e} ${s.name}: ${s.message}`)}console.log(j?"\n存在问题,请修复后重试。":"\n系统状态正常。"),j&&(process.exitCode=1)}});
@@ -1 +1 @@
1
- import{readFileSync as t}from"node:fs";import{defineCommand as o}from"citty";import{loadConfig as r}from"../../config/loader.js";import{getAgentEnv as e,getMissingAgentEnvRuntimeIssues as n,inspectAgentEnvRequirements as i}from"../../config/helpers.js";import{AgentStore as s}from"../../registry/store.js";import{McpClientManager as l}from"../../mcp/client-manager.js";import{resolveTransportWithDevSpawnSpec as a}from"../../registry/dev-spawn.js";import{createProviderModel as c}from"../../llm/providers.js";import{formatValidationIssuesMessage as f}from"../../tool-runtime/messages.js";import{preflightToolCall as p}from"../../tool-runtime/preflight.js";import{formatMissingToolMessage as u,normalizeListedTools as m}from"../utils/agent-tools.js";import{extractTextContent as g,formatToolResultForJsonOutput as d,isToolErrorResult as h}from"../utils/tool-results.js";import{log as j,redactToolArgsForLog as v}from"../utils/output.js";import{shouldSkipRuntimeReadinessForTool as y}from"../../config/runtime-env.js";export default o({meta:{description:"声明式调用 Agent 的指定 tool"},args:{agent:{type:"positional",description:"Agent 名称",required:!0},tool:{type:"positional",description:"Tool 名称",required:!0},json:{type:"boolean",description:"JSON 格式输出",default:!1},verbose:{type:"boolean",alias:"v",description:"输出调试日志",default:!1},"input-json":{type:"string",description:"以 JSON 字符串提供完整 tool 输入对象"},"input-file":{type:"string",description:"从 JSON 文件读取完整 tool 输入对象"}},async run({args:t,rawArgs:o}){const{config:w}=r(),S=new s(w.agents.dataDir).findByName(t.agent);if(!S)return j.error(`Agent "${t.agent}" 未注册。使用 \`roll agent list\` 查看已注册 Agent。`),void(process.exitCode=1);let N;try{N=resolveToolArgs(o)}catch(t){return j.error(t instanceof Error?t.message:String(t)),void(process.exitCode=1)}const $=new l;try{const o=w.llm.defaultProvider,r=w.llm.providers[o],s=r?c(o,w.llm.defaultModel,r.apiKey,r.baseUrl):void 0;j.info(`连接 Agent "${S.skill.name}"...`);const l=e(w,S.skill.name),A=a(S),b=await $.connect(S.skill.name,A,S.installPath,{...s?{samplingModel:s}:{},...l?{env:l}:{}}),x=m((await b.listTools()).tools),E=x.find(o=>o.name===t.tool);if(!E)return j.error(u(S.skill.name,t.tool,x)),void(process.exitCode=1);const k=i(S.skill.name,S.skill.env,w.agents.env),J=y(E.name)?[]:n(k),O=p(E,N,{runtimeIssues:J});if(!O.ok)return j.error(f(S.skill.name,t.tool,O.issues,O.runtimeIssues)),void(process.exitCode=1);j.info(`调用 ${S.skill.name}.${t.tool}`),j.debug(`调用参数: ${JSON.stringify(v(N))}`);const T=await b.callTool({name:t.tool,arguments:N});if(t.json)console.log(JSON.stringify(d(T),null,2));else for(const t of g(T.content))console.log(t);if(h(T))return j.error("tool 返回 isError=true"),void(process.exitCode=1);j.success("调用完成")}catch(t){const o=t instanceof Error?t.message:String(t),r=t instanceof Error&&t.cause?`\n cause: ${String(t.cause)}`:"";j.error(`${o}${r}`),process.exitCode=1}finally{await $.disconnectAll()}}});const w=new Set(["json","verbose","v","help","h","version"]),S=new Set(["config","input-json","input-file"]);function N(t){return"object"==typeof t&&null!==t&&!Array.isArray(t)}function $(t,o){let r;try{r=JSON.parse(t)}catch(t){throw new Error(`${o} 不是合法 JSON: ${t instanceof Error?t.message:String(t)}`)}if(!N(r))throw new Error(`${o} 必须是 JSON object`);return r}function A(t,o){let r=0;for(;r<t.length&&!t[r]?.startsWith("--");)r++;for(;r<t.length;){if(t[r]!==`--${o}`){r++;continue}const e=t[r+1];if(!e||e.startsWith("--"))throw new Error(`选项 --${o} 需要提供值`);return e}}export function parseExplicitToolInput(o){const r=A(o,"input-json"),e=A(o,"input-file"),n=b(o);if([r,e,n].filter(t=>void 0!==t).length>1)throw new Error("不能同时使用 positional JSON、--input-json 和 --input-file");if(r)return $(r,"--input-json");if(e){return $(t(e,"utf-8"),`输入文件 ${e}`)}return n?$(n,"positional JSON input"):void 0}function b(t){const o=[];for(const r of t){if(r.startsWith("--"))break;o.push(r)}const r=o.slice(2);if(0!==r.length){if(1===r.length&&r[0]?.trim().startsWith("{"))return r[0];throw new Error("roll run 只接受 agent/tool 两个位置参数;tool 输入请使用 --key value、--input-json、--input-file,或第三个位置参数 JSON object")}}export function resolveToolArgs(t){return{...parseExplicitToolInput(t)??{},...parseToolArgs(t)}}export function parseToolArgs(t){const o={};let r=0;for(;r<t.length&&!t[r]?.startsWith("--");)r++;for(;r<t.length;){const e=t[r];if(!e?.startsWith("--")){r++;continue}const n=e.slice(2),i=t[r+1];if(w.has(n))r++;else if(S.has(n))r+=i&&!i.startsWith("--")?2:1;else if(!i||i.startsWith("--"))o[n]=!0,r++;else{const t=Number(i);o[n]=Number.isNaN(t)?i:t,r+=2}}return o}
1
+ import{readFileSync as t}from"node:fs";import{defineCommand as o}from"citty";import{loadConfig as e}from"../../config/loader.js";import{getAgentEnv as r,getMissingAgentEnvRuntimeIssues as n,inspectAgentEnvRequirements as i}from"../../config/helpers.js";import{AgentStore as s}from"../../registry/store.js";import{McpClientManager as l}from"../../mcp/client-manager.js";import{resolveTransportWithDevSpawnSpec as a}from"../../registry/dev-spawn.js";import{createProviderModel as c}from"../../llm/providers.js";import{formatValidationIssuesMessage as f}from"../../tool-runtime/messages.js";import{preflightToolCall as p}from"../../tool-runtime/preflight.js";import{formatMissingToolMessage as u,normalizeListedTools as m}from"../utils/agent-tools.js";import{extractTextContent as g,formatToolResultForJsonOutput as d,isToolErrorResult as h}from"../utils/tool-results.js";import{log as j,redactToolArgsForLog as v}from"../utils/output.js";import{shouldSkipRuntimeReadinessForTool as y}from"../../config/runtime-env.js";export default o({meta:{description:"直接调用已注册 Agent 的指定 MCP tool"},args:{agent:{type:"positional",description:"Agent 名称",required:!0},tool:{type:"positional",description:"MCP tool 名称",required:!0},json:{type:"boolean",description:"JSON 格式输出",default:!1},verbose:{type:"boolean",alias:"v",description:"输出调试日志",default:!1},"input-json":{type:"string",description:"以 JSON 字符串提供完整 tool 输入对象;不能与 --input-file 同用"},"input-file":{type:"string",description:"从 JSON 文件读取完整 tool 输入对象;不能与 --input-json 同用"}},async run({args:t,rawArgs:o}){const{config:w}=e(),S=new s(w.agents.dataDir).findByName(t.agent);if(!S)return j.error(`Agent "${t.agent}" 未注册。使用 \`roll agent list\` 查看已注册 Agent。`),void(process.exitCode=1);let N;try{N=resolveToolArgs(o)}catch(t){return j.error(t instanceof Error?t.message:String(t)),void(process.exitCode=1)}const $=new l;try{const o=w.llm.defaultProvider,e=w.llm.providers[o],s=e?c(o,w.llm.defaultModel,e.apiKey,e.baseUrl):void 0;j.info(`连接 Agent "${S.skill.name}"...`);const l=r(w,S.skill.name),A=a(S),b=await $.connect(S.skill.name,A,S.installPath,{...s?{samplingModel:s}:{},...l?{env:l}:{}}),x=m((await b.listTools()).tools),E=x.find(o=>o.name===t.tool);if(!E)return j.error(u(S.skill.name,t.tool,x)),void(process.exitCode=1);const k=i(S.skill.name,S.skill.env,w.agents.env),J=y(E.name)?[]:n(k),O=p(E,N,{runtimeIssues:J});if(!O.ok)return j.error(f(S.skill.name,t.tool,O.issues,O.runtimeIssues)),void(process.exitCode=1);j.info(`调用 ${S.skill.name}.${t.tool}`),j.debug(`调用参数: ${JSON.stringify(v(N))}`);const C=await b.callTool({name:t.tool,arguments:N});if(t.json)console.log(JSON.stringify(d(C),null,2));else for(const t of g(C.content))console.log(t);if(h(C))return j.error("tool 返回 isError=true"),void(process.exitCode=1);j.success("调用完成")}catch(t){const o=t instanceof Error?t.message:String(t),e=t instanceof Error&&t.cause?`\n cause: ${String(t.cause)}`:"";j.error(`${o}${e}`),process.exitCode=1}finally{await $.disconnectAll()}}});const w=new Set(["json","verbose","v","help","h","version"]),S=new Set(["config","input-json","input-file"]);function N(t){return"object"==typeof t&&null!==t&&!Array.isArray(t)}function $(t,o){let e;try{e=JSON.parse(t)}catch(t){throw new Error(`${o} 不是合法 JSON: ${t instanceof Error?t.message:String(t)}`)}if(!N(e))throw new Error(`${o} 必须是 JSON object`);return e}function A(t,o){let e=0;for(;e<t.length&&!t[e]?.startsWith("--");)e++;for(;e<t.length;){if(t[e]!==`--${o}`){e++;continue}const r=t[e+1];if(!r||r.startsWith("--"))throw new Error(`选项 --${o} 需要提供值`);return r}}export function parseExplicitToolInput(o){const e=A(o,"input-json"),r=A(o,"input-file"),n=b(o);if([e,r,n].filter(t=>void 0!==t).length>1)throw new Error("不能同时使用 positional JSON、--input-json 和 --input-file");if(e)return $(e,"--input-json");if(r){return $(t(r,"utf-8"),`输入文件 ${r}`)}return n?$(n,"positional JSON input"):void 0}function b(t){const o=[];for(const e of t){if(e.startsWith("--"))break;o.push(e)}const e=o.slice(2);if(0!==e.length){if(1===e.length&&e[0]?.trim().startsWith("{"))return e[0];throw new Error("roll run 只接受 agent/tool 两个位置参数;tool 输入请使用 --key value、--input-json、--input-file,或第三个位置参数 JSON object")}}export function resolveToolArgs(t){return{...parseExplicitToolInput(t)??{},...parseToolArgs(t)}}export function parseToolArgs(t){const o={};let e=0;for(;e<t.length&&!t[e]?.startsWith("--");)e++;for(;e<t.length;){const r=t[e];if(!r?.startsWith("--")){e++;continue}const n=r.slice(2),i=t[e+1];if(w.has(n))e++;else if(S.has(n))e+=i&&!i.startsWith("--")?2:1;else if(!i||i.startsWith("--"))o[n]=!0,e++;else{const t=Number(i);o[n]=Number.isNaN(t)?i:t,e+=2}}return o}
@@ -6,7 +6,7 @@ declare const _default: import("citty").CommandDef<{
6
6
  description: string;
7
7
  default: false;
8
8
  };
9
- skipBrowserSetup: {
9
+ "skip-browser-setup": {
10
10
  type: "boolean";
11
11
  description: string;
12
12
  default: false;
@@ -1 +1 @@
1
- import{defineCommand as e}from"citty";import{resolve as a}from"node:path";import{existsSync as t}from"node:fs";import{execFile as r}from"node:child_process";import{promisify as n}from"node:util";import{inspectConfigFile as s,loadAgentsConfig as i}from"../../config/loader.js";import{getAgentPid as o,startAgent as l,stopAgentGracefully as c,waitForAgentReady as u}from"../../registry/process-manager.js";import{resolveTransportWithDevSpawnSpec as m}from"../../registry/dev-spawn.js";import{runAgentSetup as p}from"../../registry/runtime-setup.js";import{AgentStore as k}from"../../registry/store.js";import{discoverAgent as d}from"../../registry/discovery.js";import{inferAgentSourceType as f,readInstalledPackageManifest as g,resolveInstalledPackageRoot as $}from"../../registry/source.js";import{McpClientManager as h}from"../../mcp/client-manager.js";import{log as w,createSpinner as y}from"../utils/output.js";import{checkForUpdate as v,checkPublishedPackageUpdate as S,getCurrentVersion as V}from"../utils/update-checker.js";import{detectInstallCommand as b,formatPackageManagerError as j,runPackageManager as B}from"../utils/package-manager.js";const P=n(r);export{inferAgentSourceType as inferSourceType}from"../../registry/source.js";export{detectInstallCommand}from"../utils/package-manager.js";function D(e,a){switch(e.status){case"needs-migration":{const t="post-update"===a?"升级后需要迁移本地配置":"检测到本地配置需要迁移";w.warn(`${t}: ${e.configPath}`);for(const a of e.report.issues)w.warn(` - ${a.message}`);e.report.canAutoMigrate&&w.info("建议命令: roll config migrate");break}case"invalid":w.warn(`本地配置存在问题: ${e.configPath??"(unknown path)"}`),w.warn(` - ${e.error.message}`),"post-update"===a&&w.info("请修复配置文件后再继续使用相关命令。")}}function A(e){switch(e){case"up-to-date":return"✅";case"update-available":return"⬆";case"pinned-behind":return"📌";case"unsupported-spec":case"unknown":return"?"}}function N(e,a){switch(e.status){case"up-to-date":return e.currentVersion?`已是最新版本 (v${e.currentVersion})`:"已是最新版本";case"update-available":return e.currentVersion&&e.latestVersion?`可更新 v${e.currentVersion} → v${e.latestVersion}`:"检测到可用更新";case"pinned-behind":return e.currentVersion&&e.latestVersion?`固定版本 v${e.currentVersion};latest=v${e.latestVersion}`:"固定版本,需手动调整 package spec";case"unsupported-spec":return`不支持检查此 package spec: ${a}`;case"unknown":return e.currentVersion?`无法检查最新版本 (current=v${e.currentVersion})`:"无法检查最新版本"}}function T(e,a){if("installed-package"!==e.source?.type)return e;const t=$(e.source.installDir,e.source.packageName),r=g(t);if(!r)return e;const n={...e.source,...r.name?{packageName:r.name}:{},...r.version?{installedVersion:r.version}:{}},s=n.packageName!==e.source.packageName||n.installedVersion!==e.source.installedVersion,i=t!==e.installPath;if(!s&&!i)return e;const o={...e,installPath:t,source:n};return a&&a.replace(e.skill.name,o),o}async function E(e,a){const t=V();if(t===e)return w.info(`roll 已是最新版本 (v${t})`),!1;if(w.info(`roll v${t} → v${e}`),a)return w.info("[dry-run] 跳过实际更新"),!0;const r=y("正在更新 @roll-agent/core...").start(),n={command:"npm",args:["install","-g",`@roll-agent/core@${e}`]};try{return await B(n,{timeout:6e4}),r.succeed(`roll 已更新到 v${e}`),!0}catch(e){return r.fail("更新失败"),w.error(j(n,e)),!1}}async function C(e){const r=y(`更新 ${e.skill.name} (git pull)...`).start();try{await P("git",["pull"],{cwd:e.installPath,timeout:3e4}),r.succeed(`${e.skill.name} 代码已更新`);const n=a(e.installPath,"package.json");if(t(n)){const a=b(e.installPath);if(a){const t=y(`安装 ${e.skill.name} 依赖...`).start();try{await B(a,{cwd:e.installPath,timeout:6e4}),t.succeed(`${e.skill.name} 依赖已更新 (${a.command} ${a.args.join(" ")})`)}catch(r){return t.fail(`${e.skill.name} 依赖安装失败`),w.error(j(a,r)),!1}}else w.warn(`${e.skill.name} 未检测到 packageManager 或 lockfile,跳过依赖安装。`)}return!0}catch(a){return r.fail(`${e.skill.name} 更新失败`),w.error(a instanceof Error?a.message:String(a)),!1}}async function M(e){if("installed-package"!==e.source?.type)return;const a=y(`更新 ${e.skill.name} (npm install)...`).start(),r={command:"npm",args:["install","--prefix",e.source.installDir,e.source.packageSpec]};try{await B(r,{timeout:12e4});const n=$(e.source.installDir,e.source.packageName);if(!t(n))throw new Error(`Installed package root not found: ${n}`);const s=g(n);return a.succeed(`${e.skill.name} 已重新安装`),{packageRoot:n,packageName:s?.name??e.source.packageName,...s?.version?{installedVersion:s.version}:{}}}catch(t){return a.fail(`${e.skill.name} 更新失败`),void w.error(j(r,t))}}async function x(e){const a=y(`刷新 ${e.skill.name} (MCP tools/list)...`).start(),t=new h;try{const r=m(e),n=await t.connect(e.skill.name,r,e.installPath),{tools:s}=await n.listTools();return a.succeed(`${e.skill.name} 元数据已刷新(${s.length} 个 tool)`),!0}catch(t){return a.fail(`${e.skill.name} 刷新失败`),w.error(t instanceof Error?t.message:String(t)),!1}finally{await t.disconnectAll()}}export default e({meta:{description:"更新 roll 及已注册的 Agent"},args:{check:{type:"boolean",description:"仅检查可用更新,不执行",default:!1},skipBrowserSetup:{type:"boolean",description:"跳过浏览器运行时安装",default:!1}},async run({args:e}){const a=e.check,t=s();w.info("检查 roll 更新...");const r=await v({forceRefresh:!0});let n,l;r.hasUpdate?w.success(`roll 有新版本: v${r.current} → v${r.latest}`):w.info(`roll 已是最新版本 (v${r.current})`);let u=[];try{n=i().agentsConfig,l=new k(n.dataDir),u=l.list()}catch(e){w.warn(`无法读取 Agent 配置,跳过已注册 Agent 检查:${e instanceof Error?e.message:String(e)}`)}const m=[];for(const e of u){let a=e;"installed-package"===f(a)&&(a=T(a,l));const t=f(a);switch(t){case"git":m.push({name:a.skill.name,sourceType:t,icon:"~",action:"可执行 git pull + 重新安装依赖"});break;case"installed-package":{if("installed-package"!==a.source?.type){m.push({name:a.skill.name,sourceType:t,icon:"?",action:"无法检查已安装包版本"});break}const e=await S({packageName:a.source.packageName,packageSpec:a.source.packageSpec,...a.source.installedVersion?{currentVersion:a.source.installedVersion}:{}},{forceRefresh:!0});m.push({name:a.skill.name,sourceType:t,icon:A(e.status),action:N(e,a.source.packageSpec)});break}case"remote-manifest":m.push({name:a.skill.name,sourceType:t,icon:"~",action:"可刷新本地 manifest + MCP 元数据"});break;case"local-path":m.push({name:a.skill.name,sourceType:t,icon:"⏭",action:"刷新本地 SKILL/manifest"})}}if(u.length>0){w.info(`\n已注册 Agent (${u.length}):`);for(const e of m)w.info(` ${e.icon} ${e.name} [${e.sourceType}] — ${e.action}`)}else n&&w.info("无已注册 Agent");if("needs-migration"!==t.status&&"invalid"!==t.status||(w.info(""),D(t,a?"check":"pre-update")),a)return;w.info("");let g=!1,$=!1;r.hasUpdate&&(g=await E(r.latest,!1),g||($=!0)),!g||"needs-migration"!==t.status&&"invalid"!==t.status||(w.info(""),D(t,"post-update"));let h=0,y=0;for(const a of u){if(!l||!n)break;switch(f(a)){case"git":{const e="core-managed"===a.runtime.ownership&&void 0!==o(n.dataDir,a.skill.name);if(await C(a))try{const t=d(a.installPath),r={...a,skill:t.skill,transport:t.transport,runtime:t.runtime,...t.skillBody.length>0?{skillBody:t.skillBody}:{}};l.replace(a.skill.name,r)?(await R(r,e,n.dataDir),h++):(w.warn(`${a.skill.name} 已从注册表中移除,跳过元数据刷新`),y++)}catch(e){l.updateStatus(a.skill.name,"error"),w.warn(`${a.skill.name} metadata 刷新或重启失败: ${e instanceof Error?e.message:String(e)}`),y++}else y++;break}case"installed-package":{const t="core-managed"===a.runtime.ownership&&void 0!==o(n.dataDir,a.skill.name);if(t)try{await c(n.dataDir,a.skill.name)}catch(e){l.updateStatus(a.skill.name,"error"),w.warn(`${a.skill.name} 停止失败,无法继续升级: ${e instanceof Error?e.message:String(e)}`),y++;break}const r=await M(a);if(r)try{const s=d(r.packageRoot),i="installed-package"===a.source?.type?{...a.source,packageName:r.packageName,...r.installedVersion?{installedVersion:r.installedVersion}:{}}:void 0,o={...a,skill:s.skill,transport:s.transport,runtime:s.runtime,installPath:r.packageRoot,...i?{source:i}:{},...s.skillBody.length>0?{skillBody:s.skillBody}:{}},c=await p(o,{skipBrowserSetup:e.skipBrowserSetup});c.ok||(w.warn(`${o.skill.name} setup 失败:${c.message}`),c.retryCommand&&w.info(`重试命令: ${c.retryCommand}`));l.replace(a.skill.name,o)?c.ok?(t&&await I(o,n.dataDir),h++):(l.updateStatus(o.skill.name,"error"),y++):(w.warn(`${a.skill.name} 已从注册表中移除,跳过元数据刷新`),y++)}catch(e){l.updateStatus(a.skill.name,"error"),w.warn(`${a.skill.name} metadata 刷新、setup 或重启失败: ${e instanceof Error?e.message:String(e)}`),y++}else{if(t)try{await I(a,n.dataDir)}catch{l.updateStatus(a.skill.name,"error")}y++}break}case"remote-manifest":try{const e=d(a.installPath),t={...a,skill:e.skill,transport:e.transport,runtime:e.runtime,...e.skillBody.length>0?{skillBody:e.skillBody}:{}};if(!l.replace(a.skill.name,t)){w.warn(`${a.skill.name} 已从注册表中移除,跳过元数据刷新`),y++;break}await x(t)?h++:y++}catch(e){w.warn(`${a.skill.name} manifest 刷新失败: ${e instanceof Error?e.message:String(e)}`),y++}break;case"local-path":{const e="core-managed"===a.runtime.ownership&&void 0!==o(n.dataDir,a.skill.name);try{const t=d(a.installPath),r={...a,skill:t.skill,transport:t.transport,runtime:t.runtime,...t.skillBody.length>0?{skillBody:t.skillBody}:{}};l.replace(a.skill.name,r)?(await R(r,e,n.dataDir),h++):(w.warn(`${a.skill.name} 已从注册表中移除,跳过元数据刷新`),y++)}catch(e){l.updateStatus(a.skill.name,"error"),w.warn(`${a.skill.name} 本地 metadata 刷新或重启失败: ${e instanceof Error?e.message:String(e)}`),y++}break}}}w.info("");if(y+($?1:0)>0){process.exitCode=1;const e=$?"roll 更新失败":g?"roll ✓":"roll 无更新";return void w.warn(`更新完成但有失败:${e}${y>0?`,${y} 个 Agent 更新失败`:""}${h>0?`,${h} 个 Agent 已更新`:""}`)}g||h>0?w.success(`更新完成:${g?"roll ✓":"roll 无更新"}${h>0?`,${h} 个 Agent 已更新`:""}`):w.success("一切都已是最新版本")}});async function R(e,a,t){a&&"core-managed"===e.runtime.ownership&&(await c(t,e.skill.name),await I(e,t))}async function I(e,a){let t=!1;try{l(e,a),t=!0,await u(e,{startupTimeoutMs:15e3,probeTimeoutMs:2e3})}catch(r){throw t&&await c(a,e.skill.name).catch(()=>{}),r}}
1
+ import{defineCommand as e}from"citty";import{resolve as a}from"node:path";import{existsSync as t}from"node:fs";import{execFile as r}from"node:child_process";import{promisify as n}from"node:util";import{inspectConfigFile as s,loadAgentsConfig as i}from"../../config/loader.js";import{getAgentPid as o,startAgent as l,stopAgentGracefully as c,waitForAgentReady as u}from"../../registry/process-manager.js";import{resolveTransportWithDevSpawnSpec as m}from"../../registry/dev-spawn.js";import{runAgentSetup as p}from"../../registry/runtime-setup.js";import{AgentStore as k}from"../../registry/store.js";import{discoverAgent as d}from"../../registry/discovery.js";import{inferAgentSourceType as g,readInstalledPackageManifest as f,resolveInstalledPackageRoot as $}from"../../registry/source.js";import{McpClientManager as h}from"../../mcp/client-manager.js";import{log as w,createSpinner as y}from"../utils/output.js";import{checkForUpdate as v,checkPublishedPackageUpdate as S,getCurrentVersion as V}from"../utils/update-checker.js";import{detectInstallCommand as b,formatPackageManagerError as P,runPackageManager as j}from"../utils/package-manager.js";const B=n(r);export{inferAgentSourceType as inferSourceType}from"../../registry/source.js";export{detectInstallCommand}from"../utils/package-manager.js";function A(e,a){switch(e.status){case"needs-migration":{const t="post-update"===a?"升级后需要迁移本地配置":"检测到本地配置需要迁移";w.warn(`${t}: ${e.configPath}`);for(const a of e.report.issues)w.warn(` - ${a.message}`);e.report.canAutoMigrate&&w.info("建议命令: roll config migrate");break}case"invalid":w.warn(`本地配置存在问题: ${e.configPath??"(unknown path)"}`),w.warn(` - ${e.error.message}`),"post-update"===a&&w.info("请修复配置文件后再继续使用相关命令。")}}function D(e){switch(e){case"up-to-date":return"✅";case"update-available":return"⬆";case"pinned-behind":return"📌";case"unsupported-spec":case"unknown":return"?"}}function N(e,a){switch(e.status){case"up-to-date":return e.currentVersion?`已是最新版本 (v${e.currentVersion})`:"已是最新版本";case"update-available":return e.currentVersion&&e.latestVersion?`可更新 v${e.currentVersion} → v${e.latestVersion}`:"检测到可用更新";case"pinned-behind":return e.currentVersion&&e.latestVersion?`固定版本 v${e.currentVersion};latest=v${e.latestVersion}`:"固定版本,需手动调整 package spec";case"unsupported-spec":return`不支持检查此 package spec: ${a}`;case"unknown":return e.currentVersion?`无法检查最新版本 (current=v${e.currentVersion})`:"无法检查最新版本"}}function T(e,a){if("installed-package"!==e.source?.type)return e;const t=$(e.source.installDir,e.source.packageName),r=f(t);if(!r)return e;const n={...e.source,...r.name?{packageName:r.name}:{},...r.version?{installedVersion:r.version}:{}},s=n.packageName!==e.source.packageName||n.installedVersion!==e.source.installedVersion,i=t!==e.installPath;if(!s&&!i)return e;const o={...e,installPath:t,source:n};return a&&a.replace(e.skill.name,o),o}async function E(e,a){const t=V();if(t===e)return w.info(`roll 已是最新版本 (v${t})`),!1;if(w.info(`roll v${t} → v${e}`),a)return w.info("[dry-run] 跳过实际更新"),!0;const r=y("正在更新 @roll-agent/core...").start(),n={command:"npm",args:["install","-g",`@roll-agent/core@${e}`]};try{return await j(n,{timeout:6e4}),r.succeed(`roll 已更新到 v${e}`),!0}catch(e){return r.fail("更新失败"),w.error(P(n,e)),!1}}async function C(e){const r=y(`更新 ${e.skill.name} (git pull)...`).start();try{await B("git",["pull"],{cwd:e.installPath,timeout:3e4}),r.succeed(`${e.skill.name} 代码已更新`);const n=a(e.installPath,"package.json");if(t(n)){const a=b(e.installPath);if(a){const t=y(`安装 ${e.skill.name} 依赖...`).start();try{await j(a,{cwd:e.installPath,timeout:6e4}),t.succeed(`${e.skill.name} 依赖已更新 (${a.command} ${a.args.join(" ")})`)}catch(r){return t.fail(`${e.skill.name} 依赖安装失败`),w.error(P(a,r)),!1}}else w.warn(`${e.skill.name} 未检测到 packageManager 或 lockfile,跳过依赖安装。`)}return!0}catch(a){return r.fail(`${e.skill.name} 更新失败`),w.error(a instanceof Error?a.message:String(a)),!1}}async function M(e){if("installed-package"!==e.source?.type)return;const a=y(`更新 ${e.skill.name} (npm install)...`).start(),r={command:"npm",args:["install","--prefix",e.source.installDir,e.source.packageSpec]};try{await j(r,{timeout:12e4});const n=$(e.source.installDir,e.source.packageName);if(!t(n))throw new Error(`Installed package root not found: ${n}`);const s=f(n);return a.succeed(`${e.skill.name} 已重新安装`),{packageRoot:n,packageName:s?.name??e.source.packageName,...s?.version?{installedVersion:s.version}:{}}}catch(t){return a.fail(`${e.skill.name} 更新失败`),void w.error(P(r,t))}}async function x(e){const a=y(`刷新 ${e.skill.name} (MCP tools/list)...`).start(),t=new h;try{const r=m(e),n=await t.connect(e.skill.name,r,e.installPath),{tools:s}=await n.listTools();return a.succeed(`${e.skill.name} 元数据已刷新(${s.length} 个 tool)`),!0}catch(t){return a.fail(`${e.skill.name} 刷新失败`),w.error(t instanceof Error?t.message:String(t)),!1}finally{await t.disconnectAll()}}export default e({meta:{description:"检查并更新 roll 及已注册 Agent"},args:{check:{type:"boolean",description:"仅检查 roll/Agent 可用更新,不执行安装或刷新",default:!1},"skip-browser-setup":{type:"boolean",description:"跳过 Playwright 浏览器运行时安装/校验",default:!1}},async run({args:e}){const a=e.check,t=s();w.info("检查 roll 更新...");const r=await v({forceRefresh:!0});let n,l;r.hasUpdate?w.success(`roll 有新版本: v${r.current} → v${r.latest}`):w.info(`roll 已是最新版本 (v${r.current})`);let u=[];try{n=i().agentsConfig,l=new k(n.dataDir),u=l.list()}catch(e){w.warn(`无法读取 Agent 配置,跳过已注册 Agent 检查:${e instanceof Error?e.message:String(e)}`)}const m=[];for(const e of u){let a=e;"installed-package"===g(a)&&(a=T(a,l));const t=g(a);switch(t){case"git":m.push({name:a.skill.name,sourceType:t,icon:"~",action:"可执行 git pull + 重新安装依赖"});break;case"installed-package":{if("installed-package"!==a.source?.type){m.push({name:a.skill.name,sourceType:t,icon:"?",action:"无法检查已安装包版本"});break}const e=await S({packageName:a.source.packageName,packageSpec:a.source.packageSpec,...a.source.installedVersion?{currentVersion:a.source.installedVersion}:{}},{forceRefresh:!0});m.push({name:a.skill.name,sourceType:t,icon:D(e.status),action:N(e,a.source.packageSpec)});break}case"remote-manifest":m.push({name:a.skill.name,sourceType:t,icon:"~",action:"可刷新本地 manifest + MCP 元数据"});break;case"local-path":m.push({name:a.skill.name,sourceType:t,icon:"⏭",action:"刷新本地 SKILL/manifest"})}}if(u.length>0){w.info(`\n已注册 Agent (${u.length}):`);for(const e of m)w.info(` ${e.icon} ${e.name} [${e.sourceType}] — ${e.action}`)}else n&&w.info("无已注册 Agent");if("needs-migration"!==t.status&&"invalid"!==t.status||(w.info(""),A(t,a?"check":"pre-update")),a)return;w.info("");let f=!1,$=!1;r.hasUpdate&&(f=await E(r.latest,!1),f||($=!0)),!f||"needs-migration"!==t.status&&"invalid"!==t.status||(w.info(""),A(t,"post-update"));let h=0,y=0;for(const a of u){if(!l||!n)break;switch(g(a)){case"git":{const e="core-managed"===a.runtime.ownership&&void 0!==o(n.dataDir,a.skill.name);if(await C(a))try{const t=d(a.installPath),r={...a,skill:t.skill,transport:t.transport,runtime:t.runtime,...t.skillBody.length>0?{skillBody:t.skillBody}:{}};l.replace(a.skill.name,r)?(await R(r,e,n.dataDir),h++):(w.warn(`${a.skill.name} 已从注册表中移除,跳过元数据刷新`),y++)}catch(e){l.updateStatus(a.skill.name,"error"),w.warn(`${a.skill.name} metadata 刷新或重启失败: ${e instanceof Error?e.message:String(e)}`),y++}else y++;break}case"installed-package":{const t="core-managed"===a.runtime.ownership&&void 0!==o(n.dataDir,a.skill.name);if(t)try{await c(n.dataDir,a.skill.name)}catch(e){l.updateStatus(a.skill.name,"error"),w.warn(`${a.skill.name} 停止失败,无法继续升级: ${e instanceof Error?e.message:String(e)}`),y++;break}const r=await M(a);if(r)try{const s=d(r.packageRoot),i="installed-package"===a.source?.type?{...a.source,packageName:r.packageName,...r.installedVersion?{installedVersion:r.installedVersion}:{}}:void 0,o={...a,skill:s.skill,transport:s.transport,runtime:s.runtime,installPath:r.packageRoot,...i?{source:i}:{},...s.skillBody.length>0?{skillBody:s.skillBody}:{}},c=await p(o,{skipBrowserSetup:e["skip-browser-setup"]});c.ok||(w.warn(`${o.skill.name} setup 失败:${c.message}`),c.retryCommand&&w.info(`重试命令: ${c.retryCommand}`));l.replace(a.skill.name,o)?c.ok?(t&&await I(o,n.dataDir),h++):(l.updateStatus(o.skill.name,"error"),y++):(w.warn(`${a.skill.name} 已从注册表中移除,跳过元数据刷新`),y++)}catch(e){l.updateStatus(a.skill.name,"error"),w.warn(`${a.skill.name} metadata 刷新、setup 或重启失败: ${e instanceof Error?e.message:String(e)}`),y++}else{if(t)try{await I(a,n.dataDir)}catch{l.updateStatus(a.skill.name,"error")}y++}break}case"remote-manifest":try{const e=d(a.installPath),t={...a,skill:e.skill,transport:e.transport,runtime:e.runtime,...e.skillBody.length>0?{skillBody:e.skillBody}:{}};if(!l.replace(a.skill.name,t)){w.warn(`${a.skill.name} 已从注册表中移除,跳过元数据刷新`),y++;break}await x(t)?h++:y++}catch(e){w.warn(`${a.skill.name} manifest 刷新失败: ${e instanceof Error?e.message:String(e)}`),y++}break;case"local-path":{const e="core-managed"===a.runtime.ownership&&void 0!==o(n.dataDir,a.skill.name);try{const t=d(a.installPath),r={...a,skill:t.skill,transport:t.transport,runtime:t.runtime,...t.skillBody.length>0?{skillBody:t.skillBody}:{}};l.replace(a.skill.name,r)?(await R(r,e,n.dataDir),h++):(w.warn(`${a.skill.name} 已从注册表中移除,跳过元数据刷新`),y++)}catch(e){l.updateStatus(a.skill.name,"error"),w.warn(`${a.skill.name} 本地 metadata 刷新或重启失败: ${e instanceof Error?e.message:String(e)}`),y++}break}}}w.info("");if(y+($?1:0)>0){process.exitCode=1;const e=$?"roll 更新失败":f?"roll ✓":"roll 无更新";return void w.warn(`更新完成但有失败:${e}${y>0?`,${y} 个 Agent 更新失败`:""}${h>0?`,${h} 个 Agent 已更新`:""}`)}f||h>0?w.success(`更新完成:${f?"roll ✓":"roll 无更新"}${h>0?`,${h} 个 Agent 已更新`:""}`):w.success("一切都已是最新版本")}});async function R(e,a,t){a&&"core-managed"===e.runtime.ownership&&(await c(t,e.skill.name),await I(e,t))}async function I(e,a){let t=!1;try{l(e,a),t=!0,await u(e,{startupTimeoutMs:15e3,probeTimeoutMs:2e3})}catch(r){throw t&&await c(a,e.skill.name).catch(()=>{}),r}}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@roll-agent/core",
3
- "version": "0.6.5",
3
+ "version": "0.6.6",
4
4
  "type": "module",
5
5
  "repository": {
6
6
  "type": "git",