@starlink-awaken/agentmesh 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.
@@ -0,0 +1,122 @@
1
+ import { execa } from 'execa';
2
+ import { v4 as uuidv4 } from 'uuid';
3
+ import { BaseAgentAdapter } from './base.js';
4
+ import type { AgentMessage } from '../types/index.js';
5
+
6
+ export class ClaudeCodeAdapter extends BaseAgentAdapter {
7
+ id = 'claude-code';
8
+ name = 'Claude Code';
9
+ type = 'claude-code';
10
+ capabilities = [
11
+ 'code-generation',
12
+ 'code-review',
13
+ 'debugging',
14
+ 'refactoring',
15
+ 'documentation',
16
+ 'file-operations'
17
+ ];
18
+
19
+ private readonly cliPath: string;
20
+
21
+ constructor(cliPath: string = 'claude') {
22
+ super();
23
+ this.cliPath = cliPath;
24
+ }
25
+
26
+ /**
27
+ * 调用 Claude Code 执行任务
28
+ */
29
+ async invoke(request: AgentMessage): Promise<AgentMessage> {
30
+ const task = request.payload?.task || '';
31
+ const correlationId = request.correlation_id || uuidv4();
32
+
33
+ try {
34
+ // Claude Code 的 -p 模式需要从 stdin 输入
35
+ const result = await execa(this.cliPath, ['-p'], {
36
+ input: task,
37
+ timeout: (request.payload?.options?.timeout || 300) * 1000,
38
+ stdio: ['pipe', 'pipe', 'pipe']
39
+ });
40
+
41
+ return {
42
+ id: uuidv4(),
43
+ type: 'response',
44
+ source: this.id,
45
+ target: request.source,
46
+ correlation_id: correlationId,
47
+ timestamp: Date.now(),
48
+ result: result.stdout
49
+ };
50
+ } catch (error: any) {
51
+ return {
52
+ id: uuidv4(),
53
+ type: 'response',
54
+ source: this.id,
55
+ target: request.source,
56
+ correlation_id: correlationId,
57
+ timestamp: Date.now(),
58
+ error: {
59
+ code: 'EXECUTION_ERROR',
60
+ message: error.message || String(error)
61
+ }
62
+ };
63
+ }
64
+ }
65
+
66
+ /**
67
+ * 流式调用
68
+ */
69
+ override async *invokeStream(request: AgentMessage): AsyncGenerator<AgentMessage, void, unknown> {
70
+ const task = request.payload?.task || '';
71
+ const correlationId = request.correlation_id || uuidv4();
72
+
73
+ try {
74
+ const process = execa(this.cliPath, ['-p', task], {
75
+ timeout: (request.payload?.options?.timeout || 300) * 1000,
76
+ stdio: ['pipe', 'pipe', 'pipe']
77
+ });
78
+
79
+ // 监听 stdout 流
80
+ process.stdout?.on('data', (_chunk: Buffer) => {
81
+ // 这里可以发送流式消息
82
+ });
83
+
84
+ const result = await process;
85
+
86
+ yield {
87
+ id: uuidv4(),
88
+ type: 'stream_end',
89
+ source: this.id,
90
+ target: request.source,
91
+ correlation_id: correlationId,
92
+ timestamp: Date.now(),
93
+ result: result.stdout
94
+ };
95
+ } catch (error: any) {
96
+ yield {
97
+ id: uuidv4(),
98
+ type: 'response',
99
+ source: this.id,
100
+ target: request.source,
101
+ correlation_id: correlationId,
102
+ timestamp: Date.now(),
103
+ error: {
104
+ code: 'EXECUTION_ERROR',
105
+ message: error.message || String(error)
106
+ }
107
+ };
108
+ }
109
+ }
110
+
111
+ /**
112
+ * 健康检查
113
+ */
114
+ async health(): Promise<boolean> {
115
+ try {
116
+ await execa(this.cliPath, ['--version'], { timeout: 5000 });
117
+ return true;
118
+ } catch {
119
+ return false;
120
+ }
121
+ }
122
+ }
@@ -0,0 +1,120 @@
1
+ import { execa } from 'execa';
2
+ import { v4 as uuidv4 } from 'uuid';
3
+ import { BaseAgentAdapter } from './base.js';
4
+ import type { AgentMessage } from '../types/index.js';
5
+
6
+ export class OpenClawAdapter extends BaseAgentAdapter {
7
+ id = 'openclaw';
8
+ name = 'OpenClaw';
9
+ type = 'openclaw';
10
+ capabilities = [
11
+ 'browser-automation',
12
+ 'web-scraping',
13
+ 'form-filling',
14
+ 'ui-testing'
15
+ ];
16
+
17
+ private readonly cliPath: string;
18
+
19
+ constructor(cliPath: string = 'openclaw') {
20
+ super();
21
+ this.cliPath = cliPath;
22
+ }
23
+
24
+ /**
25
+ * 调用 OpenClaw 执行任务
26
+ */
27
+ async invoke(request: AgentMessage): Promise<AgentMessage> {
28
+ const task = request.payload?.task || '';
29
+ const correlationId = request.correlation_id || uuidv4();
30
+
31
+ try {
32
+ // OpenClaw CLI 调用方式(假设使用 --task 或类似参数)
33
+ const result = await execa(this.cliPath, ['--task', task], {
34
+ timeout: (request.payload?.options?.timeout || 300) * 1000,
35
+ stdio: ['pipe', 'pipe', 'pipe']
36
+ });
37
+
38
+ return {
39
+ id: uuidv4(),
40
+ type: 'response',
41
+ source: this.id,
42
+ target: request.source,
43
+ correlation_id: correlationId,
44
+ timestamp: Date.now(),
45
+ result: result.stdout
46
+ };
47
+ } catch (error: unknown) {
48
+ const errorMessage = error instanceof Error ? error.message : String(error);
49
+ return {
50
+ id: uuidv4(),
51
+ type: 'response',
52
+ source: this.id,
53
+ target: request.source,
54
+ correlation_id: correlationId,
55
+ timestamp: Date.now(),
56
+ error: {
57
+ code: 'EXECUTION_ERROR',
58
+ message: errorMessage
59
+ }
60
+ };
61
+ }
62
+ }
63
+
64
+ /**
65
+ * 流式调用
66
+ */
67
+ override async *invokeStream(request: AgentMessage): AsyncGenerator<AgentMessage, void, unknown> {
68
+ const task = request.payload?.task || '';
69
+ const correlationId = request.correlation_id || uuidv4();
70
+
71
+ try {
72
+ const process = execa(this.cliPath, ['--task', task], {
73
+ timeout: (request.payload?.options?.timeout || 300) * 1000,
74
+ stdio: ['pipe', 'pipe', 'pipe']
75
+ });
76
+
77
+ process.stdout?.on('data', (_chunk: Buffer) => {
78
+ // 可以发送流式消息
79
+ });
80
+
81
+ const result = await process;
82
+
83
+ yield {
84
+ id: uuidv4(),
85
+ type: 'stream_end',
86
+ source: this.id,
87
+ target: request.source,
88
+ correlation_id: correlationId,
89
+ timestamp: Date.now(),
90
+ result: result.stdout
91
+ };
92
+ } catch (error: unknown) {
93
+ const errorMessage = error instanceof Error ? error.message : String(error);
94
+ yield {
95
+ id: uuidv4(),
96
+ type: 'response',
97
+ source: this.id,
98
+ target: request.source,
99
+ correlation_id: correlationId,
100
+ timestamp: Date.now(),
101
+ error: {
102
+ code: 'EXECUTION_ERROR',
103
+ message: errorMessage
104
+ }
105
+ };
106
+ }
107
+ }
108
+
109
+ /**
110
+ * 健康检查
111
+ */
112
+ async health(): Promise<boolean> {
113
+ try {
114
+ await execa(this.cliPath, ['--version'], { timeout: 5000 });
115
+ return true;
116
+ } catch {
117
+ return false;
118
+ }
119
+ }
120
+ }
@@ -0,0 +1,136 @@
1
+ import { execa } from 'execa';
2
+ import { v4 as uuidv4 } from 'uuid';
3
+ import { BaseAgentAdapter } from './base.js';
4
+ import type { AgentMessage } from '../types/index.js';
5
+
6
+ interface ProcessConfig {
7
+ command: string;
8
+ args?: string[];
9
+ env?: Record<string, string>;
10
+ }
11
+
12
+ export class ProcessAdapter extends BaseAgentAdapter {
13
+ id: string;
14
+ name: string;
15
+ type = 'process';
16
+ capabilities: string[];
17
+ private config: ProcessConfig;
18
+
19
+ constructor(id: string, name: string, capabilities: string[], config: ProcessConfig) {
20
+ super();
21
+ this.id = id;
22
+ this.name = name;
23
+ this.capabilities = capabilities;
24
+ this.config = config;
25
+ }
26
+
27
+ /**
28
+ * 调用进程执行任务
29
+ */
30
+ async invoke(request: AgentMessage): Promise<AgentMessage> {
31
+ const task = request.payload?.task || '';
32
+ const correlationId = request.correlation_id || uuidv4();
33
+ const timeout = (request.payload?.options?.timeout || 300) * 1000;
34
+
35
+ try {
36
+ // 构建参数:将任务作为 stdin 或参数传入
37
+ const args = this.config.args || [];
38
+
39
+ const result = await execa(this.config.command, [...args, task], {
40
+ timeout,
41
+ stdio: ['pipe', 'pipe', 'pipe'],
42
+ env: { ...process.env, ...this.config.env }
43
+ });
44
+
45
+ return {
46
+ id: uuidv4(),
47
+ type: 'response',
48
+ source: this.id,
49
+ target: request.source,
50
+ correlation_id: correlationId,
51
+ timestamp: Date.now(),
52
+ result: result.stdout
53
+ };
54
+ } catch (error: unknown) {
55
+ const errorMessage = error instanceof Error ? error.message : String(error);
56
+ return {
57
+ id: uuidv4(),
58
+ type: 'response',
59
+ source: this.id,
60
+ target: request.source,
61
+ correlation_id: correlationId,
62
+ timestamp: Date.now(),
63
+ error: {
64
+ code: 'EXECUTION_ERROR',
65
+ message: errorMessage
66
+ }
67
+ };
68
+ }
69
+ }
70
+
71
+ /**
72
+ * 流式调用
73
+ */
74
+ override async *invokeStream(request: AgentMessage): AsyncGenerator<AgentMessage, void, unknown> {
75
+ const task = request.payload?.task || '';
76
+ const correlationId = request.correlation_id || uuidv4();
77
+ const timeout = (request.payload?.options?.timeout || 300) * 1000;
78
+
79
+ try {
80
+ const args = this.config.args || [];
81
+ const proc = execa(this.config.command, [...args, task], {
82
+ timeout,
83
+ stdio: ['pipe', 'pipe', 'pipe'],
84
+ env: { ...globalThis.process.env, ...this.config.env }
85
+ });
86
+
87
+ proc.stdout?.on('data', (_chunk: Buffer) => {
88
+ // 可以发送流式消息
89
+ });
90
+
91
+ const result = await proc;
92
+
93
+ yield {
94
+ id: uuidv4(),
95
+ type: 'stream_end',
96
+ source: this.id,
97
+ target: request.source,
98
+ correlation_id: correlationId,
99
+ timestamp: Date.now(),
100
+ result: result.stdout
101
+ };
102
+ } catch (error: unknown) {
103
+ const errorMessage = error instanceof Error ? error.message : String(error);
104
+ yield {
105
+ id: uuidv4(),
106
+ type: 'response',
107
+ source: this.id,
108
+ target: request.source,
109
+ correlation_id: correlationId,
110
+ timestamp: Date.now(),
111
+ error: {
112
+ code: 'EXECUTION_ERROR',
113
+ message: errorMessage
114
+ }
115
+ };
116
+ }
117
+ }
118
+
119
+ /**
120
+ * 健康检查
121
+ */
122
+ async health(): Promise<boolean> {
123
+ try {
124
+ await execa(this.config.command, ['--version'], { timeout: 5000 });
125
+ return true;
126
+ } catch {
127
+ // 尝试其他版本检查方式
128
+ try {
129
+ await execa(this.config.command, ['-v'], { timeout: 5000 });
130
+ return true;
131
+ } catch {
132
+ return false;
133
+ }
134
+ }
135
+ }
136
+ }
package/src/cli.ts ADDED
@@ -0,0 +1,284 @@
1
+ #!/usr/bin/env bun
2
+
3
+ import type { AgentMessage } from './types/index.js';
4
+
5
+ const BASE_URL = process.env.AGENT_GATEWAY_URL || 'http://localhost:3000';
6
+
7
+ async function request<T = any>(path: string, options?: RequestInit): Promise<T> {
8
+ const response = await fetch(`${BASE_URL}${path}`, {
9
+ ...options,
10
+ headers: {
11
+ 'Content-Type': 'application/json',
12
+ ...options?.headers
13
+ }
14
+ });
15
+
16
+ if (!response.ok) {
17
+ const error = await response.json().catch(() => ({ message: 'Unknown error' }));
18
+ throw new Error(`HTTP ${response.status}: ${JSON.stringify(error)}`);
19
+ }
20
+
21
+ return response.json() as Promise<T>;
22
+ }
23
+
24
+ // 命令
25
+ const commands = {
26
+ // 列出所有 Agent
27
+ async listAgents() {
28
+ const agents = await request<any[]>('/agents');
29
+ console.log('\n📋 可用 Agent:\n');
30
+ agents.forEach(agent => {
31
+ console.log(` ${agent.id.padEnd(15)} ${agent.name.padEnd(20)} [${agent.status}]`);
32
+ console.log(` 能力: ${agent.capabilities.join(', ')}\n`);
33
+ });
34
+ },
35
+
36
+ // 提交任务
37
+ async submitTask(args: string[]) {
38
+ const task = args.join(' ');
39
+ if (!task) {
40
+ console.error('❌ 请提供任务描述');
41
+ process.exit(1);
42
+ }
43
+
44
+ console.log(`\n📤 提交任务: ${task}\n`);
45
+
46
+ const message: Partial<AgentMessage> = {
47
+ type: 'request',
48
+ source: 'cli',
49
+ target: 'gateway',
50
+ payload: {
51
+ task,
52
+ options: {
53
+ stream: false,
54
+ timeout: 300
55
+ }
56
+ }
57
+ };
58
+
59
+ const result = await request<any>('/tasks', {
60
+ method: 'POST',
61
+ body: JSON.stringify(message)
62
+ });
63
+
64
+ console.log(`✅ 任务已提交: ${result.task_id}`);
65
+ console.log(` 状态: ${result.status}\n`);
66
+
67
+ // 轮询获取结果
68
+ console.log('Waiting for result...\n');
69
+
70
+ let completed = false;
71
+ let attempts = 0;
72
+ const maxAttempts = 60;
73
+
74
+ while (!completed && attempts < maxAttempts) {
75
+ await new Promise(resolve => setTimeout(resolve, 2000));
76
+
77
+ const taskResult = await request<any>(`/tasks/${result.task_id}`);
78
+
79
+ if (taskResult.status === 'completed') {
80
+ completed = true;
81
+ console.log('✅ 任务完成!\n');
82
+ console.log('📊 结果:');
83
+ if (typeof taskResult.result === 'object') {
84
+ Object.entries(taskResult.result).forEach(([agent, res]) => {
85
+ console.log(`\n--- ${agent} ---`);
86
+ console.log(res);
87
+ });
88
+ } else {
89
+ console.log(taskResult.result);
90
+ }
91
+ } else if (taskResult.status === 'failed') {
92
+ completed = true;
93
+ console.log('❌ 任务失败!');
94
+ console.log('错误:', taskResult.error);
95
+ } else {
96
+ attempts++;
97
+ process.stdout.write('.');
98
+ }
99
+ }
100
+
101
+ if (!completed) {
102
+ console.log('\n⚠️ 任务超时\n');
103
+ }
104
+ },
105
+
106
+ // 提交到指定 Agent
107
+ async submitToAgent(agentId: string, args: string[]) {
108
+ const task = args.join(' ');
109
+ if (!task) {
110
+ console.error('❌ 请提供任务描述');
111
+ process.exit(1);
112
+ }
113
+
114
+ console.log(`\n📤 提交任务到 ${agentId}: ${task}\n`);
115
+
116
+ const message: Partial<AgentMessage> = {
117
+ type: 'request',
118
+ source: 'cli',
119
+ target: agentId,
120
+ payload: {
121
+ task,
122
+ options: {
123
+ stream: false,
124
+ timeout: 300
125
+ }
126
+ }
127
+ };
128
+
129
+ const result = await request<any>('/tasks', {
130
+ method: 'POST',
131
+ body: JSON.stringify(message)
132
+ });
133
+
134
+ console.log(`✅ 任务已提交: ${result.task_id}`);
135
+ console.log(` 状态: ${result.status}\n`);
136
+
137
+ // 轮询获取结果
138
+ console.log('⏳ 等待执行结果...\n');
139
+
140
+ let completed = false;
141
+ let attempts = 0;
142
+ const maxAttempts = 60;
143
+
144
+ while (!completed && attempts < maxAttempts) {
145
+ await new Promise(resolve => setTimeout(resolve, 2000));
146
+
147
+ const taskResult = await request<any>(`/tasks/${result.task_id}`);
148
+
149
+ if (taskResult.status === 'completed') {
150
+ completed = true;
151
+ console.log('\n✅ 任务完成!\n');
152
+ console.log('📊 结果:');
153
+ console.log(taskResult.result);
154
+ } else if (taskResult.status === 'failed') {
155
+ completed = true;
156
+ console.log('\n❌ 任务失败!');
157
+ console.log('错误:', taskResult.error);
158
+ } else {
159
+ attempts++;
160
+ process.stdout.write('.');
161
+ }
162
+ }
163
+
164
+ if (!completed) {
165
+ console.log('\n⚠️ 任务超时\n');
166
+ }
167
+ },
168
+
169
+ // 创建共享空间
170
+ async createSpace() {
171
+ const result = await request<{ space_id: string }>('/spaces', {
172
+ method: 'POST',
173
+ body: JSON.stringify({ metadata: { createdBy: 'cli' } })
174
+ });
175
+
176
+ console.log(`\n✅ 共享空间已创建: ${result.space_id}\n`);
177
+ return result.space_id;
178
+ },
179
+
180
+ // 列出任务
181
+ async listTasks() {
182
+ const tasks = await request<any[]>('/tasks');
183
+ console.log('\n📋 任务列表:\n');
184
+ if (tasks.length === 0) {
185
+ console.log(' (无任务)\n');
186
+ return;
187
+ }
188
+ tasks.forEach(task => {
189
+ console.log(` ${task.id?.slice(0, 8) || 'unknown'}... ${task.status?.padEnd(10) || 'unknown'} ${new Date(task.created_at).toLocaleString()}`);
190
+ });
191
+ console.log('');
192
+ },
193
+
194
+ // 健康检查
195
+ async health() {
196
+ const result = await request<any>('/health');
197
+ console.log('\n🔍 Gateway 状态:\n');
198
+ console.log(` 状态: ${result.status}`);
199
+ console.log(` Agent 数量: ${result.agents?.length || 0}`);
200
+ console.log(` 时间: ${new Date(result.timestamp).toLocaleString()}\n`);
201
+ }
202
+ };
203
+
204
+ // 主入口
205
+ async function main() {
206
+ const args = process.argv.slice(2);
207
+
208
+ if (args.length === 0) {
209
+ console.log(`
210
+ 🤖 Agent Gateway CLI
211
+
212
+ 用法:
213
+ agent-gateway <command> [options]
214
+
215
+ 命令:
216
+ agents, list 列出所有可用 Agent
217
+ task <description> 提交通用任务(自动路由)
218
+ to <agent> <task> 提交任务到指定 Agent
219
+ space, create-space 创建共享空间
220
+ tasks, list-tasks 列出所有任务
221
+ health, status 检查 Gateway 状态
222
+
223
+ 示例:
224
+ agent-gateway agents
225
+ agent-gateway task 帮我写一个排序算法
226
+ agent-gateway to claude-code 帮我review这段代码
227
+ agent-gateway health
228
+ `);
229
+ process.exit(0);
230
+ }
231
+
232
+ const command = args[0];
233
+ const commandArgs = args.slice(1);
234
+
235
+ try {
236
+ switch (command) {
237
+ case 'agents':
238
+ case 'list':
239
+ case 'ls':
240
+ await commands.listAgents();
241
+ break;
242
+
243
+ case 'task':
244
+ await commands.submitTask(commandArgs);
245
+ break;
246
+
247
+ case 'to':
248
+ if (commandArgs.length < 2) {
249
+ console.error('用法: agent-gateway to <agent> <task>');
250
+ process.exit(1);
251
+ }
252
+ const agentId = commandArgs[0];
253
+ if (agentId) {
254
+ await commands.submitToAgent(agentId, commandArgs.slice(1));
255
+ }
256
+ break;
257
+
258
+ case 'space':
259
+ case 'create-space':
260
+ await commands.createSpace();
261
+ break;
262
+
263
+ case 'tasks':
264
+ case 'list-tasks':
265
+ await commands.listTasks();
266
+ break;
267
+
268
+ case 'health':
269
+ case 'status':
270
+ await commands.health();
271
+ break;
272
+
273
+ default:
274
+ console.error(`❌ 未知命令: ${command}`);
275
+ console.log('运行 agent-gateway 查看帮助');
276
+ process.exit(1);
277
+ }
278
+ } catch (error: any) {
279
+ console.error(`\n❌ 错误: ${error.message}\n`);
280
+ process.exit(1);
281
+ }
282
+ }
283
+
284
+ main();