@ai-setting/roy-agent-cli 1.0.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/README.md +126 -0
- package/dist/bin/roy.js +127297 -0
- package/dist/roy-agent-darwin-arm64/bin/roy.js +127297 -0
- package/dist/roy-agent-darwin-x64/bin/roy.js +127297 -0
- package/dist/roy-agent-linux-arm64/bin/roy.js +127297 -0
- package/dist/roy-agent-linux-x64/bin/roy.js +127297 -0
- package/dist/roy-agent-windows-x64/bin/roy.js +127297 -0
- package/package.json +91 -0
- package/src/bin/roy.ts +12 -0
- package/src/cli.ts +101 -0
- package/src/commands/act.ts +480 -0
- package/src/commands/commands-add.ts +110 -0
- package/src/commands/commands-dirs.ts +70 -0
- package/src/commands/commands-info.ts +90 -0
- package/src/commands/commands-list.ts +161 -0
- package/src/commands/commands-remove.ts +147 -0
- package/src/commands/commands.ts +55 -0
- package/src/commands/config/config-service.test.ts +449 -0
- package/src/commands/config/config-service.ts +312 -0
- package/src/commands/config/deep-merge.test.ts +168 -0
- package/src/commands/config/deep-merge.ts +63 -0
- package/src/commands/config/export.ts +97 -0
- package/src/commands/config/filter-history-e2e.test.ts +141 -0
- package/src/commands/config/import-preserve-refs.test.ts +212 -0
- package/src/commands/config/import.ts +119 -0
- package/src/commands/config/index.ts +35 -0
- package/src/commands/config/list.ts +281 -0
- package/src/commands/config/roy-config-e2e.test.ts +297 -0
- package/src/commands/config/types.ts +54 -0
- package/src/commands/debug/index.ts +38 -0
- package/src/commands/debug/log.test.ts +233 -0
- package/src/commands/debug/log.ts +123 -0
- package/src/commands/debug/span.test.ts +297 -0
- package/src/commands/debug/span.ts +211 -0
- package/src/commands/debug/trace.test.ts +254 -0
- package/src/commands/debug/trace.ts +140 -0
- package/src/commands/eventsource/add.ts +133 -0
- package/src/commands/eventsource/index.ts +48 -0
- package/src/commands/eventsource/list.ts +194 -0
- package/src/commands/eventsource/remove.ts +95 -0
- package/src/commands/eventsource/start.ts +103 -0
- package/src/commands/eventsource/status.ts +185 -0
- package/src/commands/eventsource/stop.ts +89 -0
- package/src/commands/index.ts +22 -0
- package/src/commands/input-handler.test.ts +76 -0
- package/src/commands/input-handler.ts +43 -0
- package/src/commands/interactive-esc.test.ts +254 -0
- package/src/commands/interactive.shutdown.test.ts +122 -0
- package/src/commands/interactive.test.ts +221 -0
- package/src/commands/interactive.ts +1015 -0
- package/src/commands/lsp/check.ts +92 -0
- package/src/commands/lsp/index.ts +32 -0
- package/src/commands/lsp/install.ts +126 -0
- package/src/commands/lsp/list.ts +64 -0
- package/src/commands/mcp/index.ts +27 -0
- package/src/commands/mcp/list.ts +116 -0
- package/src/commands/mcp/reload.ts +70 -0
- package/src/commands/mcp/tools.ts +121 -0
- package/src/commands/memory/extract-e2e.test.ts +388 -0
- package/src/commands/memory/index.ts +11 -0
- package/src/commands/memory/memory-simplified.test.ts +58 -0
- package/src/commands/memory/memory.ts +25 -0
- package/src/commands/memory/organize.ts +300 -0
- package/src/commands/memory/recall.test.ts +120 -0
- package/src/commands/memory/recall.ts +88 -0
- package/src/commands/memory/record-extract-handle-query.test.ts +385 -0
- package/src/commands/memory/record-prompt-component.test.ts +343 -0
- package/src/commands/memory/record.test.ts +92 -0
- package/src/commands/memory/record.ts +332 -0
- package/src/commands/plugin.test.ts +292 -0
- package/src/commands/plugin.ts +267 -0
- package/src/commands/sessions/active.ts +96 -0
- package/src/commands/sessions/add-message.ts +96 -0
- package/src/commands/sessions/checkpoints.ts +154 -0
- package/src/commands/sessions/compact.test.ts +215 -0
- package/src/commands/sessions/compact.ts +269 -0
- package/src/commands/sessions/delete.ts +236 -0
- package/src/commands/sessions/get.ts +165 -0
- package/src/commands/sessions/grep.ts +233 -0
- package/src/commands/sessions/index.ts +95 -0
- package/src/commands/sessions/list.ts +210 -0
- package/src/commands/sessions/messages.test.ts +333 -0
- package/src/commands/sessions/messages.ts +248 -0
- package/src/commands/sessions/mock.ts +194 -0
- package/src/commands/sessions/new.ts +82 -0
- package/src/commands/sessions/rename.ts +98 -0
- package/src/commands/shared/event-handler.ts +213 -0
- package/src/commands/shared/event-message-formatter.ts +295 -0
- package/src/commands/shared/index.ts +11 -0
- package/src/commands/shared/query-executor.test.ts +434 -0
- package/src/commands/shared/query-executor.ts +324 -0
- package/src/commands/shared/repl-engine.test.ts +354 -0
- package/src/commands/shared/session-manager.test.ts +212 -0
- package/src/commands/shared/session-manager.ts +114 -0
- package/src/commands/skills/get.ts +90 -0
- package/src/commands/skills/index.ts +39 -0
- package/src/commands/skills/list.ts +129 -0
- package/src/commands/skills/reload.ts +59 -0
- package/src/commands/skills/search.ts +132 -0
- package/src/commands/skills/show-config.ts +93 -0
- package/src/commands/tasks/complete.ts +92 -0
- package/src/commands/tasks/create.ts +118 -0
- package/src/commands/tasks/delete.ts +86 -0
- package/src/commands/tasks/get.ts +116 -0
- package/src/commands/tasks/index.ts +53 -0
- package/src/commands/tasks/list.ts +140 -0
- package/src/commands/tasks/operations.ts +120 -0
- package/src/commands/tasks/update.ts +122 -0
- package/src/commands/tools/exec-tool.ts +128 -0
- package/src/commands/tools/get.ts +114 -0
- package/src/commands/tools/index.ts +35 -0
- package/src/commands/tools/list.ts +107 -0
- package/src/commands/tools/shared/index.ts +7 -0
- package/src/commands/tools/shared/schema-helper.ts +111 -0
- package/src/commands/workflow/commands/add.ts +315 -0
- package/src/commands/workflow/commands/get.ts +193 -0
- package/src/commands/workflow/commands/list.ts +137 -0
- package/src/commands/workflow/commands/nodes.ts +528 -0
- package/src/commands/workflow/commands/remove.ts +94 -0
- package/src/commands/workflow/commands/run.ts +398 -0
- package/src/commands/workflow/commands/status.ts +147 -0
- package/src/commands/workflow/commands/stop.ts +91 -0
- package/src/commands/workflow/commands/update.ts +130 -0
- package/src/commands/workflow/commands/validate.ts +139 -0
- package/src/commands/workflow/commands/workflow-cli.test.ts +196 -0
- package/src/commands/workflow/index.ts +65 -0
- package/src/commands/workflow/renderers.ts +358 -0
- package/src/commands/workflow/validators/index.ts +8 -0
- package/src/commands/workflow/validators/node-validator-factory.ts +40 -0
- package/src/commands/workflow/validators/node-validator.ts +125 -0
- package/src/commands/workflow/validators/nodes/agent-node-validator.ts +58 -0
- package/src/commands/workflow/validators/nodes/condition-node-validator.ts +34 -0
- package/src/commands/workflow/validators/nodes/decorator-node-validator.ts +45 -0
- package/src/commands/workflow/validators/nodes/merge-node-validator.ts +46 -0
- package/src/commands/workflow/validators/nodes/skill-node-validator.ts +33 -0
- package/src/commands/workflow/validators/nodes/tool-node-validator.ts +54 -0
- package/src/commands/workflow/validators/nodes/workflow-node-validator.ts +33 -0
- package/src/commands/workflow/validators/types.ts +78 -0
- package/src/commands/workflow/validators/workflow-validator.test.ts +273 -0
- package/src/commands/workflow/validators/workflow-validator.ts +320 -0
- package/src/index.ts +19 -0
- package/src/plugin/apply.ts +103 -0
- package/src/plugin/discover.ts +219 -0
- package/src/plugin/index.ts +45 -0
- package/src/plugin/registry.ts +272 -0
- package/src/plugin/types.ts +165 -0
- package/src/services/context-handler.service.test.ts +501 -0
- package/src/services/context-handler.service.ts +372 -0
- package/src/services/environment.service.commands-prompt.test.ts +167 -0
- package/src/services/environment.service.ts +656 -0
- package/src/services/output.service.test.ts +92 -0
- package/src/services/output.service.ts +122 -0
- package/src/services/quiet-mode.service.test.ts +114 -0
- package/src/services/quiet-mode.service.ts +81 -0
- package/src/services/stream-output.service.test.ts +214 -0
- package/src/services/stream-output.service.ts +323 -0
- package/src/util/which.test.ts +101 -0
- package/src/util/which.ts +55 -0
|
@@ -0,0 +1,656 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Environment Service
|
|
3
|
+
*
|
|
4
|
+
* 封装 Environment 实例的创建和管理
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import path from "path";
|
|
8
|
+
import os from "os";
|
|
9
|
+
import fs from "fs/promises";
|
|
10
|
+
import {
|
|
11
|
+
BaseEnvironment,
|
|
12
|
+
ConfigComponent,
|
|
13
|
+
SessionComponent,
|
|
14
|
+
LLMComponent,
|
|
15
|
+
ToolComponent,
|
|
16
|
+
AgentComponent,
|
|
17
|
+
LogTraceComponent,
|
|
18
|
+
PromptComponent,
|
|
19
|
+
TaskComponent,
|
|
20
|
+
SkillComponent,
|
|
21
|
+
McpComponent,
|
|
22
|
+
CommandsComponent,
|
|
23
|
+
MemoryComponent,
|
|
24
|
+
EventSourceComponent,
|
|
25
|
+
EventSourceComponentInterface,
|
|
26
|
+
type Environment,
|
|
27
|
+
} from "@ai-setting/roy-agent-core";
|
|
28
|
+
// 导入 env/workflow 目录下的完整实现
|
|
29
|
+
import { WorkflowComponent } from "@ai-setting/roy-agent-core";
|
|
30
|
+
// 导入 MemoryPlugin
|
|
31
|
+
import { MemoryPlugin } from "@ai-setting/roy-agent-core";
|
|
32
|
+
// 导入 logger
|
|
33
|
+
import { createLogger } from "@ai-setting/roy-agent-core";
|
|
34
|
+
const logger = createLogger("environment-service");
|
|
35
|
+
import { OutputService } from "./output.service";
|
|
36
|
+
import { CliQuietModeService } from "./quiet-mode.service";
|
|
37
|
+
|
|
38
|
+
// Plugin 机制 - 使用全局注册表
|
|
39
|
+
import { globalPluginRegistry } from "../plugin/registry";
|
|
40
|
+
import type { ComponentPluginDefinition, ComponentPluginInstance } from "../plugin/types";
|
|
41
|
+
|
|
42
|
+
export interface EnvironmentServiceOptions {
|
|
43
|
+
/** 工作目录 */
|
|
44
|
+
workDir?: string;
|
|
45
|
+
/** 环境名称 */
|
|
46
|
+
envName?: string;
|
|
47
|
+
/** 配置文件路径(绝对路径) */
|
|
48
|
+
configPath?: string;
|
|
49
|
+
/** 安静模式 - 抑制日志输出到控制台(仅影响环境初始化日志) */
|
|
50
|
+
quiet?: boolean;
|
|
51
|
+
/** 要加载的插件名称列表 */
|
|
52
|
+
plugins?: string[];
|
|
53
|
+
/** Session 存储配置(用于测试隔离) */
|
|
54
|
+
sessionStorage?: {
|
|
55
|
+
/** 存储类型 */
|
|
56
|
+
type?: "memory" | "sqlite";
|
|
57
|
+
/** SQLite 数据库路径(当 type=sqlite 时) */
|
|
58
|
+
dbPath?: string;
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export interface ComponentInitOptions {
|
|
63
|
+
/** ConfigComponent 配置 */
|
|
64
|
+
configPath?: string;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* CLI 配置目录常量
|
|
69
|
+
*
|
|
70
|
+
* 优先级:
|
|
71
|
+
* 1. 环境变量 XDG_CONFIG_HOME/roy-agent(XDG 标准)
|
|
72
|
+
* 2. ~/.roy-agent(用户家目录,默认)
|
|
73
|
+
* 3. {workDir}/.roy-agent(工作目录,项目级覆盖)
|
|
74
|
+
*/
|
|
75
|
+
const CLI_CONFIG_DIR_NAME = "roy-agent";
|
|
76
|
+
const DEFAULT_CONFIG_FILE = "config.jsonc";
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* 获取 CLI 用户配置目录
|
|
80
|
+
*
|
|
81
|
+
* 按以下顺序查找:
|
|
82
|
+
* 1. $XDG_CONFIG_HOME/roy-agent(XDG 标准)
|
|
83
|
+
* 2. ~/.roy-agent(用户家目录,默认)
|
|
84
|
+
*/
|
|
85
|
+
function getUserConfigDir(): string {
|
|
86
|
+
// XDG_CONFIG_HOME 优先
|
|
87
|
+
const xdgConfigHome = process.env.XDG_CONFIG_HOME;
|
|
88
|
+
if (xdgConfigHome) {
|
|
89
|
+
return path.join(xdgConfigHome, CLI_CONFIG_DIR_NAME);
|
|
90
|
+
}
|
|
91
|
+
// 回退到 ~/.roy-agent
|
|
92
|
+
return path.join(os.homedir(), ".roy-agent");
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* EnvironmentService - CLI 环境服务
|
|
97
|
+
*
|
|
98
|
+
* 负责创建和管理 BaseEnvironment 实例及其组件
|
|
99
|
+
*/
|
|
100
|
+
export class EnvironmentService {
|
|
101
|
+
private environment: BaseEnvironment | null = null;
|
|
102
|
+
private output: OutputService;
|
|
103
|
+
private options: ComponentInitOptions = {};
|
|
104
|
+
|
|
105
|
+
// 组件实例(用于 getSession, getLLM 等方法)
|
|
106
|
+
private configComponent?: ConfigComponent;
|
|
107
|
+
private sessionComponent?: SessionComponent;
|
|
108
|
+
private llmComponent?: LLMComponent;
|
|
109
|
+
private toolComponent?: ToolComponent;
|
|
110
|
+
private agentComponent?: AgentComponent;
|
|
111
|
+
private skillComponent?: SkillComponent;
|
|
112
|
+
private logTraceComponent?: LogTraceComponent;
|
|
113
|
+
private promptComponent?: PromptComponent;
|
|
114
|
+
private taskComponent?: TaskComponent;
|
|
115
|
+
private mcpComponent?: McpComponent;
|
|
116
|
+
private commandsComponent?: CommandsComponent;
|
|
117
|
+
private memoryComponent?: MemoryComponent;
|
|
118
|
+
private memoryPlugin?: MemoryPlugin;
|
|
119
|
+
private eventSourceComponent?: EventSourceComponent;
|
|
120
|
+
private workflowComponent?: WorkflowComponent;
|
|
121
|
+
|
|
122
|
+
constructor(output?: OutputService) {
|
|
123
|
+
this.output = output ?? new OutputService();
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* 获取已创建的 Environment 实例
|
|
128
|
+
*/
|
|
129
|
+
getEnvironment(): BaseEnvironment | null {
|
|
130
|
+
return this.environment;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* 获取 SessionComponent 实例
|
|
135
|
+
*/
|
|
136
|
+
getSession(): SessionComponent | null {
|
|
137
|
+
return this.sessionComponent || null;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* 获取 LLMComponent 实例
|
|
142
|
+
*/
|
|
143
|
+
getLLM(): LLMComponent | null {
|
|
144
|
+
return this.llmComponent || null;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* 获取 ToolComponent 实例
|
|
149
|
+
*/
|
|
150
|
+
getTool(): ToolComponent | null {
|
|
151
|
+
return this.toolComponent || null;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* 获取 AgentComponent 实例
|
|
156
|
+
*/
|
|
157
|
+
getAgent(): AgentComponent | null {
|
|
158
|
+
return this.agentComponent || null;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* 获取 SkillComponent 实例
|
|
163
|
+
*/
|
|
164
|
+
getSkill(): SkillComponent | null {
|
|
165
|
+
return this.skillComponent || null;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* 获取 CommandsComponent 实例
|
|
170
|
+
*/
|
|
171
|
+
getCommands(): CommandsComponent | null {
|
|
172
|
+
return this.commandsComponent || null;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* 获取 LogTraceComponent 实例
|
|
177
|
+
*/
|
|
178
|
+
getLogTrace(): LogTraceComponent | null {
|
|
179
|
+
return this.logTraceComponent || null;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* 获取 MemoryPlugin 实例(用于 shared instances with MemoryComponent)
|
|
184
|
+
*/
|
|
185
|
+
getMemoryPlugin(): MemoryPlugin | null {
|
|
186
|
+
return this.memoryPlugin || null;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* 释放环境资源
|
|
191
|
+
*/
|
|
192
|
+
async dispose(): Promise<void> {
|
|
193
|
+
// 停止所有组件
|
|
194
|
+
for (const component of [
|
|
195
|
+
this.eventSourceComponent,
|
|
196
|
+
this.memoryComponent,
|
|
197
|
+
this.commandsComponent,
|
|
198
|
+
this.mcpComponent,
|
|
199
|
+
this.skillComponent,
|
|
200
|
+
this.taskComponent,
|
|
201
|
+
this.promptComponent,
|
|
202
|
+
this.logTraceComponent,
|
|
203
|
+
this.agentComponent,
|
|
204
|
+
this.toolComponent,
|
|
205
|
+
this.sessionComponent,
|
|
206
|
+
this.llmComponent,
|
|
207
|
+
this.configComponent,
|
|
208
|
+
this.workflowComponent,
|
|
209
|
+
]) {
|
|
210
|
+
if (component && typeof component.stop === "function") {
|
|
211
|
+
try {
|
|
212
|
+
await component.stop();
|
|
213
|
+
} catch (e) {
|
|
214
|
+
// ignore
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
this.environment = null;
|
|
220
|
+
this.sessionComponent = undefined;
|
|
221
|
+
this.llmComponent = undefined;
|
|
222
|
+
this.toolComponent = undefined;
|
|
223
|
+
this.agentComponent = undefined;
|
|
224
|
+
this.skillComponent = undefined;
|
|
225
|
+
this.logTraceComponent = undefined;
|
|
226
|
+
this.configComponent = undefined;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* 创建 CLI 环境
|
|
231
|
+
*
|
|
232
|
+
* 配置目录优先级:
|
|
233
|
+
* 1. --config 参数指定的路径
|
|
234
|
+
* 2. 工作目录下的 .roy-agent/config.jsonc(项目级覆盖)
|
|
235
|
+
* 3. ~/.roy-agent/config.jsonc(用户级默认)
|
|
236
|
+
*/
|
|
237
|
+
async create(options?: EnvironmentServiceOptions): Promise<BaseEnvironment> {
|
|
238
|
+
const workDir = options?.workDir ?? process.cwd();
|
|
239
|
+
const envName = options?.envName ?? "roy-cli";
|
|
240
|
+
|
|
241
|
+
// 启用安静模式(抑制日志输出到控制台)
|
|
242
|
+
if (options?.quiet) {
|
|
243
|
+
CliQuietModeService.getInstance().setQuiet(true);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// 确定配置文件路径
|
|
247
|
+
let configPath = options?.configPath;
|
|
248
|
+
let configDir: string;
|
|
249
|
+
|
|
250
|
+
if (!configPath) {
|
|
251
|
+
// 1. 优先使用工作目录下的配置(项目级覆盖)
|
|
252
|
+
const workDirConfigPath = path.join(workDir, CLI_CONFIG_DIR_NAME, DEFAULT_CONFIG_FILE);
|
|
253
|
+
try {
|
|
254
|
+
await fs.access(workDirConfigPath);
|
|
255
|
+
configPath = workDirConfigPath;
|
|
256
|
+
configDir = path.dirname(configPath);
|
|
257
|
+
} catch {
|
|
258
|
+
// 2. 回退到用户家目录 ~/.roy-agent
|
|
259
|
+
configDir = getUserConfigDir();
|
|
260
|
+
configPath = path.join(configDir, DEFAULT_CONFIG_FILE);
|
|
261
|
+
}
|
|
262
|
+
} else {
|
|
263
|
+
configDir = path.dirname(configPath);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// 确保配置文件目录存在(如果需要初始化的话)
|
|
267
|
+
try {
|
|
268
|
+
await fs.mkdir(configDir, { recursive: true });
|
|
269
|
+
} catch {
|
|
270
|
+
// 目录已存在,忽略错误
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// 保存配置选项
|
|
274
|
+
this.options = {
|
|
275
|
+
configPath,
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
// 1. 创建 BaseEnvironment
|
|
279
|
+
const env = new BaseEnvironment({
|
|
280
|
+
name: envName,
|
|
281
|
+
version: "1.0.0",
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
// 2. 创建并注册核心组件
|
|
285
|
+
|
|
286
|
+
// ConfigComponent - 必须在其他组件之前创建
|
|
287
|
+
this.configComponent = new ConfigComponent();
|
|
288
|
+
const configComponent = this.configComponent;
|
|
289
|
+
|
|
290
|
+
// 设置 XDG_DATA_HOME 为配置文件目录,这样 relativePath 可以正常工作
|
|
291
|
+
// 注意:这会影响到所有组件的路径解析
|
|
292
|
+
configComponent.setXdgDataHome(configDir);
|
|
293
|
+
|
|
294
|
+
// LLMComponent - 需要 ConfigComponent 和配置文件路径
|
|
295
|
+
// 注意:必须在 env.init() 之前初始化,传入配置选项
|
|
296
|
+
this.llmComponent = new LLMComponent();
|
|
297
|
+
const llmComponent = this.llmComponent;
|
|
298
|
+
await llmComponent.init({
|
|
299
|
+
name: "llm",
|
|
300
|
+
version: "1.0.0",
|
|
301
|
+
enabled: true,
|
|
302
|
+
env,
|
|
303
|
+
options: {
|
|
304
|
+
configComponent,
|
|
305
|
+
configPath: path.basename(configPath), // 使用相对路径
|
|
306
|
+
},
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
// SessionComponent
|
|
310
|
+
this.sessionComponent = new SessionComponent();
|
|
311
|
+
const sessionComponent = this.sessionComponent;
|
|
312
|
+
|
|
313
|
+
// 构建 session 配置
|
|
314
|
+
const sessionConfig: Record<string, unknown> = {};
|
|
315
|
+
if (options?.sessionStorage) {
|
|
316
|
+
if (options.sessionStorage.type) {
|
|
317
|
+
sessionConfig.storage = { type: options.sessionStorage.type };
|
|
318
|
+
}
|
|
319
|
+
if (options.sessionStorage.dbPath) {
|
|
320
|
+
sessionConfig.storage = {
|
|
321
|
+
...(sessionConfig.storage as Record<string, unknown> || {}),
|
|
322
|
+
dbPath: options.sessionStorage.dbPath,
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
await sessionComponent.init({
|
|
328
|
+
name: "session",
|
|
329
|
+
version: "1.0.0",
|
|
330
|
+
enabled: true,
|
|
331
|
+
env,
|
|
332
|
+
options: { configComponent, config: Object.keys(sessionConfig).length > 0 ? sessionConfig : undefined },
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
// ToolComponent
|
|
336
|
+
this.toolComponent = new ToolComponent();
|
|
337
|
+
const toolComponent = this.toolComponent;
|
|
338
|
+
await toolComponent.init({
|
|
339
|
+
name: "tool",
|
|
340
|
+
version: "1.0.0",
|
|
341
|
+
enabled: true,
|
|
342
|
+
env,
|
|
343
|
+
options: { configComponent },
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
// AgentComponent
|
|
347
|
+
this.agentComponent = new AgentComponent();
|
|
348
|
+
const agentComponent = this.agentComponent;
|
|
349
|
+
await agentComponent.init({
|
|
350
|
+
name: "agent",
|
|
351
|
+
version: "1.0.0",
|
|
352
|
+
enabled: true,
|
|
353
|
+
env,
|
|
354
|
+
options: {
|
|
355
|
+
configComponent,
|
|
356
|
+
configPath: path.basename(configPath), // 传入配置路径,以便从配置文件中加载 agent 配置
|
|
357
|
+
},
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
// LogTraceComponent
|
|
361
|
+
this.logTraceComponent = new LogTraceComponent();
|
|
362
|
+
const logTraceComponent = this.logTraceComponent;
|
|
363
|
+
await logTraceComponent.init({
|
|
364
|
+
name: "log-trace",
|
|
365
|
+
version: "1.0.0",
|
|
366
|
+
enabled: true,
|
|
367
|
+
env,
|
|
368
|
+
options: {
|
|
369
|
+
configComponent,
|
|
370
|
+
configPath: path.basename(configPath), // 传入相对路径,让 LogTraceComponent 从 XDG_DATA_HOME 读取
|
|
371
|
+
},
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
// PromptComponent
|
|
375
|
+
this.promptComponent = new PromptComponent();
|
|
376
|
+
const promptComponent = this.promptComponent;
|
|
377
|
+
await promptComponent.init({
|
|
378
|
+
name: "prompt",
|
|
379
|
+
version: "1.0.0",
|
|
380
|
+
enabled: true,
|
|
381
|
+
env,
|
|
382
|
+
options: { configComponent },
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
// TaskComponent
|
|
386
|
+
this.taskComponent = new TaskComponent();
|
|
387
|
+
const taskComponent = this.taskComponent;
|
|
388
|
+
await taskComponent.init({
|
|
389
|
+
name: "task",
|
|
390
|
+
version: "1.0.0",
|
|
391
|
+
enabled: true,
|
|
392
|
+
env,
|
|
393
|
+
options: { configComponent },
|
|
394
|
+
});
|
|
395
|
+
|
|
396
|
+
// SkillComponent
|
|
397
|
+
this.skillComponent = new SkillComponent();
|
|
398
|
+
const skillComponent = this.skillComponent;
|
|
399
|
+
await skillComponent.init({
|
|
400
|
+
name: "skill",
|
|
401
|
+
version: "1.0.0",
|
|
402
|
+
enabled: true,
|
|
403
|
+
env,
|
|
404
|
+
options: { configComponent },
|
|
405
|
+
});
|
|
406
|
+
|
|
407
|
+
// McpComponent - 必须在 ToolComponent 之后初始化,以便注册 MCP tools
|
|
408
|
+
this.mcpComponent = new McpComponent();
|
|
409
|
+
const mcpComponent = this.mcpComponent;
|
|
410
|
+
await mcpComponent.init({
|
|
411
|
+
name: "mcp",
|
|
412
|
+
version: "1.0.0",
|
|
413
|
+
enabled: true,
|
|
414
|
+
env,
|
|
415
|
+
options: { configComponent },
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
// CommandsComponent
|
|
419
|
+
this.commandsComponent = new CommandsComponent();
|
|
420
|
+
const commandsComponent = this.commandsComponent;
|
|
421
|
+
await commandsComponent.init({
|
|
422
|
+
name: "commands",
|
|
423
|
+
version: "2.0.0",
|
|
424
|
+
enabled: true,
|
|
425
|
+
env,
|
|
426
|
+
options: { configComponent },
|
|
427
|
+
});
|
|
428
|
+
|
|
429
|
+
// MemoryPlugin - 在 MemoryComponent 之前创建,以便共享实例
|
|
430
|
+
const memoryWorkDir = (workDir as any) || process.cwd();
|
|
431
|
+
this.memoryPlugin = new MemoryPlugin(memoryWorkDir, {
|
|
432
|
+
enabled: true,
|
|
433
|
+
} as any);
|
|
434
|
+
await this.memoryPlugin.initialize();
|
|
435
|
+
|
|
436
|
+
// MemoryComponent - 必须在 ToolComponent 之后初始化,以便注册 memory tools
|
|
437
|
+
// 传入 memoryPlugin 以复用其 DraftManager/Organizer 实例
|
|
438
|
+
this.memoryComponent = new MemoryComponent();
|
|
439
|
+
const memoryComponent = this.memoryComponent;
|
|
440
|
+
await memoryComponent.init({
|
|
441
|
+
name: "memory",
|
|
442
|
+
version: "1.0.0",
|
|
443
|
+
enabled: true,
|
|
444
|
+
env,
|
|
445
|
+
options: { configComponent, memoryPlugin: this.memoryPlugin },
|
|
446
|
+
});
|
|
447
|
+
|
|
448
|
+
// EventSourceComponent - 事件源组件
|
|
449
|
+
this.eventSourceComponent = new EventSourceComponent();
|
|
450
|
+
const eventSourceComponent = this.eventSourceComponent;
|
|
451
|
+
await eventSourceComponent.init({
|
|
452
|
+
name: "event-source",
|
|
453
|
+
version: "1.0.0",
|
|
454
|
+
enabled: true,
|
|
455
|
+
env,
|
|
456
|
+
options: { configComponent },
|
|
457
|
+
});
|
|
458
|
+
|
|
459
|
+
// 启用安静模式(必须在 WorkflowComponent 初始化之前设置)
|
|
460
|
+
if (options?.quiet) {
|
|
461
|
+
CliQuietModeService.getInstance().setQuiet(true);
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
// WorkflowComponent - 工作流组件
|
|
465
|
+
this.workflowComponent = new WorkflowComponent();
|
|
466
|
+
const workflowComponent = this.workflowComponent;
|
|
467
|
+
await workflowComponent.init({
|
|
468
|
+
env,
|
|
469
|
+
options: {
|
|
470
|
+
configComponent,
|
|
471
|
+
toolComponent,
|
|
472
|
+
llmComponent,
|
|
473
|
+
logTraceComponent,
|
|
474
|
+
skillRegistry: skillComponent,
|
|
475
|
+
env,
|
|
476
|
+
},
|
|
477
|
+
} as any);
|
|
478
|
+
await workflowComponent.start();
|
|
479
|
+
|
|
480
|
+
// 注册所有组件
|
|
481
|
+
env.registerComponent(configComponent);
|
|
482
|
+
env.registerComponent(sessionComponent);
|
|
483
|
+
env.registerComponent(llmComponent);
|
|
484
|
+
env.registerComponent(toolComponent);
|
|
485
|
+
env.registerComponent(agentComponent);
|
|
486
|
+
env.registerComponent(logTraceComponent);
|
|
487
|
+
env.registerComponent(promptComponent);
|
|
488
|
+
env.registerComponent(taskComponent);
|
|
489
|
+
env.registerComponent(skillComponent);
|
|
490
|
+
env.registerComponent(mcpComponent);
|
|
491
|
+
env.registerComponent(commandsComponent);
|
|
492
|
+
env.registerComponent(memoryComponent);
|
|
493
|
+
env.registerComponent(eventSourceComponent as any);
|
|
494
|
+
env.registerComponent(workflowComponent);
|
|
495
|
+
|
|
496
|
+
// 注册 commands-prompt Hook(将收藏命令动态注入 default prompt)
|
|
497
|
+
this.registerCommandsPromptHook(promptComponent, commandsComponent);
|
|
498
|
+
|
|
499
|
+
// 初始化 BaseEnvironment
|
|
500
|
+
await env.init();
|
|
501
|
+
|
|
502
|
+
// 启动 Environment - 触发所有组件的 start() 生命周期钩子
|
|
503
|
+
// 这对于依赖 start() 注册工具的组件至关重要(如 TaskComponent, MemoryComponent、SkillComponent)
|
|
504
|
+
await env.start();
|
|
505
|
+
|
|
506
|
+
// 刷新 AgentComponent 的依赖组件引用
|
|
507
|
+
// 原因:AgentComponent 在 onInit() 时调用 refreshDependencies(),
|
|
508
|
+
// 但此时 env.start() 还未被调用,组件尚未注册到 Environment
|
|
509
|
+
// 所以需要在 env.start() 之后再次刷新,确保 AgentComponent 能获取到 LLMComponent
|
|
510
|
+
agentComponent.refreshDependencies();
|
|
511
|
+
logger.debug("[EnvironmentService] AgentComponent dependencies refreshed after env.start()");
|
|
512
|
+
|
|
513
|
+
// 加载 Component Plugins(组件插件)
|
|
514
|
+
if (options?.plugins && options.plugins.length > 0) {
|
|
515
|
+
await this.loadComponentPlugins(options.plugins, {
|
|
516
|
+
task: taskComponent,
|
|
517
|
+
memory: memoryComponent,
|
|
518
|
+
llm: llmComponent,
|
|
519
|
+
});
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
// 加载 Coder Plugins
|
|
523
|
+
if (options?.plugins && options.plugins.length > 0) {
|
|
524
|
+
await this.loadCoderPlugins(options.plugins);
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
this.environment = env;
|
|
528
|
+
return env;
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
/**
|
|
532
|
+
* 注册 commands-prompt Hook
|
|
533
|
+
*
|
|
534
|
+
* 将收藏命令的能力动态注入 default prompt
|
|
535
|
+
*/
|
|
536
|
+
private registerCommandsPromptHook(
|
|
537
|
+
promptComponent: PromptComponent,
|
|
538
|
+
commandsComponent: CommandsComponent
|
|
539
|
+
): void {
|
|
540
|
+
(promptComponent as any).addHook("prompt.after-render", "commands-prompt", async (ctx: any) => {
|
|
541
|
+
// 只对 default prompt 注入
|
|
542
|
+
const data = ctx.data as { name: string; renderedContent: string };
|
|
543
|
+
if (data.name !== "default") return;
|
|
544
|
+
|
|
545
|
+
try {
|
|
546
|
+
const metaList = await commandsComponent.getCommandsMeta();
|
|
547
|
+
|
|
548
|
+
// 无命令时不追加内容
|
|
549
|
+
if (metaList.length === 0) return;
|
|
550
|
+
|
|
551
|
+
const commandsSection = this.renderCommandsSection(metaList);
|
|
552
|
+
data.renderedContent += "\n\n" + commandsSection;
|
|
553
|
+
|
|
554
|
+
this.output.log(`[Environment] Injected ${metaList.length} commands into default prompt`);
|
|
555
|
+
} catch (error) {
|
|
556
|
+
this.output.error(`Failed to inject commands into prompt: ${error}`);
|
|
557
|
+
}
|
|
558
|
+
});
|
|
559
|
+
|
|
560
|
+
this.output.info("Registered commands-prompt Hook");
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
/**
|
|
564
|
+
* 加载组件插件
|
|
565
|
+
*
|
|
566
|
+
* 通用方法,从全局注册表获取组件插件定义,加载并注册到目标组件
|
|
567
|
+
*/
|
|
568
|
+
private async loadComponentPlugins(
|
|
569
|
+
pluginNames: string[],
|
|
570
|
+
components: Record<string, unknown>
|
|
571
|
+
): Promise<void> {
|
|
572
|
+
for (const name of pluginNames) {
|
|
573
|
+
try {
|
|
574
|
+
// 从全局注册表获取组件插件定义
|
|
575
|
+
const definition = globalPluginRegistry.getComponentPlugin(name);
|
|
576
|
+
if (!definition) {
|
|
577
|
+
this.output.warn(`[EnvironmentService] Component plugin not found: ${name}`);
|
|
578
|
+
continue;
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
// 创建插件实例
|
|
582
|
+
const plugin = await definition.factory();
|
|
583
|
+
|
|
584
|
+
// 注册到目标组件
|
|
585
|
+
const targetComponent = components[definition.targetComponent];
|
|
586
|
+
if (targetComponent && typeof (targetComponent as any).registerPlugin === "function") {
|
|
587
|
+
(targetComponent as any).registerPlugin(plugin);
|
|
588
|
+
|
|
589
|
+
// 验证插件已注册
|
|
590
|
+
const registeredPlugins = (targetComponent as any).listPlugins?.() || [];
|
|
591
|
+
const isRegistered = registeredPlugins.some((p: any) => p.name === plugin.name);
|
|
592
|
+
|
|
593
|
+
this.output.log(`[EnvironmentService] Loaded component plugin: ${name} -> ${definition.targetComponent}`);
|
|
594
|
+
this.output.log(`[EnvironmentService] Plugin verified: ${isRegistered ? "YES" : "NO"} (${plugin.name} in ${definition.targetComponent})`);
|
|
595
|
+
} else {
|
|
596
|
+
this.output.warn(`[EnvironmentService] Target component not found or not registerable: ${definition.targetComponent}`);
|
|
597
|
+
}
|
|
598
|
+
} catch (error) {
|
|
599
|
+
this.output.error(`[EnvironmentService] Error loading component plugin "${name}": ${error}`);
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
/**
|
|
605
|
+
* 渲染命令表格 section
|
|
606
|
+
*/
|
|
607
|
+
private renderCommandsSection(metaList: { name: string; description: string }[]): string {
|
|
608
|
+
// 按名称排序
|
|
609
|
+
const sortedMeta = [...metaList].sort((a, b) => a.name.localeCompare(b.name));
|
|
610
|
+
|
|
611
|
+
const rows = sortedMeta
|
|
612
|
+
.map(m => `| \`${m.name}\` | ${m.description} |`)
|
|
613
|
+
.join("\n");
|
|
614
|
+
|
|
615
|
+
return `## Your Commands Arsenal
|
|
616
|
+
|
|
617
|
+
You have access to these custom commands:
|
|
618
|
+
|
|
619
|
+
| Command | Description |
|
|
620
|
+
|---------|-------------|
|
|
621
|
+
${rows}
|
|
622
|
+
|
|
623
|
+
Progressive Discovery: Run \`<command> --help\` to explore full capabilities.`;
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
/**
|
|
627
|
+
* 加载 Coder Plugins
|
|
628
|
+
*
|
|
629
|
+
* 根据插件名称列表加载对应的 coder-harness 插件
|
|
630
|
+
*/
|
|
631
|
+
private async loadCoderPlugins(pluginNames: string[]): Promise<void> {
|
|
632
|
+
const { createPluginHookAdapter } = await import("@ai-setting/roy-agent-coder-harness");
|
|
633
|
+
const { globalHookManager } = await import("@ai-setting/roy-agent-core");
|
|
634
|
+
|
|
635
|
+
// 确定要加载的插件配置
|
|
636
|
+
const pluginConfigs = {
|
|
637
|
+
enableLSP: pluginNames.includes("lsp"),
|
|
638
|
+
enableCodeCheck: pluginNames.includes("code-check"),
|
|
639
|
+
enableReminder: pluginNames.includes("reminder"),
|
|
640
|
+
};
|
|
641
|
+
|
|
642
|
+
// 只有在有需要时才创建 adapter
|
|
643
|
+
if (!pluginConfigs.enableLSP && !pluginConfigs.enableCodeCheck && !pluginConfigs.enableReminder) {
|
|
644
|
+
return;
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
// 使用全局 HookManager
|
|
648
|
+
const hookManager = globalHookManager;
|
|
649
|
+
|
|
650
|
+
// 创建并初始化 PluginHookAdapter
|
|
651
|
+
const adapter = createPluginHookAdapter(hookManager, pluginConfigs);
|
|
652
|
+
await adapter.initialize();
|
|
653
|
+
|
|
654
|
+
this.output.log(`[EnvironmentService] Loaded coder plugins: ${pluginNames.join(", ")}`);
|
|
655
|
+
}
|
|
656
|
+
}
|