@roll-agent/core 0.6.6 → 0.6.7

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 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)}`}}}
1
+ import{defineCommand as t}from"citty";import{loadAgentsConfig as e}from"../../config/loader.js";import{getAgentLogPath as a,getAgentPid as r,inspectManagedAgentRuntime as n,probeAgentEndpoint as s}from"../../registry/process-manager.js";import{AgentStore as o}from"../../registry/store.js";import{log as i}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:a}=e(),r=new o(a.dataDir),n=r.list();if(t.restart&&i.warn("`--restart` 仅为兼容保留参数;v1 不执行自动重启逻辑。"),0===n.length)return t.json?void console.log("[]"):void i.info("暂无已注册 Agent。");const s=[];for(const t of n)s.push(await l(t,r,a.dataDir));const p=s.filter(t=>!t.healthy);if(t.json)return console.log(JSON.stringify(s,null,2)),void(p.length>0&&(process.exitCode=1));for(const t of s)t.healthy?i.success(`${t.agentName} [${t.transport}]: ${t.message}`):i.error(`${t.agentName} [${t.transport}]: ${t.message}`);p.length>0&&(process.exitCode=1)}});async function l(t,e,a){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 p(t,e);case"core-managed":return m(t,e,a)}}async function p(t,e){try{return await s(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(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)}`:`外部服务不可连接: ${a instanceof Error?a.message:String(a)}`}}}async function m(t,e,o){const i=r(o,t.skill.name);if(void 0===i)return e.updateStatus(t.skill.name,"stopped"),{agentName:t.skill.name,transport:t.transport.type,healthy:!1,message:`未运行(缺少活动 PID)。日志: ${a(o,t.skill.name)}`};const l=n(t,o);if(l.issues.length>0)return e.updateStatus(t.skill.name,"error"),{agentName:t.skill.name,transport:t.transport.type,healthy:!1,message:`${l.issues.map(t=>t.message).join(";")}。日志: ${a(o,t.skill.name)}`};try{return await s(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(i)}),可连接 (${t.transport.endpoint})`:`运行中 (PID: ${String(i)})`}}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)}。日志: ${a(o,t.skill.name)}`:`进程存在但不可连接: ${r instanceof Error?r.message:String(r)}。日志: ${a(o,t.skill.name)}`}}}
@@ -1,3 +1,14 @@
1
+ export interface CheckResult {
2
+ readonly name: string;
3
+ readonly status: "ok" | "warn" | "fail";
4
+ readonly message: string;
5
+ readonly fix?: string;
6
+ }
7
+ export interface DoctorFixResult {
8
+ readonly name: string;
9
+ readonly status: "applied" | "skipped" | "failed";
10
+ readonly message: string;
11
+ }
1
12
  export declare function isNodeVersionSupported(version: string): boolean;
