@ai-setting/roy-agent-core 1.4.13 → 1.4.14
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/packages/core/src/config/index.js +32 -0
- package/dist/packages/core/src/env/agent/index.js +24 -0
- package/dist/packages/core/src/env/commands/index.js +14 -0
- package/dist/packages/core/src/env/debug/formatters/index.js +11 -0
- package/dist/packages/core/src/env/debug/index.js +26 -0
- package/dist/packages/core/src/env/hook/index.js +29 -0
- package/dist/packages/core/src/env/index.js +81 -0
- package/dist/packages/core/src/env/llm/index.js +40 -0
- package/dist/packages/core/src/env/log-trace/index.js +83 -0
- package/dist/packages/core/src/env/mcp/index.js +39 -0
- package/dist/packages/core/src/env/mcp/tool/index.js +14 -0
- package/dist/packages/core/src/env/memory/built-in/index.js +11 -0
- package/dist/packages/core/src/env/memory/index.js +56 -0
- package/dist/packages/core/src/env/memory/plugin/index.js +36 -0
- package/dist/packages/core/src/env/prompt/index.js +20 -0
- package/dist/packages/core/src/env/session/index.js +25 -0
- package/dist/packages/core/src/env/session/storage/index.js +18 -0
- package/dist/packages/core/src/env/skill/index.js +34 -0
- package/dist/packages/core/src/env/skill/tool/index.js +9 -0
- package/dist/packages/core/src/env/task/delegate/index.js +18 -0
- package/dist/packages/core/src/env/task/hooks/index.js +7 -0
- package/dist/packages/core/src/env/task/index.js +30 -0
- package/dist/packages/core/src/env/task/plugins/index.js +23 -0
- package/dist/packages/core/src/env/task/storage/index.js +14 -0
- package/dist/packages/core/src/env/task/tools/index.js +17 -0
- package/dist/packages/core/src/env/task/tools/operation/index.js +15 -0
- package/dist/{shared/chunk-1d4rwms4.js → packages/core/src/env/tool/built-in/index.js} +4 -4
- package/dist/packages/core/src/env/tool/index.js +39 -0
- package/dist/packages/core/src/env/workflow/decorators/index.js +27 -0
- package/dist/packages/core/src/env/workflow/engine/index.js +28 -0
- package/dist/packages/core/src/env/workflow/index.js +132 -0
- package/dist/packages/core/src/env/workflow/nodes/index.js +19 -0
- package/dist/packages/core/src/env/workflow/service/index.js +13 -0
- package/dist/packages/core/src/env/workflow/storage/index.js +27 -0
- package/dist/packages/core/src/env/workflow/tools/index.js +159 -0
- package/dist/packages/core/src/env/workflow/types/index.js +94 -0
- package/dist/packages/core/src/env/workflow/utils/index.js +637 -0
- package/dist/packages/core/src/index.js +398 -0
- package/dist/shared/@ai-setting/roy-agent-core-04fm8177.js +393 -0
- package/dist/shared/@ai-setting/roy-agent-core-04qgbjbe.js +172 -0
- package/dist/shared/@ai-setting/roy-agent-core-084qqd7t.js +11 -0
- package/dist/shared/@ai-setting/roy-agent-core-0gekht4e.js +1130 -0
- package/dist/shared/@ai-setting/roy-agent-core-0hdry23r.js +419 -0
- package/dist/shared/{chunk-ze20rksg.js → @ai-setting/roy-agent-core-0sgn3de4.js} +2 -2
- package/dist/shared/@ai-setting/roy-agent-core-12x57kf1.js +286 -0
- package/dist/shared/@ai-setting/roy-agent-core-1f3xrrm6.js +393 -0
- package/dist/shared/{chunk-q9j99fsm.js → @ai-setting/roy-agent-core-1k28kg7h.js} +6 -6
- package/dist/shared/{chunk-2b5kbhx3.js → @ai-setting/roy-agent-core-1z1zv5g8.js} +9 -117
- package/dist/shared/@ai-setting/roy-agent-core-2hqxnaf3.js +851 -0
- package/dist/shared/@ai-setting/roy-agent-core-3dfq8awb.js +587 -0
- package/dist/shared/@ai-setting/roy-agent-core-3takar0s.js +93 -0
- package/dist/shared/@ai-setting/roy-agent-core-3tnb2005.js +117 -0
- package/dist/shared/@ai-setting/roy-agent-core-4vmcvkav.js +14 -0
- package/dist/shared/{chunk-25x2pdtp.js → @ai-setting/roy-agent-core-4ws8atva.js} +2 -2
- package/dist/shared/@ai-setting/roy-agent-core-5fbp24se.js +603 -0
- package/dist/shared/@ai-setting/roy-agent-core-5my94ywp.js +66 -0
- package/dist/shared/{chunk-rncy3rtd.js → @ai-setting/roy-agent-core-6j0zcmwk.js} +128 -1122
- package/dist/shared/@ai-setting/roy-agent-core-6w4pmxc7.js +266 -0
- package/dist/shared/{chunk-yqmx37vm.js → @ai-setting/roy-agent-core-7vrk3add.js} +2 -2
- package/dist/shared/@ai-setting/roy-agent-core-8dvbn7tw.js +64 -0
- package/dist/shared/@ai-setting/roy-agent-core-8mbmrwzs.js +171 -0
- package/dist/shared/@ai-setting/roy-agent-core-8wzz66qe.js +620 -0
- package/dist/shared/@ai-setting/roy-agent-core-9ykq91jc.js +762 -0
- package/dist/shared/@ai-setting/roy-agent-core-dde19zke.js +1305 -0
- package/dist/shared/@ai-setting/roy-agent-core-f7g67gce.js +913 -0
- package/dist/shared/@ai-setting/roy-agent-core-fq5mtxsy.js +341 -0
- package/dist/shared/@ai-setting/roy-agent-core-fvd9g6k8.js +1205 -0
- package/dist/shared/@ai-setting/roy-agent-core-gv1hrn3x.js +378 -0
- package/dist/shared/@ai-setting/roy-agent-core-gy0wp5h7.js +213 -0
- package/dist/shared/{chunk-mf5xqbdh.js → @ai-setting/roy-agent-core-hyza1gm7.js} +3 -2
- package/dist/shared/{chunk-1qwabsm0.js → @ai-setting/roy-agent-core-j8zx62zr.js} +2 -2
- package/dist/shared/@ai-setting/roy-agent-core-jb2exr0d.js +442 -0
- package/dist/shared/@ai-setting/roy-agent-core-jv3b7v9w.js +57 -0
- package/dist/shared/@ai-setting/roy-agent-core-k1rxf9ya.js +513 -0
- package/dist/shared/@ai-setting/roy-agent-core-kydc9nwb.js +60 -0
- package/dist/shared/@ai-setting/roy-agent-core-m2x48hw6.js +97 -0
- package/dist/shared/@ai-setting/roy-agent-core-m6y668cc.js +377 -0
- package/dist/shared/{chunk-1aakcfp1.js → @ai-setting/roy-agent-core-nczzf0ms.js} +3 -3
- package/dist/shared/@ai-setting/roy-agent-core-nfj6knp5.js +36 -0
- package/dist/shared/@ai-setting/roy-agent-core-ntrp979d.js +204 -0
- package/dist/shared/@ai-setting/roy-agent-core-pd7g8z5v.js +1387 -0
- package/dist/shared/@ai-setting/roy-agent-core-pzk1syce.js +14 -0
- package/dist/shared/@ai-setting/roy-agent-core-q50tg9m2.js +862 -0
- package/dist/shared/{chunk-9qzt1v1p.js → @ai-setting/roy-agent-core-qg9tcaph.js} +3 -2
- package/dist/shared/{chunk-91bas8w5.js → @ai-setting/roy-agent-core-qhyerewk.js} +5 -5
- package/dist/shared/{chunk-1pf5mfgd.js → @ai-setting/roy-agent-core-qxybm159.js} +3 -3
- package/dist/shared/{chunk-g6j5n3gv.js → @ai-setting/roy-agent-core-rh9dpkpw.js} +10 -10
- package/dist/shared/{chunk-0q6s9wm6.js → @ai-setting/roy-agent-core-rr9p1g43.js} +7 -51
- package/dist/shared/@ai-setting/roy-agent-core-sbzvpfn7.js +284 -0
- package/dist/shared/@ai-setting/roy-agent-core-t22nqt4d.js +788 -0
- package/dist/shared/{chunk-t1rh6jtm.js → @ai-setting/roy-agent-core-tkr5ynkh.js} +8 -13
- package/dist/shared/@ai-setting/roy-agent-core-v4aabsf0.js +303 -0
- package/dist/shared/@ai-setting/roy-agent-core-w75rafhy.js +202 -0
- package/dist/shared/@ai-setting/roy-agent-core-w76hqkmg.js +584 -0
- package/dist/shared/@ai-setting/roy-agent-core-yfbgwes2.js +408 -0
- package/dist/shared/{chunk-a9qmy3sc.js → @ai-setting/roy-agent-core-yn761yve.js} +7 -4
- package/dist/shared/@ai-setting/roy-agent-core-yrzmn4m1.js +492 -0
- package/dist/shared/@ai-setting/roy-agent-core-yt8wdh2w.js +206 -0
- package/package.json +8 -7
- package/dist/index.d.ts +0 -7825
- package/dist/index.js +0 -16551
- package/dist/shared/chunk-hs7tbmje.js +0 -24
- /package/dist/shared/{chunk-wbkh7wat.js → @ai-setting/roy-agent-core-fs0mn2jk.js} +0 -0
|
@@ -0,0 +1,492 @@
|
|
|
1
|
+
import {
|
|
2
|
+
envKeyToConfigKey,
|
|
3
|
+
toEnvKey
|
|
4
|
+
} from "./roy-agent-core-jv3b7v9w.js";
|
|
5
|
+
import {
|
|
6
|
+
BaseComponent
|
|
7
|
+
} from "./roy-agent-core-m2x48hw6.js";
|
|
8
|
+
import {
|
|
9
|
+
createLogger,
|
|
10
|
+
init_logger
|
|
11
|
+
} from "./roy-agent-core-yn761yve.js";
|
|
12
|
+
|
|
13
|
+
// packages/core/src/env/commands/commands-component.ts
|
|
14
|
+
import path2 from "path";
|
|
15
|
+
import os from "os";
|
|
16
|
+
init_logger();
|
|
17
|
+
import * as fs2 from "fs/promises";
|
|
18
|
+
import { exec } from "child_process";
|
|
19
|
+
import { promisify } from "util";
|
|
20
|
+
|
|
21
|
+
// packages/core/src/env/commands/parser.ts
|
|
22
|
+
init_logger();
|
|
23
|
+
import fs from "fs/promises";
|
|
24
|
+
import path from "path";
|
|
25
|
+
var logger = createLogger("commands-parser");
|
|
26
|
+
function parseFrontmatter(text) {
|
|
27
|
+
const result = {};
|
|
28
|
+
const lines = text.split(`
|
|
29
|
+
`);
|
|
30
|
+
for (const line of lines) {
|
|
31
|
+
const match = line.match(/^(\w+):\s*"?(.+?)"?\s*$/);
|
|
32
|
+
if (match) {
|
|
33
|
+
const key = match[1];
|
|
34
|
+
let value = match[2];
|
|
35
|
+
if (value.startsWith('"') && value.endsWith('"')) {
|
|
36
|
+
value = value.slice(1, -1);
|
|
37
|
+
}
|
|
38
|
+
result[key] = value;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return result;
|
|
42
|
+
}
|
|
43
|
+
async function parseMetaFile(filePath, source) {
|
|
44
|
+
try {
|
|
45
|
+
const content = await fs.readFile(filePath, "utf-8");
|
|
46
|
+
const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---\n?/);
|
|
47
|
+
if (!frontmatterMatch) {
|
|
48
|
+
logger.warn(`[Parser] Missing frontmatter in: ${filePath}`);
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
const frontmatter = parseFrontmatter(frontmatterMatch[1]);
|
|
52
|
+
const name = frontmatter.name;
|
|
53
|
+
const description = frontmatter.description;
|
|
54
|
+
if (!name) {
|
|
55
|
+
logger.warn(`[Parser] Missing name in frontmatter: ${filePath}`);
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
if (!description) {
|
|
59
|
+
logger.warn(`[Parser] Missing description in frontmatter: ${filePath}`);
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
return {
|
|
63
|
+
name,
|
|
64
|
+
description,
|
|
65
|
+
tips: frontmatter.tips,
|
|
66
|
+
filePath,
|
|
67
|
+
source
|
|
68
|
+
};
|
|
69
|
+
} catch (error) {
|
|
70
|
+
logger.error(`[Parser] Failed to parse file: ${filePath}`, error);
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// packages/core/src/env/commands/commands-config-registration.ts
|
|
76
|
+
var COMMANDS_CONFIG_REGISTRATION = {
|
|
77
|
+
name: "command",
|
|
78
|
+
sources: [
|
|
79
|
+
{ type: "env", envPrefix: "COMMAND", priority: 20, watch: false }
|
|
80
|
+
],
|
|
81
|
+
keys: [
|
|
82
|
+
{ key: "command.userCommandsDir", sources: ["env", "file"] },
|
|
83
|
+
{ key: "command.projectCommandsDir", sources: ["env", "file"] },
|
|
84
|
+
{ key: "command.cacheDir", sources: ["env", "file"] }
|
|
85
|
+
]
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
// packages/core/src/env/commands/commands-component.ts
|
|
89
|
+
var execAsync = promisify(exec);
|
|
90
|
+
var logger2 = createLogger("commands");
|
|
91
|
+
var DEFAULT_USER_DIR = ".roy-agent/commands";
|
|
92
|
+
var DEFAULT_PROJECT_DIR = ".roy/commands";
|
|
93
|
+
var DEFAULT_CACHE_DIR = ".cache/roy-agent/commands";
|
|
94
|
+
var DESCRIPTIONS_FILE = "descriptions.json";
|
|
95
|
+
var CONFIG_KEYS = [
|
|
96
|
+
"command.userCommandsDir",
|
|
97
|
+
"command.projectCommandsDir",
|
|
98
|
+
"command.cacheDir"
|
|
99
|
+
];
|
|
100
|
+
|
|
101
|
+
class CommandsComponent extends BaseComponent {
|
|
102
|
+
name = "commands";
|
|
103
|
+
version = "2.0.0";
|
|
104
|
+
config;
|
|
105
|
+
configComponent;
|
|
106
|
+
userCommandsDir = "";
|
|
107
|
+
projectCommandsDir = "";
|
|
108
|
+
descriptionCache = new Map;
|
|
109
|
+
descriptionCacheFile = "";
|
|
110
|
+
constructor() {
|
|
111
|
+
super();
|
|
112
|
+
this.initDefaultDirs();
|
|
113
|
+
}
|
|
114
|
+
initDefaultDirs() {
|
|
115
|
+
const homeDir = os.homedir();
|
|
116
|
+
this.userCommandsDir = path2.join(homeDir, DEFAULT_USER_DIR);
|
|
117
|
+
this.projectCommandsDir = DEFAULT_PROJECT_DIR;
|
|
118
|
+
this.descriptionCacheFile = path2.join(homeDir, DEFAULT_CACHE_DIR, DESCRIPTIONS_FILE);
|
|
119
|
+
}
|
|
120
|
+
async init(config) {
|
|
121
|
+
await super.init(config);
|
|
122
|
+
const options = config?.options;
|
|
123
|
+
if (!options?.configComponent) {
|
|
124
|
+
throw new Error("ConfigComponent is required for CommandsComponent initialization");
|
|
125
|
+
}
|
|
126
|
+
this.configComponent = options.configComponent;
|
|
127
|
+
await this.registerConfig(options);
|
|
128
|
+
await fs2.mkdir(this.userCommandsDir, { recursive: true });
|
|
129
|
+
await this.loadDescriptionCache();
|
|
130
|
+
logger2.info(`CommandsComponent initialized: user=${this.userCommandsDir}, project=${this.projectCommandsDir}`);
|
|
131
|
+
}
|
|
132
|
+
async registerConfig(options) {
|
|
133
|
+
const configComponent = this.configComponent;
|
|
134
|
+
if (!configComponent)
|
|
135
|
+
return;
|
|
136
|
+
const { configPath, envPrefix, config } = options;
|
|
137
|
+
configComponent.registerComponent(COMMANDS_CONFIG_REGISTRATION);
|
|
138
|
+
if (configPath) {
|
|
139
|
+
configComponent.registerSource({
|
|
140
|
+
type: "file",
|
|
141
|
+
relativePath: configPath,
|
|
142
|
+
optional: true,
|
|
143
|
+
watch: false
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
await configComponent.load("command");
|
|
147
|
+
const prefix = envPrefix !== undefined ? envPrefix : "COMMAND";
|
|
148
|
+
for (const key of CONFIG_KEYS) {
|
|
149
|
+
const envKey = toEnvKey(key, prefix);
|
|
150
|
+
const value = process.env[envKey];
|
|
151
|
+
if (value !== undefined) {
|
|
152
|
+
await configComponent.set(key, value);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
const loadedKeys = new Set(CONFIG_KEYS);
|
|
156
|
+
for (const envKey of Object.keys(process.env)) {
|
|
157
|
+
const configKey = envKeyToConfigKey(envKey, prefix, "command");
|
|
158
|
+
if (!configKey)
|
|
159
|
+
continue;
|
|
160
|
+
if (loadedKeys.has(configKey))
|
|
161
|
+
continue;
|
|
162
|
+
loadedKeys.add(configKey);
|
|
163
|
+
const value = process.env[envKey];
|
|
164
|
+
if (value !== undefined) {
|
|
165
|
+
await configComponent.set(configKey, value);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
if (config) {
|
|
169
|
+
const flatConfig = this.flattenConfig(config);
|
|
170
|
+
for (const [key, value] of Object.entries(flatConfig)) {
|
|
171
|
+
if (value !== undefined) {
|
|
172
|
+
await configComponent.set(key, value);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
const homeDir = os.homedir();
|
|
177
|
+
const defaultDirs = {
|
|
178
|
+
"command.userCommandsDir": path2.join(homeDir, DEFAULT_USER_DIR),
|
|
179
|
+
"command.projectCommandsDir": DEFAULT_PROJECT_DIR,
|
|
180
|
+
"command.cacheDir": path2.join(homeDir, DEFAULT_CACHE_DIR)
|
|
181
|
+
};
|
|
182
|
+
for (const [key, value] of Object.entries(defaultDirs)) {
|
|
183
|
+
if (configComponent.get(key) === undefined) {
|
|
184
|
+
await configComponent.set(key, value);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
this.applyConfig(configComponent);
|
|
188
|
+
}
|
|
189
|
+
flattenConfig(config) {
|
|
190
|
+
const result = {};
|
|
191
|
+
if (config.userCommandsDir !== undefined) {
|
|
192
|
+
result["command.userCommandsDir"] = config.userCommandsDir;
|
|
193
|
+
}
|
|
194
|
+
if (config.projectCommandsDir !== undefined) {
|
|
195
|
+
result["command.projectCommandsDir"] = config.projectCommandsDir;
|
|
196
|
+
}
|
|
197
|
+
if (config.cacheDir !== undefined) {
|
|
198
|
+
result["command.cacheDir"] = config.cacheDir;
|
|
199
|
+
}
|
|
200
|
+
return result;
|
|
201
|
+
}
|
|
202
|
+
applyConfig(configComponent) {
|
|
203
|
+
const userDir = configComponent.get("command.userCommandsDir");
|
|
204
|
+
const projectDir = configComponent.get("command.projectCommandsDir");
|
|
205
|
+
const cacheDir = configComponent.get("command.cacheDir");
|
|
206
|
+
this.userCommandsDir = userDir || path2.join(os.homedir(), DEFAULT_USER_DIR);
|
|
207
|
+
this.projectCommandsDir = projectDir || DEFAULT_PROJECT_DIR;
|
|
208
|
+
this.descriptionCacheFile = path2.join(cacheDir || path2.join(os.homedir(), DEFAULT_CACHE_DIR), DESCRIPTIONS_FILE);
|
|
209
|
+
this.config = {
|
|
210
|
+
userCommandsDir: this.userCommandsDir,
|
|
211
|
+
projectCommandsDir: this.projectCommandsDir,
|
|
212
|
+
cacheDir
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
async start() {
|
|
216
|
+
await super.start();
|
|
217
|
+
logger2.debug("CommandsComponent started");
|
|
218
|
+
}
|
|
219
|
+
async stop() {
|
|
220
|
+
await this.saveDescriptionCache();
|
|
221
|
+
await super.stop();
|
|
222
|
+
logger2.debug("CommandsComponent stopped");
|
|
223
|
+
}
|
|
224
|
+
async getCommandDirs() {
|
|
225
|
+
return {
|
|
226
|
+
user: this.userCommandsDir,
|
|
227
|
+
project: this.projectCommandsDir
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
async discover(options) {
|
|
231
|
+
const commands = [];
|
|
232
|
+
let userCount = 0;
|
|
233
|
+
let projectCount = 0;
|
|
234
|
+
try {
|
|
235
|
+
const projectCommands = await this.scanDir(this.projectCommandsDir, "project", options?.pattern);
|
|
236
|
+
commands.push(...projectCommands);
|
|
237
|
+
projectCount = projectCommands.length;
|
|
238
|
+
} catch (error) {
|
|
239
|
+
logger2.debug(`Failed to scan project dir: ${error}`);
|
|
240
|
+
}
|
|
241
|
+
try {
|
|
242
|
+
const userCommands = await this.scanDir(this.userCommandsDir, "user", options?.pattern);
|
|
243
|
+
for (const cmd of userCommands) {
|
|
244
|
+
const exists = commands.find((c) => c.name === cmd.name);
|
|
245
|
+
if (!exists) {
|
|
246
|
+
commands.push(cmd);
|
|
247
|
+
userCount++;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
} catch (error) {
|
|
251
|
+
logger2.debug(`Failed to scan user dir: ${error}`);
|
|
252
|
+
}
|
|
253
|
+
commands.sort((a, b) => a.name.localeCompare(b.name));
|
|
254
|
+
return {
|
|
255
|
+
commands,
|
|
256
|
+
stats: { userCount, projectCount }
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
async add(options) {
|
|
260
|
+
const { name, target, source = "user", description, tips } = options;
|
|
261
|
+
const dir = source === "user" ? this.userCommandsDir : this.projectCommandsDir;
|
|
262
|
+
const targetPath = path2.resolve(target);
|
|
263
|
+
await fs2.mkdir(dir, { recursive: true });
|
|
264
|
+
const commandPath = path2.join(dir, name);
|
|
265
|
+
try {
|
|
266
|
+
await fs2.access(commandPath);
|
|
267
|
+
throw new Error(`Command '${name}' already exists`);
|
|
268
|
+
} catch (error) {
|
|
269
|
+
if (error.code !== "ENOENT") {
|
|
270
|
+
throw error;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
await fs2.symlink(targetPath, commandPath);
|
|
274
|
+
logger2.info(`Added command '${name}' -> ${targetPath} (${source})`);
|
|
275
|
+
const commandDir = path2.join(dir, name);
|
|
276
|
+
await this.createCommandMeta(commandDir, {
|
|
277
|
+
name,
|
|
278
|
+
target: targetPath,
|
|
279
|
+
description: description || `Command: ${name}`,
|
|
280
|
+
tips,
|
|
281
|
+
source
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
async createCommandMeta(commandDir, options) {
|
|
285
|
+
const parentDir = path2.dirname(commandDir);
|
|
286
|
+
const metaPath = path2.join(parentDir, `${options.name}.meta.md`);
|
|
287
|
+
const tipsContent = options.tips ? `
|
|
288
|
+
tips: "${options.tips}"` : "";
|
|
289
|
+
const content = `---
|
|
290
|
+
name: ${options.name}
|
|
291
|
+
description: ${options.description}${tipsContent}
|
|
292
|
+
source: ${options.source}
|
|
293
|
+
---
|
|
294
|
+
|
|
295
|
+
# ${options.name} Command
|
|
296
|
+
|
|
297
|
+
## Target
|
|
298
|
+
\`${options.target}\`
|
|
299
|
+
|
|
300
|
+
## 功能
|
|
301
|
+
${options.description}
|
|
302
|
+
|
|
303
|
+
## 探索提示
|
|
304
|
+
Run \`${options.name} --help\` to explore full capabilities.
|
|
305
|
+
`;
|
|
306
|
+
await fs2.writeFile(metaPath, content, "utf-8");
|
|
307
|
+
logger2.info(`[CommandsComponent] Created meta.md for '${options.name}'`);
|
|
308
|
+
}
|
|
309
|
+
async getCommandsMeta() {
|
|
310
|
+
const metaList = [];
|
|
311
|
+
try {
|
|
312
|
+
const entries = await fs2.readdir(this.userCommandsDir, { withFileTypes: true });
|
|
313
|
+
for (const entry of entries) {
|
|
314
|
+
if (entry.isFile() && entry.name.endsWith(".meta.md")) {
|
|
315
|
+
const metaPath = path2.join(this.userCommandsDir, entry.name);
|
|
316
|
+
const meta = await parseMetaFile(metaPath, "user");
|
|
317
|
+
if (meta) {
|
|
318
|
+
metaList.push(meta);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
} catch (error) {
|
|
323
|
+
if (error.code !== "ENOENT") {
|
|
324
|
+
logger2.debug(`Failed to scan user commands dir: ${error}`);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
try {
|
|
328
|
+
const entries = await fs2.readdir(this.projectCommandsDir, { withFileTypes: true });
|
|
329
|
+
for (const entry of entries) {
|
|
330
|
+
if (entry.isFile() && entry.name.endsWith(".meta.md")) {
|
|
331
|
+
const metaPath = path2.join(this.projectCommandsDir, entry.name);
|
|
332
|
+
const meta = await parseMetaFile(metaPath, "project");
|
|
333
|
+
if (meta) {
|
|
334
|
+
metaList.push(meta);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
} catch (error) {
|
|
339
|
+
if (error.code !== "ENOENT") {
|
|
340
|
+
logger2.debug(`Failed to scan project commands dir: ${error}`);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
return metaList;
|
|
344
|
+
}
|
|
345
|
+
async remove(options) {
|
|
346
|
+
const { name, source } = options;
|
|
347
|
+
const dirsToCheck = source ? [source === "user" ? this.userCommandsDir : this.projectCommandsDir] : [this.userCommandsDir, this.projectCommandsDir];
|
|
348
|
+
for (const dir of dirsToCheck) {
|
|
349
|
+
const commandPath = path2.join(dir, name);
|
|
350
|
+
try {
|
|
351
|
+
await fs2.unlink(commandPath);
|
|
352
|
+
logger2.info(`Removed command '${name}' from ${dir}`);
|
|
353
|
+
return;
|
|
354
|
+
} catch (error) {
|
|
355
|
+
if (error.code !== "ENOENT") {
|
|
356
|
+
throw error;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
throw new Error(`Command '${name}' not found`);
|
|
361
|
+
}
|
|
362
|
+
async getInfo(name) {
|
|
363
|
+
const dirsToCheck = [this.projectCommandsDir, this.userCommandsDir];
|
|
364
|
+
for (const dir of dirsToCheck) {
|
|
365
|
+
const commandPath = path2.join(dir, name);
|
|
366
|
+
try {
|
|
367
|
+
const stat = await fs2.lstat(commandPath);
|
|
368
|
+
const realPath = stat.isSymbolicLink() ? await fs2.readlink(commandPath) : commandPath;
|
|
369
|
+
const cmdSource = dir === this.projectCommandsDir ? "project" : "user";
|
|
370
|
+
const shortDescription = await this.getDescription(realPath);
|
|
371
|
+
return {
|
|
372
|
+
name,
|
|
373
|
+
path: realPath,
|
|
374
|
+
source: cmdSource,
|
|
375
|
+
shortDescription
|
|
376
|
+
};
|
|
377
|
+
} catch (error) {
|
|
378
|
+
if (error.code !== "ENOENT") {
|
|
379
|
+
throw error;
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
return null;
|
|
384
|
+
}
|
|
385
|
+
async exec(options) {
|
|
386
|
+
const { name, args = [], cwd } = options;
|
|
387
|
+
const info = await this.getInfo(name);
|
|
388
|
+
if (!info) {
|
|
389
|
+
return {
|
|
390
|
+
stdout: "",
|
|
391
|
+
stderr: `Command '${name}' not found`,
|
|
392
|
+
exitCode: 127,
|
|
393
|
+
success: false
|
|
394
|
+
};
|
|
395
|
+
}
|
|
396
|
+
const cmd = `${info.path} ${args.join(" ")}`;
|
|
397
|
+
try {
|
|
398
|
+
const { stdout, stderr } = await execAsync(cmd, {
|
|
399
|
+
cwd: cwd || process.cwd(),
|
|
400
|
+
timeout: 30000
|
|
401
|
+
});
|
|
402
|
+
return {
|
|
403
|
+
stdout,
|
|
404
|
+
stderr,
|
|
405
|
+
exitCode: 0,
|
|
406
|
+
success: true
|
|
407
|
+
};
|
|
408
|
+
} catch (error) {
|
|
409
|
+
return {
|
|
410
|
+
stdout: error.stdout || "",
|
|
411
|
+
stderr: error.stderr || error.message,
|
|
412
|
+
exitCode: error.code || 1,
|
|
413
|
+
success: false
|
|
414
|
+
};
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
async scanDir(dir, source, pattern) {
|
|
418
|
+
const commands = [];
|
|
419
|
+
try {
|
|
420
|
+
const entries = await fs2.readdir(dir);
|
|
421
|
+
for (const entry of entries) {
|
|
422
|
+
if (entry.startsWith("."))
|
|
423
|
+
continue;
|
|
424
|
+
if (pattern && !this.matchPattern(entry, pattern))
|
|
425
|
+
continue;
|
|
426
|
+
const fullPath = path2.join(dir, entry);
|
|
427
|
+
try {
|
|
428
|
+
const stat = await fs2.lstat(fullPath);
|
|
429
|
+
if (!stat.isSymbolicLink() && !stat.isFile())
|
|
430
|
+
continue;
|
|
431
|
+
if (stat.isFile()) {
|
|
432
|
+
try {
|
|
433
|
+
await fs2.access(fullPath, fs2.constants.X_OK);
|
|
434
|
+
} catch {
|
|
435
|
+
continue;
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
const realPath = stat.isSymbolicLink() ? await fs2.readlink(fullPath) : fullPath;
|
|
439
|
+
const shortDescription = await this.getDescription(realPath);
|
|
440
|
+
commands.push({
|
|
441
|
+
name: entry,
|
|
442
|
+
path: realPath,
|
|
443
|
+
source,
|
|
444
|
+
shortDescription
|
|
445
|
+
});
|
|
446
|
+
} catch (error) {
|
|
447
|
+
logger2.debug(`Failed to stat ${fullPath}: ${error}`);
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
} catch (error) {
|
|
451
|
+
logger2.debug(`Failed to scan dir ${dir}: ${error}`);
|
|
452
|
+
}
|
|
453
|
+
return commands;
|
|
454
|
+
}
|
|
455
|
+
matchPattern(name, pattern) {
|
|
456
|
+
const regex = new RegExp("^" + pattern.replace(/\*/g, ".*").replace(/\?/g, ".") + "$");
|
|
457
|
+
return regex.test(name);
|
|
458
|
+
}
|
|
459
|
+
async getDescription(commandPath) {
|
|
460
|
+
const cached = this.descriptionCache.get(commandPath);
|
|
461
|
+
if (cached !== undefined)
|
|
462
|
+
return cached;
|
|
463
|
+
try {
|
|
464
|
+
const { stdout } = await execAsync(`${commandPath} --help 2>/dev/null | head -1`, {
|
|
465
|
+
timeout: 2000
|
|
466
|
+
});
|
|
467
|
+
const description = stdout.trim().split(`
|
|
468
|
+
`)[0] || "";
|
|
469
|
+
this.descriptionCache.set(commandPath, description);
|
|
470
|
+
return description;
|
|
471
|
+
} catch {
|
|
472
|
+
return "";
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
async loadDescriptionCache() {
|
|
476
|
+
try {
|
|
477
|
+
const content = await fs2.readFile(this.descriptionCacheFile, "utf-8");
|
|
478
|
+
const data = JSON.parse(content);
|
|
479
|
+
this.descriptionCache = new Map(Object.entries(data));
|
|
480
|
+
} catch {}
|
|
481
|
+
}
|
|
482
|
+
async saveDescriptionCache() {
|
|
483
|
+
try {
|
|
484
|
+
await fs2.mkdir(path2.dirname(this.descriptionCacheFile), { recursive: true });
|
|
485
|
+
const data = Object.fromEntries(this.descriptionCache);
|
|
486
|
+
await fs2.writeFile(this.descriptionCacheFile, JSON.stringify(data, null, 2));
|
|
487
|
+
} catch (error) {
|
|
488
|
+
logger2.debug(`Failed to save description cache: ${error}`);
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
export { CommandsComponent };
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
// packages/core/src/env/workflow/service/workflow-service.ts
|
|
2
|
+
class WorkflowService {
|
|
3
|
+
workflowRepository;
|
|
4
|
+
engineFactory;
|
|
5
|
+
sessionComponent;
|
|
6
|
+
constructor(workflowRepository, engineFactory, sessionComponent) {
|
|
7
|
+
this.workflowRepository = workflowRepository;
|
|
8
|
+
this.engineFactory = engineFactory;
|
|
9
|
+
this.sessionComponent = sessionComponent;
|
|
10
|
+
}
|
|
11
|
+
async createWorkflow(definition, options) {
|
|
12
|
+
const existing = this.workflowRepository.getByName(definition.name);
|
|
13
|
+
if (existing) {
|
|
14
|
+
if (options?.force !== true) {
|
|
15
|
+
throw new Error(`Workflow already exists: ${definition.name}`);
|
|
16
|
+
}
|
|
17
|
+
return this.workflowRepository.update(existing.id, {
|
|
18
|
+
name: definition.name,
|
|
19
|
+
version: definition.version,
|
|
20
|
+
description: definition.description || existing.description,
|
|
21
|
+
definition,
|
|
22
|
+
config: definition.config || {},
|
|
23
|
+
metadata: {
|
|
24
|
+
...options.metadata || definition.metadata || existing.metadata || {},
|
|
25
|
+
taskId: options.taskId || definition.metadata?.taskId || existing.metadata?.taskId
|
|
26
|
+
},
|
|
27
|
+
tags: options.tags || definition.metadata?.tags || []
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
const workflow = {
|
|
31
|
+
name: definition.name,
|
|
32
|
+
version: definition.version,
|
|
33
|
+
description: definition.description,
|
|
34
|
+
definition,
|
|
35
|
+
config: definition.config || {},
|
|
36
|
+
metadata: {
|
|
37
|
+
...options?.metadata || definition.metadata || {},
|
|
38
|
+
taskId: options?.taskId || definition.metadata?.taskId
|
|
39
|
+
},
|
|
40
|
+
tags: options?.tags || definition.metadata?.tags || []
|
|
41
|
+
};
|
|
42
|
+
return this.workflowRepository.create(workflow);
|
|
43
|
+
}
|
|
44
|
+
getWorkflow(idOrName) {
|
|
45
|
+
let workflow = this.workflowRepository.getById(idOrName);
|
|
46
|
+
if (workflow)
|
|
47
|
+
return workflow;
|
|
48
|
+
return this.workflowRepository.getByName(idOrName);
|
|
49
|
+
}
|
|
50
|
+
getWorkflowById(id) {
|
|
51
|
+
return this.workflowRepository.getById(id);
|
|
52
|
+
}
|
|
53
|
+
getWorkflowByName(name) {
|
|
54
|
+
return this.workflowRepository.getByName(name);
|
|
55
|
+
}
|
|
56
|
+
listWorkflows(options) {
|
|
57
|
+
return this.workflowRepository.list(options);
|
|
58
|
+
}
|
|
59
|
+
async updateWorkflow(idOrName, updates, options) {
|
|
60
|
+
let workflow = this.workflowRepository.getByName(idOrName);
|
|
61
|
+
if (!workflow) {
|
|
62
|
+
workflow = this.workflowRepository.getById(idOrName);
|
|
63
|
+
}
|
|
64
|
+
if (!workflow) {
|
|
65
|
+
throw new Error(`Workflow not found: ${idOrName}`);
|
|
66
|
+
}
|
|
67
|
+
const updateData = {
|
|
68
|
+
...updates
|
|
69
|
+
};
|
|
70
|
+
if (options?.tags !== undefined) {
|
|
71
|
+
updateData.tags = options.tags;
|
|
72
|
+
}
|
|
73
|
+
const updated = this.workflowRepository.update(workflow.id, updateData);
|
|
74
|
+
if (!updated) {
|
|
75
|
+
throw new Error(`Failed to update workflow: ${idOrName}`);
|
|
76
|
+
}
|
|
77
|
+
return updated;
|
|
78
|
+
}
|
|
79
|
+
async deleteWorkflow(idOrName) {
|
|
80
|
+
let workflow = this.workflowRepository.getByName(idOrName);
|
|
81
|
+
if (!workflow) {
|
|
82
|
+
workflow = this.workflowRepository.getById(idOrName);
|
|
83
|
+
}
|
|
84
|
+
if (!workflow) {
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
return this.workflowRepository.delete(workflow.id);
|
|
88
|
+
}
|
|
89
|
+
async runWorkflow(idOrNameOrDefinition, input, options) {
|
|
90
|
+
let workflow = null;
|
|
91
|
+
let definition = null;
|
|
92
|
+
if (typeof idOrNameOrDefinition === "string") {
|
|
93
|
+
workflow = this.getWorkflow(idOrNameOrDefinition);
|
|
94
|
+
if (!workflow) {
|
|
95
|
+
throw new Error(`Workflow not found: ${idOrNameOrDefinition}`);
|
|
96
|
+
}
|
|
97
|
+
definition = workflow.definition;
|
|
98
|
+
} else {
|
|
99
|
+
definition = idOrNameOrDefinition;
|
|
100
|
+
workflow = this.getWorkflowByName(definition.name);
|
|
101
|
+
if (!workflow) {
|
|
102
|
+
workflow = {
|
|
103
|
+
id: `wf_inline_${definition.name}`,
|
|
104
|
+
name: definition.name,
|
|
105
|
+
version: definition.version,
|
|
106
|
+
description: definition.description,
|
|
107
|
+
definition,
|
|
108
|
+
config: definition.config || {},
|
|
109
|
+
metadata: definition.metadata || {},
|
|
110
|
+
tags: definition.metadata?.tags || [],
|
|
111
|
+
createdAt: new Date,
|
|
112
|
+
updatedAt: new Date
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
const engine = this.engineFactory(workflow, {
|
|
117
|
+
...options,
|
|
118
|
+
input
|
|
119
|
+
});
|
|
120
|
+
const result = await engine.runWorkflow(workflow, {
|
|
121
|
+
...options,
|
|
122
|
+
input
|
|
123
|
+
});
|
|
124
|
+
return {
|
|
125
|
+
...result,
|
|
126
|
+
runId: result.runId || result.sessionId || "",
|
|
127
|
+
sessionId: result.sessionId || result.runId || ""
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
async runWorkflowByName(name, input, options) {
|
|
131
|
+
const workflow = this.getWorkflowByName(name);
|
|
132
|
+
if (!workflow) {
|
|
133
|
+
throw new Error(`Workflow not found: ${name}`);
|
|
134
|
+
}
|
|
135
|
+
return this.runWorkflow(name, input, options);
|
|
136
|
+
}
|
|
137
|
+
async stopRun(sessionId, reason) {
|
|
138
|
+
const session = await this.sessionComponent?.get(sessionId);
|
|
139
|
+
if (!session) {
|
|
140
|
+
throw new Error(`Session not found: ${sessionId}`);
|
|
141
|
+
}
|
|
142
|
+
const metadata = session.metadata;
|
|
143
|
+
if (metadata.type !== "workflow") {
|
|
144
|
+
throw new Error(`Session is not a workflow session: ${sessionId}`);
|
|
145
|
+
}
|
|
146
|
+
const workflow = this.getWorkflowByName(metadata.workflowName);
|
|
147
|
+
if (!workflow) {
|
|
148
|
+
throw new Error(`Workflow not found: ${metadata.workflowName}`);
|
|
149
|
+
}
|
|
150
|
+
const engine = this.engineFactory(workflow);
|
|
151
|
+
await engine.stop(sessionId, reason);
|
|
152
|
+
}
|
|
153
|
+
async getSession(sessionId) {
|
|
154
|
+
if (!this.sessionComponent) {
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
const session = await this.sessionComponent.get(sessionId);
|
|
158
|
+
return session ?? null;
|
|
159
|
+
}
|
|
160
|
+
async listSessions(options) {
|
|
161
|
+
if (!this.sessionComponent?.list) {
|
|
162
|
+
return [];
|
|
163
|
+
}
|
|
164
|
+
return this.sessionComponent.list(options);
|
|
165
|
+
}
|
|
166
|
+
async getSessionMessages(sessionId) {
|
|
167
|
+
if (!this.sessionComponent) {
|
|
168
|
+
return [];
|
|
169
|
+
}
|
|
170
|
+
return this.sessionComponent.getMessages(sessionId);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
// packages/core/src/env/workflow/service/registry.ts
|
|
174
|
+
class WorkflowServiceRegistry {
|
|
175
|
+
static instance;
|
|
176
|
+
service = null;
|
|
177
|
+
constructor() {}
|
|
178
|
+
static getInstance() {
|
|
179
|
+
if (!WorkflowServiceRegistry.instance) {
|
|
180
|
+
WorkflowServiceRegistry.instance = new WorkflowServiceRegistry;
|
|
181
|
+
}
|
|
182
|
+
return WorkflowServiceRegistry.instance;
|
|
183
|
+
}
|
|
184
|
+
register(service) {
|
|
185
|
+
this.service = service;
|
|
186
|
+
}
|
|
187
|
+
get() {
|
|
188
|
+
if (!this.service) {
|
|
189
|
+
throw new Error("WorkflowService is not registered. Call register() first.");
|
|
190
|
+
}
|
|
191
|
+
return this.service;
|
|
192
|
+
}
|
|
193
|
+
isRegistered() {
|
|
194
|
+
return this.service !== null;
|
|
195
|
+
}
|
|
196
|
+
reset() {
|
|
197
|
+
this.service = null;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
function getWorkflowService() {
|
|
201
|
+
return WorkflowServiceRegistry.getInstance().get();
|
|
202
|
+
}
|
|
203
|
+
function registerWorkflowService(service) {
|
|
204
|
+
WorkflowServiceRegistry.getInstance().register(service);
|
|
205
|
+
}
|
|
206
|
+
export { WorkflowService, WorkflowServiceRegistry, getWorkflowService, registerWorkflowService };
|