@starlink-awaken/agentmesh 1.0.3 → 1.2.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 +60 -41
- package/README.zh-CN.md +137 -167
- package/config/gateway.yaml +78 -0
- package/dist/src/cli/connect.d.ts +26 -0
- package/dist/src/cli/connect.js +544 -0
- package/dist/src/cli/setup.d.ts +2 -0
- package/dist/src/cli/setup.js +97 -0
- package/dist/src/cli.js +410 -0
- package/dist/src/core/logger.d.ts +14 -0
- package/dist/src/core/logger.js +57 -0
- package/dist/{index.js → src/index.js} +40 -8
- package/dist/src/model-gateway/circuit-breaker.d.ts +21 -0
- package/dist/src/model-gateway/circuit-breaker.js +86 -0
- package/dist/src/model-gateway/health.d.ts +12 -0
- package/dist/src/model-gateway/health.js +80 -0
- package/dist/src/model-gateway/providers.d.ts +4 -0
- package/dist/src/model-gateway/providers.js +113 -0
- package/dist/src/model-gateway/quota.d.ts +4 -0
- package/dist/src/model-gateway/quota.js +107 -0
- package/dist/src/model-gateway/rate-limit.d.ts +12 -0
- package/dist/src/model-gateway/rate-limit.js +51 -0
- package/dist/src/model-gateway/retry.d.ts +14 -0
- package/dist/src/model-gateway/retry.js +48 -0
- package/dist/src/model-gateway/router.d.ts +4 -0
- package/dist/src/model-gateway/router.js +79 -0
- package/dist/src/model-gateway/routes.d.ts +2 -0
- package/dist/src/model-gateway/routes.js +172 -0
- package/dist/src/model-gateway/types.d.ts +47 -0
- package/dist/src/model-gateway/types.js +1 -0
- package/dist/tests/core/context-manager.test.d.ts +1 -0
- package/dist/tests/core/context-manager.test.js +35 -0
- package/dist/tests/core/router.test.d.ts +1 -0
- package/dist/tests/core/router.test.js +79 -0
- package/dist/tests/model-gateway/circuit-breaker.test.d.ts +1 -0
- package/dist/tests/model-gateway/circuit-breaker.test.js +84 -0
- package/dist/tests/model-gateway/providers.test.d.ts +1 -0
- package/dist/tests/model-gateway/providers.test.js +80 -0
- package/dist/tests/model-gateway/quota.test.d.ts +1 -0
- package/dist/tests/model-gateway/quota.test.js +60 -0
- package/dist/tests/model-gateway/rate-limit.test.d.ts +1 -0
- package/dist/tests/model-gateway/rate-limit.test.js +42 -0
- package/dist/tests/model-gateway/retry.test.d.ts +1 -0
- package/dist/tests/model-gateway/retry.test.js +47 -0
- package/dist/tests/model-gateway/router.test.d.ts +1 -0
- package/dist/tests/model-gateway/router.test.js +108 -0
- package/dist/tests/model-gateway/routes.test.d.ts +1 -0
- package/dist/tests/model-gateway/routes.test.js +83 -0
- package/docs/api.md +187 -460
- package/docs/architecture.md +138 -0
- package/docs/configuration.md +188 -0
- package/package.json +3 -1
- package/dist/cli.js +0 -246
- /package/dist/{adapters → src/adapters}/base.d.ts +0 -0
- /package/dist/{adapters → src/adapters}/base.js +0 -0
- /package/dist/{adapters → src/adapters}/claude-code.d.ts +0 -0
- /package/dist/{adapters → src/adapters}/claude-code.js +0 -0
- /package/dist/{adapters → src/adapters}/openclaw.d.ts +0 -0
- /package/dist/{adapters → src/adapters}/openclaw.js +0 -0
- /package/dist/{adapters → src/adapters}/process.d.ts +0 -0
- /package/dist/{adapters → src/adapters}/process.js +0 -0
- /package/dist/{cli.d.ts → src/cli.d.ts} +0 -0
- /package/dist/{core → src/core}/agent-registry.d.ts +0 -0
- /package/dist/{core → src/core}/agent-registry.js +0 -0
- /package/dist/{core → src/core}/config.d.ts +0 -0
- /package/dist/{core → src/core}/config.js +0 -0
- /package/dist/{core → src/core}/context-manager.d.ts +0 -0
- /package/dist/{core → src/core}/context-manager.js +0 -0
- /package/dist/{core → src/core}/event-bus.d.ts +0 -0
- /package/dist/{core → src/core}/event-bus.js +0 -0
- /package/dist/{core → src/core}/metrics.d.ts +0 -0
- /package/dist/{core → src/core}/metrics.js +0 -0
- /package/dist/{core → src/core}/router.d.ts +0 -0
- /package/dist/{core → src/core}/router.js +0 -0
- /package/dist/{core → src/core}/task-manager.d.ts +0 -0
- /package/dist/{core → src/core}/task-manager.js +0 -0
- /package/dist/{core → src/core}/vector-store.d.ts +0 -0
- /package/dist/{core → src/core}/vector-store.js +0 -0
- /package/dist/{index.d.ts → src/index.d.ts} +0 -0
- /package/dist/{routes → src/routes}/api.d.ts +0 -0
- /package/dist/{routes → src/routes}/api.js +0 -0
- /package/dist/{routes → src/routes}/websocket.d.ts +0 -0
- /package/dist/{routes → src/routes}/websocket.js +0 -0
- /package/dist/{types → src/types}/index.d.ts +0 -0
- /package/dist/{types → src/types}/index.js +0 -0
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
# Architecture
|
|
2
|
+
|
|
3
|
+
## System Overview
|
|
4
|
+
|
|
5
|
+
```
|
|
6
|
+
┌──────────────────────────────────────────────────────────┐
|
|
7
|
+
│ Tool Consumers │
|
|
8
|
+
│ Codex Desktop Claude Code Gemini CLI Other Tools │
|
|
9
|
+
└────────────┬─────────────────────────────────────────────┘
|
|
10
|
+
│ /v1/chat/completions /v1/responses
|
|
11
|
+
▼
|
|
12
|
+
┌──────────────────────────────────────────────────────────┐
|
|
13
|
+
│ Agent Mesh Gateway :3000 │
|
|
14
|
+
│ │
|
|
15
|
+
│ ┌──────────────────┐ ┌──────────────────────┐ │
|
|
16
|
+
│ │ Model Gateway │ │ Agent Orchestrator │ │
|
|
17
|
+
│ │ ┌──────────────┐ │ │ ┌──────────────────┐ │ │
|
|
18
|
+
│ │ │ Quota Probe │ │ │ │ Agent Registry │ │ │
|
|
19
|
+
│ │ │ (codexbar) │ │ │ │ (25+ adapters) │ │ │
|
|
20
|
+
│ │ └──────┬───────┘ │ │ └────────┬─────────┘ │ │
|
|
21
|
+
│ │ │ │ │ │ │ │
|
|
22
|
+
│ │ ┌──────┴───────┐ │ │ ┌────────┴─────────┐ │ │
|
|
23
|
+
│ │ │ Model Router │ │ │ │ Task Router │ │ │
|
|
24
|
+
│ │ │ (fallback) │ │ │ │ (keyword match) │ │ │
|
|
25
|
+
│ │ └──────┬───────┘ │ │ └────────┬─────────┘ │ │
|
|
26
|
+
│ │ │ │ │ │ │ │
|
|
27
|
+
│ │ ┌──────┴───────┐ │ │ ┌────────┴─────────┐ │ │
|
|
28
|
+
│ │ │Provider Client│ │ │ │ Event Bus │ │ │
|
|
29
|
+
│ │ │(HTTP Stream) │ │ │ │ (Pub/Sub) │ │ │
|
|
30
|
+
│ │ └──────────────┘ │ │ └──────────────────┘ │ │
|
|
31
|
+
│ └──────────────────┘ └──────────────────────┘ │
|
|
32
|
+
│ │
|
|
33
|
+
│ ┌──────────────────────────────────────────────────┐ │
|
|
34
|
+
│ │ Context Manager │ │
|
|
35
|
+
│ │ Shared Spaces File Cache Vector Store (ChromaDB)│ │
|
|
36
|
+
│ └──────────────────────────────────────────────────┘ │
|
|
37
|
+
└──────────┬───────────────────────────────────────────────┘
|
|
38
|
+
│
|
|
39
|
+
┌──────┼──────┬──────────┐
|
|
40
|
+
▼ ▼ ▼ ▼
|
|
41
|
+
DeepSeek OpenAI OpenRouter Ollama
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Model Gateway Layer
|
|
45
|
+
|
|
46
|
+
### Quota Probe (`quota.ts`)
|
|
47
|
+
- Calls `codexbar usage --format json --provider all`
|
|
48
|
+
- Caches results for 60 seconds
|
|
49
|
+
- Parses provider-specific quota formats:
|
|
50
|
+
- codex: credits.remaining
|
|
51
|
+
- deepseek: balance in ¥ (from resetDescription)
|
|
52
|
+
- openrouter: openRouterUsage.balance
|
|
53
|
+
- ollama: always available
|
|
54
|
+
|
|
55
|
+
### Model Router (`router.ts`)
|
|
56
|
+
- `resolveProvider(model)`: determines which provider to use
|
|
57
|
+
- Priority: model_routing config → fallback_chain → first available
|
|
58
|
+
- Checks quota availability via `isProviderAvailable()`
|
|
59
|
+
- Checks API key availability via env vars or config
|
|
60
|
+
|
|
61
|
+
### Provider Client (`providers.ts`)
|
|
62
|
+
- `callChatCompletions()`: unified OpenAI-compatible client
|
|
63
|
+
- `callResponsesApi()`: converts Responses API → Chat Completions
|
|
64
|
+
- Extracts messages from `input` array
|
|
65
|
+
- Maps `input_text`/`output_text` content types
|
|
66
|
+
- Preserves tool definitions
|
|
67
|
+
|
|
68
|
+
### Routes (`routes.ts`)
|
|
69
|
+
- `GET /v1/models`: aggregated model list
|
|
70
|
+
- `POST /v1/chat/completions`: proxy with streaming support
|
|
71
|
+
- `POST /v1/responses`: Codex Desktop adapter
|
|
72
|
+
- `GET /model-gateway/health`: quota-aware health check
|
|
73
|
+
- `GET /model-gateway/quota`: full quota details
|
|
74
|
+
|
|
75
|
+
## Agent Orchestration Layer
|
|
76
|
+
|
|
77
|
+
### Agent Registry
|
|
78
|
+
- Manages 25+ agent adapters
|
|
79
|
+
- Capability-based agent discovery
|
|
80
|
+
- Health check monitoring
|
|
81
|
+
|
|
82
|
+
### Task Router
|
|
83
|
+
- Keyword-based task-to-agent routing
|
|
84
|
+
- Priority-based rule matching
|
|
85
|
+
- Broadcast support for multi-agent tasks
|
|
86
|
+
|
|
87
|
+
### Context Manager
|
|
88
|
+
- Shared spaces with file persistence
|
|
89
|
+
- Message history per space
|
|
90
|
+
- Artifact storage
|
|
91
|
+
|
|
92
|
+
## Data Flow
|
|
93
|
+
|
|
94
|
+
### Chat Completions Flow
|
|
95
|
+
```
|
|
96
|
+
1. Client → POST /v1/chat/completions { model, messages }
|
|
97
|
+
2. Router: parse model → check routing config → check quota → select provider
|
|
98
|
+
3. Provider Client: POST to provider API with streaming
|
|
99
|
+
4. Response: SSE stream or JSON → Client
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### Codex Desktop Flow
|
|
103
|
+
```
|
|
104
|
+
1. Codex Desktop → POST /v1/responses { input: [...], model }
|
|
105
|
+
2. Adapter: convert input items → messages array
|
|
106
|
+
3. Router: resolve provider (same as chat completions)
|
|
107
|
+
4. Provider Client: POST /v1/chat/completions to provider
|
|
108
|
+
5. Adapter: convert chat response → responses format (output items)
|
|
109
|
+
6. Response → Codex Desktop
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### Quota Refresh Flow
|
|
113
|
+
```
|
|
114
|
+
1. First API call triggers quota probe
|
|
115
|
+
2. codexbar subprocess runs (~10-30s)
|
|
116
|
+
3. Results parsed and cached (60s TTL)
|
|
117
|
+
4. Router uses cached data for decisions
|
|
118
|
+
5. After 60s, next request triggers refresh
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## Technology Stack
|
|
122
|
+
|
|
123
|
+
- **Runtime**: Bun (>=1.0.0)
|
|
124
|
+
- **Server**: Fastify 5
|
|
125
|
+
- **HTTP Client**: Native Fetch API
|
|
126
|
+
- **Streaming**: Server-Sent Events (SSE)
|
|
127
|
+
- **WebSocket**: Fastify WebSocket plugin
|
|
128
|
+
- **Vector Store**: ChromaDB (optional)
|
|
129
|
+
- **Logging**: Custom structured logger + Pino
|
|
130
|
+
- **Config**: YAML
|
|
131
|
+
|
|
132
|
+
## Design Decisions
|
|
133
|
+
|
|
134
|
+
1. **All providers via OpenAI-compatible API**: Avoids per-provider client complexity
|
|
135
|
+
2. **Quota caching**: codexbar is slow, 60s cache prevents latency spikes
|
|
136
|
+
3. **Streaming passthrough**: Zero-copy SSE forwarding for efficiency
|
|
137
|
+
4. **No additional deps for model gateway**: Uses Bun's built-in Fetch API
|
|
138
|
+
5. **codexbar as quota source**: Already aggregates 30+ providers, no duplicate logic
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
# Configuration Guide
|
|
2
|
+
|
|
3
|
+
## Config File
|
|
4
|
+
|
|
5
|
+
Default location: `config/gateway.yaml`
|
|
6
|
+
|
|
7
|
+
## Full Configuration Reference
|
|
8
|
+
|
|
9
|
+
```yaml
|
|
10
|
+
# Server
|
|
11
|
+
port: 3000
|
|
12
|
+
wsPort: 3001
|
|
13
|
+
host: "0.0.0.0"
|
|
14
|
+
dataDir: "./data"
|
|
15
|
+
logDir: "./logs"
|
|
16
|
+
logLevel: "info" # debug | info | warn | error
|
|
17
|
+
|
|
18
|
+
# =============================================================================
|
|
19
|
+
# Model Gateway
|
|
20
|
+
# =============================================================================
|
|
21
|
+
models:
|
|
22
|
+
default_model: deepseek-chat
|
|
23
|
+
|
|
24
|
+
providers:
|
|
25
|
+
deepseek:
|
|
26
|
+
base_url: https://api.deepseek.com/v1
|
|
27
|
+
api_key_env: DEEPSEEK_API_KEY # 从环境变量读取
|
|
28
|
+
models:
|
|
29
|
+
- deepseek-chat
|
|
30
|
+
- deepseek-reasoner
|
|
31
|
+
- deepseek-v4-pro
|
|
32
|
+
- deepseek-v4-flash
|
|
33
|
+
|
|
34
|
+
openai:
|
|
35
|
+
base_url: https://api.openai.com/v1
|
|
36
|
+
api_key_env: OPENAI_API_KEY
|
|
37
|
+
models:
|
|
38
|
+
- gpt-5.1
|
|
39
|
+
- gpt-5.1-codex
|
|
40
|
+
- o4-mini
|
|
41
|
+
|
|
42
|
+
openrouter:
|
|
43
|
+
base_url: https://openrouter.ai/api/v1
|
|
44
|
+
api_key_env: OPENROUTER_API_KEY
|
|
45
|
+
|
|
46
|
+
ollama:
|
|
47
|
+
base_url: http://127.0.0.1:11434/v1
|
|
48
|
+
api_key: ollama # 直接指定 key
|
|
49
|
+
models:
|
|
50
|
+
- qwen3:14b
|
|
51
|
+
- codestral:22b
|
|
52
|
+
|
|
53
|
+
# Provider 优先级(从高到低)
|
|
54
|
+
fallback_chain:
|
|
55
|
+
- deepseek
|
|
56
|
+
- openrouter
|
|
57
|
+
- ollama
|
|
58
|
+
|
|
59
|
+
# 模型名匹配规则
|
|
60
|
+
model_routing:
|
|
61
|
+
"deepseek": # 匹配包含 "deepseek" 的模型名
|
|
62
|
+
- deepseek
|
|
63
|
+
"gpt-": # GPT 系列优先 openai,配额不够用 deepseek
|
|
64
|
+
- openai
|
|
65
|
+
- deepseek
|
|
66
|
+
"o1": # 推理模型
|
|
67
|
+
- openai
|
|
68
|
+
- deepseek
|
|
69
|
+
"claude": # Claude 通过 OpenRouter
|
|
70
|
+
- openrouter
|
|
71
|
+
|
|
72
|
+
# =============================================================================
|
|
73
|
+
# Agent Configuration
|
|
74
|
+
# =============================================================================
|
|
75
|
+
routing:
|
|
76
|
+
defaultAgent: "claude-code"
|
|
77
|
+
rules:
|
|
78
|
+
- name: code-generation
|
|
79
|
+
keywords: [write code, generate code, 写代码, 生成代码]
|
|
80
|
+
agent: claude-code
|
|
81
|
+
priority: 10
|
|
82
|
+
|
|
83
|
+
- name: code-review
|
|
84
|
+
keywords: [review, code review, 代码审查]
|
|
85
|
+
agent: claude-code
|
|
86
|
+
priority: 15
|
|
87
|
+
|
|
88
|
+
- name: browser-automation
|
|
89
|
+
keywords: [browser, scrape, 浏览器, 爬虫]
|
|
90
|
+
agent: openclaw
|
|
91
|
+
priority: 15
|
|
92
|
+
|
|
93
|
+
- name: multi-agent
|
|
94
|
+
keywords: [collaborate, team, 协作]
|
|
95
|
+
strategy: broadcast
|
|
96
|
+
agents: [claude-code, openclaw]
|
|
97
|
+
priority: 20
|
|
98
|
+
|
|
99
|
+
agents:
|
|
100
|
+
- id: claude-code
|
|
101
|
+
name: Claude Code
|
|
102
|
+
type: claude-code
|
|
103
|
+
capabilities: [code-generation, code-review, debugging, refactoring]
|
|
104
|
+
|
|
105
|
+
- id: openclaw
|
|
106
|
+
name: OpenClaw
|
|
107
|
+
type: openclaw
|
|
108
|
+
capabilities: [browser-automation, web-scraping, ui-testing]
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## API Key Configuration
|
|
112
|
+
|
|
113
|
+
API Keys can be set in three ways:
|
|
114
|
+
|
|
115
|
+
### 1. `.env` file (recommended)
|
|
116
|
+
```
|
|
117
|
+
DEEPSEEK_API_KEY=sk-xxx
|
|
118
|
+
OPENAI_API_KEY=sk-xxx
|
|
119
|
+
OPENROUTER_API_KEY=sk-xxx
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### 2. Environment variables
|
|
123
|
+
```bash
|
|
124
|
+
export DEEPSEEK_API_KEY=sk-xxx
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### 3. Inline in config (ollama only)
|
|
128
|
+
```yaml
|
|
129
|
+
providers:
|
|
130
|
+
ollama:
|
|
131
|
+
api_key: ollama # local, no real key needed
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### Key Resolution Order
|
|
135
|
+
1. Inline `api_key` in config
|
|
136
|
+
2. Environment variable via `api_key_env`
|
|
137
|
+
3. Not found → provider skipped
|
|
138
|
+
|
|
139
|
+
## Provider Configuration
|
|
140
|
+
|
|
141
|
+
### Adding a new provider
|
|
142
|
+
|
|
143
|
+
```yaml
|
|
144
|
+
models:
|
|
145
|
+
providers:
|
|
146
|
+
my-provider:
|
|
147
|
+
base_url: https://api.my-provider.com/v1
|
|
148
|
+
api_key_env: MY_PROVIDER_API_KEY
|
|
149
|
+
models: [my-model-v1, my-model-v2]
|
|
150
|
+
|
|
151
|
+
fallback_chain:
|
|
152
|
+
- deepseek
|
|
153
|
+
- my-provider # add to chain
|
|
154
|
+
- ollama
|
|
155
|
+
|
|
156
|
+
model_routing:
|
|
157
|
+
"my-model": # route by model name
|
|
158
|
+
- my-provider
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### Provider Requirements
|
|
162
|
+
- Must implement OpenAI-compatible `/v1/chat/completions` endpoint
|
|
163
|
+
- Must support Bearer token authentication
|
|
164
|
+
- Optional: supports SSE streaming
|
|
165
|
+
|
|
166
|
+
## Adding a new Agent
|
|
167
|
+
|
|
168
|
+
```yaml
|
|
169
|
+
agents:
|
|
170
|
+
- id: my-agent
|
|
171
|
+
name: My Custom Agent
|
|
172
|
+
type: process
|
|
173
|
+
command: my-agent-cli
|
|
174
|
+
args: ["--interactive"]
|
|
175
|
+
capabilities:
|
|
176
|
+
- my-capability-1
|
|
177
|
+
- my-capability-2
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
## Logging
|
|
181
|
+
|
|
182
|
+
Logs are written to `logs/agentmesh-YYYY-MM-DD.log`.
|
|
183
|
+
|
|
184
|
+
Log levels:
|
|
185
|
+
- `debug` — verbose provider calls, routing decisions
|
|
186
|
+
- `info` — startup, quota refresh, provider selection
|
|
187
|
+
- `warn` — fallback triggered, quota probe failures
|
|
188
|
+
- `error` — provider errors, crashes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@starlink-awaken/agentmesh",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "Unified Agent Gateway - Multi-Agent Scheduler and Router",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -25,6 +25,8 @@
|
|
|
25
25
|
"cli": "bun run src/cli.ts",
|
|
26
26
|
"build": "tsc",
|
|
27
27
|
"test": "bun test",
|
|
28
|
+
"typecheck": "tsc --noEmit",
|
|
29
|
+
"lint": "tsc --noEmit",
|
|
28
30
|
"prepublishOnly": "bun run build"
|
|
29
31
|
},
|
|
30
32
|
"engines": {
|
package/dist/cli.js
DELETED
|
@@ -1,246 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bun
|
|
2
|
-
const BASE_URL = process.env.AGENT_GATEWAY_URL || 'http://localhost:3000';
|
|
3
|
-
async function request(path, options) {
|
|
4
|
-
const response = await fetch(`${BASE_URL}${path}`, {
|
|
5
|
-
...options,
|
|
6
|
-
headers: {
|
|
7
|
-
'Content-Type': 'application/json',
|
|
8
|
-
...options?.headers
|
|
9
|
-
}
|
|
10
|
-
});
|
|
11
|
-
if (!response.ok) {
|
|
12
|
-
const error = await response.json().catch(() => ({ message: 'Unknown error' }));
|
|
13
|
-
throw new Error(`HTTP ${response.status}: ${JSON.stringify(error)}`);
|
|
14
|
-
}
|
|
15
|
-
return response.json();
|
|
16
|
-
}
|
|
17
|
-
// 命令
|
|
18
|
-
const commands = {
|
|
19
|
-
// 列出所有 Agent
|
|
20
|
-
async listAgents() {
|
|
21
|
-
const agents = await request('/agents');
|
|
22
|
-
console.log('\n📋 可用 Agent:\n');
|
|
23
|
-
agents.forEach(agent => {
|
|
24
|
-
console.log(` ${agent.id.padEnd(15)} ${agent.name.padEnd(20)} [${agent.status}]`);
|
|
25
|
-
console.log(` 能力: ${agent.capabilities.join(', ')}\n`);
|
|
26
|
-
});
|
|
27
|
-
},
|
|
28
|
-
// 提交任务
|
|
29
|
-
async submitTask(args) {
|
|
30
|
-
const task = args.join(' ');
|
|
31
|
-
if (!task) {
|
|
32
|
-
console.error('❌ 请提供任务描述');
|
|
33
|
-
process.exit(1);
|
|
34
|
-
}
|
|
35
|
-
console.log(`\n📤 提交任务: ${task}\n`);
|
|
36
|
-
const message = {
|
|
37
|
-
type: 'request',
|
|
38
|
-
source: 'cli',
|
|
39
|
-
target: 'gateway',
|
|
40
|
-
payload: {
|
|
41
|
-
task,
|
|
42
|
-
options: {
|
|
43
|
-
stream: false,
|
|
44
|
-
timeout: 300
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
};
|
|
48
|
-
const result = await request('/tasks', {
|
|
49
|
-
method: 'POST',
|
|
50
|
-
body: JSON.stringify(message)
|
|
51
|
-
});
|
|
52
|
-
console.log(`✅ 任务已提交: ${result.task_id}`);
|
|
53
|
-
console.log(` 状态: ${result.status}\n`);
|
|
54
|
-
// 轮询获取结果
|
|
55
|
-
console.log('Waiting for result...\n');
|
|
56
|
-
let completed = false;
|
|
57
|
-
let attempts = 0;
|
|
58
|
-
const maxAttempts = 60;
|
|
59
|
-
while (!completed && attempts < maxAttempts) {
|
|
60
|
-
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
61
|
-
const taskResult = await request(`/tasks/${result.task_id}`);
|
|
62
|
-
if (taskResult.status === 'completed') {
|
|
63
|
-
completed = true;
|
|
64
|
-
console.log('✅ 任务完成!\n');
|
|
65
|
-
console.log('📊 结果:');
|
|
66
|
-
if (typeof taskResult.result === 'object') {
|
|
67
|
-
Object.entries(taskResult.result).forEach(([agent, res]) => {
|
|
68
|
-
console.log(`\n--- ${agent} ---`);
|
|
69
|
-
console.log(res);
|
|
70
|
-
});
|
|
71
|
-
}
|
|
72
|
-
else {
|
|
73
|
-
console.log(taskResult.result);
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
else if (taskResult.status === 'failed') {
|
|
77
|
-
completed = true;
|
|
78
|
-
console.log('❌ 任务失败!');
|
|
79
|
-
console.log('错误:', taskResult.error);
|
|
80
|
-
}
|
|
81
|
-
else {
|
|
82
|
-
attempts++;
|
|
83
|
-
process.stdout.write('.');
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
if (!completed) {
|
|
87
|
-
console.log('\n⚠️ 任务超时\n');
|
|
88
|
-
}
|
|
89
|
-
},
|
|
90
|
-
// 提交到指定 Agent
|
|
91
|
-
async submitToAgent(agentId, args) {
|
|
92
|
-
const task = args.join(' ');
|
|
93
|
-
if (!task) {
|
|
94
|
-
console.error('❌ 请提供任务描述');
|
|
95
|
-
process.exit(1);
|
|
96
|
-
}
|
|
97
|
-
console.log(`\n📤 提交任务到 ${agentId}: ${task}\n`);
|
|
98
|
-
const message = {
|
|
99
|
-
type: 'request',
|
|
100
|
-
source: 'cli',
|
|
101
|
-
target: agentId,
|
|
102
|
-
payload: {
|
|
103
|
-
task,
|
|
104
|
-
options: {
|
|
105
|
-
stream: false,
|
|
106
|
-
timeout: 300
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
};
|
|
110
|
-
const result = await request('/tasks', {
|
|
111
|
-
method: 'POST',
|
|
112
|
-
body: JSON.stringify(message)
|
|
113
|
-
});
|
|
114
|
-
console.log(`✅ 任务已提交: ${result.task_id}`);
|
|
115
|
-
console.log(` 状态: ${result.status}\n`);
|
|
116
|
-
// 轮询获取结果
|
|
117
|
-
console.log('⏳ 等待执行结果...\n');
|
|
118
|
-
let completed = false;
|
|
119
|
-
let attempts = 0;
|
|
120
|
-
const maxAttempts = 60;
|
|
121
|
-
while (!completed && attempts < maxAttempts) {
|
|
122
|
-
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
123
|
-
const taskResult = await request(`/tasks/${result.task_id}`);
|
|
124
|
-
if (taskResult.status === 'completed') {
|
|
125
|
-
completed = true;
|
|
126
|
-
console.log('\n✅ 任务完成!\n');
|
|
127
|
-
console.log('📊 结果:');
|
|
128
|
-
console.log(taskResult.result);
|
|
129
|
-
}
|
|
130
|
-
else if (taskResult.status === 'failed') {
|
|
131
|
-
completed = true;
|
|
132
|
-
console.log('\n❌ 任务失败!');
|
|
133
|
-
console.log('错误:', taskResult.error);
|
|
134
|
-
}
|
|
135
|
-
else {
|
|
136
|
-
attempts++;
|
|
137
|
-
process.stdout.write('.');
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
if (!completed) {
|
|
141
|
-
console.log('\n⚠️ 任务超时\n');
|
|
142
|
-
}
|
|
143
|
-
},
|
|
144
|
-
// 创建共享空间
|
|
145
|
-
async createSpace() {
|
|
146
|
-
const result = await request('/spaces', {
|
|
147
|
-
method: 'POST',
|
|
148
|
-
body: JSON.stringify({ metadata: { createdBy: 'cli' } })
|
|
149
|
-
});
|
|
150
|
-
console.log(`\n✅ 共享空间已创建: ${result.space_id}\n`);
|
|
151
|
-
return result.space_id;
|
|
152
|
-
},
|
|
153
|
-
// 列出任务
|
|
154
|
-
async listTasks() {
|
|
155
|
-
const tasks = await request('/tasks');
|
|
156
|
-
console.log('\n📋 任务列表:\n');
|
|
157
|
-
if (tasks.length === 0) {
|
|
158
|
-
console.log(' (无任务)\n');
|
|
159
|
-
return;
|
|
160
|
-
}
|
|
161
|
-
tasks.forEach(task => {
|
|
162
|
-
console.log(` ${task.id?.slice(0, 8) || 'unknown'}... ${task.status?.padEnd(10) || 'unknown'} ${new Date(task.created_at).toLocaleString()}`);
|
|
163
|
-
});
|
|
164
|
-
console.log('');
|
|
165
|
-
},
|
|
166
|
-
// 健康检查
|
|
167
|
-
async health() {
|
|
168
|
-
const result = await request('/health');
|
|
169
|
-
console.log('\n🔍 Gateway 状态:\n');
|
|
170
|
-
console.log(` 状态: ${result.status}`);
|
|
171
|
-
console.log(` Agent 数量: ${result.agents?.length || 0}`);
|
|
172
|
-
console.log(` 时间: ${new Date(result.timestamp).toLocaleString()}\n`);
|
|
173
|
-
}
|
|
174
|
-
};
|
|
175
|
-
// 主入口
|
|
176
|
-
async function main() {
|
|
177
|
-
const args = process.argv.slice(2);
|
|
178
|
-
if (args.length === 0) {
|
|
179
|
-
console.log(`
|
|
180
|
-
🤖 Agent Gateway CLI
|
|
181
|
-
|
|
182
|
-
用法:
|
|
183
|
-
agent-gateway <command> [options]
|
|
184
|
-
|
|
185
|
-
命令:
|
|
186
|
-
agents, list 列出所有可用 Agent
|
|
187
|
-
task <description> 提交通用任务(自动路由)
|
|
188
|
-
to <agent> <task> 提交任务到指定 Agent
|
|
189
|
-
space, create-space 创建共享空间
|
|
190
|
-
tasks, list-tasks 列出所有任务
|
|
191
|
-
health, status 检查 Gateway 状态
|
|
192
|
-
|
|
193
|
-
示例:
|
|
194
|
-
agent-gateway agents
|
|
195
|
-
agent-gateway task 帮我写一个排序算法
|
|
196
|
-
agent-gateway to claude-code 帮我review这段代码
|
|
197
|
-
agent-gateway health
|
|
198
|
-
`);
|
|
199
|
-
process.exit(0);
|
|
200
|
-
}
|
|
201
|
-
const command = args[0];
|
|
202
|
-
const commandArgs = args.slice(1);
|
|
203
|
-
try {
|
|
204
|
-
switch (command) {
|
|
205
|
-
case 'agents':
|
|
206
|
-
case 'list':
|
|
207
|
-
case 'ls':
|
|
208
|
-
await commands.listAgents();
|
|
209
|
-
break;
|
|
210
|
-
case 'task':
|
|
211
|
-
await commands.submitTask(commandArgs);
|
|
212
|
-
break;
|
|
213
|
-
case 'to':
|
|
214
|
-
if (commandArgs.length < 2) {
|
|
215
|
-
console.error('用法: agent-gateway to <agent> <task>');
|
|
216
|
-
process.exit(1);
|
|
217
|
-
}
|
|
218
|
-
const agentId = commandArgs[0];
|
|
219
|
-
if (agentId) {
|
|
220
|
-
await commands.submitToAgent(agentId, commandArgs.slice(1));
|
|
221
|
-
}
|
|
222
|
-
break;
|
|
223
|
-
case 'space':
|
|
224
|
-
case 'create-space':
|
|
225
|
-
await commands.createSpace();
|
|
226
|
-
break;
|
|
227
|
-
case 'tasks':
|
|
228
|
-
case 'list-tasks':
|
|
229
|
-
await commands.listTasks();
|
|
230
|
-
break;
|
|
231
|
-
case 'health':
|
|
232
|
-
case 'status':
|
|
233
|
-
await commands.health();
|
|
234
|
-
break;
|
|
235
|
-
default:
|
|
236
|
-
console.error(`❌ 未知命令: ${command}`);
|
|
237
|
-
console.log('运行 agent-gateway 查看帮助');
|
|
238
|
-
process.exit(1);
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
catch (error) {
|
|
242
|
-
console.error(`\n❌ 错误: ${error.message}\n`);
|
|
243
|
-
process.exit(1);
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
main();
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|