2
13
  declare const _default: import("citty").CommandDef<{
3
14
  json: {
@@ -5,5 +16,29 @@ declare const _default: import("citty").CommandDef<{
5
16
  description: string;
6
17
  default: false;
7
18
  };
19
+ "fix-plan": {
20
+ type: "boolean";
21
+ description: string;
22
+ default: false;
23
+ };
24
+ fix: {
25
+ type: "boolean";
26
+ description: string;
27
+ default: false;
28
+ };
8
29
  }>;
9
30
  export default _default;
31
+ export declare function formatDoctorCheckLines(check: CheckResult, options: {
32
+ readonly fixPlan: boolean;
33
+ }): string[];
34
+ export declare function formatDoctorChecksForJsonOutput(checks: readonly CheckResult[], options: {
35
+ readonly fixPlan: boolean;
36
+ }): readonly CheckResult[];
37
+ export declare function formatDoctorJsonOutput(checks: readonly CheckResult[], options: {
38
+ readonly fixPlan: boolean;
39
+ readonly fixes: readonly DoctorFixResult[];
40
+ }): {
41
+ readonly checks: readonly CheckResult[];
42
+ readonly fixes: readonly DoctorFixResult[];
43
+ };
44
+ export declare function formatDoctorFixLines(fix: DoctorFixResult): string[];
@@ -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 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
+ import{defineCommand as e}from"citty";import{existsSync as t,mkdirSync as s,writeFileSync as n}from"node:fs";import{stringify as a}from"yaml";import{getAgentEnvFromAgentsConfig as o,inspectAgentEnvRequirements as i}from"../../config/helpers.js";import{inspectConfigFile as r,loadAgentsConfig as m,loadConfig as g,parseConfigDocument as u,validateConfigText as f}from"../../config/loader.js";import{applyKnownConfigMigrations as l}from"../../config/migration.js";import{inspectAgentRuntimeEnvRequirements as c,summarizeAgentRuntimeEnvReport as p}from"../../config/runtime-env.js";import{inspectAgentRuntimeEnv as d}from"../../mcp/agent-diagnostics.js";import{cleanupOrphanAgentRuntimeMetadata as h,inspectManagedAgentRuntime as x}from"../../registry/process-manager.js";import{AgentStore as k}from"../../registry/store.js";const $={ok:"✓",warn:"⚠",fail:"✗"},j={applied:"✓",skipped:"•",failed:"✗"},S={major:22,minor:6,patch:0};export function isNodeVersionSupported(e){const t=/^(\d+)\.(\d+)\.(\d+)/.exec(e);if(!t)return!1;const s=Number(t[1]),n=Number(t[2]),a=Number(t[3]);return s>S.major||!(s<S.major)&&(n>S.minor||!(n<S.minor)&&a>=S.patch)}export default e({meta:{description:"诊断 Roll 配置、Agent 注册表和运行时状态"},args:{json:{type:"boolean",description:"JSON 格式输出",default:!1},"fix-plan":{type:"boolean",description:"输出可执行修复建议(不自动修改)",default:!1},fix:{type:"boolean",description:"执行安全自动修复:配置自动迁移、创建 dataDir、清理孤儿 runtime 元数据",default:!1}},async run({args:e}){const s=[],n=[],a=!0===e.fix;let u;const f=process.versions.node;s.push(isNodeVersionSupported(f)?{name:"Node.js 版本",status:"ok",message:`v${f}`}:{name:"Node.js 版本",status:"fail",message:`v${f} (需要 ≥22.6.0)`,fix:"升级 Node.js 到 22.6.0 或更高版本"});let l,h=r();if("needs-migration"===h.status&&a){const e=v(h);n.push(e),"applied"===e.status&&(h=r())}switch(h.status){case"not-found":s.push({name:"配置文件",status:"warn",message:"未找到,使用默认配置",fix:"运行 `roll config init` 生成显式配置文件"}),u=g().config;break;case"valid":s.push({name:"配置文件",status:"ok",message:h.configPath}),u=h.config;break;case"needs-migration":s.push({name:"配置文件",status:"warn",message:`${h.configPath} (需要迁移,运行 roll config migrate)`,fix:"运行 `roll config migrate`,确认备份文件后提交配置变更"});break;case"invalid":s.push({name:"配置文件",status:"fail",message:h.error.message,fix:"修正配置文件语法或运行 `roll config migrate` 处理 breaking schema change"})}try{l=m().agentsConfig}catch(e){s.push({name:"Agent 配置",status:"fail",message:e instanceof Error?e.message:String(e),fix:"检查 `roll.config.yaml` 的 `agents` 段;如配置版本过旧,运行 `roll config migrate`"})}if(u){const e=Object.keys(u.llm.providers);if(e.length>0){const t=e.filter(e=>{const t=u.llm.providers[e]?.apiKey??"";return t.startsWith("${")||0===t.length});t.length>0?s.push({name:"LLM Providers",status:"warn",message:`${e.join(", ")} (${t.join(", ")} API key 未设置)`,fix:`配置 ${t.map(e=>`llm.providers.${e}.apiKey`).join(", ")} 或对应环境变量`}):s.push({name:"LLM Providers",status:"ok",message:e.join(", ")})}else s.push({name:"LLM Providers",status:"warn",message:"未配置任何 provider",fix:"运行 `roll config init` 或手动配置 `llm.providers`"})}else"needs-migration"===h.status&&s.push({name:"LLM Providers",status:"warn",message:"配置需要迁移,跳过完整 LLM 配置校验",fix:"先运行 `roll config migrate`,再重新执行 `roll doctor`"});if(l){const e=l.dataDir;let r=t(e);if(!r&&a){const s=P(e);n.push(s),r=t(e)}s.push(r?{name:"Agent 数据目录",status:"ok",message:e}:{name:"Agent 数据目录",status:"warn",message:`${e} (不存在,首次 add 时创建)`,fix:"运行 `roll doctor --fix` 创建目录,或手动创建该目录"});const m=new k(l.dataDir).list();s.push({name:"已注册 Agent",status:m.length>0?"ok":"warn",message:m.length>0?`${String(m.length)} 个 (${m.map(e=>e.skill.name).join(", ")})`:"无",...0===m.length?{fix:"运行 `roll agent add <path|git-url>` 注册 Agent"}:{}});for(const e of m){if("core-managed"===e.runtime.ownership){let t=x(e,l.dataDir);if(a&&t.issues.some(e=>"orphan-sidecar"===e.code)){const s=y(l.dataDir,e.skill.name);n.push(s),"applied"===s.status&&(t=x(e,l.dataDir))}(void 0!==t.pid||t.issues.length>0)&&s.push({name:`Agent runtime (${e.skill.name})`,status:t.issues.length>0?"warn":"ok",message:t.issues.length>0?t.issues.map(e=>e.message).join(";"):`PID ${String(t.pid)},runtime sidecar 与当前配置一致`,...t.issues.length>0?{fix:A(t.issues.map(e=>e.fix)).join(";")}:{}})}const t=i(e.skill.name,e.skill.env,l.env);if(!t)continue;const r=await d(e,{agentsConfig:l}),m=c(t,o(l,e.skill.name),r),g=p(m);s.push({name:`Agent 环境变量 (${e.skill.name})`,status:g.status,message:g.message,..."ok"===g.status?{}:{fix:`在 \`roll.config.yaml\` 的 \`agents.env.${e.skill.name}\` 或 shell 环境中配置缺失变量`}})}}const $=s.some(e=>"fail"===e.status),j=n.some(e=>"failed"===e.status),S=s.some(e=>"warn"===e.status),D=!0===e["fix-plan"]||a;if(e.json){const e=a?formatDoctorJsonOutput(s,{fixPlan:D,fixes:n}):formatDoctorChecksForJsonOutput(s,{fixPlan:D});return console.log(JSON.stringify(e,null,2)),void(($||j)&&(process.exitCode=1))}console.log("Roll Agent 系统诊断\n");for(const e of s)for(const t of formatDoctorCheckLines(e,{fixPlan:D}))console.log(t);if(a)if(console.log("\nFix 执行结果\n"),0===n.length)console.log(" ✓ 无可自动修复项");else for(const e of n)for(const t of formatDoctorFixLines(e))console.log(t);console.log($||j?"\n存在问题,请修复后重试。":S?"\n存在警告,可按 fix plan 处理。":"\n系统状态正常。"),($||j)&&(process.exitCode=1)}});export function formatDoctorCheckLines(e,t){const s=[` ${$[e.status]} ${e.name}: ${e.message}`];return t.fixPlan&&e.fix&&s.push(` fix: ${e.fix}`),s}export function formatDoctorChecksForJsonOutput(e,t){return t.fixPlan?e:e.map(e=>({name:e.name,status:e.status,message:e.message}))}export function formatDoctorJsonOutput(e,t){return{checks:formatDoctorChecksForJsonOutput(e,{fixPlan:t.fixPlan}),fixes:t.fixes}}export function formatDoctorFixLines(e){return[` ${j[e.status]} ${e.name}: ${e.message}`]}function v(e){if(!e.report.canAutoMigrate)return{name:"配置迁移",status:"skipped",message:"检测到需要人工处理的配置迁移项,未自动修改"};try{const t=u(e.raw,e.configPath),s=l(t);if(!s.ok)return{name:"配置迁移",status:"skipped",message:`无法自动迁移:${w(s.issues)}`};if(!s.changed)return{name:"配置迁移",status:"skipped",message:"配置文件已是最新格式"};const o=a(s.document,{lineWidth:0});f(o,e.configPath);const i=D(e.configPath);return n(i,e.raw,"utf-8"),n(e.configPath,o,"utf-8"),{name:"配置迁移",status:"applied",message:`已迁移 ${e.configPath},备份 ${i}`}}catch(e){return{name:"配置迁移",status:"failed",message:e instanceof Error?e.message:String(e)}}}function P(e){try{return s(e,{recursive:!0}),{name:"Agent 数据目录",status:"applied",message:`已创建 ${e}`}}catch(e){return{name:"Agent 数据目录",status:"failed",message:e instanceof Error?e.message:String(e)}}}function y(e,t){try{return h(e,t)?{name:`Agent runtime (${t})`,status:"applied",message:"已清理孤儿 runtime 元数据"}:{name:`Agent runtime (${t})`,status:"skipped",message:"检测到活动 PID,跳过 runtime 元数据清理"}}catch(e){return{name:`Agent runtime (${t})`,status:"failed",message:e instanceof Error?e.message:String(e)}}}function D(e){const t=new Date;return`${e}.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 w(e){return e.map(e=>e.message).join(";")}function A(e){return[...new Set(e)]}
@@ -2,12 +2,12 @@ declare const _default: import("citty").CommandDef<{
2
2
  agent: {
3
3
  type: "positional";
4
4
  description: string;
5
- required: true;
5
+ required: false;
6
6
  };
7
7
  tool: {
8
8
  type: "positional";
9
9
  description: string;
10
- required: true;
10
+ required: false;
11
11
  };
12
12
  json: {
13
13
  type: "boolean";
@@ -20,6 +20,11 @@ declare const _default: import("citty").CommandDef<{
20
20
  description: string;
21
21
  default: false;
22
22
  };
23
+ bail: {
24
+ type: "boolean";
25
+ description: string;
26
+ default: false;
27
+ };
23
28
  "input-json": {
24
29
  type: "string";
25
30
  description: string;
@@ -28,9 +33,48 @@ declare const _default: import("citty").CommandDef<{
28
33
  type: "string";
29
34
  description: string;
30
35
  };
36
+ "batch-json": {
37
+ type: "string";
38
+ description: string;
39
+ };
40
+ "batch-file": {
41
+ type: "string";
42
+ description: string;
43
+ };
44
+ "batch-stdin": {
45
+ type: "boolean";
46
+ description: string;
47
+ default: false;
48
+ };
31
49
  }>;
32
50
  export default _default;
51
+ export interface RunBatchItem {
52
+ readonly agent: string;
53
+ readonly tool: string;
54
+ readonly input: Readonly<Record<string, unknown>>;
55
+ readonly label?: string;
56
+ }
57
+ interface RunToolBaseResult {
58
+ readonly index: number;
59
+ readonly agent: string;
60
+ readonly tool: string;
61
+ readonly label?: string;
62
+ }
63
+ export interface RunToolSuccessResult extends RunToolBaseResult {
64
+ readonly ok: true;
65
+ readonly result: unknown;
66
+ }
67
+ export interface RunToolFailureResult extends RunToolBaseResult {
68
+ readonly ok: false;
69
+ readonly error: string;
70
+ readonly result?: unknown;
71
+ }
72
+ export type RunToolResult = RunToolSuccessResult | RunToolFailureResult;
33
73
  export declare function parseExplicitToolInput(rawArgs: string[]): Record<string, unknown> | undefined;
74
+ interface ParseBatchInputOptions {
75
+ readonly readStdin?: () => string;
76
+ }
77
+ export declare function parseBatchInput(rawArgs: string[], options?: ParseBatchInputOptions): readonly RunBatchItem[] | undefined;
34
78
  export declare function resolveToolArgs(rawArgs: string[]): Record<string, unknown>;
35
79
  /**
36
80
  * 从 rawArgs 中解析 --key value 格式的参数。
@@ -1 +1 @@
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}
1
+ import{readFileSync as t}from"node:fs";import{defineCommand as n}from"citty";import{loadConfig as o}from"../../config/loader.js";import{getAgentEnv as r,getMissingAgentEnvRuntimeIssues as e,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 u}from"../../tool-runtime/preflight.js";import{formatMissingToolMessage as p,normalizeListedTools as g}from"../utils/agent-tools.js";import{extractTextContent as h,formatToolResultForJsonOutput as m,isToolErrorResult as d}from"../utils/tool-results.js";import{log as b,redactToolArgsForLog as w}from"../utils/output.js";import{shouldSkipRuntimeReadinessForTool as y}from"../../config/runtime-env.js";export default n({meta:{description:"直接调用已注册 Agent 的指定 MCP tool"},args:{agent:{type:"positional",description:"Agent 名称",required:!1},tool:{type:"positional",description:"MCP tool 名称",required:!1},json:{type:"boolean",description:"JSON 格式输出",default:!1},verbose:{type:"boolean",alias:"v",description:"输出调试日志",default:!1},bail:{type:"boolean",description:"batch 模式下遇到第一条失败即停止",default:!1},"input-json":{type:"string",description:"以 JSON 字符串提供完整 tool 输入对象;不能与 --input-file 同用"},"input-file":{type:"string",description:"从 JSON 文件读取完整 tool 输入对象;不能与 --input-json 同用"},"batch-json":{type:"string",description:"以 JSON array 提供多条调用;每项包含 agent/tool/input"},"batch-file":{type:"string",description:"从 JSON 文件读取 batch array;不能与 --batch-json 同用"},"batch-stdin":{type:"boolean",description:"从 stdin 读取 batch array;不能与 --batch-json 或 --batch-file 同用",default:!1}},async run({args:t,rawArgs:n}){const{config:r}=o(),e=new s(r.agents.dataDir),i=new l,a=new Map;try{const o=P(r),s={store:e,config:r,clientManager:i,agentConnections:a,...o?{samplingModel:o}:{}};if(O(n)&&T(n)>0)return b.error("batch 模式不接受 agent/tool 位置参数;请在每个 batch item 中声明 agent tool"),void(process.exitCode=1);const l=parseBatchInput(n,{readStdin:C});if(l){const n=await D({items:l,...s,bail:!0===t.bail});return t.json?console.log(JSON.stringify(n.map(U),null,2)):Y(n),void(n.some(t=>!t.ok)&&(process.exitCode=1))}const c=I(t.agent,"agent"),f=I(t.tool,"tool");if(!c||!f)return b.error("roll run 需要提供 agent/tool 位置参数,或使用 --batch-json / --batch-file / --batch-stdin 进入 batch 模式"),void(process.exitCode=1);let u;try{u=resolveToolArgs(n)}catch(t){return b.error(t instanceof Error?t.message:String(t)),void(process.exitCode=1)}const p=await q({item:{agent:c,tool:f,input:u},index:0,...s});p.ok?(t.json?console.log(JSON.stringify(m(p.result),null,2)):K(p.result),b.success("调用完成")):(b.error(p.error),process.exitCode=1)}catch(t){const n=t instanceof Error?t.message:String(t),o=t instanceof Error&&t.cause?`\n cause: ${String(t.cause)}`:"";b.error(`${n}${o}`),process.exitCode=1}finally{await i.disconnectAll()}}});const j=new Set(["json","verbose","v","bail","help","h","version"]),$=new Set(["batch-stdin"]),S=new Set(["config","input-json","input-file","batch-json","batch-file"]);function v(t){return"object"==typeof t&&null!==t&&!Array.isArray(t)}function E(t,n){let o;try{o=JSON.parse(t)}catch(t){throw new Error(`${n} 不是合法 JSON: ${t instanceof Error?t.message:String(t)}`)}if(!v(o))throw new Error(`${n} 必须是 JSON object`);return o}function N(t,n){let o;try{o=JSON.parse(t)}catch(t){throw new Error(`${n} 不是合法 JSON: ${t instanceof Error?t.message:String(t)}`)}if(!Array.isArray(o))throw new Error(`${n} 必须是 JSON array`);return o}function k(t,n){let o=0;for(;o<t.length&&!t[o]?.startsWith("--");)o++;for(;o<t.length;){if(t[o]!==`--${n}`){o++;continue}const r=t[o+1];if(!r||r.startsWith("--"))throw new Error(`选项 --${n} 需要提供值`);return r}}function J(t,n){let o=0;for(;o<t.length&&!t[o]?.startsWith("--");)o++;for(;o<t.length;){if(t[o]===`--${n}`)return!0;o++}return!1}function O(t){return x(t,"batch-json")||x(t,"batch-file")||x(t,"batch-stdin")}function x(t,n){let o=0;for(;o<t.length&&!t[o]?.startsWith("--");)o++;for(;o<t.length;){if(t[o]===`--${n}`)return!0;o++}return!1}export function parseExplicitToolInput(n){const o=k(n,"input-json"),r=k(n,"input-file"),e=M(n);if([o,r,e].filter(t=>void 0!==t).length>1)throw new Error("不能同时使用 positional JSON、--input-json 和 --input-file");if(o)return E(o,"--input-json");if(r){return E(t(r,"utf-8"),`输入文件 ${r}`)}return e?E(e,"positional JSON input"):void 0}export function parseBatchInput(n,o={}){const r=k(n,"batch-json"),e=k(n,"batch-file"),i=J(n,"batch-stdin"),s=k(n,"input-json"),l=k(n,"input-file");if([r,e,i?"--batch-stdin":void 0].filter(t=>void 0!==t).length>1)throw new Error("不能同时使用 --batch-json、--batch-file 和 --batch-stdin");if(!r&&!e&&!i)return;if(s||l)throw new Error("batch 模式不能同时使用 --input-json 或 --input-file");const a=r?"--batch-json":e?`batch 文件 ${e}`:"--batch-stdin",c=N(r??(e?t(e,"utf-8"):A(o)),a);if(0===c.length)throw new Error(`${a} 至少需要包含一条调用`);return c.map((t,n)=>W(t,n))}function A(t){if(!t.readStdin)throw new Error("--batch-stdin 需要可用的 stdin 读取器");return t.readStdin()}function C(){if(process.stdin.isTTY)throw new Error("--batch-stdin 需要从 stdin 管道或文件重定向读取 JSON array");return t(0,"utf-8")}function W(t,n){const o=`batch[${String(n)}]`;if(!v(t))throw new Error(`${o} 必须是 JSON object`);const r=t.agent,e=t.tool,i="input"in t?t.input:{},s=t.label;if("string"!=typeof r||0===r.trim().length)throw new Error(`${o}.agent 必须是非空字符串`);if("string"!=typeof e||0===e.trim().length)throw new Error(`${o}.tool 必须是非空字符串`);if(void 0!==i&&!v(i))throw new Error(`${o}.input 必须是 JSON object`);if(void 0!==s&&"string"!=typeof s)throw new Error(`${o}.label 必须是字符串`);return{agent:r,tool:e,input:i??{},...void 0!==s?{label:s}:{}}}function M(t){const n=[];for(const o of t){if(o.startsWith("--"))break;n.push(o)}const o=n.slice(2);if(0!==o.length){if(1===o.length&&o[0]?.trim().startsWith("{"))return o[0];throw new Error("roll run 只接受 agent/tool 两个位置参数;tool 输入请使用 --key value、--input-json、--input-file,或第三个位置参数 JSON object")}}function T(t){let n=0;for(const o of t){if(o.startsWith("--"))return n;n+=1}return n}export function resolveToolArgs(t){return{...parseExplicitToolInput(t)??{},...parseToolArgs(t)}}export function parseToolArgs(t){const n={};let o=0;for(;o<t.length&&!t[o]?.startsWith("--");)o++;for(;o<t.length;){const r=t[o];if(!r?.startsWith("--")){o++;continue}const e=r.slice(2),i=t[o+1];if(j.has(e)||$.has(e))o++;else if(S.has(e))o+=i&&!i.startsWith("--")?2:1;else if(!i||i.startsWith("--"))n[e]=!0,o++;else{const t=Number(i);n[e]=Number.isNaN(t)?i:t,o+=2}}return n}function I(t,n){if("string"==typeof t&&0!==t.trim().length)return t;b.error(`缺少必填位置参数:${n}`)}function P(t){const n=t.llm.defaultProvider,o=t.llm.providers[n];return o?c(n,t.llm.defaultModel,o.apiKey,o.baseUrl):void 0}async function B(t){const n=t.agentConnections.get(t.item.agent);if(n)return n;const o=t.store.findByName(t.item.agent);if(!o)return`Agent "${t.item.agent}" 未注册。使用 \`roll agent list\` 查看已注册 Agent。`;b.info(`连接 Agent "${o.skill.name}"...`);const e=r(t.config,o.skill.name),s=a(o),l=await t.clientManager.connect(o.skill.name,s,o.installPath,{...t.samplingModel?{samplingModel:t.samplingModel}:{},...e?{env:e}:{}}),c=g((await l.listTools()).tools),f={agent:o,client:l,tools:c,envReport:i(o.skill.name,o.skill.env,t.config.agents.env)};return t.agentConnections.set(t.item.agent,f),f}async function q(t){const n=R(t.item,t.index);try{const o=await B(t);if("string"==typeof o)return{...n,ok:!1,error:o};const r=o.tools.find(n=>n.name===t.item.tool);if(!r)return{...n,ok:!1,error:p(o.agent.skill.name,t.item.tool,o.tools)};const i=y(r.name)?[]:e(o.envReport),s=u(r,t.item.input,{runtimeIssues:i});if(!s.ok)return{...n,ok:!1,error:f(o.agent.skill.name,t.item.tool,s.issues,s.runtimeIssues)};b.info(`调用 ${o.agent.skill.name}.${t.item.tool}`),b.debug(`调用参数: ${JSON.stringify(w(t.item.input))}`);const l=await o.client.callTool({name:t.item.tool,arguments:t.item.input});return d(l)?{...n,ok:!1,error:"tool 返回 isError=true",result:l}:{...n,ok:!0,result:l}}catch(t){const o=t instanceof Error?t.message:String(t),r=t instanceof Error&&t.cause?`\n cause: ${String(t.cause)}`:"";return{...n,ok:!1,error:`${o}${r}`}}}function R(t,n){return{index:n,agent:t.agent,tool:t.tool,...t.label?{label:t.label}:{}}}async function D(t){const n=[];for(const[o,r]of t.items.entries()){const e=await q({...t,item:r,index:o});if(n.push(e),!e.ok&&t.bail)break}return n}function K(t){if("object"==typeof t&&null!==t&&"content"in t)for(const n of h(t.content))console.log(n);else"string"!=typeof t?console.log(JSON.stringify(t,null,2)):console.log(t)}function U(t){return t.ok||"result"in t?{...t,result:m(t.result)}:t}function Y(t){for(const n of t){const t=n.label?`[${String(n.index+1)}] ${n.label} (${n.agent}.${n.tool})`:`[${String(n.index+1)}] ${n.agent}.${n.tool}`;console.log(t),n.ok?K(n.result):console.log(n.error)}}
@@ -0,0 +1,18 @@
1
+ declare const _default: import("citty").CommandDef<{
2
+ name: {
3
+ type: "positional";
4
+ description: string;
5
+ required: true;
6
+ };
7
+ json: {
8
+ type: "boolean";
9
+ description: string;
10
+ default: false;
11
+ };
12
+ "include-references": {
13
+ type: "boolean";
14
+ description: string;
15
+ default: false;
16
+ };
17
+ }>;
18
+ export default _default;
@@ -0,0 +1 @@
1
+ import{defineCommand as e}from"citty";import{loadAgentsConfig as n}from"../../config/loader.js";import{AgentStore as o}from"../../registry/store.js";import{resolveAgentSkillDocument as r}from"./skills-utils.js";export default e({meta:{description:"输出指定 Agent 的 skill 文档内容"},args:{name:{type:"positional",description:"Agent 名称",required:!0},json:{type:"boolean",description:"JSON 格式输出",default:!1},"include-references":{type:"boolean",description:"同时输出 SKILL.md 中引用的 references/* 文档",default:!1}},run({args:e}){const{agentsConfig:t}=n(),s=new o(t.dataDir).findByName(e.name);if(!s)return console.error(`✗ Agent "${e.name}" 未找到`),void(process.exitCode=1);const i=r(s,{includeReferences:!0===e["include-references"]});if(e.json)console.log(JSON.stringify(i,null,2));else if(console.log(i.content.trimEnd()),i.references&&i.references.length>0)for(const e of i.references)console.log(`\n--- ${e.relativePath} ---\n`),console.log(e.content.trimEnd())}});
@@ -0,0 +1,8 @@
1
+ declare const _default: import("citty").CommandDef<{
2
+ json: {
3
+ type: "boolean";
4
+ description: string;
5
+ default: false;
6
+ };
7
+ }>;
8
+ export default _default;
@@ -0,0 +1 @@
1
+ import{defineCommand as t}from"citty";import o from"cli-table3";import{loadAgentsConfig as r}from"../../config/loader.js";import{AgentStore as n}from"../../registry/store.js";import{formatLocationForDisplay as i,resolveTerminalColumns as e}from"../utils/terminal.js";import{listAgentSkills as s}from"./skills-utils.js";const l=6,a=2;export default t({meta:{description:"列出已注册 Agent 的 skill 文档来源"},args:{json:{type:"boolean",description:"JSON 格式输出",default:!1}},run({args:t}){const{agentsConfig:l}=r(),a=new n(l.dataDir),f=s(a.list());if(t.json)return void console.log(JSON.stringify(f,null,2));if(0===f.length)return void console.log("暂无已注册 Agent skill。可先运行 `roll agent add <path|git-url>`。");const g=c(e()),u=new o({head:["Name","Source","Path","Description"],colWidths:[...g],truncate:"…",style:{head:["cyan"]}});for(const t of f)u.push([t.name,t.source,i(t.path??"(registry snapshot)",m(g[2])),i(t.description,m(g[3]))]);console.log(u.toString())}});function c(t){const o=t-6;return[24,12,Math.max(24,o-24-12-34),34]}function m(t){return Math.max(1,t-2)}
@@ -0,0 +1,13 @@
1
+ declare const _default: import("citty").CommandDef<{
2
+ name: {
3
+ type: "positional";
4
+ description: string;
5
+ required: true;
6
+ };
7
+ json: {
8
+ type: "boolean";
9
+ description: string;
10
+ default: false;
11
+ };
12
+ }>;
13
+ export default _default;
@@ -0,0 +1 @@
1
+ import{defineCommand as e}from"citty";import{loadAgentsConfig as o}from"../../config/loader.js";import{AgentStore as r}from"../../registry/store.js";import{resolveAgentSkillPath as s}from"./skills-utils.js";export default e({meta:{description:"输出指定 Agent 的 SKILL.md 文件路径"},args:{name:{type:"positional",description:"Agent 名称",required:!0},json:{type:"boolean",description:"JSON 格式输出",default:!1}},run({args:e}){const{agentsConfig:t}=o(),n=new r(t.dataDir).findByName(e.name);if(!n)return console.error(`✗ Agent "${e.name}" 未找到`),void(process.exitCode=1);const i=s(n);e.json?console.log(JSON.stringify({name:n.skill.name,source:i?"filesystem":"registry",path:i??null},null,2)):i?console.log(i):console.error(`✗ Agent "${n.skill.name}" 没有可用的本地 SKILL.md 文件`),i||(process.exitCode=1)}});
@@ -0,0 +1,28 @@
1
+ import type { RegisteredAgent } from "../../types/agent.ts";
2
+ export type SkillDocumentSource = "filesystem" | "registry";
3
+ export interface AgentSkillReferenceDocument {
4
+ readonly relativePath: string;
5
+ readonly path: string;
6
+ readonly content: string;
7
+ }
8
+ export interface AgentSkillDocument {
9
+ readonly name: string;
10
+ readonly description: string;
11
+ readonly source: SkillDocumentSource;
12
+ readonly content: string;
13
+ readonly path?: string;
14
+ readonly references?: readonly AgentSkillReferenceDocument[];
15
+ }
16
+ export interface AgentSkillListItem {
17
+ readonly name: string;
18
+ readonly description: string;
19
+ readonly source: SkillDocumentSource;
20
+ readonly path?: string;
21
+ }
22
+ export interface ResolveAgentSkillDocumentOptions {
23
+ readonly includeReferences?: boolean;
24
+ }
25
+ export declare function getAgentSkillPath(agent: RegisteredAgent): string;
26
+ export declare function resolveAgentSkillPath(agent: RegisteredAgent): string | undefined;
27
+ export declare function resolveAgentSkillDocument(agent: RegisteredAgent, options?: ResolveAgentSkillDocumentOptions): AgentSkillDocument;
28
+ export declare function listAgentSkills(agents: readonly RegisteredAgent[]): readonly AgentSkillListItem[];
@@ -0,0 +1 @@
1
+ import{existsSync as t,readFileSync as e,realpathSync as n,statSync as i}from"node:fs";import{dirname as s,extname as r,isAbsolute as o,normalize as l,relative as c,resolve as a,sep as u}from"node:path";const f="SKILL.md",p=/(?:\.\/)?references\/[A-Za-z0-9._~!$&+,;=:@%/-]+/g,m=new Set([".md",".yaml",".yml",".json",".txt"]);export function getAgentSkillPath(t){return a(t.installPath,f)}export function resolveAgentSkillPath(e){const n=getAgentSkillPath(e);return t(n)?n:void 0}export function resolveAgentSkillDocument(t,n={}){const i=resolveAgentSkillPath(t);if(i){const s=e(i,"utf-8");return{name:t.skill.name,description:t.skill.description,source:"filesystem",path:i,content:s,...n.includeReferences?{references:d(i,s)}:{}}}return{name:t.skill.name,description:t.skill.description,source:"registry",content:h(t),...n.includeReferences?{references:[]}:{}}}export function listAgentSkills(t){return t.map(t=>{const e=resolveAgentSkillPath(t);return{name:t.skill.name,description:t.skill.description,source:e?"filesystem":"registry",...e?{path:e}:{}}})}function h(t){const e=["---",`name: ${k(t.skill.name)}`,`description: ${k(t.skill.description)}`];t.skill.license&&e.push(`license: ${k(t.skill.license)}`),t.skill.compatibility&&e.push(`compatibility: ${k(t.skill.compatibility)}`),e.push("---");const n=t.skillBody?.trim();return`${e.join("\n")}\n\n${n&&n.length>0?n:t.skill.description}\n`}function k(t){return JSON.stringify(t)}function d(t,e){const i=s(t),r=n(i),o=[];for(const t of g(e)){const e=S(i,r,t);e&&o.push(e)}return o}function g(t){const e=new Set;for(const n of t.matchAll(p)){const t=y(n[0]);t&&e.add(t)}return[...e].sort()}function y(t){const e=((t.startsWith("./")?t.slice(2):t).split("#",1)[0]??"").split("?",1)[0]??"",n=l(e);if(0!==n.length&&!o(n)&&!n.startsWith("..")&&"references"!==n&&n.startsWith(`references${u}`)&&m.has(r(n).toLowerCase()))return n}function S(s,r,l){const f=a(s,l);if(!t(f))return;const p=n(f),m=c(r,p);return m.startsWith("..")||o(m)||!i(p).isFile()?void 0:{relativePath:l.split(u).join("/"),path:p,content:e(p,"utf-8")}}
@@ -0,0 +1,2 @@
1
+ declare const _default: import("citty").CommandDef<import("citty").ArgsDef>;
2
+ export default _default;
@@ -0,0 +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,s,i,r,n){return s?e?".jsx":".js":!i||r&&n?i+r+"."+n.toLowerCase()+"js":t}):t};import{defineCommand as e}from"citty";const s=import.meta.url.endsWith(".ts")?"ts":"js";function i(e){const i=new URL(`./${e}.${s}`,import.meta.url).href;return import(t(i)).then(t=>t.default)}export default e({meta:{description:"读取已注册 Agent 的 SKILL.md(list/get/path)"},subCommands:{list:()=>i("skills-list"),get:()=>i("skills-get"),path:()=>i("skills-path")}});
package/dist/cli/index.js CHANGED
@@ -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,r,o,n,a){return r?e?".jsx":".js":!o||n&&a?o+n+"."+a.toLowerCase()+"js":t}):t};import{defineCommand as e,runMain as r}from"citty";import o from"chalk";import{checkForUpdate as n,getCurrentVersion as a}from"./utils/update-checker.js";import{resolveLogLevelFromArgv as s,setLogLevel as c}from"./utils/output.js";const i=a(),l=import.meta.url.endsWith(".ts")?"ts":"js";function u(e){const r=new URL(`./commands/${e}.${l}`,import.meta.url).href;return import(t(r)).then(t=>t.default)}const m=e({meta:{name:"roll",version:i,description:"花卷 Agent — 轻量级 Agent 编排系统"},args:{verbose:{type:"boolean",alias:"v",description:"输出调试日志",default:!1}},subCommands:{agent:()=>u("agent"),run:()=>u("run"),ask:()=>u("ask"),chat:()=>u("chat"),config:()=>u("config"),doctor:()=>u("doctor"),update:()=>u("update")}}),p=n({allowNetwork:!1}).catch(()=>{});c(s(process.argv.slice(2))),r(m).then(()=>{p.then(t=>{t?.hasUpdate&&console.error(`\n${o.yellow("⬆")} roll ${o.green(`v${t.latest}`)} available (current: v${t.current}). Run ${o.cyan("roll update")} to upgrade.`)}).catch(()=>{})});
1
+ var t=this&&this.__rewriteRelativeImportExtension||function(t,e){return"string"==typeof t&&/^\.\.?\//.test(t)?t.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i,function(t,r,o,s,n){return r?e?".jsx":".js":!o||s&&n?o+s+"."+n.toLowerCase()+"js":t}):t};import{defineCommand as e,runMain as r}from"citty";import o from"chalk";import{checkForUpdate as s,getCurrentVersion as n}from"./utils/update-checker.js";import{resolveLogLevelFromArgv as a,setLogLevel as i}from"./utils/output.js";const c=n(),l=import.meta.url.endsWith(".ts")?"ts":"js";function u(e){const r=new URL(`./commands/${e}.${l}`,import.meta.url).href;return import(t(r)).then(t=>t.default)}const m=e({meta:{name:"roll",version:c,description:"花卷 Agent — 轻量级 Agent 编排系统"},args:{verbose:{type:"boolean",alias:"v",description:"输出调试日志",default:!1}},subCommands:{agent:()=>u("agent"),run:()=>u("run"),ask:()=>u("ask"),chat:()=>u("chat"),config:()=>u("config"),skills:()=>u("skills"),doctor:()=>u("doctor"),update:()=>u("update")}}),p=s({allowNetwork:!1}).catch(()=>{});i(a(process.argv.slice(2))),r(m).then(()=>{p.then(t=>{t?.hasUpdate&&console.error(`\n${o.yellow("⬆")} roll ${o.green(`v${t.latest}`)} available (current: v${t.current}). Run ${o.cyan("roll update")} to upgrade.`)}).catch(()=>{})});
@@ -1,8 +1,35 @@
1
1
  import type { RegisteredAgent } from "../types/agent.ts";
2
+ declare const RUNTIME_SIDECAR_SCHEMA_VERSION: 1;
3
+ export type ManagedAgentRuntimeIssueCode = "missing-sidecar" | "invalid-sidecar" | "orphan-sidecar" | "pid-mismatch" | "version-mismatch" | "endpoint-mismatch";
4
+ export interface ManagedAgentRuntimeSidecar {
5
+ readonly schemaVersion: typeof RUNTIME_SIDECAR_SCHEMA_VERSION;
6
+ readonly agentName: string;
7
+ readonly pid: number;
8
+ readonly coreVersion: string;
9
+ readonly startedAt: string;
10
+ readonly endpoint?: string;
11
+ }
12
+ export interface ManagedAgentRuntimeIssue {
13
+ readonly code: ManagedAgentRuntimeIssueCode;
14
+ readonly message: string;
15
+ readonly fix: string;
16
+ }
17
+ export interface ManagedAgentRuntimeInspection {
18
+ readonly pid?: number;
19
+ readonly sidecar?: ManagedAgentRuntimeSidecar;
20
+ readonly expectedCoreVersion: string;
21
+ readonly expectedEndpoint?: string;
22
+ readonly issues: readonly ManagedAgentRuntimeIssue[];
23
+ }
24
+ /** 仅在没有活动 PID 时清理 runtime 元数据。 */
25
+ export declare function cleanupOrphanAgentRuntimeMetadata(dataDir: string, agentName: string): boolean;
2
26
  /** Agent 日志文件路径 */
3
27
  export declare function getAgentLogPath(dataDir: string, agentName: string): string;
4
28
  /** 读取 Agent 的 PID,如果不存在或进程已死则返回 undefined */
5
29
  export declare function getAgentPid(dataDir: string, agentName: string): number | undefined;
30
+ export declare function getRollCoreVersion(): string;
31
+ export declare function writeAgentRuntimeSidecar(agent: RegisteredAgent, dataDir: string, pid: number): void;
32
+ export declare function inspectManagedAgentRuntime(agent: RegisteredAgent, dataDir: string): ManagedAgentRuntimeInspection;
6
33
  /** 检查 core-managed Agent 对应的 MCP endpoint 是否已就绪。 */
7
34
  export declare function probeAgentEndpoint(agent: RegisteredAgent, options?: {
8
35
  readonly timeoutMs?: number;
@@ -26,3 +53,4 @@ export declare function stopAgentGracefully(dataDir: string, agentName: string,
26
53
  readonly timeoutMs?: number;
27
54
  readonly intervalMs?: number;
28
55
  }): Promise<boolean>;
56
+ export {};
@@ -1 +1 @@
1
- import{spawn as t}from"node:child_process";import{closeSync as n,existsSync as r,mkdirSync as e,openSync as o,readFileSync as i,unlinkSync as a,writeFileSync as s}from"node:fs";import{resolve as c,dirname as m}from"node:path";import{McpClientManager as u}from"../mcp/client-manager.js";import{resolveDevSpawnSpec as p}from"./dev-spawn.js";import{inferAgentSourceType as l}from"./source.js";function d(t,n){return c(t,"pids",`${n}.pid`)}function g(t,n){const e=d(t,n);r(e)&&a(e)}export function getAgentLogPath(t,n){return c(t,"logs",`${n}.log`)}function f(t){try{return process.kill(t,0),!0}catch{return!1}}export function getAgentPid(t,n){const e=d(t,n);if(!r(e))return;const o=Number(i(e,"utf-8").trim());if(!Number.isNaN(o)&&f(o))return o;g(t,n)}export async function probeAgentEndpoint(t,n={}){const r=new u;try{const e=await r.connect(t.skill.name,t.transport,t.installPath,void 0!==n.timeoutMs?{timeoutMs:n.timeoutMs}:{});await e.listTools()}finally{await r.disconnectAll()}}export async function waitForAgentReady(t,n={}){const r=n.startupTimeoutMs??15e3,e=n.probeTimeoutMs??2e3,o=n.intervalMs??500,i=Date.now()+r;let a;for(;Date.now()<i;)try{return void await probeAgentEndpoint(t,{timeoutMs:e})}catch(t){a=t,await h(o)}throw new Error(`Agent "${t.skill.name}" did not become ready within ${r}ms${a?`: ${a instanceof Error?a.message:String(a)}`:""}`)}export function startAgent(i,a,c){if("streamable-http"===i.transport.type&&"core-managed"!==i.runtime.ownership)throw new Error(`Agent "${i.skill.name}" 使用 streamable-http 传输且非 core-managed,请手动启动服务。\n 端点: ${i.transport.endpoint}`);const u=getAgentPid(a,i.skill.name);if(void 0!==u)throw new Error(`Agent "${i.skill.name}" 已在运行 (PID: ${String(u)})`);const p=w(i),l=getAgentLogPath(a,i.skill.name),g=m(l);r(g)||e(g,{recursive:!0});const f=o(l,"a"),h=t(p.command,[...p.args??[]],{cwd:i.installPath,detached:!0,stdio:["ignore",f,f],...c?{env:{...process.env,...c}}:{}});if(n(f),h.unref(),!h.pid)throw new Error(`Failed to start agent "${i.skill.name}"`);const A=d(a,i.skill.name),y=m(A);return r(y)||e(y,{recursive:!0}),s(A,String(h.pid),"utf-8"),h.pid}export function stopAgent(t,n){const r=getAgentPid(t,n);if(void 0===r)return!1;try{process.kill(r,"SIGTERM")}catch{}return g(t,n),!0}export async function stopAgentGracefully(t,n,r={}){const e=getAgentPid(t,n);if(void 0===e)return!1;try{process.kill(e,"SIGTERM")}catch{return g(t,n),!0}const o=r.timeoutMs??15e3,i=r.intervalMs??200,a=Date.now()+o;for(;Date.now()<a;){if(!f(e))return g(t,n),!0;await h(i)}throw new Error(`Agent "${n}" did not stop within ${o}ms`)}function w(t){if("stdio"===t.transport.type){const n=p(t.transport.command,t.transport.args,t.installPath,l(t));return n||{command:t.transport.command,...t.transport.args?{args:t.transport.args}:{}}}if("core-managed"===t.runtime.ownership){const n=p(t.runtime.start.command,t.runtime.start.args,t.installPath,l(t));return n||{command:t.runtime.start.command,...t.runtime.start.args?{args:t.runtime.start.args}:{}}}throw new Error(`Agent "${t.skill.name}" does not have a managed runtime start command`)}function h(t){return new Promise(n=>{setTimeout(n,t)})}
1
+ import{spawn as t}from"node:child_process";import{closeSync as e,existsSync as n,mkdirSync as r,openSync as i,readFileSync as o,unlinkSync as s,writeFileSync as a}from"node:fs";import{resolve as c,dirname as l}from"node:path";import{McpClientManager as p}from"../mcp/client-manager.js";import{resolveDevSpawnSpec as d}from"./dev-spawn.js";import{inferAgentSourceType as m}from"./source.js";const u=1,g="unknown";function f(t,e){return c(t,"pids",`${e}.pid`)}function h(t,e){return c(t,"pids",`${e}.runtime.json`)}function w(t,e){for(const r of[f(t,e),h(t,e)])n(r)&&s(r)}export function cleanupOrphanAgentRuntimeMetadata(t,e){return void 0===getAgentPid(t,e)&&(w(t,e),!0)}export function getAgentLogPath(t,e){return c(t,"logs",`${e}.log`)}function $(t){try{return process.kill(t,0),!0}catch{return!1}}export function getAgentPid(t,e){const r=f(t,e);if(!n(r))return;const i=Number(o(r,"utf-8").trim());if(!Number.isNaN(i)&&$(i))return i;w(t,e)}export function getRollCoreVersion(){try{const t=c(import.meta.dirname,"../../package.json"),e=JSON.parse(o(t,"utf-8"));return A(e)&&"string"==typeof e.version?e.version:g}catch{return g}}export function writeAgentRuntimeSidecar(t,e,i){const o=h(e,t.skill.name),s=l(o);n(s)||r(s,{recursive:!0});const c={schemaVersion:1,agentName:t.skill.name,pid:i,coreVersion:getRollCoreVersion(),startedAt:(new Date).toISOString(),..."streamable-http"===t.transport.type?{endpoint:t.transport.endpoint}:{}};a(o,`${JSON.stringify(c,null,2)}\n`,"utf-8")}export function inspectManagedAgentRuntime(t,e){const n=getAgentPid(e,t.skill.name),r=getRollCoreVersion(),i="streamable-http"===t.transport.type?t.transport.endpoint:void 0;if(void 0===n){const n=v(e,t.skill.name);return void 0!==n?{..."invalid"!==n?{sidecar:n}:{},expectedCoreVersion:r,...i?{expectedEndpoint:i}:{},issues:[{code:"orphan-sidecar",message:"invalid"===n?"runtime sidecar 存在但没有活动 PID,且无法解析":`runtime sidecar 记录 PID ${String(n.pid)},但没有活动 PID`,fix:`运行 \`roll doctor --fix\` 清理 ${t.skill.name} 的过期 runtime 元数据`}]}:{expectedCoreVersion:r,...i?{expectedEndpoint:i}:{},issues:[]}}const o=[],s=v(e,t.skill.name);return"invalid"===s?(o.push({code:"invalid-sidecar",message:"runtime sidecar 无法解析",fix:`运行 \`roll agent stop ${t.skill.name}\` 后重新 \`roll agent start ${t.skill.name}\``}),{pid:n,expectedCoreVersion:r,...i?{expectedEndpoint:i}:{},issues:o}):s?(s.pid!==n&&o.push({code:"pid-mismatch",message:`runtime sidecar PID ${String(s.pid)} 与活动 PID ${String(n)} 不一致`,fix:`运行 \`roll agent stop ${t.skill.name}\` 后重新 \`roll agent start ${t.skill.name}\``}),s.coreVersion!==r&&o.push({code:"version-mismatch",message:`runtime sidecar 来自 core ${s.coreVersion},当前 core 是 ${r}`,fix:`运行 \`roll agent stop ${t.skill.name}\` 后重新 \`roll agent start ${t.skill.name}\``}),i&&s.endpoint!==i&&o.push({code:"endpoint-mismatch",message:`runtime sidecar endpoint 是 ${s.endpoint??"n/a"},当前配置是 ${i}`,fix:`运行 \`roll agent stop ${t.skill.name}\` 后重新 \`roll agent start ${t.skill.name}\``}),{pid:n,sidecar:s,expectedCoreVersion:r,...i?{expectedEndpoint:i}:{},issues:o}):(o.push({code:"missing-sidecar",message:"进程存在但缺少 runtime sidecar",fix:`运行 \`roll agent stop ${t.skill.name}\` 后重新 \`roll agent start ${t.skill.name}\``}),{pid:n,expectedCoreVersion:r,...i?{expectedEndpoint:i}:{},issues:o})}export async function probeAgentEndpoint(t,e={}){const n=new p;try{const r=await n.connect(t.skill.name,t.transport,t.installPath,void 0!==e.timeoutMs?{timeoutMs:e.timeoutMs}:{});await r.listTools()}finally{await n.disconnectAll()}}export async function waitForAgentReady(t,e={}){const n=e.startupTimeoutMs??15e3,r=e.probeTimeoutMs??2e3,i=e.intervalMs??500,o=Date.now()+n;let s;for(;Date.now()<o;)try{return void await probeAgentEndpoint(t,{timeoutMs:r})}catch(t){s=t,await y(i)}throw new Error(`Agent "${t.skill.name}" did not become ready within ${n}ms${s?`: ${s instanceof Error?s.message:String(s)}`:""}`)}export function startAgent(o,s,c){if("streamable-http"===o.transport.type&&"core-managed"!==o.runtime.ownership)throw new Error(`Agent "${o.skill.name}" 使用 streamable-http 传输且非 core-managed,请手动启动服务。\n 端点: ${o.transport.endpoint}`);const p=getAgentPid(s,o.skill.name);if(void 0!==p)throw new Error(`Agent "${o.skill.name}" 已在运行 (PID: ${String(p)})`);const d=k(o),m=getAgentLogPath(s,o.skill.name),u=l(m);n(u)||r(u,{recursive:!0});const g=i(m,"a"),h=t(d.command,[...d.args??[]],{cwd:o.installPath,detached:!0,stdio:["ignore",g,g],...c?{env:{...process.env,...c}}:{}});if(e(g),!h.pid)throw new Error(`Failed to start agent "${o.skill.name}"`);const $=f(s,o.skill.name),y=l($);n(y)||r(y,{recursive:!0});try{a($,String(h.pid),"utf-8"),writeAgentRuntimeSidecar(o,s,h.pid)}catch(t){try{process.kill(h.pid,"SIGTERM")}catch{}throw w(s,o.skill.name),new Error(`Failed to persist runtime metadata for agent "${o.skill.name}"`,{cause:t})}return h.unref(),h.pid}export function stopAgent(t,e){const n=getAgentPid(t,e);if(void 0===n)return!1;try{process.kill(n,"SIGTERM")}catch{}return w(t,e),!0}export async function stopAgentGracefully(t,e,n={}){const r=getAgentPid(t,e);if(void 0===r)return!1;try{process.kill(r,"SIGTERM")}catch{return w(t,e),!0}const i=n.timeoutMs??15e3,o=n.intervalMs??200,s=Date.now()+i;for(;Date.now()<s;){if(!$(r))return w(t,e),!0;await y(o)}throw new Error(`Agent "${e}" did not stop within ${i}ms`)}function k(t){if("stdio"===t.transport.type){const e=d(t.transport.command,t.transport.args,t.installPath,m(t));return e||{command:t.transport.command,...t.transport.args?{args:t.transport.args}:{}}}if("core-managed"===t.runtime.ownership){const e=d(t.runtime.start.command,t.runtime.start.args,t.installPath,m(t));return e||{command:t.runtime.start.command,...t.runtime.start.args?{args:t.runtime.start.args}:{}}}throw new Error(`Agent "${t.skill.name}" does not have a managed runtime start command`)}function y(t){return new Promise(e=>{setTimeout(e,t)})}function v(t,e){const r=h(t,e);if(!n(r))return;let i;try{i=JSON.parse(o(r,"utf-8"))}catch{return"invalid"}return x(i)?i:"invalid"}function x(t){return!!A(t)&&(1===t.schemaVersion&&(!("string"!=typeof t.agentName||"number"!=typeof t.pid||!Number.isInteger(t.pid)||"string"!=typeof t.coreVersion||"string"!=typeof t.startedAt)&&(void 0===t.endpoint||"string"==typeof t.endpoint)))}function A(t){return"object"==typeof t&&null!==t&&!Array.isArray(t)}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@roll-agent/core",
3
- "version": "0.6.6",
3
+ "version": "0.6.7",
4
4
  "type": "module",
5
5
  "repository": {
6
6
  "type": "git",