@roll-agent/core 0.3.1 → 0.3.3
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-add.d.ts +0 -1
- package/dist/cli/commands/agent-add.js +1 -166
- package/dist/cli/commands/agent-health.d.ts +0 -1
- package/dist/cli/commands/agent-health.js +1 -131
- package/dist/cli/commands/agent-info.d.ts +0 -1
- package/dist/cli/commands/agent-info.js +1 -57
- package/dist/cli/commands/agent-install.d.ts +0 -1
- package/dist/cli/commands/agent-install.js +1 -173
- package/dist/cli/commands/agent-list.d.ts +0 -1
- package/dist/cli/commands/agent-list.js +1 -39
- package/dist/cli/commands/agent-remove.d.ts +0 -1
- package/dist/cli/commands/agent-remove.js +1 -64
- package/dist/cli/commands/agent-start.d.ts +0 -1
- package/dist/cli/commands/agent-start.js +1 -71
- package/dist/cli/commands/agent-stop.d.ts +0 -1
- package/dist/cli/commands/agent-stop.js +1 -50
- package/dist/cli/commands/agent.d.ts +0 -1
- package/dist/cli/commands/agent.js +1 -28
- package/dist/cli/commands/ask.d.ts +0 -1
- package/dist/cli/commands/ask.js +1 -206
- package/dist/cli/commands/chat.d.ts +0 -1
- package/dist/cli/commands/chat.js +1 -33
- package/dist/cli/commands/config.d.ts +0 -1
- package/dist/cli/commands/config.js +1 -234
- package/dist/cli/commands/doctor.d.ts +0 -1
- package/dist/cli/commands/doctor.js +1 -186
- package/dist/cli/commands/run.d.ts +0 -1
- package/dist/cli/commands/run.js +1 -200
- package/dist/cli/commands/update.d.ts +0 -1
- package/dist/cli/commands/update.js +1 -477
- package/dist/cli/index.d.ts +0 -1
- package/dist/cli/index.js +1 -45
- package/dist/cli/utils/output.d.ts +0 -1
- package/dist/cli/utils/output.js +1 -52
- package/dist/cli/utils/prompt.d.ts +0 -1
- package/dist/cli/utils/prompt.js +1 -4
- package/dist/cli/utils/update-checker.d.ts +0 -1
- package/dist/cli/utils/update-checker.js +1 -145
- package/dist/config/defaults.d.ts +0 -1
- package/dist/config/defaults.js +1 -15
- package/dist/config/helpers.d.ts +0 -1
- package/dist/config/helpers.js +1 -70
- package/dist/config/index.d.ts +0 -1
- package/dist/config/index.js +1 -5
- package/dist/config/loader.d.ts +0 -1
- package/dist/config/loader.js +1 -289
- package/dist/config/migration.d.ts +0 -1
- package/dist/config/migration.js +1 -260
- package/dist/config/schema.d.ts +0 -1
- package/dist/config/schema.js +1 -25
- package/dist/llm/engine.d.ts +0 -1
- package/dist/llm/engine.js +1 -31
- package/dist/llm/index.d.ts +0 -1
- package/dist/llm/index.js +1 -3
- package/dist/llm/providers.d.ts +0 -1
- package/dist/llm/providers.js +1 -39
- package/dist/mcp/client-manager.d.ts +0 -1
- package/dist/mcp/client-manager.js +1 -82
- package/dist/mcp/index.d.ts +0 -1
- package/dist/mcp/index.js +1 -2
- package/dist/mcp/sampling-handler.d.ts +0 -1
- package/dist/mcp/sampling-handler.js +1 -58
- package/dist/registry/dev-spawn.d.ts +7 -0
- package/dist/registry/dev-spawn.js +1 -0
- package/dist/registry/discovery.d.ts +0 -1
- package/dist/registry/discovery.js +1 -313
- package/dist/registry/health-check.d.ts +0 -1
- package/dist/registry/health-check.js +1 -65
- package/dist/registry/index.d.ts +0 -1
- package/dist/registry/index.js +1 -5
- package/dist/registry/manifest.d.ts +0 -1
- package/dist/registry/manifest.js +1 -24
- package/dist/registry/process-manager.d.ts +0 -1
- package/dist/registry/process-manager.js +1 -204
- package/dist/registry/runtime-setup.d.ts +0 -1
- package/dist/registry/runtime-setup.js +1 -67
- package/dist/registry/source.d.ts +0 -1
- package/dist/registry/source.js +1 -139
- package/dist/registry/store.d.ts +0 -1
- package/dist/registry/store.js +1 -372
- package/dist/router/declarative.d.ts +0 -1
- package/dist/router/declarative.js +1 -9
- package/dist/router/index.d.ts +0 -1
- package/dist/router/index.js +1 -3
- package/dist/router/llm-router.d.ts +0 -1
- package/dist/router/llm-router.js +1 -58
- package/dist/tool-runtime/argument-extractor.d.ts +0 -1
- package/dist/tool-runtime/argument-extractor.js +1 -97
- package/dist/tool-runtime/extraction-schema.d.ts +0 -1
- package/dist/tool-runtime/extraction-schema.js +1 -220
- package/dist/tool-runtime/messages.d.ts +0 -1
- package/dist/tool-runtime/messages.js +1 -26
- package/dist/tool-runtime/preflight.d.ts +0 -1
- package/dist/tool-runtime/preflight.js +1 -130
- package/dist/tool-runtime/schema.d.ts +0 -1
- package/dist/tool-runtime/schema.js +1 -64
- package/dist/types/agent.d.ts +0 -1
- package/dist/types/agent.js +1 -22
- package/dist/types/ask.d.ts +0 -1
- package/dist/types/ask.js +1 -15
- package/dist/types/chat.d.ts +0 -1
- package/dist/types/chat.js +1 -9
- package/dist/types/config.d.ts +0 -1
- package/dist/types/config.js +1 -2
- package/dist/types/mcp.d.ts +0 -1
- package/dist/types/mcp.js +1 -2
- package/dist/types/router.d.ts +0 -1
- package/dist/types/router.js +1 -8
- package/package.json +2 -2
- package/dist/cli/commands/agent-add.d.ts.map +0 -1
- package/dist/cli/commands/agent-add.js.map +0 -1
- package/dist/cli/commands/agent-health.d.ts.map +0 -1
- package/dist/cli/commands/agent-health.js.map +0 -1
- package/dist/cli/commands/agent-info.d.ts.map +0 -1
- package/dist/cli/commands/agent-info.js.map +0 -1
- package/dist/cli/commands/agent-install.d.ts.map +0 -1
- package/dist/cli/commands/agent-install.js.map +0 -1
- package/dist/cli/commands/agent-list.d.ts.map +0 -1
- package/dist/cli/commands/agent-list.js.map +0 -1
- package/dist/cli/commands/agent-remove.d.ts.map +0 -1
- package/dist/cli/commands/agent-remove.js.map +0 -1
- package/dist/cli/commands/agent-start.d.ts.map +0 -1
- package/dist/cli/commands/agent-start.js.map +0 -1
- package/dist/cli/commands/agent-stop.d.ts.map +0 -1
- package/dist/cli/commands/agent-stop.js.map +0 -1
- package/dist/cli/commands/agent.d.ts.map +0 -1
- package/dist/cli/commands/agent.js.map +0 -1
- package/dist/cli/commands/ask.d.ts.map +0 -1
- package/dist/cli/commands/ask.js.map +0 -1
- package/dist/cli/commands/chat.d.ts.map +0 -1
- package/dist/cli/commands/chat.js.map +0 -1
- package/dist/cli/commands/config.d.ts.map +0 -1
- package/dist/cli/commands/config.js.map +0 -1
- package/dist/cli/commands/doctor.d.ts.map +0 -1
- package/dist/cli/commands/doctor.js.map +0 -1
- package/dist/cli/commands/run.d.ts.map +0 -1
- package/dist/cli/commands/run.js.map +0 -1
- package/dist/cli/commands/update.d.ts.map +0 -1
- package/dist/cli/commands/update.js.map +0 -1
- package/dist/cli/index.d.ts.map +0 -1
- package/dist/cli/index.js.map +0 -1
- package/dist/cli/utils/output.d.ts.map +0 -1
- package/dist/cli/utils/output.js.map +0 -1
- package/dist/cli/utils/prompt.d.ts.map +0 -1
- package/dist/cli/utils/prompt.js.map +0 -1
- package/dist/cli/utils/update-checker.d.ts.map +0 -1
- package/dist/cli/utils/update-checker.js.map +0 -1
- package/dist/config/defaults.d.ts.map +0 -1
- package/dist/config/defaults.js.map +0 -1
- package/dist/config/helpers.d.ts.map +0 -1
- package/dist/config/helpers.js.map +0 -1
- package/dist/config/index.d.ts.map +0 -1
- package/dist/config/index.js.map +0 -1
- package/dist/config/loader.d.ts.map +0 -1
- package/dist/config/loader.js.map +0 -1
- package/dist/config/migration.d.ts.map +0 -1
- package/dist/config/migration.js.map +0 -1
- package/dist/config/schema.d.ts.map +0 -1
- package/dist/config/schema.js.map +0 -1
- package/dist/llm/engine.d.ts.map +0 -1
- package/dist/llm/engine.js.map +0 -1
- package/dist/llm/index.d.ts.map +0 -1
- package/dist/llm/index.js.map +0 -1
- package/dist/llm/providers.d.ts.map +0 -1
- package/dist/llm/providers.js.map +0 -1
- package/dist/mcp/client-manager.d.ts.map +0 -1
- package/dist/mcp/client-manager.js.map +0 -1
- package/dist/mcp/index.d.ts.map +0 -1
- package/dist/mcp/index.js.map +0 -1
- package/dist/mcp/sampling-handler.d.ts.map +0 -1
- package/dist/mcp/sampling-handler.js.map +0 -1
- package/dist/registry/discovery.d.ts.map +0 -1
- package/dist/registry/discovery.js.map +0 -1
- package/dist/registry/health-check.d.ts.map +0 -1
- package/dist/registry/health-check.js.map +0 -1
- package/dist/registry/index.d.ts.map +0 -1
- package/dist/registry/index.js.map +0 -1
- package/dist/registry/manifest.d.ts.map +0 -1
- package/dist/registry/manifest.js.map +0 -1
- package/dist/registry/process-manager.d.ts.map +0 -1
- package/dist/registry/process-manager.js.map +0 -1
- package/dist/registry/runtime-setup.d.ts.map +0 -1
- package/dist/registry/runtime-setup.js.map +0 -1
- package/dist/registry/source.d.ts.map +0 -1
- package/dist/registry/source.js.map +0 -1
- package/dist/registry/store.d.ts.map +0 -1
- package/dist/registry/store.js.map +0 -1
- package/dist/router/declarative.d.ts.map +0 -1
- package/dist/router/declarative.js.map +0 -1
- package/dist/router/index.d.ts.map +0 -1
- package/dist/router/index.js.map +0 -1
- package/dist/router/llm-router.d.ts.map +0 -1
- package/dist/router/llm-router.js.map +0 -1
- package/dist/tool-runtime/argument-extractor.d.ts.map +0 -1
- package/dist/tool-runtime/argument-extractor.js.map +0 -1
- package/dist/tool-runtime/extraction-schema.d.ts.map +0 -1
- package/dist/tool-runtime/extraction-schema.js.map +0 -1
- package/dist/tool-runtime/messages.d.ts.map +0 -1
- package/dist/tool-runtime/messages.js.map +0 -1
- package/dist/tool-runtime/preflight.d.ts.map +0 -1
- package/dist/tool-runtime/preflight.js.map +0 -1
- package/dist/tool-runtime/schema.d.ts.map +0 -1
- package/dist/tool-runtime/schema.js.map +0 -1
- package/dist/types/agent.d.ts.map +0 -1
- package/dist/types/agent.js.map +0 -1
- package/dist/types/ask.d.ts.map +0 -1
- package/dist/types/ask.js.map +0 -1
- package/dist/types/chat.d.ts.map +0 -1
- package/dist/types/chat.js.map +0 -1
- package/dist/types/config.d.ts.map +0 -1
- package/dist/types/config.js.map +0 -1
- package/dist/types/mcp.d.ts.map +0 -1
- package/dist/types/mcp.js.map +0 -1
- package/dist/types/router.d.ts.map +0 -1
- package/dist/types/router.js.map +0 -1
|
@@ -1,166 +1 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { resolve } from "node:path";
|
|
3
|
-
import { existsSync, mkdirSync } from "node:fs";
|
|
4
|
-
import { execFile } from "node:child_process";
|
|
5
|
-
import { promisify } from "node:util";
|
|
6
|
-
import { inspectAgentEnvRequirements } from "../../config/helpers.js";
|
|
7
|
-
import { loadAgentsConfig } from "../../config/loader.js";
|
|
8
|
-
import { discoverAgent } from "../../registry/discovery.js";
|
|
9
|
-
import { writeRemoteSkillManifest } from "../../registry/manifest.js";
|
|
10
|
-
import { AgentStore } from "../../registry/store.js";
|
|
11
|
-
import { log } from "../utils/output.js";
|
|
12
|
-
const execFileAsync = promisify(execFile);
|
|
13
|
-
/** 判断输入是否为 Git URL */
|
|
14
|
-
function isGitUrl(input) {
|
|
15
|
-
return (input.startsWith("https://") ||
|
|
16
|
-
input.startsWith("http://") ||
|
|
17
|
-
input.startsWith("git@") ||
|
|
18
|
-
input.endsWith(".git"));
|
|
19
|
-
}
|
|
20
|
-
/** 从 Git URL 中提取仓库名作为目录名 */
|
|
21
|
-
function repoNameFromUrl(url) {
|
|
22
|
-
const last = url.split("/").pop() ?? url;
|
|
23
|
-
return last.replace(/\.git$/, "");
|
|
24
|
-
}
|
|
25
|
-
export default defineCommand({
|
|
26
|
-
meta: { description: "注册一个 Agent(本地路径、Git URL 或远程 endpoint)" },
|
|
27
|
-
args: {
|
|
28
|
-
path: { type: "positional", description: "Agent 本地路径或 Git URL", required: false },
|
|
29
|
-
remote: { type: "string", description: "远程 MCP endpoint(需配合 --name/--description)" },
|
|
30
|
-
name: { type: "string", description: "远程 Agent 名称" },
|
|
31
|
-
description: { type: "string", description: "远程 Agent 描述" },
|
|
32
|
-
},
|
|
33
|
-
async run({ args }) {
|
|
34
|
-
const { agentsConfig } = loadAgentsConfig();
|
|
35
|
-
let agentDir;
|
|
36
|
-
if (args.remote) {
|
|
37
|
-
if (!args.name || !args.description) {
|
|
38
|
-
log.error("远程注册需要同时提供 --name 和 --description");
|
|
39
|
-
process.exitCode = 1;
|
|
40
|
-
return;
|
|
41
|
-
}
|
|
42
|
-
agentDir = writeRemoteSkillManifest({
|
|
43
|
-
dataDir: agentsConfig.dataDir,
|
|
44
|
-
name: args.name,
|
|
45
|
-
description: args.description,
|
|
46
|
-
endpoint: args.remote,
|
|
47
|
-
});
|
|
48
|
-
}
|
|
49
|
-
else if (!args.path) {
|
|
50
|
-
log.error("请提供本地路径、Git URL,或使用 --remote <endpoint> 注册远程 Agent");
|
|
51
|
-
process.exitCode = 1;
|
|
52
|
-
return;
|
|
53
|
-
}
|
|
54
|
-
else if (isGitUrl(args.path)) {
|
|
55
|
-
// Git URL 模式:克隆到 dataDir 下
|
|
56
|
-
const repoName = repoNameFromUrl(args.path);
|
|
57
|
-
const cloneTarget = resolve(agentsConfig.dataDir, "repos", repoName);
|
|
58
|
-
if (existsSync(cloneTarget)) {
|
|
59
|
-
log.info(`仓库目录已存在,拉取最新代码: ${cloneTarget}`);
|
|
60
|
-
try {
|
|
61
|
-
await execFileAsync("git", ["pull"], { cwd: cloneTarget });
|
|
62
|
-
}
|
|
63
|
-
catch (err) {
|
|
64
|
-
log.error(`git pull 失败: ${err instanceof Error ? err.message : String(err)}`);
|
|
65
|
-
process.exitCode = 1;
|
|
66
|
-
return;
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
else {
|
|
70
|
-
log.info(`克隆 ${args.path}...`);
|
|
71
|
-
const parentDir = resolve(agentsConfig.dataDir, "repos");
|
|
72
|
-
if (!existsSync(parentDir)) {
|
|
73
|
-
mkdirSync(parentDir, { recursive: true });
|
|
74
|
-
}
|
|
75
|
-
try {
|
|
76
|
-
await execFileAsync("git", ["clone", args.path, cloneTarget]);
|
|
77
|
-
log.success("克隆完成");
|
|
78
|
-
}
|
|
79
|
-
catch (err) {
|
|
80
|
-
log.error(`git clone 失败: ${err instanceof Error ? err.message : String(err)}`);
|
|
81
|
-
process.exitCode = 1;
|
|
82
|
-
return;
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
agentDir = cloneTarget;
|
|
86
|
-
}
|
|
87
|
-
else {
|
|
88
|
-
agentDir = resolve(args.path);
|
|
89
|
-
if (!existsSync(agentDir)) {
|
|
90
|
-
log.error(`路径不存在: ${agentDir}`);
|
|
91
|
-
process.exitCode = 1;
|
|
92
|
-
return;
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
// 1. 解析 SKILL.md
|
|
96
|
-
log.info("解析 SKILL.md...");
|
|
97
|
-
const discovered = discoverAgent(agentDir);
|
|
98
|
-
log.debug(`名称: ${discovered.skill.name}`);
|
|
99
|
-
log.debug(`描述: ${discovered.skill.description}`);
|
|
100
|
-
log.debug(`传输: ${discovered.transport.type}`);
|
|
101
|
-
// 2. 安装依赖(如果存在 package.json)
|
|
102
|
-
const packageJsonPath = resolve(agentDir, "package.json");
|
|
103
|
-
const skipInstall = process.env["ROLL_SKIP_INSTALL"] === "1";
|
|
104
|
-
if (args.remote) {
|
|
105
|
-
log.info("远程 Agent 使用本地 manifest,无需安装依赖。");
|
|
106
|
-
}
|
|
107
|
-
else if (existsSync(packageJsonPath) && !skipInstall) {
|
|
108
|
-
log.info("安装依赖...");
|
|
109
|
-
try {
|
|
110
|
-
await execFileAsync("pnpm", ["install"], { cwd: agentDir });
|
|
111
|
-
log.success("依赖安装完成");
|
|
112
|
-
}
|
|
113
|
-
catch (err) {
|
|
114
|
-
log.error(`依赖安装失败: ${err instanceof Error ? err.message : ""}`);
|
|
115
|
-
process.exitCode = 1;
|
|
116
|
-
return;
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
else if (existsSync(packageJsonPath) && skipInstall) {
|
|
120
|
-
log.warn("检测到 ROLL_SKIP_INSTALL=1,跳过依赖安装。");
|
|
121
|
-
}
|
|
122
|
-
// 3. 确定 Agent 来源
|
|
123
|
-
const source = args.remote
|
|
124
|
-
? { type: "remote-manifest", endpoint: args.remote }
|
|
125
|
-
: args.path && isGitUrl(args.path)
|
|
126
|
-
? { type: "git", url: args.path }
|
|
127
|
-
: { type: "local-path", path: agentDir };
|
|
128
|
-
// 4. 注册到 store
|
|
129
|
-
const store = new AgentStore(agentsConfig.dataDir);
|
|
130
|
-
const agent = {
|
|
131
|
-
skill: discovered.skill,
|
|
132
|
-
transport: discovered.transport,
|
|
133
|
-
runtime: discovered.runtime,
|
|
134
|
-
installPath: agentDir,
|
|
135
|
-
registeredAt: new Date().toISOString(),
|
|
136
|
-
status: "idle",
|
|
137
|
-
source,
|
|
138
|
-
...(discovered.skillBody.length > 0 ? { skillBody: discovered.skillBody } : {}),
|
|
139
|
-
};
|
|
140
|
-
try {
|
|
141
|
-
store.add(agent);
|
|
142
|
-
log.success(`Agent "${discovered.skill.name}" 注册成功`);
|
|
143
|
-
reportAgentEnvGuidance(discovered.skill.name, discovered.skill.env, agentsConfig.env);
|
|
144
|
-
}
|
|
145
|
-
catch (err) {
|
|
146
|
-
log.error(err instanceof Error ? err.message : String(err));
|
|
147
|
-
process.exitCode = 1;
|
|
148
|
-
}
|
|
149
|
-
},
|
|
150
|
-
});
|
|
151
|
-
function reportAgentEnvGuidance(agentName, envDeclarations, envMap) {
|
|
152
|
-
const envReport = inspectAgentEnvRequirements(agentName, envDeclarations, envMap);
|
|
153
|
-
if (!envReport) {
|
|
154
|
-
return;
|
|
155
|
-
}
|
|
156
|
-
if (envReport.missingRequired.length > 0) {
|
|
157
|
-
log.warn(`Agent "${agentName}" 仍缺少必填环境变量: ${envReport.missingRequired.map((item) => item.name).join(", ")}`);
|
|
158
|
-
log.info(`请在 roll.config.yaml 的 agents.env.${agentName} 中显式配置这些项。`);
|
|
159
|
-
return;
|
|
160
|
-
}
|
|
161
|
-
if (envReport.processEnvOnlyRequired.length > 0) {
|
|
162
|
-
log.warn(`Agent "${agentName}" 当前依赖 shell 环境变量: ${envReport.processEnvOnlyRequired.map((item) => item.name).join(", ")}`);
|
|
163
|
-
log.info(`建议将这些项写入 roll.config.yaml 的 agents.env.${agentName}。`);
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
//# sourceMappingURL=agent-add.js.map
|
|
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,131 +1 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { loadAgentsConfig } from "../../config/loader.js";
|
|
3
|
-
import { getAgentLogPath, getAgentPid, probeAgentEndpoint } from "../../registry/process-manager.js";
|
|
4
|
-
import { AgentStore } from "../../registry/store.js";
|
|
5
|
-
import { log } from "../utils/output.js";
|
|
6
|
-
export default defineCommand({
|
|
7
|
-
meta: { description: "检查 Agent 健康状态(兼容 on-demand / core-managed / external-managed)" },
|
|
8
|
-
args: {
|
|
9
|
-
restart: {
|
|
10
|
-
type: "boolean",
|
|
11
|
-
description: "兼容旧参数,stdio 按需模式下不生效",
|
|
12
|
-
default: false,
|
|
13
|
-
},
|
|
14
|
-
json: { type: "boolean", description: "JSON 格式输出", default: false },
|
|
15
|
-
},
|
|
16
|
-
async run({ args }) {
|
|
17
|
-
const { agentsConfig } = loadAgentsConfig();
|
|
18
|
-
const store = new AgentStore(agentsConfig.dataDir);
|
|
19
|
-
const agents = store.list();
|
|
20
|
-
if (args.restart) {
|
|
21
|
-
log.warn("`--restart` 仅为兼容保留参数;v1 不执行自动重启逻辑。");
|
|
22
|
-
}
|
|
23
|
-
if (agents.length === 0) {
|
|
24
|
-
if (args.json) {
|
|
25
|
-
console.log("[]");
|
|
26
|
-
return;
|
|
27
|
-
}
|
|
28
|
-
log.info("暂无已注册 Agent。");
|
|
29
|
-
return;
|
|
30
|
-
}
|
|
31
|
-
const results = [];
|
|
32
|
-
for (const agent of agents) {
|
|
33
|
-
results.push(await checkAgentHealth(agent, store, agentsConfig.dataDir));
|
|
34
|
-
}
|
|
35
|
-
const unhealthy = results.filter((result) => !result.healthy);
|
|
36
|
-
if (args.json) {
|
|
37
|
-
console.log(JSON.stringify(results, null, 2));
|
|
38
|
-
if (unhealthy.length > 0) {
|
|
39
|
-
process.exitCode = 1;
|
|
40
|
-
}
|
|
41
|
-
return;
|
|
42
|
-
}
|
|
43
|
-
for (const result of results) {
|
|
44
|
-
if (result.healthy) {
|
|
45
|
-
log.success(`${result.agentName} [${result.transport}]: ${result.message}`);
|
|
46
|
-
}
|
|
47
|
-
else {
|
|
48
|
-
log.error(`${result.agentName} [${result.transport}]: ${result.message}`);
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
if (unhealthy.length > 0) {
|
|
52
|
-
process.exitCode = 1;
|
|
53
|
-
}
|
|
54
|
-
},
|
|
55
|
-
});
|
|
56
|
-
async function checkAgentHealth(agent, store, dataDir) {
|
|
57
|
-
switch (agent.runtime.ownership) {
|
|
58
|
-
case "on-demand":
|
|
59
|
-
return {
|
|
60
|
-
agentName: agent.skill.name,
|
|
61
|
-
transport: agent.transport.type,
|
|
62
|
-
healthy: true,
|
|
63
|
-
message: "按需模式:无需常驻进程,由 run/ask 在调用时启动",
|
|
64
|
-
};
|
|
65
|
-
case "external-managed":
|
|
66
|
-
return checkExternalManagedHealth(agent, store);
|
|
67
|
-
case "core-managed":
|
|
68
|
-
return checkCoreManagedHealth(agent, store, dataDir);
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
async function checkExternalManagedHealth(agent, store) {
|
|
72
|
-
try {
|
|
73
|
-
await probeAgentEndpoint(agent, { timeoutMs: 5_000 });
|
|
74
|
-
store.updateStatus(agent.skill.name, "online");
|
|
75
|
-
return {
|
|
76
|
-
agentName: agent.skill.name,
|
|
77
|
-
transport: agent.transport.type,
|
|
78
|
-
healthy: true,
|
|
79
|
-
message: agent.transport.type === "streamable-http"
|
|
80
|
-
? `外部服务可连接 (${agent.transport.endpoint})`
|
|
81
|
-
: "外部服务可连接",
|
|
82
|
-
};
|
|
83
|
-
}
|
|
84
|
-
catch (err) {
|
|
85
|
-
store.updateStatus(agent.skill.name, "error");
|
|
86
|
-
return {
|
|
87
|
-
agentName: agent.skill.name,
|
|
88
|
-
transport: agent.transport.type,
|
|
89
|
-
healthy: false,
|
|
90
|
-
message: agent.transport.type === "streamable-http"
|
|
91
|
-
? `外部服务不可连接 (${agent.transport.endpoint}): ${err instanceof Error ? err.message : String(err)}`
|
|
92
|
-
: `外部服务不可连接: ${err instanceof Error ? err.message : String(err)}`,
|
|
93
|
-
};
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
async function checkCoreManagedHealth(agent, store, dataDir) {
|
|
97
|
-
const pid = getAgentPid(dataDir, agent.skill.name);
|
|
98
|
-
if (pid === undefined) {
|
|
99
|
-
store.updateStatus(agent.skill.name, "stopped");
|
|
100
|
-
return {
|
|
101
|
-
agentName: agent.skill.name,
|
|
102
|
-
transport: agent.transport.type,
|
|
103
|
-
healthy: false,
|
|
104
|
-
message: `未运行(缺少活动 PID)。日志: ${getAgentLogPath(dataDir, agent.skill.name)}`,
|
|
105
|
-
};
|
|
106
|
-
}
|
|
107
|
-
try {
|
|
108
|
-
await probeAgentEndpoint(agent, { timeoutMs: 5_000 });
|
|
109
|
-
store.updateStatus(agent.skill.name, "online");
|
|
110
|
-
return {
|
|
111
|
-
agentName: agent.skill.name,
|
|
112
|
-
transport: agent.transport.type,
|
|
113
|
-
healthy: true,
|
|
114
|
-
message: agent.transport.type === "streamable-http"
|
|
115
|
-
? `运行中 (PID: ${String(pid)}),可连接 (${agent.transport.endpoint})`
|
|
116
|
-
: `运行中 (PID: ${String(pid)})`,
|
|
117
|
-
};
|
|
118
|
-
}
|
|
119
|
-
catch (err) {
|
|
120
|
-
store.updateStatus(agent.skill.name, "error");
|
|
121
|
-
return {
|
|
122
|
-
agentName: agent.skill.name,
|
|
123
|
-
transport: agent.transport.type,
|
|
124
|
-
healthy: false,
|
|
125
|
-
message: agent.transport.type === "streamable-http"
|
|
126
|
-
? `进程存在但不可连接 (${agent.transport.endpoint}): ${err instanceof Error ? err.message : String(err)}。日志: ${getAgentLogPath(dataDir, agent.skill.name)}`
|
|
127
|
-
: `进程存在但不可连接: ${err instanceof Error ? err.message : String(err)}。日志: ${getAgentLogPath(dataDir, agent.skill.name)}`,
|
|
128
|
-
};
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
//# sourceMappingURL=agent-health.js.map
|
|
1
|
+
import{defineCommand as t}from"citty";import{loadAgentsConfig as e}from"../../config/loader.js";import{getAgentLogPath as r,getAgentPid as a,probeAgentEndpoint as n}from"../../registry/process-manager.js";import{AgentStore as s}from"../../registry/store.js";import{log as o}from"../utils/output.js";export default t({meta:{description:"检查 Agent 健康状态(兼容 on-demand / core-managed / external-managed)"},args:{restart:{type:"boolean",description:"兼容旧参数,stdio 按需模式下不生效",default:!1},json:{type:"boolean",description:"JSON 格式输出",default:!1}},async run({args:t}){const{agentsConfig:r}=e(),a=new s(r.dataDir),n=a.list();if(t.restart&&o.warn("`--restart` 仅为兼容保留参数;v1 不执行自动重启逻辑。"),0===n.length)return t.json?void console.log("[]"):void o.info("暂无已注册 Agent。");const l=[];for(const t of n)l.push(await i(t,a,r.dataDir));const p=l.filter(t=>!t.healthy);if(t.json)return console.log(JSON.stringify(l,null,2)),void(p.length>0&&(process.exitCode=1));for(const t of l)t.healthy?o.success(`${t.agentName} [${t.transport}]: ${t.message}`):o.error(`${t.agentName} [${t.transport}]: ${t.message}`);p.length>0&&(process.exitCode=1)}});async function i(t,e,r){switch(t.runtime.ownership){case"on-demand":return{agentName:t.skill.name,transport:t.transport.type,healthy:!0,message:"按需模式:无需常驻进程,由 run/ask 在调用时启动"};case"external-managed":return l(t,e);case"core-managed":return p(t,e,r)}}async function l(t,e){try{return await n(t,{timeoutMs:5e3}),e.updateStatus(t.skill.name,"online"),{agentName:t.skill.name,transport:t.transport.type,healthy:!0,message:"streamable-http"===t.transport.type?`外部服务可连接 (${t.transport.endpoint})`:"外部服务可连接"}}catch(r){return e.updateStatus(t.skill.name,"error"),{agentName:t.skill.name,transport:t.transport.type,healthy:!1,message:"streamable-http"===t.transport.type?`外部服务不可连接 (${t.transport.endpoint}): ${r instanceof Error?r.message:String(r)}`:`外部服务不可连接: ${r instanceof Error?r.message:String(r)}`}}}async function p(t,e,s){const o=a(s,t.skill.name);if(void 0===o)return e.updateStatus(t.skill.name,"stopped"),{agentName:t.skill.name,transport:t.transport.type,healthy:!1,message:`未运行(缺少活动 PID)。日志: ${r(s,t.skill.name)}`};try{return await n(t,{timeoutMs:5e3}),e.updateStatus(t.skill.name,"online"),{agentName:t.skill.name,transport:t.transport.type,healthy:!0,message:"streamable-http"===t.transport.type?`运行中 (PID: ${String(o)}),可连接 (${t.transport.endpoint})`:`运行中 (PID: ${String(o)})`}}catch(a){return e.updateStatus(t.skill.name,"error"),{agentName:t.skill.name,transport:t.transport.type,healthy:!1,message:"streamable-http"===t.transport.type?`进程存在但不可连接 (${t.transport.endpoint}): ${a instanceof Error?a.message:String(a)}。日志: ${r(s,t.skill.name)}`:`进程存在但不可连接: ${a instanceof Error?a.message:String(a)}。日志: ${r(s,t.skill.name)}`}}}
|
|
@@ -1,57 +1 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { loadAgentsConfig } from "../../config/loader.js";
|
|
3
|
-
import { inspectAgentEnvRequirements } from "../../config/helpers.js";
|
|
4
|
-
import { formatAgentSourceType, getAgentLocation, inferAgentSourceType, } from "../../registry/source.js";
|
|
5
|
-
import { AgentStore } from "../../registry/store.js";
|
|
6
|
-
export default defineCommand({
|
|
7
|
-
meta: { description: "查看 Agent 详情" },
|
|
8
|
-
args: {
|
|
9
|
-
name: { type: "positional", description: "Agent 名称", required: true },
|
|
10
|
-
},
|
|
11
|
-
run({ args }) {
|
|
12
|
-
const { agentsConfig } = loadAgentsConfig();
|
|
13
|
-
const store = new AgentStore(agentsConfig.dataDir);
|
|
14
|
-
const agent = store.findByName(args.name);
|
|
15
|
-
if (!agent) {
|
|
16
|
-
console.error(`✗ Agent "${args.name}" 未找到`);
|
|
17
|
-
process.exitCode = 1;
|
|
18
|
-
return;
|
|
19
|
-
}
|
|
20
|
-
console.log(`名称: ${agent.skill.name}`);
|
|
21
|
-
console.log(`描述: ${agent.skill.description}`);
|
|
22
|
-
console.log(`状态: ${agent.status}`);
|
|
23
|
-
console.log(`来源: ${formatAgentSourceType(inferAgentSourceType(agent))}`);
|
|
24
|
-
console.log(`传输: ${agent.transport.type}`);
|
|
25
|
-
console.log(`位置: ${getAgentLocation(agent)}`);
|
|
26
|
-
console.log(`注册时间: ${agent.registeredAt}`);
|
|
27
|
-
if (agent.source?.type === "installed-package") {
|
|
28
|
-
console.log(`安装包: ${agent.source.packageSpec}`);
|
|
29
|
-
console.log(`安装目录: ${agent.source.installDir}`);
|
|
30
|
-
}
|
|
31
|
-
if (Object.keys(agent.skill.metadata).length > 0) {
|
|
32
|
-
console.log(`元数据:`);
|
|
33
|
-
for (const [key, value] of Object.entries(agent.skill.metadata)) {
|
|
34
|
-
console.log(` ${key}: ${value}`);
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
const envReport = inspectAgentEnvRequirements(agent.skill.name, agent.skill.env, agentsConfig.env);
|
|
38
|
-
if (envReport) {
|
|
39
|
-
console.log(`环境变量:`);
|
|
40
|
-
for (const item of envReport.items) {
|
|
41
|
-
const status = item.source === "agents.env"
|
|
42
|
-
? "已配置于 agents.env"
|
|
43
|
-
: item.source === "process.env"
|
|
44
|
-
? "仅当前 shell 环境"
|
|
45
|
-
: item.source === "default"
|
|
46
|
-
? `默认值 (${item.default})`
|
|
47
|
-
: "缺失";
|
|
48
|
-
const level = item.required ? "必填" : "可选";
|
|
49
|
-
console.log(` ${item.name}: [${level}] ${status}`);
|
|
50
|
-
if (item.purpose) {
|
|
51
|
-
console.log(` 用途: ${item.purpose}`);
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
},
|
|
56
|
-
});
|
|
57
|
-
//# sourceMappingURL=agent-info.js.map
|
|
1
|
+
import{defineCommand as o}from"citty";import{loadAgentsConfig as e}from"../../config/loader.js";import{inspectAgentEnvRequirements as s}from"../../config/helpers.js";import{formatAgentSourceType as l,getAgentLocation as n,inferAgentSourceType as r}from"../../registry/source.js";import{AgentStore as t}from"../../registry/store.js";export default o({meta:{description:"查看 Agent 详情"},args:{name:{type:"positional",description:"Agent 名称",required:!0}},run({args:o}){const{agentsConfig:c}=e(),i=new t(c.dataDir).findByName(o.name);if(!i)return console.error(`✗ Agent "${o.name}" 未找到`),void(process.exitCode=1);if(console.log(`名称: ${i.skill.name}`),console.log(`描述: ${i.skill.description}`),console.log(`状态: ${i.status}`),console.log(`来源: ${l(r(i))}`),console.log(`传输: ${i.transport.type}`),console.log(`位置: ${n(i)}`),console.log(`注册时间: ${i.registeredAt}`),"installed-package"===i.source?.type&&(console.log(`安装包: ${i.source.packageSpec}`),console.log(`安装目录: ${i.source.installDir}`)),Object.keys(i.skill.metadata).length>0){console.log("元数据:");for(const[o,e]of Object.entries(i.skill.metadata))console.log(` ${o}: ${e}`)}const a=s(i.skill.name,i.skill.env,c.env);if(a){console.log("环境变量:");for(const o of a.items){const e="agents.env"===o.source?"已配置于 agents.env":"process.env"===o.source?"仅当前 shell 环境":"default"===o.source?`默认值 (${o.default})`:"缺失",s=o.required?"必填":"可选";console.log(` ${o.name}: [${s}] ${e}`),o.purpose&&console.log(` 用途: ${o.purpose}`)}}}});
|
|
@@ -1,173 +1 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { existsSync, mkdirSync, statSync } from "node:fs";
|
|
3
|
-
import { resolve } from "node:path";
|
|
4
|
-
import { execFile } from "node:child_process";
|
|
5
|
-
import { promisify } from "node:util";
|
|
6
|
-
import { inspectAgentEnvRequirements } from "../../config/helpers.js";
|
|
7
|
-
import { loadAgentsConfig } from "../../config/loader.js";
|
|
8
|
-
import { discoverAgent } from "../../registry/discovery.js";
|
|
9
|
-
import { startAgent, stopAgentGracefully, waitForAgentReady, } from "../../registry/process-manager.js";
|
|
10
|
-
import { runAgentSetup } from "../../registry/runtime-setup.js";
|
|
11
|
-
import { AgentStore } from "../../registry/store.js";
|
|
12
|
-
import { parsePackageName, resolveInstalledPackageRoot, sanitizeInstallId, } from "../../registry/source.js";
|
|
13
|
-
import { log } from "../utils/output.js";
|
|
14
|
-
const execFileAsync = promisify(execFile);
|
|
15
|
-
function isGitUrl(input) {
|
|
16
|
-
return (input.startsWith("git@") ||
|
|
17
|
-
input.startsWith("git+") ||
|
|
18
|
-
input.startsWith("github:") ||
|
|
19
|
-
input.startsWith("gitlab:") ||
|
|
20
|
-
input.startsWith("bitbucket:") ||
|
|
21
|
-
input.endsWith(".git"));
|
|
22
|
-
}
|
|
23
|
-
export default defineCommand({
|
|
24
|
-
meta: { description: "安装已编译的 Agent 包并注册到本地" },
|
|
25
|
-
args: {
|
|
26
|
-
package: { type: "positional", description: "npm package spec", required: true },
|
|
27
|
-
skipBrowserSetup: {
|
|
28
|
-
type: "boolean",
|
|
29
|
-
description: "跳过浏览器运行时安装",
|
|
30
|
-
default: false,
|
|
31
|
-
},
|
|
32
|
-
noStart: {
|
|
33
|
-
type: "boolean",
|
|
34
|
-
description: "安装后不自动启动 core-managed Agent",
|
|
35
|
-
default: false,
|
|
36
|
-
},
|
|
37
|
-
},
|
|
38
|
-
async run({ args }) {
|
|
39
|
-
const { agentsConfig } = loadAgentsConfig();
|
|
40
|
-
const packageSpec = args.package;
|
|
41
|
-
if (isGitUrl(packageSpec)) {
|
|
42
|
-
log.error(`Git URL 请使用 \`roll agent add ${packageSpec}\` 注册,不要使用 \`roll agent install\``);
|
|
43
|
-
process.exitCode = 1;
|
|
44
|
-
return;
|
|
45
|
-
}
|
|
46
|
-
const resolvedInputPath = resolve(packageSpec);
|
|
47
|
-
if (existsSync(resolvedInputPath) && statSync(resolvedInputPath).isDirectory()) {
|
|
48
|
-
log.error(`本地源码目录请使用 \`roll agent add ${packageSpec}\` 注册,不要使用 \`roll agent install\``);
|
|
49
|
-
process.exitCode = 1;
|
|
50
|
-
return;
|
|
51
|
-
}
|
|
52
|
-
const packageName = parsePackageName(packageSpec);
|
|
53
|
-
const installDir = resolve(agentsConfig.dataDir, "installed", sanitizeInstallId(packageName));
|
|
54
|
-
if (!existsSync(installDir)) {
|
|
55
|
-
mkdirSync(installDir, { recursive: true });
|
|
56
|
-
}
|
|
57
|
-
log.info(`安装 ${packageSpec}...`);
|
|
58
|
-
try {
|
|
59
|
-
await execFileAsync("npm", ["install", "--prefix", installDir, packageSpec], {
|
|
60
|
-
timeout: 120_000,
|
|
61
|
-
});
|
|
62
|
-
}
|
|
63
|
-
catch (err) {
|
|
64
|
-
log.error(`安装失败: ${err instanceof Error ? err.message : String(err)}`);
|
|
65
|
-
process.exitCode = 1;
|
|
66
|
-
return;
|
|
67
|
-
}
|
|
68
|
-
const packageRoot = resolveInstalledPackageRoot(installDir, packageName);
|
|
69
|
-
if (!existsSync(packageRoot)) {
|
|
70
|
-
log.error(`安装完成但未找到包目录: ${packageRoot}`);
|
|
71
|
-
process.exitCode = 1;
|
|
72
|
-
return;
|
|
73
|
-
}
|
|
74
|
-
log.info("解析已安装 Agent 的 SKILL.md...");
|
|
75
|
-
const discovered = discoverAgent(packageRoot);
|
|
76
|
-
const store = new AgentStore(agentsConfig.dataDir);
|
|
77
|
-
const agent = {
|
|
78
|
-
skill: discovered.skill,
|
|
79
|
-
transport: discovered.transport,
|
|
80
|
-
runtime: discovered.runtime,
|
|
81
|
-
installPath: packageRoot,
|
|
82
|
-
registeredAt: new Date().toISOString(),
|
|
83
|
-
status: "idle",
|
|
84
|
-
source: {
|
|
85
|
-
type: "installed-package",
|
|
86
|
-
packageName,
|
|
87
|
-
packageSpec,
|
|
88
|
-
installDir,
|
|
89
|
-
},
|
|
90
|
-
...(discovered.skillBody.length > 0 ? { skillBody: discovered.skillBody } : {}),
|
|
91
|
-
};
|
|
92
|
-
if (agent.runtime.ownership === "core-managed" &&
|
|
93
|
-
agent.runtime.setup?.playwright &&
|
|
94
|
-
!args.skipBrowserSetup) {
|
|
95
|
-
log.info(`即将安装浏览器运行时 (${agent.runtime.setup.playwright.browsers.join(", ")}),这可能需要一些时间...`);
|
|
96
|
-
}
|
|
97
|
-
const setupResult = await runAgentSetup(agent, {
|
|
98
|
-
skipBrowserSetup: args.skipBrowserSetup,
|
|
99
|
-
});
|
|
100
|
-
if (!setupResult.ok) {
|
|
101
|
-
log.warn(`Agent setup 失败:${setupResult.message}`);
|
|
102
|
-
if (setupResult.retryCommand) {
|
|
103
|
-
log.info(`重试命令: ${setupResult.retryCommand}`);
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
else if (!setupResult.skipped) {
|
|
107
|
-
log.success(setupResult.message);
|
|
108
|
-
}
|
|
109
|
-
else {
|
|
110
|
-
log.info(setupResult.message);
|
|
111
|
-
}
|
|
112
|
-
const existing = store.findByName(discovered.skill.name);
|
|
113
|
-
try {
|
|
114
|
-
const wasRunning = existing?.runtime.ownership === "core-managed" &&
|
|
115
|
-
existing.status === "online";
|
|
116
|
-
if (existing?.source?.type === "installed-package") {
|
|
117
|
-
store.replace(existing.skill.name, agent);
|
|
118
|
-
}
|
|
119
|
-
else {
|
|
120
|
-
store.add(agent);
|
|
121
|
-
}
|
|
122
|
-
if (!setupResult.ok) {
|
|
123
|
-
store.updateStatus(discovered.skill.name, "error");
|
|
124
|
-
process.exitCode = 1;
|
|
125
|
-
return;
|
|
126
|
-
}
|
|
127
|
-
if (agent.runtime.ownership === "core-managed" && !args.noStart) {
|
|
128
|
-
if (wasRunning) {
|
|
129
|
-
await stopAgentGracefully(agentsConfig.dataDir, agent.skill.name);
|
|
130
|
-
}
|
|
131
|
-
store.updateStatus(agent.skill.name, "starting");
|
|
132
|
-
let started = false;
|
|
133
|
-
try {
|
|
134
|
-
startAgent(agent, agentsConfig.dataDir);
|
|
135
|
-
started = true;
|
|
136
|
-
await waitForAgentReady(agent, { startupTimeoutMs: 15_000, probeTimeoutMs: 2_000 });
|
|
137
|
-
store.updateStatus(agent.skill.name, "online");
|
|
138
|
-
}
|
|
139
|
-
catch (err) {
|
|
140
|
-
if (started) {
|
|
141
|
-
await stopAgentGracefully(agentsConfig.dataDir, agent.skill.name).catch(() => { });
|
|
142
|
-
}
|
|
143
|
-
store.updateStatus(agent.skill.name, "error");
|
|
144
|
-
log.error(`Agent "${discovered.skill.name}" 已安装,但自动启动失败:${err instanceof Error ? err.message : String(err)}`);
|
|
145
|
-
process.exitCode = 1;
|
|
146
|
-
return;
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
log.success(`Agent "${discovered.skill.name}" 安装并注册成功`);
|
|
150
|
-
reportAgentEnvGuidance(discovered.skill.name, discovered.skill.env, agentsConfig.env);
|
|
151
|
-
}
|
|
152
|
-
catch (err) {
|
|
153
|
-
log.error(err instanceof Error ? err.message : String(err));
|
|
154
|
-
process.exitCode = 1;
|
|
155
|
-
}
|
|
156
|
-
},
|
|
157
|
-
});
|
|
158
|
-
function reportAgentEnvGuidance(agentName, envDeclarations, envMap) {
|
|
159
|
-
const envReport = inspectAgentEnvRequirements(agentName, envDeclarations, envMap);
|
|
160
|
-
if (!envReport) {
|
|
161
|
-
return;
|
|
162
|
-
}
|
|
163
|
-
if (envReport.missingRequired.length > 0) {
|
|
164
|
-
log.warn(`Agent "${agentName}" 仍缺少必填环境变量: ${envReport.missingRequired.map((item) => item.name).join(", ")}`);
|
|
165
|
-
log.info(`请在 roll.config.yaml 的 agents.env.${agentName} 中显式配置这些项。`);
|
|
166
|
-
return;
|
|
167
|
-
}
|
|
168
|
-
if (envReport.processEnvOnlyRequired.length > 0) {
|
|
169
|
-
log.warn(`Agent "${agentName}" 当前依赖 shell 环境变量: ${envReport.processEnvOnlyRequired.map((item) => item.name).join(", ")}`);
|
|
170
|
-
log.info(`建议将这些项写入 roll.config.yaml 的 agents.env.${agentName}。`);
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
//# sourceMappingURL=agent-install.js.map
|
|
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,resolveInstalledPackageRoot as k,sanitizeInstallId as y}from"../../registry/source.js";import{log as h}from"../utils/output.js";const w=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 h.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 h.error(`本地源码目录请使用 \`roll agent add ${n}\` 注册,不要使用 \`roll agent install\``),void(process.exitCode=1);const v=f(n),j=s(o.dataDir,"installed",y(v));t(j)||r(j,{recursive:!0}),h.info(`安装 ${n}...`);try{await w("npm",["install","--prefix",j,n],{timeout:12e4})}catch(e){return h.error(`安装失败: ${e instanceof Error?e.message:String(e)}`),void(process.exitCode=1)}const C=k(j,v);if(!t(C))return h.error(`安装完成但未找到包目录: ${C}`),void(process.exitCode=1);h.info("解析已安装 Agent 的 SKILL.md...");const x=p(C),A=new u(o.dataDir),b={skill:x.skill,transport:x.transport,runtime:x.runtime,installPath:C,registeredAt:(new Date).toISOString(),status:"idle",source:{type:"installed-package",packageName:v,packageSpec:n,installDir:j},...x.skillBody.length>0?{skillBody:x.skillBody}:{}};"core-managed"===b.runtime.ownership&&b.runtime.setup?.playwright&&!e.skipBrowserSetup&&h.info(`即将安装浏览器运行时 (${b.runtime.setup.playwright.browsers.join(", ")}),这可能需要一些时间...`);const B=await g(b,{skipBrowserSetup:e.skipBrowserSetup});B.ok?B.skipped?h.info(B.message):h.success(B.message):(h.warn(`Agent setup 失败:${B.message}`),B.retryCommand&&h.info(`重试命令: ${B.retryCommand}`));const D=A.findByName(x.skill.name);try{const t="core-managed"===D?.runtime.ownership&&"online"===D.status;if("installed-package"===D?.source?.type?A.replace(D.skill.name,b):A.add(b),!B.ok)return A.updateStatus(x.skill.name,"error"),void(process.exitCode=1);if("core-managed"===b.runtime.ownership&&!e.noStart){t&&await c(o.dataDir,b.skill.name),A.updateStatus(b.skill.name,"starting");let e=!1;try{m(b,o.dataDir),e=!0,await d(b,{startupTimeoutMs:15e3,probeTimeoutMs:2e3}),A.updateStatus(b.skill.name,"online")}catch(t){return e&&await c(o.dataDir,b.skill.name).catch(()=>{}),A.updateStatus(b.skill.name,"error"),h.error(`Agent "${x.skill.name}" 已安装,但自动启动失败:${t instanceof Error?t.message:String(t)}`),void(process.exitCode=1)}}h.success(`Agent "${x.skill.name}" 安装并注册成功`),$(x.skill.name,x.skill.env,o.env)}catch(e){h.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?(h.warn(`Agent "${e}" 仍缺少必填环境变量: ${i.missingRequired.map(e=>e.name).join(", ")}`),void h.info(`请在 roll.config.yaml 的 agents.env.${e} 中显式配置这些项。`)):void(i.processEnvOnlyRequired.length>0&&(h.warn(`Agent "${e}" 当前依赖 shell 环境变量: ${i.processEnvOnlyRequired.map(e=>e.name).join(", ")}`),h.info(`建议将这些项写入 roll.config.yaml 的 agents.env.${e}。`)))}
|
|
@@ -1,39 +1 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import Table from "cli-table3";
|
|
3
|
-
import { loadAgentsConfig } from "../../config/loader.js";
|
|
4
|
-
import { formatAgentSourceType, getAgentLocation, inferAgentSourceType, } from "../../registry/source.js";
|
|
5
|
-
import { AgentStore } from "../../registry/store.js";
|
|
6
|
-
export default defineCommand({
|
|
7
|
-
meta: { description: "列出所有已注册 Agent" },
|
|
8
|
-
args: {
|
|
9
|
-
json: { type: "boolean", description: "JSON 格式输出", default: false },
|
|
10
|
-
},
|
|
11
|
-
run({ args }) {
|
|
12
|
-
const { agentsConfig } = loadAgentsConfig();
|
|
13
|
-
const store = new AgentStore(agentsConfig.dataDir);
|
|
14
|
-
const agents = store.list();
|
|
15
|
-
if (args.json) {
|
|
16
|
-
console.log(JSON.stringify(agents, null, 2));
|
|
17
|
-
return;
|
|
18
|
-
}
|
|
19
|
-
if (agents.length === 0) {
|
|
20
|
-
console.log("暂无已注册的 Agent。可使用 `roll agent add <path>`、`roll agent install <package>` 或 `roll agent add --remote <endpoint>`。");
|
|
21
|
-
return;
|
|
22
|
-
}
|
|
23
|
-
const table = new Table({
|
|
24
|
-
head: ["Name", "Status", "Source", "Transport", "Location"],
|
|
25
|
-
style: { head: ["cyan"] },
|
|
26
|
-
});
|
|
27
|
-
for (const agent of agents) {
|
|
28
|
-
table.push([
|
|
29
|
-
agent.skill.name,
|
|
30
|
-
agent.status,
|
|
31
|
-
formatAgentSourceType(inferAgentSourceType(agent)),
|
|
32
|
-
agent.transport.type,
|
|
33
|
-
getAgentLocation(agent),
|
|
34
|
-
]);
|
|
35
|
-
}
|
|
36
|
-
console.log(table.toString());
|
|
37
|
-
},
|
|
38
|
-
});
|
|
39
|
-
//# sourceMappingURL=agent-list.js.map
|
|
1
|
+
import{defineCommand as o}from"citty";import t from"cli-table3";import{loadAgentsConfig as r}from"../../config/loader.js";import{formatAgentSourceType as e,getAgentLocation as n,inferAgentSourceType as s}from"../../registry/source.js";import{AgentStore as a}from"../../registry/store.js";export default o({meta:{description:"列出所有已注册 Agent"},args:{json:{type:"boolean",description:"JSON 格式输出",default:!1}},run({args:o}){const{agentsConfig:i}=r(),l=new a(i.dataDir).list();if(o.json)return void console.log(JSON.stringify(l,null,2));if(0===l.length)return void console.log("暂无已注册的 Agent。可使用 `roll agent add <path>`、`roll agent install <package>` 或 `roll agent add --remote <endpoint>`。");const g=new t({head:["Name","Status","Source","Transport","Location"],style:{head:["cyan"]}});for(const o of l)g.push([o.skill.name,o.status,e(s(o)),o.transport.type,n(o)]);console.log(g.toString())}});
|