@roll-agent/core 0.3.3 → 0.5.0
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.
- package/dist/cli/commands/agent-install.js +1 -1
- package/dist/cli/commands/ask.js +1 -1
- package/dist/cli/commands/update.js +1 -1
- package/dist/cli/utils/update-checker.d.ts +18 -14
- package/dist/cli/utils/update-checker.js +1 -1
- package/dist/llm/providers.d.ts +15 -1
- package/dist/llm/providers.js +1 -1
- package/dist/registry/source.d.ts +5 -0
- package/dist/registry/source.js +1 -1
- package/dist/registry/store.js +1 -1
- package/dist/router/llm-router.d.ts +4 -4
- package/dist/router/llm-router.js +1 -1
- package/dist/tool-runtime/argument-extractor.d.ts +2 -2
- package/dist/tool-runtime/argument-extractor.js +1 -1
- package/dist/types/agent.d.ts +1 -0
- package/package.json +6 -6
|
@@ -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,
|
|
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}。`)))}
|
package/dist/cli/commands/ask.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{defineCommand as e}from"citty";import{loadConfig as t}from"../../config/loader.js";import{getAgentEnv as o}from"../../config/helpers.js";import{createProviderModel as s}from"../../llm/providers.js";import{McpClientManager as
|
|
1
|
+
import{defineCommand as e}from"citty";import{loadConfig as t}from"../../config/loader.js";import{getAgentEnv as o}from"../../config/helpers.js";import{createProviderModel as s,resolveLLMCall as r}from"../../llm/providers.js";import{McpClientManager as n}from"../../mcp/client-manager.js";import{AgentStore as i}from"../../registry/store.js";import{resolveTransportWithDevSpawnSpec as a}from"../../registry/dev-spawn.js";import{routeWithLLM as l}from"../../router/llm-router.js";import{extractToolInput as c}from"../../tool-runtime/argument-extractor.js";import{formatValidationIssuesMessage as m}from"../../tool-runtime/messages.js";import{preflightToolCall as u}from"../../tool-runtime/preflight.js";import{log as d}from"../utils/output.js";const f=.5;function g(e){if(!Array.isArray(e))return[];const t=[];for(const o of e)"object"==typeof o&&null!==o&&"type"in o&&"text"===o.type&&"text"in o&&"string"==typeof o.text&&t.push(o.text);return t}function p(e){return"object"==typeof e&&null!==e&&"isError"in e&&!0===e.isError}function y(e){console.log(JSON.stringify(e,null,2))}function j(e){const t=g("object"==typeof e.result&&null!==e.result&&"content"in e.result?e.result.content:void 0);for(const e of t)console.log(e)}export default e({meta:{description:"LLM 智能路由,自动选择 Agent 和 tool"},args:{message:{type:"positional",description:"自然语言消息",required:!0},json:{type:"boolean",description:"JSON 格式输出",default:!1}},async run({args:e}){const{config:v}=t(),x=new i(v.agents.dataDir).list();if(0===x.length)return d.error("暂无已注册的 Agent。可使用 `roll agent add <path>`、`roll agent install <package>` 或 `roll agent add --remote <endpoint>`。"),void(process.exitCode=1);const $=t=>{if(e.json)y(t);else if("success"===t.status)j(t)},N=v.llm.defaultProvider,k=v.ask.llmModel??v.llm.defaultModel,w=v.llm.providers[N];if(!w)return d.error(`LLM provider "${N}" 未配置。请检查 roll.config.yaml`),void(process.exitCode=1);const{model:h,providerOptions:A}=r(N,k,w.apiKey,"structured-output",w.baseUrl);let C;d.info(`分析意图: "${e.message}"`);try{C=await l(e.message,x,h,A)}catch(e){const t={status:"failed",stage:"route",message:`LLM 路由失败: ${e instanceof Error?e.message:String(e)}`};return d.error(t.message),$(t),void(process.exitCode=1)}d.info(`路由决策: ${C.agentName}.${C.toolName} (置信度: ${String(C.confidence)})`);const L=v.ask.confirmThreshold??f;if(C.confidence<L){const e={status:"needs_confirmation",decision:C,message:`置信度 ${String(C.confidence)} 低于阈值 ${String(L)},跳过执行。 可使用 \`roll run ${C.agentName} ${C.toolName}\` 手动调用。`};return d.warn(e.message),$(e),void(process.exitCode=1)}const M=x.find(e=>e.skill.name===C.agentName);if(!M){const e={status:"failed",stage:"route",decision:C,message:`Agent "${C.agentName}" 未找到(LLM 返回了无效的 Agent 名称)`};return d.error(e.message),$(e),void(process.exitCode=1)}const S=s(N,v.llm.defaultModel,w.apiKey,w.baseUrl),b=new n;let T="connect";try{d.info(`连接 Agent "${M.skill.name}"...`);const t=o(v,M.skill.name),s=a(M),r=await b.connect(M.skill.name,s,M.installPath,{samplingModel:S,...t?{env:t}:{}}),{tools:n}=await r.listTools(),i=n.find(e=>e.name===C.toolName);if(!i){const e={status:"failed",stage:"route",decision:C,message:`Tool "${C.toolName}" 不存在于 Agent "${M.skill.name}" 中`};return d.error(e.message),$(e),void(process.exitCode=1)}T="execute";const l=await c(e.message,i,h,A),f={...C,input:l},y=u(i,f.input);if(!y.ok){const e={status:"needs_input",decision:f,validationIssues:y.issues,message:m(M.skill.name,C.toolName,y.issues)};return d.warn(e.message),$(e),void(process.exitCode=1)}d.info(`调用 ${M.skill.name}.${C.toolName}(${JSON.stringify(f.input)})`);const j=await r.callTool({name:C.toolName,arguments:f.input});if(p(j)){const e={status:"failed",stage:"execute",decision:f,message:g(j.content).join("\n")||"Tool 调用失败"};return d.error(e.message),$(e),void(process.exitCode=1)}$({status:"success",decision:f,result:j}),d.success("调用完成")}catch(e){const t={status:"failed",stage:T,decision:C,message:e instanceof Error?e.message:String(e)};d.error(t.message),$(t),process.exitCode=1}finally{await b.disconnectAll()}}});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{defineCommand as t}from"citty";import{resolve as e}from"node:path";import{existsSync as a,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 m,waitForAgentReady as p}from"../../registry/process-manager.js";import{resolveTransportWithDevSpawnSpec as u}from"../../registry/dev-spawn.js";import{runAgentSetup as f}from"../../registry/runtime-setup.js";import{AgentStore as k}from"../../registry/store.js";import{discoverAgent as g}from"../../registry/discovery.js";import{inferAgentSourceType as d,resolveInstalledPackageRoot as $}from"../../registry/source.js";import{McpClientManager as h}from"../../mcp/client-manager.js";import{log as y,createSpinner as w}from"../utils/output.js";import{checkForUpdate as S,getCurrentVersion as b}from"../utils/update-checker.js";const v=s(n);export{inferAgentSourceType as inferSourceType}from"../../registry/source.js";function j(t,e){switch(t.status){case"needs-migration":{const a="post-update"===e?"升级后需要迁移本地配置":"检测到本地配置需要迁移";y.warn(`${a}: ${t.configPath}`);for(const e of t.report.issues)y.warn(` - ${e.message}`);t.report.canAutoMigrate&&y.info("建议命令: roll config migrate");break}case"invalid":y.warn(`本地配置存在问题: ${t.configPath??"(unknown path)"}`),y.warn(` - ${t.error.message}`),"post-update"===e&&y.info("请修复配置文件后再继续使用相关命令。")}}async function B(t,e){const a=b();if(a===t)return y.info(`roll 已是最新版本 (v${a})`),!1;if(y.info(`roll v${a} → v${t}`),e)return y.info("[dry-run] 跳过实际更新"),!0;const r=w("正在更新 @roll-agent/core...").start();try{return await v("npm",["install","-g",`@roll-agent/core@${t}`],{timeout:6e4}),r.succeed(`roll 已更新到 v${t}`),!0}catch(t){return r.fail("更新失败"),y.error(t instanceof Error?t.message:String(t)),!1}}async function P(t){const r=w(`更新 ${t.skill.name} (git pull)...`).start();try{await v("git",["pull"],{cwd:t.installPath,timeout:3e4}),r.succeed(`${t.skill.name} 代码已更新`);const n=e(t.installPath,"package.json");if(a(n)){const e=detectInstallCommand(t.installPath);if(e){const a=w(`安装 ${t.skill.name} 依赖...`).start();try{await v(e.command,e.args,{cwd:t.installPath,timeout:6e4}),a.succeed(`${t.skill.name} 依赖已更新 (${e.command} ${e.args.join(" ")})`)}catch(e){return a.fail(`${t.skill.name} 依赖安装失败`),y.error(e instanceof Error?e.message:String(e)),!1}}else y.warn(`${t.skill.name} 未检测到 packageManager 或 lockfile,跳过依赖安装。`)}return!0}catch(e){return r.fail(`${t.skill.name} 更新失败`),y.error(e instanceof Error?e.message:String(e)),!1}}async function A(t){if("installed-package"!==t.source?.type)return!1;const e=w(`更新 ${t.skill.name} (npm install)...`).start();try{await v("npm",["install","--prefix",t.source.installDir,t.source.packageSpec],{timeout:12e4});const r=$(t.source.installDir,t.source.packageName);if(!a(r))throw new Error(`Installed package root not found: ${r}`);return e.succeed(`${t.skill.name} 已重新安装`),!0}catch(a){return e.fail(`${t.skill.name} 更新失败`),y.error(a instanceof Error?a.message:String(a)),!1}}async function E(t){const e=w(`刷新 ${t.skill.name} (MCP tools/list)...`).start(),a=new h;try{const r=u(t),n=await a.connect(t.skill.name,r,t.installPath),{tools:s}=await n.listTools();return e.succeed(`${t.skill.name} 元数据已刷新(${s.length} 个 tool)`),!0}catch(a){return e.fail(`${t.skill.name} 刷新失败`),y.error(a instanceof Error?a.message:String(a)),!1}finally{await a.disconnectAll()}}export default t({meta:{description:"更新 roll 及已注册的 Agent"},args:{check:{type:"boolean",description:"仅检查可用更新,不执行",default:!1},skipBrowserSetup:{type:"boolean",description:"跳过浏览器运行时安装",default:!1}},async run({args:t}){const e=t.check,a=i();y.info("检查 roll 更新...");const r=await S({forceRefresh:!0});let n,s;r.hasUpdate?y.success(`roll 有新版本: v${r.current} → v${r.latest}`):y.info(`roll 已是最新版本 (v${r.current})`);let c=[];try{n=o().agentsConfig,s=new k(n.dataDir),c=s.list()}catch(t){y.warn(`无法读取 Agent 配置,跳过已注册 Agent 检查:${t instanceof Error?t.message:String(t)}`)}const m=[];for(const t of c){const e=d(t);let a;switch(e){case"git":a="git pull + 重新安装依赖";break;case"installed-package":a="重新安装 npm 包";break;case"remote-manifest":a="刷新本地 manifest + MCP 元数据";break;case"local-path":a="刷新本地 SKILL/manifest"}m.push({name:t.skill.name,sourceType:e,action:a})}if(c.length>0){y.info(`\n已注册 Agent (${c.length}):`);for(const t of m){const e="local-path"===t.sourceType?"⏭":"⬆";y.info(` ${e} ${t.name} [${t.sourceType}] — ${t.action}`)}}else n&&y.info("无已注册 Agent");if("needs-migration"!==a.status&&"invalid"!==a.status||(y.info(""),j(a,e?"check":"pre-update")),e)return;y.info("");let p=!1,u=!1;r.hasUpdate&&(p=await B(r.latest,!1),p||(u=!0)),!p||"needs-migration"!==a.status&&"invalid"!==a.status||(y.info(""),j(a,"post-update"));let h=0,w=0;for(const e of c){if(!s||!n)break;switch(d(e)){case"git":{const t="core-managed"===e.runtime.ownership&&void 0!==l(n.dataDir,e.skill.name);if(await P(e))try{const a=g(e.installPath),r={...e,skill:a.skill,transport:a.transport,runtime:a.runtime,...a.skillBody.length>0?{skillBody:a.skillBody}:{}};s.replace(e.skill.name,r)?(await D(r,t,n.dataDir),h++):(y.warn(`${e.skill.name} 已从注册表中移除,跳过元数据刷新`),w++)}catch(t){s.updateStatus(e.skill.name,"error"),y.warn(`${e.skill.name} metadata 刷新或重启失败: ${t instanceof Error?t.message:String(t)}`),w++}else w++;break}case"installed-package":{const a="core-managed"===e.runtime.ownership&&void 0!==l(n.dataDir,e.skill.name);if(await A(e))try{const r="installed-package"===e.source?.type?$(e.source.installDir,e.source.packageName):e.installPath,i=g(r),o={...e,skill:i.skill,transport:i.transport,runtime:i.runtime,installPath:r,...i.skillBody.length>0?{skillBody:i.skillBody}:{}},l=await f(o,{skipBrowserSetup:t.skipBrowserSetup});l.ok||(y.warn(`${o.skill.name} setup 失败:${l.message}`),l.retryCommand&&y.info(`重试命令: ${l.retryCommand}`));s.replace(e.skill.name,o)?l.ok?(await D(o,a,n.dataDir),h++):(s.updateStatus(o.skill.name,"error"),w++):(y.warn(`${e.skill.name} 已从注册表中移除,跳过元数据刷新`),w++)}catch(t){s.updateStatus(e.skill.name,"error"),y.warn(`${e.skill.name} metadata 刷新、setup 或重启失败: ${t instanceof Error?t.message:String(t)}`),w++}else w++;break}case"remote-manifest":try{const t=g(e.installPath),a={...e,skill:t.skill,transport:t.transport,runtime:t.runtime,...t.skillBody.length>0?{skillBody:t.skillBody}:{}};if(!s.replace(e.skill.name,a)){y.warn(`${e.skill.name} 已从注册表中移除,跳过元数据刷新`),w++;break}await E(a)?h++:w++}catch(t){y.warn(`${e.skill.name} manifest 刷新失败: ${t instanceof Error?t.message:String(t)}`),w++}break;case"local-path":{const t="core-managed"===e.runtime.ownership&&void 0!==l(n.dataDir,e.skill.name);try{const a=g(e.installPath),r={...e,skill:a.skill,transport:a.transport,runtime:a.runtime,...a.skillBody.length>0?{skillBody:a.skillBody}:{}};s.replace(e.skill.name,r)?(await D(r,t,n.dataDir),h++):(y.warn(`${e.skill.name} 已从注册表中移除,跳过元数据刷新`),w++)}catch(t){s.updateStatus(e.skill.name,"error"),y.warn(`${e.skill.name} 本地 metadata 刷新或重启失败: ${t instanceof Error?t.message:String(t)}`),w++}break}}}y.info("");if(w+(u?1:0)>0){process.exitCode=1;const t=u?"roll 更新失败":p?"roll ✓":"roll 无更新";return void y.warn(`更新完成但有失败:${t}${w>0?`,${w} 个 Agent 更新失败`:""}${h>0?`,${h} 个 Agent 已更新`:""}`)}p||h>0?y.success(`更新完成:${p?"roll ✓":"roll 无更新"}${h>0?`,${h} 个 Agent 已更新`:""}`):y.success("一切都已是最新版本")}});export function detectInstallCommand(t){const r=e(t,"package.json");if(a(r)){const t=M(r);if(t)return{command:t,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(a(e(t,r)))return{command:s,args:["install"]}}async function D(t,e,a){if(!e||"core-managed"!==t.runtime.ownership)return;await m(a,t.skill.name);let r=!1;try{c(t,a),r=!0,await p(t,{startupTimeoutMs:15e3,probeTimeoutMs:2e3})}catch(e){throw r&&await m(a,t.skill.name).catch(()=>{}),e}}function M(t){try{const e=JSON.parse(r(t,"utf-8"));if("string"!=typeof e.packageManager||0===e.packageManager.length)return;const a=e.packageManager.split("@",1)[0];return"pnpm"===a||"npm"===a||"yarn"===a||"bun"===a?a:void 0}catch{return}}
|
|
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,23 +1,27 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
export declare const PUBLISHED_PACKAGE_UPDATE_STATUSES: readonly ["up-to-date", "update-available", "pinned-behind", "unsupported-spec", "unknown"];
|
|
2
|
+
export type PublishedPackageUpdateStatus = (typeof PUBLISHED_PACKAGE_UPDATE_STATUSES)[number];
|
|
3
|
+
interface PackageVersionQueryOptions {
|
|
3
4
|
readonly forceRefresh?: boolean;
|
|
4
|
-
/** 是否允许联网查询 npm registry */
|
|
5
5
|
readonly allowNetwork?: boolean;
|
|
6
6
|
}
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
export interface PublishedPackageUpdateInfo {
|
|
8
|
+
readonly packageName: string;
|
|
9
|
+
readonly currentVersion?: string;
|
|
10
|
+
readonly latestVersion?: string;
|
|
11
|
+
readonly status: PublishedPackageUpdateStatus;
|
|
12
|
+
}
|
|
11
13
|
export interface UpdateInfo {
|
|
12
14
|
readonly current: string;
|
|
13
15
|
readonly latest: string;
|
|
14
16
|
readonly hasUpdate: boolean;
|
|
15
17
|
}
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
18
|
+
export declare function fetchLatestPublishedVersion(packageName: string, options?: PackageVersionQueryOptions): Promise<string | undefined>;
|
|
19
|
+
export declare function getCurrentVersion(): string;
|
|
20
|
+
export declare function isNewerVersion(latest: string, current: string): boolean;
|
|
21
|
+
export declare function checkPublishedPackageUpdate(input: {
|
|
22
|
+
packageName: string;
|
|
23
|
+
packageSpec: string;
|
|
24
|
+
currentVersion?: string;
|
|
25
|
+
}, options?: PackageVersionQueryOptions): Promise<PublishedPackageUpdateInfo>;
|
|
26
|
+
export declare function checkForUpdate(options?: PackageVersionQueryOptions): Promise<UpdateInfo>;
|
|
23
27
|
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{readFileSync as t,writeFileSync as e,mkdirSync as
|
|
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)}}
|
package/dist/llm/providers.d.ts
CHANGED
|
@@ -1,7 +1,21 @@
|
|
|
1
|
-
import type { LanguageModelV3 } from "@ai-sdk/provider";
|
|
1
|
+
import type { LanguageModelV3, SharedV3ProviderOptions } from "@ai-sdk/provider";
|
|
2
2
|
/**
|
|
3
3
|
* 根据 provider 名称创建 AI SDK LanguageModel 实例。
|
|
4
4
|
*
|
|
5
5
|
* 支持: anthropic, openai, deepseek, qwen
|
|
6
6
|
*/
|
|
7
7
|
export declare function createProviderModel(providerName: string, modelName: string, apiKey: string, baseURL?: string): LanguageModelV3;
|
|
8
|
+
/** generateText 调用目的 */
|
|
9
|
+
export type LLMCallPurpose = "structured-output" | "text" | "sampling";
|
|
10
|
+
/** resolveLLMCall 的返回值 */
|
|
11
|
+
export interface ResolvedLLMCall {
|
|
12
|
+
readonly model: LanguageModelV3;
|
|
13
|
+
readonly providerOptions?: SharedV3ProviderOptions;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* 按 provider + 调用目的解析 generateText 的完整调用上下文。
|
|
17
|
+
*
|
|
18
|
+
* structured-output 场景下,对 qwen provider 自动注入 enableThinking: false,
|
|
19
|
+
* 因为阿里云 thinking mode 不支持 structured output。
|
|
20
|
+
*/
|
|
21
|
+
export declare function resolveLLMCall(providerName: string, modelName: string, apiKey: string, purpose: LLMCallPurpose, baseURL?: string): ResolvedLLMCall;
|
package/dist/llm/providers.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createAlibaba as e}from"@ai-sdk/alibaba";import{createAnthropic as
|
|
1
|
+
import{createAlibaba as e}from"@ai-sdk/alibaba";import{createAnthropic as o}from"@ai-sdk/anthropic";import{createOpenAI as a}from"@ai-sdk/openai";import{createDeepSeek as i}from"@ai-sdk/deepseek";const r="https://dashscope.aliyuncs.com/compatible-mode/v1",p={anthropic:(e,{apiKey:a,baseURL:i})=>o({apiKey:a,...i?{baseURL:i}:{}})(e),openai:(e,{apiKey:o,baseURL:i})=>a({apiKey:o,...i?{baseURL:i}:{}})(e),deepseek:(e,{apiKey:o,baseURL:a})=>i({apiKey:o,...a?{baseURL:a}:{}})(e),qwen:(o,{apiKey:a,baseURL:i})=>e({apiKey:a,baseURL:i??r})(o)};export function createProviderModel(e,o,a,i){const r=p[e];if(!r){const o=Object.keys(p).join(", ");throw new Error(`Unknown LLM provider "${e}". Supported: ${o}`)}return r(o,{apiKey:a,baseURL:i})}export function resolveLLMCall(e,o,a,i,r){const p=createProviderModel(e,o,a,r);return"structured-output"===i&&"qwen"===e?{model:p,providerOptions:{alibaba:{enableThinking:!1}}}:{model:p}}
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
import type { AgentSource, AgentSourceType, RegisteredAgent } from "../types/agent.ts";
|
|
2
|
+
export interface InstalledPackageManifestInfo {
|
|
3
|
+
readonly name?: string;
|
|
4
|
+
readonly version?: string;
|
|
5
|
+
}
|
|
2
6
|
/** 推断 Agent 来源类型,兼容旧 store 数据。 */
|
|
3
7
|
export declare function inferAgentSourceType(agent: RegisteredAgent): AgentSourceType;
|
|
4
8
|
/** 适合展示到 CLI 的来源标签。 */
|
|
@@ -13,3 +17,4 @@ export declare function sanitizeInstallId(input: string): string;
|
|
|
13
17
|
export declare function parsePackageName(packageSpec: string): string;
|
|
14
18
|
/** 计算 `npm install --prefix` 后包在 node_modules 中的根目录。 */
|
|
15
19
|
export declare function resolveInstalledPackageRoot(installDir: string, packageName: string): string;
|
|
20
|
+
export declare function readInstalledPackageManifest(packageRoot: string): InstalledPackageManifestInfo | undefined;
|
package/dist/registry/source.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{existsSync as t,readFileSync as e,readdirSync as n}from"node:fs";import{resolve as r}from"node:path";const o="SKILL.md";export function inferAgentSourceType(t){return t.source?t.source.type:inferAgentSourceFromInstallPath(t.installPath,t.transport)?.type??"local-path"}export function formatAgentSourceType(t){switch(t){case"git":return"git";case"installed-package":return"installed";case"remote-manifest":return"remote";case"local-path":return"local-path"}}export function getAgentLocation(t){return"streamable-http"===t.transport.type?t.transport.endpoint:t.installPath}export function inferAgentSourceFromInstallPath(e,n){if(t(r(e,".git"))){const t=i(e);return t?{type:"git",url:t}:{type:"git"}}return t(r(e,o))?{type:"local-path",path:e}:"streamable-http"===n.type?{type:"remote-manifest",endpoint:n.endpoint}:void 0}function i(n){const o=r(n,".git","config");if(t(o))try{const t=e(o,"utf-8").match(/\[remote "origin"\]([\s\S]*?)(?:\n\[|$)/),n=t?.[1]?.match(/^\s*url\s*=\s*(.+)$/m);return n?.[1]?.trim()}catch{return}}export function sanitizeInstallId(t){const e=t.trim().replace(/^@/,"").replace(/[^a-zA-Z0-9._-]+/g,"-").replace(/^-+|-+$/g,"").toLowerCase();return e.length>0?e:"agent"}export function parsePackageName(t){if(t.startsWith("@")){const e=t.indexOf("/");if(-1===e)return t;const n=t.indexOf("@",e+1);return-1===n?t:t.slice(0,n)}const e=t.indexOf("@");return-1===e?t:t.slice(0,e)}export function resolveInstalledPackageRoot(e,n){const o=r(e,"node_modules",...n.split("/"));if(t(o))return o;const i=s(e);if(i){const n=r(e,"node_modules",...i.split("/"));if(t(n))return n}return
|
|
1
|
+
import{existsSync as t,readFileSync as e,readdirSync as n}from"node:fs";import{resolve as r}from"node:path";const o="SKILL.md";export function inferAgentSourceType(t){return t.source?t.source.type:inferAgentSourceFromInstallPath(t.installPath,t.transport)?.type??"local-path"}export function formatAgentSourceType(t){switch(t){case"git":return"git";case"installed-package":return"installed";case"remote-manifest":return"remote";case"local-path":return"local-path"}}export function getAgentLocation(t){return"streamable-http"===t.transport.type?t.transport.endpoint:t.installPath}export function inferAgentSourceFromInstallPath(e,n){if(t(r(e,".git"))){const t=i(e);return t?{type:"git",url:t}:{type:"git"}}return t(r(e,o))?{type:"local-path",path:e}:"streamable-http"===n.type?{type:"remote-manifest",endpoint:n.endpoint}:void 0}function i(n){const o=r(n,".git","config");if(t(o))try{const t=e(o,"utf-8").match(/\[remote "origin"\]([\s\S]*?)(?:\n\[|$)/),n=t?.[1]?.match(/^\s*url\s*=\s*(.+)$/m);return n?.[1]?.trim()}catch{return}}export function sanitizeInstallId(t){const e=t.trim().replace(/^@/,"").replace(/[^a-zA-Z0-9._-]+/g,"-").replace(/^-+|-+$/g,"").toLowerCase();return e.length>0?e:"agent"}export function parsePackageName(t){if(t.startsWith("@")){const e=t.indexOf("/");if(-1===e)return t;const n=t.indexOf("@",e+1);return-1===n?t:t.slice(0,n)}const e=t.indexOf("@");return-1===e?t:t.slice(0,e)}export function resolveInstalledPackageRoot(e,n){const o=r(e,"node_modules",...n.split("/"));if(t(o))return o;const i=s(e);if(i){const n=r(e,"node_modules",...i.split("/"));if(t(n))return n}return a(e)??o}export function readInstalledPackageManifest(n){const o=r(n,"package.json");if(t(o))try{const t=JSON.parse(e(o,"utf-8")),n="string"==typeof t.name&&t.name.length>0?t.name:void 0,r="string"==typeof t.version&&t.version.length>0?t.version:void 0;if(!n&&!r)return;return{...n?{name:n}:{},...r?{version:r}:{}}}catch{return}}function s(n){const o=r(n,"package.json");if(t(o))try{const t=JSON.parse(e(o,"utf-8")),n=Object.keys(t.dependencies??{});return 1===n.length?n[0]:void 0}catch{return}}function a(e){const n=r(e,"node_modules");if(!t(n))return;const i=c(n).filter(e=>t(r(e,"package.json"))&&t(r(e,o)));return 1===i.length?i[0]:void 0}function c(t){const e=[];for(const o of n(t,{withFileTypes:!0})){if(!o.isDirectory()||".bin"===o.name)continue;const i=r(t,o.name);if(o.name.startsWith("@"))for(const t of n(i,{withFileTypes:!0}))t.isDirectory()&&e.push(r(i,t.name));else e.push(i)}return e}
|
package/dist/registry/store.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{readFileSync as t,writeFileSync as e,existsSync as n,mkdirSync as r}from"node:fs";import{resolve as i,dirname as s}from"node:path";import{inferAgentSourceFromInstallPath as a}from"./source.js";import{AGENT_STATUSES as o,AGENT_STORE_SCHEMA_VERSION as u,createDefaultRuntimeForTransport as c}from"../types/agent.js";const l="agents.json";export class AgentStore{storePath;constructor(t){this.storePath=i(t,l)}list(){return this.load().agents}findByName(t){return this.list().find(e=>e.skill.name===t)}add(t){const e=[...this.list()];if(-1!==e.findIndex(e=>e.skill.name===t.skill.name))throw new Error(`Agent "${t.skill.name}" is already registered`);e.push(t),this.save(e)}remove(t){const e=this.list(),n=e.filter(e=>e.skill.name!==t);return n.length!==e.length&&(this.save([...n]),!0)}replace(t,e){const n=[...this.list()],r=n.findIndex(e=>e.skill.name===t);if(-1===r)return!1;if(-1!==n.findIndex((t,n)=>n!==r&&t.skill.name===e.skill.name))throw new Error(`Agent "${e.skill.name}" is already registered`);return n[r]=e,this.save(n),!0}updateStatus(t,e){const n=this.list().map(n=>n.skill.name===t?{...n,status:e}:n);this.save([...n])}save(t){const i=s(this.storePath);n(i)||r(i,{recursive:!0});const a={schemaVersion:u,agents:t};e(this.storePath,JSON.stringify(a,null,2),"utf-8")}load(){if(!n(this.storePath))return p();let e;try{const n=t(this.storePath,"utf-8");e=JSON.parse(n)}catch{return p()}return f(e)}}function p(){return{schemaVersion:u,agents:[]}}function f(t){if(Array.isArray(t))return{schemaVersion:u,agents:t.flatMap(t=>{const e=d(t);return e?[e]:[]})};if(!
|
|
1
|
+
import{readFileSync as t,writeFileSync as e,existsSync as n,mkdirSync as r}from"node:fs";import{resolve as i,dirname as s}from"node:path";import{inferAgentSourceFromInstallPath as a}from"./source.js";import{AGENT_STATUSES as o,AGENT_STORE_SCHEMA_VERSION as u,createDefaultRuntimeForTransport as c}from"../types/agent.js";const l="agents.json";export class AgentStore{storePath;constructor(t){this.storePath=i(t,l)}list(){return this.load().agents}findByName(t){return this.list().find(e=>e.skill.name===t)}add(t){const e=[...this.list()];if(-1!==e.findIndex(e=>e.skill.name===t.skill.name))throw new Error(`Agent "${t.skill.name}" is already registered`);e.push(t),this.save(e)}remove(t){const e=this.list(),n=e.filter(e=>e.skill.name!==t);return n.length!==e.length&&(this.save([...n]),!0)}replace(t,e){const n=[...this.list()],r=n.findIndex(e=>e.skill.name===t);if(-1===r)return!1;if(-1!==n.findIndex((t,n)=>n!==r&&t.skill.name===e.skill.name))throw new Error(`Agent "${e.skill.name}" is already registered`);return n[r]=e,this.save(n),!0}updateStatus(t,e){const n=this.list().map(n=>n.skill.name===t?{...n,status:e}:n);this.save([...n])}save(t){const i=s(this.storePath);n(i)||r(i,{recursive:!0});const a={schemaVersion:u,agents:t};e(this.storePath,JSON.stringify(a,null,2),"utf-8")}load(){if(!n(this.storePath))return p();let e;try{const n=t(this.storePath,"utf-8");e=JSON.parse(n)}catch{return p()}return f(e)}}function p(){return{schemaVersion:u,agents:[]}}function f(t){if(Array.isArray(t))return{schemaVersion:u,agents:t.flatMap(t=>{const e=d(t);return e?[e]:[]})};if(!j(t))return p();const e=t.agents;return Array.isArray(e)?{schemaVersion:t.schemaVersion===u?u:2,agents:e.flatMap(t=>{const e=d(t);return e?[e]:[]})}:p()}function d(t){if(!j(t))return;const e=m(t.skill),n=k(t.transport),r=x(t.installPath);if(!e||!n||!r)return;const i=v(t.runtime,n),s=S(t.source,r,n),a=x(t.registeredAt)??new Date(0).toISOString(),o=N(t.status),u=x(t.skillBody);return{skill:e,transport:n,runtime:i,installPath:r,registeredAt:a,status:o,...s?{source:s}:{},...u?{skillBody:u}:{}}}function m(t){if(!j(t))return;const e=x(t.name),n=x(t.description);if(!e||!n)return;const r=x(t.license),i=x(t.compatibility),s=g(t.env);return{name:e,description:n,...r?{license:r}:{},...i?{compatibility:i}:{},metadata:h(t.metadata),...s?{env:s}:{}}}function h(t){if(!j(t))return{};const e={};for(const[n,r]of Object.entries(t))e[n]=String(r);return e}function g(t){if(!j(t))return;const e=y(t.required),n=y(t.optional);return e||n?{...e?{required:e}:{},...n?{optional:n}:{}}:void 0}function y(t){if(!Array.isArray(t))return;const e=t.flatMap(t=>{if(!j(t))return[];const e=x(t.name);if(!e)return[];const n=x(t.purpose),r=x(t.example),i=x(t.default);return[{name:e,...n?{purpose:n}:{},...r?{example:r}:{},...i?{default:i}:{}}]});return e.length>0?e:void 0}function k(t){if(!j(t))return;const e=x(t.type);if("stdio"===e){const n=x(t.command);if(!n)return;const r=P(t.args);return r?{type:e,command:n,args:r}:{type:e,command:n}}if("streamable-http"===e){const n=x(t.endpoint);if(!n)return;return{type:e,endpoint:n}}}function v(t,e){if(!j(t))return c(e);const n=x(t.ownership);if("on-demand"===n)return{ownership:n};if("external-managed"===n)return{ownership:n};if("core-managed"===n&&"streamable-http"===e.type){const e=w(t.start),r=A(t.endpoint);if(e&&r){const i=b(t.setup);return i?{ownership:n,start:e,endpoint:r,setup:i}:{ownership:n,start:e,endpoint:r}}}return c(e)}function w(t){if(!j(t))return;const e=x(t.command);if(!e)return;const n=P(t.args);return n?{command:e,args:n}:{command:e}}function A(t){if(!j(t))return;const e=x(t.path),n="number"==typeof t.port?t.port:void 0;return e&&void 0!==n&&Number.isInteger(n)?{path:e,port:n}:void 0}function b(t){if(!j(t))return;const e=t.playwright;if(!j(e))return;const n=P(e.browsers);return n&&0!==n.length?{playwright:{browsers:n}}:void 0}function S(t,e,n){if(!j(t))return a(e,n);const r=x(t.type);if(!r)return a(e,n);switch(r){case"git":return D(x(t.url));case"local-path":return{type:r,path:x(t.path)??e};case"installed-package":{const e=x(t.packageName),n=x(t.packageSpec),i=x(t.installDir),s=x(t.installedVersion);if(!e||!n||!i)return;return{type:r,packageName:e,packageSpec:n,installDir:i,...s?{installedVersion:s}:{}}}case"remote-manifest":{const e=x(t.endpoint)??("streamable-http"===n.type?n.endpoint:void 0);return e?{type:r,endpoint:e}:void 0}default:return V(r,t,e,n)}}function V(t,e,n,r){switch(t){case"git":return D(x(e.url));case"local":return{type:"local-path",path:x(e.path)??n};case"installed":{const t=x(e.packageName),n=x(e.packageSpec),r=x(e.installDir),i=x(e.installedVersion);if(!t||!n||!r)return;return{type:"installed-package",packageName:t,packageSpec:n,installDir:r,...i?{installedVersion:i}:{}}}case"remote":{const t=a(n,r);if(t&&"remote-manifest"!==t.type)return t;const i=x(e.endpoint)??("streamable-http"===r.type?r.endpoint:void 0);return i?{type:"remote-manifest",endpoint:i}:void 0}}}function N(t){return"string"==typeof t&&o.includes(t)?t:"idle"}function P(t){if(!Array.isArray(t))return;return t.filter(t=>"string"==typeof t)}function x(t){return"string"==typeof t&&t.length>0?t:void 0}function j(t){return"object"==typeof t&&null!==t}function D(t){return t?{type:"git",url:t}:{type:"git"}}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import type { LanguageModelV3 } from "@ai-sdk/provider";
|
|
1
|
+
import type { LanguageModelV3, SharedV3ProviderOptions } from "@ai-sdk/provider";
|
|
2
2
|
import type { RegisteredAgent } from "../types/agent.ts";
|
|
3
3
|
import type { RouteSelection } from "../types/router.ts";
|
|
4
4
|
/**
|
|
5
5
|
* 使用 LLM 从用户自然语言意图中选择最合适的 Agent + Tool。
|
|
6
6
|
*
|
|
7
7
|
* 两步走:
|
|
8
|
-
* 1.
|
|
9
|
-
* 2.
|
|
8
|
+
* 1. 优先尝试 structured output
|
|
9
|
+
* 2. 仅当模型未遵循 schema 时,降级为纯文本 JSON fallback
|
|
10
10
|
*/
|
|
11
|
-
export declare function routeWithLLM(message: string, agents: ReadonlyArray<RegisteredAgent>, model: LanguageModelV3): Promise<RouteSelection>;
|
|
11
|
+
export declare function routeWithLLM(message: string, agents: ReadonlyArray<RegisteredAgent>, model: LanguageModelV3, structuredOutputProviderOptions?: SharedV3ProviderOptions): Promise<RouteSelection>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{generateText as
|
|
1
|
+
import{generateText as t,NoObjectGeneratedError as e,Output as n}from"ai";import{z as o}from"zod";import{asConfidence as r}from"../types/router.js";const c=o.object({agentName:o.string().describe("选择的 Agent 名称"),toolName:o.string().describe("选择的 Tool 名称"),confidence:o.number().describe("决策置信度 0-1")});function i(t){return 0===t.length?"当前没有已注册的 Agent。":t.map(t=>{const e=t.skill.name,n=t.skill.description,o=t.skillBody?`\n 能力详情:\n${t.skillBody}`:"";return`Agent: ${e}\n 描述: ${n}\n 状态: ${t.status}${o}`}).join("\n\n")}function a(t){const e=t.trim(),n=[e],o=e.match(/```(?:json)?\s*([\s\S]*?)```/i);o?.[1]&&n.push(o[1].trim());const r=e.indexOf("{"),i=e.lastIndexOf("}");r>=0&&i>r&&n.push(e.slice(r,i+1));for(const t of n)try{return c.parse(JSON.parse(t))}catch{}throw new Error("LLM router: text fallback did not produce valid route JSON")}function s(t){return[t,"","只输出一个 JSON object。","字段必须是 agentName(string)、toolName(string)、confidence(number 0-1)。","不要输出 Markdown 代码块。","不要解释,不要补充额外文本。"].join("\n")}function u(t){return t instanceof Error?t.message:String(t)}export async function routeWithLLM(o,m,l,d){const f=`你是 Roll Agent 的智能路由器。根据用户的自然语言请求,从已注册的 Agent 中选择最合适的 Agent 和 Tool。以 JSON 格式返回结果。\n\n已注册的 Agent 列表:\n${i(m)}\n\n规则:\n- 选择与用户意图最匹配的 Agent 和 Tool\n- 这一阶段只负责选择 Agent 和 Tool,不要推测或生成 Tool 参数\n- confidence 字段表示你对这个匹配的把握程度(0-1)\n- 如果没有合适的 Agent 匹配,confidence 应该很低(< 0.3)`;try{const{output:e}=await t({model:l,output:n.object({schema:c}),system:f,prompt:o,...d?{providerOptions:d}:{}});if(!e)throw new Error("structured output returned null");return{agentName:e.agentName,toolName:e.toolName,confidence:r(Math.min(1,Math.max(0,e.confidence)))}}catch(n){if(!e.isInstance(n))throw n;try{const e=a((await t({model:l,system:f,prompt:s(o)})).text);return{agentName:e.agentName,toolName:e.toolName,confidence:r(Math.min(1,Math.max(0,e.confidence)))}}catch(t){throw new Error(`LLM router: failed to produce route selection (structured output: ${u(n)}; text fallback: ${u(t)})`,{cause:t})}}}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import type { LanguageModelV3 } from "@ai-sdk/provider";
|
|
1
|
+
import type { LanguageModelV3, SharedV3ProviderOptions } from "@ai-sdk/provider";
|
|
2
2
|
import type { AgentTool } from "../types/agent.ts";
|
|
3
|
-
export declare function extractToolInput(message: string, tool: Pick<AgentTool, "name" | "description" | "inputSchema">, model: LanguageModelV3): Promise<Readonly<Record<string, unknown>>>;
|
|
3
|
+
export declare function extractToolInput(message: string, tool: Pick<AgentTool, "name" | "description" | "inputSchema">, model: LanguageModelV3, structuredOutputProviderOptions?: SharedV3ProviderOptions): Promise<Readonly<Record<string, unknown>>>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{generateText as t,jsonSchema as o,Output as r}from"ai";import{isPlainObject as n}from"./schema.js";import{createExtractionSchema as e,normalizeExtractedToolInput as c}from"./extraction-schema.js";function a(t,o,r){return["
|
|
1
|
+
import{generateText as t,jsonSchema as o,Output as r}from"ai";import{isPlainObject as n}from"./schema.js";import{createExtractionSchema as e,normalizeExtractedToolInput as c}from"./extraction-schema.js";function a(t,o,r){return["你要为一个工具调用提取参数。以 JSON 格式返回结果。","这里只提取能从当前消息中明确得到的字段,不要发明新字段。","如果用户没有提供某个值,不要猜测,也不要填充默认值。","如果用户明确提到了某个名字、品牌、城市、数字或其他实体,并且 schema 中有最匹配的字段,即使该字段是可选的,也应当提取出来。","只有在用户消息里确实没有出现对应值时,才省略该可选字段。","可选字段用省略表达,不要输出 null。","系统会在后续按原始 tool 契约继续校验;这里只输出可提取参数的 JSON object。","",`[Tool] ${o.name}`,o.description?`[Description] ${o.description}`:"","[Extraction Schema]",JSON.stringify(r,null,2),"","[User Message]",t].filter(Boolean).join("\n")}function i(t,o,r){return[a(t,o,r),"","只输出一个 JSON object。","不要输出 Markdown 代码块。","不要解释,不要补充额外文本。"].join("\n")}function s(t){return t instanceof Error?t.message:String(t)}function u(t){const o=t.trim(),r=new Set;o&&r.add(o);const n=o.match(/```(?:json)?\s*([\s\S]*?)```/i),e=n?.[1]?.trim();e&&r.add(e);const c=o.indexOf("{"),a=o.lastIndexOf("}");return c>=0&&a>c&&r.add(o.slice(c,a+1).trim()),[...r]}function m(t){for(const o of u(t))try{const t=JSON.parse(o);if(n(t))return t}catch{}throw new Error("Text fallback did not produce a valid JSON object")}export async function extractToolInput(n,u,f,l){const p=e(u.inputSchema);try{const{output:e}=await t({model:f,output:r.object({schema:o(p)}),prompt:a(n,u,p),...l?{providerOptions:l}:{}});if(!e)throw new Error(`Failed to extract arguments for tool "${u.name}"`);return c(u.inputSchema,e)}catch(o){try{const o=await t({model:f,prompt:i(n,u,p)});return c(u.inputSchema,m(o.text))}catch(t){throw new Error(`Failed to extract arguments for tool "${u.name}" (structured output: ${s(o)}; text fallback: ${s(t)})`,{cause:t})}}}
|
package/dist/types/agent.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@roll-agent/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -30,13 +30,13 @@
|
|
|
30
30
|
"node": ">=22.6.0"
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"@ai-sdk/alibaba": "^1.0.
|
|
34
|
-
"@ai-sdk/anthropic": "^3.0.
|
|
35
|
-
"@ai-sdk/deepseek": "^2.0.
|
|
36
|
-
"@ai-sdk/openai": "^3.0.
|
|
33
|
+
"@ai-sdk/alibaba": "^1.0.17",
|
|
34
|
+
"@ai-sdk/anthropic": "^3.0.68",
|
|
35
|
+
"@ai-sdk/deepseek": "^2.0.29",
|
|
36
|
+
"@ai-sdk/openai": "^3.0.52",
|
|
37
37
|
"@ai-sdk/provider": "^3.0.8",
|
|
38
38
|
"@modelcontextprotocol/sdk": "^1.12.0",
|
|
39
|
-
"ai": "^6.0.
|
|
39
|
+
"ai": "^6.0.154",
|
|
40
40
|
"chalk": "^5.4.0",
|
|
41
41
|
"citty": "^0.1.6",
|
|
42
42
|
"cli-table3": "^0.6.5",
|