@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.
- package/CHANGELOG.md +47 -0
- package/LICENSE +21 -0
- package/README.md +243 -0
- package/README.zh-CN.md +243 -0
- package/config/gateway.yaml +190 -0
- package/docs/api.md +566 -0
- package/package.json +74 -0
- package/src/adapters/base.ts +34 -0
- package/src/adapters/claude-code.ts +122 -0
- package/src/adapters/openclaw.ts +120 -0
- package/src/adapters/process.ts +136 -0
- package/src/cli.ts +284 -0
- package/src/core/agent-registry.ts +334 -0
- package/src/core/config.ts +148 -0
- package/src/core/context-manager.ts +210 -0
- package/src/core/event-bus.ts +76 -0
- package/src/core/metrics.ts +216 -0
- package/src/core/router.ts +105 -0
- package/src/core/task-manager.ts +248 -0
- package/src/core/vector-store.ts +203 -0
- package/src/index.ts +84 -0
- package/src/routes/api.ts +158 -0
- package/src/routes/websocket.ts +91 -0
- package/src/types/index.ts +90 -0
|
@@ -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();
|