@roll-agent/core 0.6.2 → 0.6.4

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";const m=n(o);function g(e){return e.startsWith("https://")||e.startsWith("http://")||e.startsWith("git@")||e.endsWith(".git")}function f(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(g(e.path)){const s=f(e.path),a=t(o.dataDir,"repos",s);if(r(a)){l.info(`仓库目录已存在,拉取最新代码: ${a}`);try{await m("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 m("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 h=t(n,"package.json"),y="1"===process.env.ROLL_SKIP_INSTALL;if(e.remote)l.info("远程 Agent 使用本地 manifest,无需安装依赖。");else if(r(h)&&!y){l.info("安装依赖...");try{await m("pnpm",["install"],{cwd:n}),l.success("依赖安装完成")}catch(e){return l.error(`依赖安装失败: ${e instanceof Error?e.message:""}`),void(process.exitCode=1)}}else r(h)&&y&&l.warn("检测到 ROLL_SKIP_INSTALL=1,跳过依赖安装。");const v=e.remote?{type:"remote-manifest",endpoint:e.remote}:e.path&&g(e.path)?{type:"git",url:e.path}:{type:"local-path",path:n},$=new d(o.dataDir),L={skill:s.skill,transport:s.transport,runtime:s.runtime,installPath:n,registeredAt:(new Date).toISOString(),status:"idle",source:v,...s.skillBody.length>0?{skillBody:s.skillBody}:{}};try{$.add(L),l.success(`Agent "${s.skill.name}" 注册成功`),u(s.skill.name,s.skill.env,o.env)}catch(e){l.error(e instanceof Error?e.message:String(e)),process.exitCode=1}}});function u(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 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 +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{execFile as o}from"node:child_process";import{promisify as n}from"node:util";import{inspectAgentEnvRequirements as a}from"../../config/helpers.js";import{loadAgentsConfig as l}from"../../config/loader.js";import{discoverAgent as p}from"../../registry/discovery.js";import{startAgent as m,stopAgentGracefully as c,waitForAgentReady as d}from"../../registry/process-manager.js";import{runAgentSetup as g}from"../../registry/runtime-setup.js";import{AgentStore as u}from"../../registry/store.js";import{parsePackageName as f,readInstalledPackageManifest as k,resolveInstalledPackageRoot as y,sanitizeInstallId as h}from"../../registry/source.js";import{log as w}from"../utils/output.js";const v=n(o);function S(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}=l(),n=e.package;if(S(n))return w.error(`Git URL 请使用 \`roll agent add ${n}\` 注册,不要使用 \`roll agent install\``),void(process.exitCode=1);const a=s(n);if(t(a)&&i(a).isDirectory())return w.error(`本地源码目录请使用 \`roll agent add ${n}\` 注册,不要使用 \`roll agent install\``),void(process.exitCode=1);const j=f(n),C=s(o.dataDir,"installed",h(j));t(C)||r(C,{recursive:!0}),w.info(`安装 ${n}...`);try{await v("npm",["install","--prefix",C,n],{timeout:12e4})}catch(e){return w.error(`安装失败: ${e instanceof Error?e.message:String(e)}`),void(process.exitCode=1)}const x=y(C,j);if(!t(x))return w.error(`安装完成但未找到包目录: ${x}`),void(process.exitCode=1);const A=k(x);w.info("解析已安装 Agent 的 SKILL.md...");const b=p(x),B=new u(o.dataDir),D={skill:b.skill,transport:b.transport,runtime:b.runtime,installPath:x,registeredAt:(new Date).toISOString(),status:"idle",source:{type:"installed-package",packageName:A?.name??j,packageSpec:n,installDir:C,...A?.version?{installedVersion:A.version}:{}},...b.skillBody.length>0?{skillBody:b.skillBody}:{}};"core-managed"===D.runtime.ownership&&D.runtime.setup?.playwright&&!e.skipBrowserSetup&&w.info(`即将安装浏览器运行时 (${D.runtime.setup.playwright.browsers.join(", ")}),这可能需要一些时间...`);const W=await g(D,{skipBrowserSetup:e.skipBrowserSetup});W.ok?W.skipped?w.info(W.message):w.success(W.message):(w.warn(`Agent setup 失败:${W.message}`),W.retryCommand&&w.info(`重试命令: ${W.retryCommand}`));const q=B.findByName(b.skill.name);try{const t="core-managed"===q?.runtime.ownership&&"online"===q.status;if("installed-package"===q?.source?.type?B.replace(q.skill.name,D):B.add(D),!W.ok)return B.updateStatus(b.skill.name,"error"),void(process.exitCode=1);if("core-managed"===D.runtime.ownership&&!e.noStart){t&&await c(o.dataDir,D.skill.name),B.updateStatus(D.skill.name,"starting");let e=!1;try{m(D,o.dataDir),e=!0,await d(D,{startupTimeoutMs:15e3,probeTimeoutMs:2e3}),B.updateStatus(D.skill.name,"online")}catch(t){return e&&await c(o.dataDir,D.skill.name).catch(()=>{}),B.updateStatus(D.skill.name,"error"),w.error(`Agent "${b.skill.name}" 已安装,但自动启动失败:${t instanceof Error?t.message:String(t)}`),void(process.exitCode=1)}}w.success(`Agent "${b.skill.name}" 安装并注册成功`),$(b.skill.name,b.skill.env,o.env)}catch(e){w.error(e instanceof Error?e.message:String(e)),process.exitCode=1}}});function $(e,t,r){const i=a(e,t,r);if(i)return i.missingRequired.length>0?(w.warn(`Agent "${e}" 仍缺少必填环境变量: ${i.missingRequired.map(e=>e.name).join(", ")}`),void w.info(`请在 roll.config.yaml 的 agents.env.${e} 中显式配置这些项。`)):void(i.processEnvOnlyRequired.length>0&&(w.warn(`Agent "${e}" 当前依赖 shell 环境变量: ${i.processEnvOnlyRequired.map(e=>e.name).join(", ")}`),w.info(`建议将这些项写入 roll.config.yaml 的 agents.env.${e}。`)))}
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,4 +1,5 @@
1
1
  export { inferAgentSourceType as inferSourceType } from "../../registry/source.ts";
2
+ export { detectInstallCommand } from "../utils/package-manager.ts";
2
3
  declare const _default: import("citty").CommandDef<{
3
4
  check: {
4
5
  type: "boolean";
@@ -12,8 +13,3 @@ declare const _default: import("citty").CommandDef<{
12
13
  };
13
14
  }>;
14
15
  export default _default;
15
- interface InstallCommandSpec {
16
- readonly command: "bun" | "npm" | "pnpm" | "yarn";
17
- readonly args: readonly ["install"];
18
- }
19
- export declare function detectInstallCommand(projectDir: string): InstallCommandSpec | undefined;
@@ -1 +1 @@
1
- import{defineCommand as e}from"citty";import{resolve as a}from"node:path";import{existsSync as t,readFileSync as r}from"node:fs";import{execFile as n}from"node:child_process";import{promisify as s}from"node:util";import{inspectConfigFile as i,loadAgentsConfig as o}from"../../config/loader.js";import{getAgentPid as l,startAgent as c,stopAgentGracefully as u,waitForAgentReady as m}from"../../registry/process-manager.js";import{resolveTransportWithDevSpawnSpec as p}from"../../registry/dev-spawn.js";import{runAgentSetup as k}from"../../registry/runtime-setup.js";import{AgentStore as g}from"../../registry/store.js";import{discoverAgent as d}from"../../registry/discovery.js";import{inferAgentSourceType as f,readInstalledPackageManifest as $,resolveInstalledPackageRoot as h}from"../../registry/source.js";import{McpClientManager as y}from"../../mcp/client-manager.js";import{log as w,createSpinner as v}from"../utils/output.js";import{checkForUpdate as S,checkPublishedPackageUpdate as b,getCurrentVersion as V}from"../utils/update-checker.js";const j=s(n);export{inferAgentSourceType as inferSourceType}from"../../registry/source.js";function B(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 P(e){switch(e){case"up-to-date":return"✅";case"update-available":return"⬆";case"pinned-behind":return"📌";case"unsupported-spec":case"unknown":return"?"}}function D(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 E(e,a){if("installed-package"!==e.source?.type)return e;const t=h(e.source.installDir,e.source.packageName),r=$(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 N(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=v("正在更新 @roll-agent/core...").start();try{return await j("npm",["install","-g",`@roll-agent/core@${e}`],{timeout:6e4}),r.succeed(`roll 已更新到 v${e}`),!0}catch(e){return r.fail("更新失败"),w.error(e instanceof Error?e.message:String(e)),!1}}async function A(e){const r=v(`更新 ${e.skill.name} (git pull)...`).start();try{await j("git",["pull"],{cwd:e.installPath,timeout:3e4}),r.succeed(`${e.skill.name} 代码已更新`);const n=a(e.installPath,"package.json");if(t(n)){const a=detectInstallCommand(e.installPath);if(a){const t=v(`安装 ${e.skill.name} 依赖...`).start();try{await j(a.command,a.args,{cwd:e.installPath,timeout:6e4}),t.succeed(`${e.skill.name} 依赖已更新 (${a.command} ${a.args.join(" ")})`)}catch(a){return t.fail(`${e.skill.name} 依赖安装失败`),w.error(a instanceof Error?a.message:String(a)),!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 T(e){if("installed-package"!==e.source?.type)return;const a=v(`更新 ${e.skill.name} (npm install)...`).start();try{await j("npm",["install","--prefix",e.source.installDir,e.source.packageSpec],{timeout:12e4});const r=h(e.source.installDir,e.source.packageName);if(!t(r))throw new Error(`Installed package root not found: ${r}`);const n=$(r);return a.succeed(`${e.skill.name} 已重新安装`),{packageRoot:r,packageName:n?.name??e.source.packageName,...n?.version?{installedVersion:n.version}:{}}}catch(t){return a.fail(`${e.skill.name} 更新失败`),void w.error(t instanceof Error?t.message:String(t))}}async function M(e){const a=v(`刷新 ${e.skill.name} (MCP tools/list)...`).start(),t=new y;try{const r=p(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=i();w.info("检查 roll 更新...");const r=await S({forceRefresh:!0});let n,s;r.hasUpdate?w.success(`roll 有新版本: v${r.current} → v${r.latest}`):w.info(`roll 已是最新版本 (v${r.current})`);let c=[];try{n=o().agentsConfig,s=new g(n.dataDir),c=s.list()}catch(e){w.warn(`无法读取 Agent 配置,跳过已注册 Agent 检查:${e instanceof Error?e.message:String(e)}`)}const m=[];for(const e of c){let a=e;"installed-package"===f(a)&&(a=E(a,s));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 b({packageName:a.source.packageName,packageSpec:a.source.packageSpec,...a.source.installedVersion?{currentVersion:a.source.installedVersion}:{}});m.push({name:a.skill.name,sourceType:t,icon:P(e.status),action:D(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(c.length>0){w.info(`\n已注册 Agent (${c.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(""),B(t,a?"check":"pre-update")),a)return;w.info("");let p=!1,$=!1;r.hasUpdate&&(p=await N(r.latest,!1),p||($=!0)),!p||"needs-migration"!==t.status&&"invalid"!==t.status||(w.info(""),B(t,"post-update"));let h=0,y=0;for(const a of c){if(!s||!n)break;switch(f(a)){case"git":{const e="core-managed"===a.runtime.ownership&&void 0!==l(n.dataDir,a.skill.name);if(await A(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}:{}};s.replace(a.skill.name,r)?(await C(r,e,n.dataDir),h++):(w.warn(`${a.skill.name} 已从注册表中移除,跳过元数据刷新`),y++)}catch(e){s.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!==l(n.dataDir,a.skill.name);if(t)try{await u(n.dataDir,a.skill.name)}catch(e){s.updateStatus(a.skill.name,"error"),w.warn(`${a.skill.name} 停止失败,无法继续升级: ${e instanceof Error?e.message:String(e)}`),y++;break}const r=await T(a);if(r)try{const i=d(r.packageRoot),o="installed-package"===a.source?.type?{...a.source,packageName:r.packageName,...r.installedVersion?{installedVersion:r.installedVersion}:{}}:void 0,l={...a,skill:i.skill,transport:i.transport,runtime:i.runtime,installPath:r.packageRoot,...o?{source:o}:{},...i.skillBody.length>0?{skillBody:i.skillBody}:{}},c=await k(l,{skipBrowserSetup:e.skipBrowserSetup});c.ok||(w.warn(`${l.skill.name} setup 失败:${c.message}`),c.retryCommand&&w.info(`重试命令: ${c.retryCommand}`));s.replace(a.skill.name,l)?c.ok?(t&&await x(l,n.dataDir),h++):(s.updateStatus(l.skill.name,"error"),y++):(w.warn(`${a.skill.name} 已从注册表中移除,跳过元数据刷新`),y++)}catch(e){s.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 x(a,n.dataDir)}catch{s.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(!s.replace(a.skill.name,t)){w.warn(`${a.skill.name} 已从注册表中移除,跳过元数据刷新`),y++;break}await M(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!==l(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}:{}};s.replace(a.skill.name,r)?(await C(r,e,n.dataDir),h++):(w.warn(`${a.skill.name} 已从注册表中移除,跳过元数据刷新`),y++)}catch(e){s.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 更新失败":p?"roll ✓":"roll 无更新";return void w.warn(`更新完成但有失败:${e}${y>0?`,${y} 个 Agent 更新失败`:""}${h>0?`,${h} 个 Agent 已更新`:""}`)}p||h>0?w.success(`更新完成:${p?"roll ✓":"roll 无更新"}${h>0?`,${h} 个 Agent 已更新`:""}`):w.success("一切都已是最新版本")}});export function detectInstallCommand(e){const r=a(e,"package.json");if(t(r)){const e=I(r);if(e)return{command:e,args:["install"]}}const n=[["pnpm-lock.yaml","pnpm"],["package-lock.json","npm"],["npm-shrinkwrap.json","npm"],["yarn.lock","yarn"],["bun.lock","bun"],["bun.lockb","bun"]];for(const[r,s]of n)if(t(a(e,r)))return{command:s,args:["install"]}}async function C(e,a,t){a&&"core-managed"===e.runtime.ownership&&(await u(t,e.skill.name),await x(e,t))}async function x(e,a){let t=!1;try{c(e,a),t=!0,await m(e,{startupTimeoutMs:15e3,probeTimeoutMs:2e3})}catch(r){throw t&&await u(a,e.skill.name).catch(()=>{}),r}}function I(e){try{const a=JSON.parse(r(e,"utf-8"));if("string"!=typeof a.packageManager||0===a.packageManager.length)return;const t=a.packageManager.split("@",1)[0];return"pnpm"===t||"npm"===t||"yarn"===t||"bun"===t?t:void 0}catch{return}}
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}}
@@ -0,0 +1,30 @@
1
+ import type { ExecFileOptions } from "node:child_process";
2
+ export declare const PACKAGE_MANAGER_COMMANDS: readonly ["bun", "npm", "pnpm", "yarn"];
3
+ export type PackageManagerCommandName = (typeof PACKAGE_MANAGER_COMMANDS)[number];
4
+ export interface PackageManagerRunSpec {
5
+ readonly command: PackageManagerCommandName;
6
+ readonly args: readonly string[];
7
+ }
8
+ export interface InstallCommandSpec {
9
+ readonly command: PackageManagerCommandName;
10
+ readonly args: readonly ["install"];
11
+ }
12
+ export interface PackageManagerRunOptions extends Omit<ExecFileOptions, "encoding" | "shell"> {
13
+ readonly platform?: NodeJS.Platform;
14
+ }
15
+ export interface PackageManagerRunResult {
16
+ readonly stdout: string;
17
+ readonly stderr: string;
18
+ }
19
+ export interface PackageManagerExecInvocation {
20
+ readonly file: string;
21
+ readonly args: readonly string[];
22
+ readonly shell: boolean;
23
+ }
24
+ export declare function detectInstallCommand(projectDir: string): InstallCommandSpec | undefined;
25
+ export declare function createInstallCommand(command?: PackageManagerCommandName): InstallCommandSpec;
26
+ export declare function runPackageManager(spec: PackageManagerRunSpec, options?: PackageManagerRunOptions): Promise<PackageManagerRunResult>;
27
+ export declare function createPackageManagerExecInvocation(spec: PackageManagerRunSpec, platform?: NodeJS.Platform): PackageManagerExecInvocation;
28
+ export declare function shouldRunPackageManagerViaShell(platform: NodeJS.Platform): boolean;
29
+ export declare function formatPackageManagerCommand(spec: PackageManagerRunSpec): string;
30
+ export declare function formatPackageManagerError(spec: PackageManagerRunSpec, error: unknown, platform?: NodeJS.Platform): string;
@@ -0,0 +1 @@
1
+ import{execFile as n}from"node:child_process";import{existsSync as r,readFileSync as e}from"node:fs";import{resolve as o}from"node:path";import{promisify as t}from"node:util";const a=t(n);export const PACKAGE_MANAGER_COMMANDS=["bun","npm","pnpm","yarn"];export function detectInstallCommand(n){const e=o(n,"package.json");if(r(e)){const n=c(e);if(n)return{command:n,args:["install"]}}const t=[["pnpm-lock.yaml","pnpm"],["package-lock.json","npm"],["npm-shrinkwrap.json","npm"],["yarn.lock","yarn"],["bun.lock","bun"],["bun.lockb","bun"]];for(const[e,a]of t)if(r(o(n,e)))return{command:a,args:["install"]}}export function createInstallCommand(n="pnpm"){return{command:n,args:["install"]}}export async function runPackageManager(n,r={}){const{platform:e,...o}=r,t=createPackageManagerExecInvocation(n,e??process.platform),c=await a(t.file,[...t.args],{...o,encoding:"utf-8",...t.shell?{shell:t.shell}:{}});return{stdout:c.stdout,stderr:c.stderr}}export function createPackageManagerExecInvocation(n,r=process.platform){return shouldRunPackageManagerViaShell(r)?{file:"cmd.exe",args:["/d","/s","/c",u(n)],shell:!1}:{file:n.command,args:n.args,shell:!1}}export function shouldRunPackageManagerViaShell(n){return"win32"===n}export function formatPackageManagerCommand(n){return[n.command,...n.args].map(i).join(" ")}export function formatPackageManagerError(n,r,e=process.platform){const o=formatPackageManagerCommand(n);return p(n.command,r,e)?[`未找到 ${n.command},无法执行 \`${o}\`。`,"win32"===e?`请确认 ${n.command}.cmd 位于 PATH 中;npm 全局目录通常是 C:\\Users\\<you>\\AppData\\Roaming\\npm。`:`请确认已安装 ${n.command},并且当前 shell 的 PATH 可以访问它。`].join(""):r instanceof Error?r.message:String(r)}function c(n){try{const r=JSON.parse(e(n,"utf-8"));if("string"!=typeof r.packageManager||0===r.packageManager.length)return;const[o]=r.packageManager.split("@",1);if(!o)return;return s(o)?o:void 0}catch{return}}function s(n){return PACKAGE_MANAGER_COMMANDS.some(r=>r===n)}function i(n){return/\s/.test(n)?JSON.stringify(n):n}function u(n){return[n.command,...n.args].map(m).join(" ")}function m(n){if(0===n.length)return'""';if(/["\r\n]/.test(n))throw new Error(`Unsupported Windows shell argument: ${n}`);return/[\s"&|<>^()%!]/.test(n)?`"${l(n)}"`:n}function l(n){return n.replace(/[\^&|<>%!]/g,n=>`^${n}`)}function p(n,r,e){if("ENOENT"===f(r))return!0;if("win32"!==e)return!1;const o=g(r).toLowerCase();return o.includes(`'${n}' is not recognized`)||o.includes(`"${n}" is not recognized`)||o.includes(`${n} is not recognized`)||o.includes(`'${n}' 不是内部或外部命令`)||o.includes(`"${n}" 不是内部或外部命令`)||o.includes(`${n} 不是内部或外部命令`)}function f(n){if("object"==typeof n&&null!==n&&"code"in n)return"string"==typeof n.code?n.code:void 0}function g(n){return n instanceof Error?n.message:String(n)}
@@ -1 +1 @@
1
- import{readFileSync as t,writeFileSync as e,mkdirSync as n,existsSync as r}from"node:fs";import{resolve as s,dirname as o}from"node:path";import{execFile as a}from"node:child_process";import{promisify as i}from"node:util";import{homedir as c}from"node:os";const u=i(a),p=864e5,f="@roll-agent/core";export const PUBLISHED_PACKAGE_UPDATE_STATUSES=["up-to-date","update-available","pinned-behind","unsupported-spec","unknown"];function l(){return s(c(),".roll-agent","update-check.json")}function h(t){return"object"==typeof t&&null!==t&&("latestVersion"in t&&"checkedAt"in t&&("string"==typeof t.latestVersion&&t.latestVersion.length>0&&"number"==typeof t.checkedAt))}function d(t){return"object"==typeof t&&null!==t&&"packages"in t&&("object"==typeof t.packages&&null!==t.packages&&Object.values(t.packages).every(t=>h(t)))}function g(t){return h(t)}function k(){const e=l();if(r(e))try{const n=t(e,"utf-8"),r=JSON.parse(n);if(d(r))return r;if(g(r))return{packages:{[f]:{latestVersion:r.latestVersion,checkedAt:r.checkedAt}}}}catch{}}function m(t){try{const s=l(),a=o(s);r(a)||n(a,{recursive:!0}),e(s,JSON.stringify(t),"utf-8")}catch{}}function V(t,e){const n=k();m({packages:{...n?.packages??{},[t]:{latestVersion:e,checkedAt:Date.now()}}})}function y(t){return k()?.packages[t]}async function w(t){try{const{stdout:e}=await u("npm",["view",t,"version","--json"],{timeout:5e3,encoding:"utf-8"}),n=e.trim();if(0===n.length)return;const r=JSON.parse(n);return"string"==typeof r&&r.length>0?r:void 0}catch{return}}export async function fetchLatestPublishedVersion(t,e={}){const n=e.forceRefresh??!1,r=e.allowNetwork??!0,s=y(t);if(!n&&s&&Date.now()-s.checkedAt<p)return s.latestVersion;if(!r)return s?.latestVersion;const o=await w(t);return o?(V(t,o),o):s?.latestVersion}export function getCurrentVersion(){try{const e=s(import.meta.dirname,"../../../package.json"),n=t(e,"utf-8"),r=JSON.parse(n);if("object"==typeof r&&null!==r&&"version"in r){const t=r.version;if("string"==typeof t)return t}}catch{}return"0.0.0"}export function isNewerVersion(t,e){const n=t=>t.replace(/^v/,"").split(".").map(Number),r=n(t),s=n(e);for(let t=0;t<3;t+=1){const e=r[t]??0,n=s[t]??0;if(e>n)return!0;if(e<n)return!1}return!1}function N(t,e){return!["file:","git+","http://","https://","link:","workspace:","npm:"].some(t=>e.startsWith(t))&&(!(e.startsWith(".")||e.startsWith("/")||e.startsWith("~")||e.endsWith(".tgz")||e.endsWith(".tar.gz"))&&(e===t||e.startsWith(`${t}@`)))}function b(t,e){if(e.startsWith(`${t}@`))return e.slice(t.length+1)}function v(t,e){const n=b(t,e);if(!n)return!1;const r=n.replace(/^v/,"");return/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/.test(r)}export async function checkPublishedPackageUpdate(t,e={}){const{packageName:n,packageSpec:r,currentVersion:s}=t;if(!N(n,r))return{packageName:n,...s?{currentVersion:s}:{},status:"unsupported-spec"};if(!s)return{packageName:n,status:"unknown"};const o=await fetchLatestPublishedVersion(n,e);return o?isNewerVersion(o,s)?v(n,r)?{packageName:n,currentVersion:s,latestVersion:o,status:"pinned-behind"}:{packageName:n,currentVersion:s,latestVersion:o,status:"update-available"}:{packageName:n,currentVersion:s,latestVersion:o,status:"up-to-date"}:{packageName:n,currentVersion:s,status:"unknown"}}export async function checkForUpdate(t={}){const e=getCurrentVersion(),n=await fetchLatestPublishedVersion(f,t)??e;return{current:e,latest:n,hasUpdate:isNewerVersion(n,e)}}
1
+ import{readFileSync as t,writeFileSync as e,mkdirSync as n,existsSync as r}from"node:fs";import{resolve as s,dirname as a}from"node:path";import{homedir as o}from"node:os";import{runPackageManager as c}from"./package-manager.js";const i=864e5,u="@roll-agent/core";export const PUBLISHED_PACKAGE_UPDATE_STATUSES=["up-to-date","update-available","pinned-behind","unsupported-spec","unknown"];function p(){return s(o(),".roll-agent","update-check.json")}function f(t){return"object"==typeof t&&null!==t&&("latestVersion"in t&&"checkedAt"in t&&("string"==typeof t.latestVersion&&t.latestVersion.length>0&&"number"==typeof t.checkedAt))}function l(t){return"object"==typeof t&&null!==t&&"packages"in t&&("object"==typeof t.packages&&null!==t.packages&&Object.values(t.packages).every(t=>f(t)))}function h(t){return f(t)}function d(){const e=p();if(r(e))try{const n=t(e,"utf-8"),r=JSON.parse(n);if(l(r))return r;if(h(r))return{packages:{[u]:{latestVersion:r.latestVersion,checkedAt:r.checkedAt}}}}catch{}}function g(t){try{const s=p(),o=a(s);r(o)||n(o,{recursive:!0}),e(s,JSON.stringify(t),"utf-8")}catch{}}function k(t,e){const n=d();g({packages:{...n?.packages??{},[t]:{latestVersion:e,checkedAt:Date.now()}}})}function m(t){return d()?.packages[t]}async function V(t){try{const{stdout:e}=await c({command:"npm",args:["view",t,"version","--json"]},{timeout:5e3}),n=e.trim();if(0===n.length)return;const r=JSON.parse(n);return"string"==typeof r&&r.length>0?r:void 0}catch{return}}export async function fetchLatestPublishedVersion(t,e={}){const n=e.forceRefresh??!1,r=e.allowNetwork??!0,s=m(t);if(!n&&s&&Date.now()-s.checkedAt<i)return s.latestVersion;if(!r)return s?.latestVersion;const a=await V(t);return a?(k(t,a),a):s?.latestVersion}export function getCurrentVersion(){try{const e=s(import.meta.dirname,"../../../package.json"),n=t(e,"utf-8"),r=JSON.parse(n);if("object"==typeof r&&null!==r&&"version"in r){const t=r.version;if("string"==typeof t)return t}}catch{}return"0.0.0"}export function isNewerVersion(t,e){const n=t=>t.replace(/^v/,"").split(".").map(Number),r=n(t),s=n(e);for(let t=0;t<3;t+=1){const e=r[t]??0,n=s[t]??0;if(e>n)return!0;if(e<n)return!1}return!1}function y(t,e){return!["file:","git+","http://","https://","link:","workspace:","npm:"].some(t=>e.startsWith(t))&&(!(e.startsWith(".")||e.startsWith("/")||e.startsWith("~")||e.endsWith(".tgz")||e.endsWith(".tar.gz"))&&(e===t||e.startsWith(`${t}@`)))}function w(t,e){if(e.startsWith(`${t}@`))return e.slice(t.length+1)}function N(t,e){const n=w(t,e);if(!n)return!1;const r=n.replace(/^v/,"");return/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/.test(r)}export async function checkPublishedPackageUpdate(t,e={}){const{packageName:n,packageSpec:r,currentVersion:s}=t;if(!y(n,r))return{packageName:n,...s?{currentVersion:s}:{},status:"unsupported-spec"};if(!s)return{packageName:n,status:"unknown"};const a=await fetchLatestPublishedVersion(n,e);return a?isNewerVersion(a,s)?N(n,r)?{packageName:n,currentVersion:s,latestVersion:a,status:"pinned-behind"}:{packageName:n,currentVersion:s,latestVersion:a,status:"update-available"}:{packageName:n,currentVersion:s,latestVersion:a,status:"up-to-date"}:{packageName:n,currentVersion:s,status:"unknown"}}export async function checkForUpdate(t={}){const e=getCurrentVersion(),n=await fetchLatestPublishedVersion(u,t)??e;return{current:e,latest:n,hasUpdate:isNewerVersion(n,e)}}
@@ -1 +1 @@
1
- import{describeValueType as e,getAdditionalPropertiesSetting as t,getSchemaDescription as n,getSchemaEnum as r,getSchemaItems as s,getSchemaProperties as i,getSchemaRequired as u,getSchemaType as o,isNaturallyExtractableSchema as c,isJsonSchemaObject as a,isPlainObject as p}from"./schema.js";function f(e,t){const r=n(t),s=t&&!c(t);return{path:e,code:s?"requires_explicit_input":"missing_required",message:s?`${e} 无法从自然语言可靠提取,需要显式提供`:`${e} 为必填字段`,...r?{description:r}:{}}}function l(e,t){if(t&&a(t)){const n=g({inputSchema:t},{},e);if(n.length>0)return n}return[f(e,t)]}function m(t,i,u){const c=[],f=n(t),l=r(t),h=o(t);if(l&&!l.some(e=>Object.is(e,i)))return c.push({path:u,code:"invalid_enum",message:`${u} 必须是以下值之一:${l.map(e=>JSON.stringify(e)).join("、")}`,...f?{description:f}:{},expected:l.map(e=>JSON.stringify(e)).join(" | "),actual:JSON.stringify(i)}),c;if(!h)return c;const d=e(i),$=()=>{c.push({path:u,code:"invalid_type",message:`${u} 应为 ${h},当前是 ${d}`,...f?{description:f}:{},expected:h,actual:d})};switch(h){case"string":return"string"!=typeof i&&$(),c;case"number":return("number"!=typeof i||Number.isNaN(i))&&$(),c;case"integer":return"number"==typeof i&&Number.isInteger(i)||$(),c;case"boolean":return"boolean"!=typeof i&&$(),c;case"null":return null!==i&&$(),c;case"array":if(!Array.isArray(i))return $(),c;{const e=s(t);return e?i.flatMap((t,n)=>m(e,t,`${u}[${String(n)}]`)):c}case"object":return p(i)?a(t)?g({inputSchema:t},i,u):c:($(),c);default:return c}}function g(e,n,r=""){const s=u(e),o=i(e.inputSchema),c=[];for(const e of s){const t=n[e];if(null!=t)continue;const s=o[e],i=r?`${r}.${e}`:e;c.push(...l(i,s))}for(const[s,i]of Object.entries(n)){const n=r?`${r}.${s}`:s,u=o[s];u?c.push(...m(u,i,n)):!1===t(e.inputSchema)&&c.push({path:n,code:"unexpected_property",message:`${n} 不是允许的参数`})}return c}export function getInputValidationIssues(e,t){return g(e,t)}export function preflightToolCall(e,t,n={}){const r=getInputValidationIssues(e,t),s=n.runtimeIssues??[];return 0===r.length&&0===s.length?{ok:!0}:{ok:!1,issues:r,runtimeIssues:s}}
1
+ import{describeValueType as e,getAdditionalPropertiesSetting as t,getSchemaDescription as n,getSchemaEnum as r,getSchemaItems as s,getSchemaMinItems as i,getSchemaProperties as u,getSchemaRequired as o,getSchemaType as c,isNaturallyExtractableSchema as a,isJsonSchemaObject as p,isPlainObject as l}from"./schema.js";function g(e,t){const r=n(t),s=t&&!a(t);return{path:e,code:s?"requires_explicit_input":"missing_required",message:s?`${e} 无法从自然语言可靠提取,需要显式提供`:`${e} 为必填字段`,...r?{description:r}:{}}}function f(e,t){if(t&&p(t)){const n=h({inputSchema:t},{},e);if(n.length>0)return n}return[g(e,t)]}function m(t,u,o){const a=[],g=n(t),f=r(t),d=c(t);if(f&&!f.some(e=>Object.is(e,u)))return a.push({path:o,code:"invalid_enum",message:`${o} 必须是以下值之一:${f.map(e=>JSON.stringify(e)).join("、")}`,...g?{description:g}:{},expected:f.map(e=>JSON.stringify(e)).join(" | "),actual:JSON.stringify(u)}),a;if(!d)return a;const $=e(u),y=()=>{a.push({path:o,code:"invalid_type",message:`${o} 应为 ${d},当前是 ${$}`,...g?{description:g}:{},expected:d,actual:$})};switch(d){case"string":return"string"!=typeof u&&y(),a;case"number":return("number"!=typeof u||Number.isNaN(u))&&y(),a;case"integer":return"number"==typeof u&&Number.isInteger(u)||y(),a;case"boolean":return"boolean"!=typeof u&&y(),a;case"null":return null!==u&&y(),a;case"array":if(!Array.isArray(u))return y(),a;{const e=i(t);void 0!==e&&u.length<e&&a.push({path:o,code:"too_small",message:`${o} 至少需要 ${String(e)} 个元素,当前是 ${String(u.length)} 个`,...g?{description:g}:{},expected:`minItems: ${String(e)}`,actual:`length: ${String(u.length)}`});const n=s(t);return n?(a.push(...u.flatMap((e,t)=>m(n,e,`${o}[${String(t)}]`))),a):a}case"object":return l(u)?p(t)?h({inputSchema:t},u,o):a:(y(),a);default:return a}}function h(e,n,r=""){const s=o(e),i=u(e.inputSchema),c=[];for(const e of s){const t=n[e];if(null!=t)continue;const s=i[e],u=r?`${r}.${e}`:e;c.push(...f(u,s))}for(const[s,u]of Object.entries(n)){const n=r?`${r}.${s}`:s,o=i[s];o?c.push(...m(o,u,n)):!1===t(e.inputSchema)&&c.push({path:n,code:"unexpected_property",message:`${n} 不是允许的参数`})}return c}export function getInputValidationIssues(e,t){return h(e,t)}export function preflightToolCall(e,t,n={}){const r=getInputValidationIssues(e,t),s=n.runtimeIssues??[];return 0===r.length&&0===s.length?{ok:!0}:{ok:!1,issues:r,runtimeIssues:s}}
@@ -5,6 +5,7 @@ export declare function getSchemaDescription(schema: object | undefined): string
5
5
  export declare function getSchemaType(schema: object | undefined): string | undefined;
6
6
  export declare function getSchemaEnum(schema: object | undefined): ReadonlyArray<unknown> | undefined;
7
7
  export declare function getSchemaItems(schema: object | undefined): object | undefined;
8
+ export declare function getSchemaMinItems(schema: object | undefined): number | undefined;
8
9
  export declare function getSchemaProperties(schema: Pick<AgentTool, "inputSchema">["inputSchema"] | object | undefined): Readonly<Record<string, object>>;
9
10
  export declare function isNaturallyExtractableSchema(schema: object | undefined): boolean;
10
11
  export declare function getSchemaRequired(tool: Pick<AgentTool, "inputSchema">): ReadonlyArray<string>;
@@ -1 +1 @@
1
- export function isPlainObject(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}export function isJsonSchemaObject(e){return"type"in e&&"object"===e.type}export function getSchemaDescription(e){return e&&"description"in e&&"string"==typeof e.description?e.description:void 0}export function getSchemaType(e){return e&&"type"in e&&"string"==typeof e.type?e.type:void 0}export function getSchemaEnum(e){return e&&"enum"in e&&Array.isArray(e.enum)?e.enum:void 0}export function getSchemaItems(e){return e&&"items"in e&&"object"==typeof e.items&&null!==e.items?e.items:void 0}export function getSchemaProperties(e){if(!(e&&"properties"in e&&e.properties&&isPlainObject(e.properties)))return{};const t=Object.entries(e.properties).filter(e=>{const[,t]=e;return"object"==typeof t&&null!==t});return Object.fromEntries(t)}export function isNaturallyExtractableSchema(e){return!!e&&("object"!==getSchemaType(e)||Object.keys(getSchemaProperties(e)).length>0)}export function getSchemaRequired(e){return e.inputSchema.required??[]}export function getAdditionalPropertiesSetting(e){return e&&"additionalProperties"in e&&"boolean"==typeof e.additionalProperties?e.additionalProperties:void 0}export function describeValueType(e){return null===e?"null":Array.isArray(e)?"array":typeof e}
1
+ export function isPlainObject(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}export function isJsonSchemaObject(e){return"type"in e&&"object"===e.type}export function getSchemaDescription(e){return e&&"description"in e&&"string"==typeof e.description?e.description:void 0}export function getSchemaType(e){return e&&"type"in e&&"string"==typeof e.type?e.type:void 0}export function getSchemaEnum(e){return e&&"enum"in e&&Array.isArray(e.enum)?e.enum:void 0}export function getSchemaItems(e){return e&&"items"in e&&"object"==typeof e.items&&null!==e.items?e.items:void 0}export function getSchemaMinItems(e){return e&&"minItems"in e&&"number"==typeof e.minItems?e.minItems:void 0}export function getSchemaProperties(e){if(!(e&&"properties"in e&&e.properties&&isPlainObject(e.properties)))return{};const t=Object.entries(e.properties).filter(e=>{const[,t]=e;return"object"==typeof t&&null!==t});return Object.fromEntries(t)}export function isNaturallyExtractableSchema(e){return!!e&&("object"!==getSchemaType(e)||Object.keys(getSchemaProperties(e)).length>0)}export function getSchemaRequired(e){return e.inputSchema.required??[]}export function getAdditionalPropertiesSetting(e){return e&&"additionalProperties"in e&&"boolean"==typeof e.additionalProperties?e.additionalProperties:void 0}export function describeValueType(e){return null===e?"null":Array.isArray(e)?"array":typeof e}
@@ -3,7 +3,7 @@ export declare const ASK_RESULT_STATUSES: readonly ["success", "needs_input", "n
3
3
  export type AskResultStatus = (typeof ASK_RESULT_STATUSES)[number];
4
4
  export declare const ASK_FAILURE_STAGES: readonly ["route", "connect", "execute"];
5
5
  export type AskFailureStage = (typeof ASK_FAILURE_STAGES)[number];
6
- export declare const ASK_VALIDATION_ISSUE_CODES: readonly ["missing_required", "requires_explicit_input", "invalid_type", "invalid_enum", "unexpected_property"];
6
+ export declare const ASK_VALIDATION_ISSUE_CODES: readonly ["missing_required", "requires_explicit_input", "invalid_type", "invalid_enum", "too_small", "unexpected_property"];
7
7
  export type AskValidationIssueCode = (typeof ASK_VALIDATION_ISSUE_CODES)[number];
8
8
  export declare const ASK_RUNTIME_ISSUE_CATEGORIES: readonly ["env"];
9
9
  export type AskRuntimeIssueCategory = (typeof ASK_RUNTIME_ISSUE_CATEGORIES)[number];
package/dist/types/ask.js CHANGED
@@ -1 +1 @@
1
- export const ASK_RESULT_STATUSES=["success","needs_input","needs_confirmation","failed"];export const ASK_FAILURE_STAGES=["route","connect","execute"];export const ASK_VALIDATION_ISSUE_CODES=["missing_required","requires_explicit_input","invalid_type","invalid_enum","unexpected_property"];export const ASK_RUNTIME_ISSUE_CATEGORIES=["env"];export const ASK_RUNTIME_ISSUE_CODES=["missing_required_env"];
1
+ export const ASK_RESULT_STATUSES=["success","needs_input","needs_confirmation","failed"];export const ASK_FAILURE_STAGES=["route","connect","execute"];export const ASK_VALIDATION_ISSUE_CODES=["missing_required","requires_explicit_input","invalid_type","invalid_enum","too_small","unexpected_property"];export const ASK_RUNTIME_ISSUE_CATEGORIES=["env"];export const ASK_RUNTIME_ISSUE_CODES=["missing_required_env"];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@roll-agent/core",
3
- "version": "0.6.2",
3
+ "version": "0.6.4",
4
4
  "type": "module",
5
5
  "repository": {
6
6
  "type": "git",