@ema.co/mcp-toolkit 0.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/LICENSE +21 -0
- package/README.md +321 -0
- package/config.example.yaml +32 -0
- package/dist/cli/index.js +333 -0
- package/dist/config.js +136 -0
- package/dist/emaClient.js +398 -0
- package/dist/index.js +109 -0
- package/dist/mcp/handlers-consolidated.js +851 -0
- package/dist/mcp/index.js +15 -0
- package/dist/mcp/prompts.js +1753 -0
- package/dist/mcp/resources.js +624 -0
- package/dist/mcp/server.js +4723 -0
- package/dist/mcp/tools-consolidated.js +590 -0
- package/dist/mcp/tools-legacy.js +736 -0
- package/dist/models.js +8 -0
- package/dist/scheduler.js +21 -0
- package/dist/sdk/client.js +788 -0
- package/dist/sdk/config.js +136 -0
- package/dist/sdk/contracts.js +429 -0
- package/dist/sdk/generation-schema.js +189 -0
- package/dist/sdk/index.js +39 -0
- package/dist/sdk/knowledge.js +2780 -0
- package/dist/sdk/models.js +8 -0
- package/dist/sdk/state.js +88 -0
- package/dist/sdk/sync-options.js +216 -0
- package/dist/sdk/sync.js +220 -0
- package/dist/sdk/validation-rules.js +355 -0
- package/dist/sdk/workflow-generator.js +291 -0
- package/dist/sdk/workflow-intent.js +1585 -0
- package/dist/state.js +88 -0
- package/dist/sync.js +416 -0
- package/dist/syncOptions.js +216 -0
- package/dist/ui.js +334 -0
- package/docs/advisor-comms-assistant-fixes.md +175 -0
- package/docs/api-contracts.md +216 -0
- package/docs/auto-builder-analysis.md +271 -0
- package/docs/data-architecture.md +166 -0
- package/docs/ema-auto-builder-guide.html +394 -0
- package/docs/ema-user-guide.md +1121 -0
- package/docs/mcp-tools-guide.md +149 -0
- package/docs/naming-conventions.md +218 -0
- package/docs/tool-consolidation-proposal.md +427 -0
- package/package.json +98 -0
- package/resources/templates/chat-ai/README.md +119 -0
- package/resources/templates/chat-ai/persona-config.json +111 -0
- package/resources/templates/dashboard-ai/README.md +156 -0
- package/resources/templates/dashboard-ai/persona-config.json +180 -0
- package/resources/templates/voice-ai/README.md +123 -0
- package/resources/templates/voice-ai/persona-config.json +74 -0
- package/resources/templates/voice-ai/workflow-prompt.md +120 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024-2025 Ema Inc.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
# @ema.co/mcp-toolkit
|
|
2
|
+
|
|
3
|
+
MCP (Model Context Protocol) server for managing Ema AI Employees. Works with Cursor, Claude Desktop, and other MCP clients.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
### Option 1: npx (Recommended)
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
# Run directly without installing
|
|
11
|
+
npx @ema.co/mcp-toolkit
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
### Option 2: Global Install
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npm install -g @ema.co/mcp-toolkit
|
|
18
|
+
ema-mcp # Start MCP server
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
### Option 3: Clone & Build (Development)
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
git clone https://github.com/Ema-Unlimited/@ema.co/mcp-toolkit.git
|
|
25
|
+
cd @ema.co/mcp-toolkit
|
|
26
|
+
npm install
|
|
27
|
+
npm run build
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## Quick Start
|
|
33
|
+
|
|
34
|
+
### 1. Set Your Credentials
|
|
35
|
+
|
|
36
|
+
**Option A: API Key (Recommended)**
|
|
37
|
+
|
|
38
|
+
API keys are stable and auto-refresh (24h token validity):
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
export EMA_API_KEY="your-api-key"
|
|
42
|
+
# Or for specific environment:
|
|
43
|
+
export EMA_PROD_API_KEY="your-prod-api-key"
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
**Option B: Bearer Token**
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
export EMA_BEARER_TOKEN="your-token"
|
|
50
|
+
# Or for specific environment:
|
|
51
|
+
export EMA_PROD_BEARER_TOKEN="your-prod-token"
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### 2. Add to Your AI Assistant
|
|
55
|
+
|
|
56
|
+
**Cursor** (`~/.cursor/mcp.json`):
|
|
57
|
+
|
|
58
|
+
```json
|
|
59
|
+
{
|
|
60
|
+
"mcpServers": {
|
|
61
|
+
"ema": {
|
|
62
|
+
"command": "npx",
|
|
63
|
+
"args": ["@ema.co/mcp-toolkit"],
|
|
64
|
+
"env": {
|
|
65
|
+
"EMA_API_KEY": "${EMA_API_KEY}"
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
> **Note**: Use `${ENV_VAR}` syntax to reference environment variables from your shell. Set them in your `~/.zshrc` or `~/.bashrc`.
|
|
73
|
+
|
|
74
|
+
**Claude Desktop** (`~/Library/Application Support/Claude/claude_desktop_config.json`):
|
|
75
|
+
|
|
76
|
+
```json
|
|
77
|
+
{
|
|
78
|
+
"mcpServers": {
|
|
79
|
+
"ema": {
|
|
80
|
+
"command": "npx",
|
|
81
|
+
"args": ["@ema.co/mcp-toolkit"],
|
|
82
|
+
"env": {
|
|
83
|
+
"EMA_API_KEY": "your-api-key-here"
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### 3. Start Using
|
|
91
|
+
|
|
92
|
+
Ask your AI assistant:
|
|
93
|
+
- "List my AI Employees"
|
|
94
|
+
- "Analyze the workflow for Customer Support Bot"
|
|
95
|
+
- "Create a new Voice AI for appointment scheduling"
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
## Authentication
|
|
100
|
+
|
|
101
|
+
### Option 1: API Key (Recommended)
|
|
102
|
+
|
|
103
|
+
API keys provide stable authentication with automatic JWT refresh:
|
|
104
|
+
|
|
105
|
+
1. Get your API key from the Ema team
|
|
106
|
+
2. Set the environment variable:
|
|
107
|
+
```bash
|
|
108
|
+
export EMA_API_KEY="your-api-key"
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
The toolkit automatically:
|
|
112
|
+
- Exchanges the API key for a JWT token
|
|
113
|
+
- Caches the token (valid 24 hours)
|
|
114
|
+
- Refreshes before expiry
|
|
115
|
+
|
|
116
|
+
### Option 2: Bearer Token
|
|
117
|
+
|
|
118
|
+
For quick testing or when API keys aren't available:
|
|
119
|
+
|
|
120
|
+
1. Log in to [Ema Platform](https://app.ema.co)
|
|
121
|
+
2. Open browser DevTools → Network tab
|
|
122
|
+
3. Find any API request's `Authorization: Bearer ...` header
|
|
123
|
+
4. Copy the token (without "Bearer " prefix)
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
export EMA_BEARER_TOKEN="your-token"
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
> **Note**: Bearer tokens expire after ~1 hour. Use API keys for production.
|
|
130
|
+
|
|
131
|
+
### Multiple Environments
|
|
132
|
+
|
|
133
|
+
Use environment-specific credentials:
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
# API keys (preferred)
|
|
137
|
+
export EMA_PROD_API_KEY="..."
|
|
138
|
+
export EMA_DEMO_API_KEY="..."
|
|
139
|
+
export EMA_DEV_API_KEY="..."
|
|
140
|
+
|
|
141
|
+
# Or bearer tokens
|
|
142
|
+
export EMA_PROD_BEARER_TOKEN="..."
|
|
143
|
+
export EMA_DEMO_BEARER_TOKEN="..."
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
## Configuration
|
|
149
|
+
|
|
150
|
+
### Zero-Config Mode (Default)
|
|
151
|
+
|
|
152
|
+
No config file needed! The toolkit auto-detects environments from environment variables:
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
# Pattern: EMA_<ENV>_API_KEY or EMA_<ENV>_BEARER_TOKEN
|
|
156
|
+
export EMA_PROD_API_KEY="..." # → creates "prod" environment
|
|
157
|
+
export EMA_DEMO_API_KEY="..." # → creates "demo" environment
|
|
158
|
+
export EMA_DEV_BEARER_TOKEN="..." # → creates "dev" environment
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
**Default Environment**:
|
|
162
|
+
- External users: `prod`
|
|
163
|
+
- Ema employees: `demo` (auto-detected if demo token exists)
|
|
164
|
+
- Override: `export EMA_ENV_NAME="dev"`
|
|
165
|
+
|
|
166
|
+
**Well-known URLs** (automatic):
|
|
167
|
+
| Environment | URL |
|
|
168
|
+
|-------------|-----|
|
|
169
|
+
| `prod` | `https://api.ema.co` |
|
|
170
|
+
| `demo` | `https://api.demo.ema.co` |
|
|
171
|
+
| `dev` | `https://api.dev.ema.co` |
|
|
172
|
+
| `staging` | `https://api.staging.ema.co` |
|
|
173
|
+
|
|
174
|
+
### Config File (Advanced)
|
|
175
|
+
|
|
176
|
+
For complex setups, create `ema.config.yaml`:
|
|
177
|
+
|
|
178
|
+
```yaml
|
|
179
|
+
environments:
|
|
180
|
+
- name: prod
|
|
181
|
+
baseUrl: https://api.ema.co
|
|
182
|
+
bearerTokenEnv: EMA_PROD_BEARER_TOKEN
|
|
183
|
+
isMaster: true
|
|
184
|
+
|
|
185
|
+
- name: dev
|
|
186
|
+
baseUrl: https://api.dev.ema.co
|
|
187
|
+
bearerTokenEnv: EMA_DEV_BEARER_TOKEN
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
---
|
|
191
|
+
|
|
192
|
+
## MCP Tools
|
|
193
|
+
|
|
194
|
+
| Tool | Purpose |
|
|
195
|
+
|------|---------|
|
|
196
|
+
| `env` | List available environments |
|
|
197
|
+
| `persona` | AI Employee management (get/list/create/update/compare) |
|
|
198
|
+
| `workflow` | Generate/analyze/deploy/optimize/explain/extend workflows |
|
|
199
|
+
| `action` | Agent lookup, docs, and recommendations |
|
|
200
|
+
| `template` | Patterns, widgets, qualifying questions |
|
|
201
|
+
| `knowledge` | Data sources + embedding (upload/list/delete/toggle) |
|
|
202
|
+
| `reference` | Concepts, guidance, validation, common mistakes |
|
|
203
|
+
| `sync` | Sync across environments |
|
|
204
|
+
| `demo` | Demo/RAG document utilities |
|
|
205
|
+
|
|
206
|
+
## Dynamic Resources
|
|
207
|
+
|
|
208
|
+
| Resource | Source |
|
|
209
|
+
|----------|--------|
|
|
210
|
+
| `ema://catalog/agents` | Live from API |
|
|
211
|
+
| `ema://catalog/templates` | Live from API |
|
|
212
|
+
| `ema://catalog/patterns` | Workflow patterns |
|
|
213
|
+
| `ema://rules/anti-patterns` | Validation rules |
|
|
214
|
+
|
|
215
|
+
---
|
|
216
|
+
|
|
217
|
+
## CLI
|
|
218
|
+
|
|
219
|
+
```bash
|
|
220
|
+
# List personas
|
|
221
|
+
ema personas list
|
|
222
|
+
|
|
223
|
+
# Sync a persona
|
|
224
|
+
ema sync persona "My Bot" --target dev --dry-run
|
|
225
|
+
|
|
226
|
+
# Check sync status
|
|
227
|
+
ema sync status
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
---
|
|
231
|
+
|
|
232
|
+
## SDK
|
|
233
|
+
|
|
234
|
+
```typescript
|
|
235
|
+
import { EmaClient } from "@ema.co/mcp-toolkit";
|
|
236
|
+
|
|
237
|
+
const client = new EmaClient({
|
|
238
|
+
name: "prod",
|
|
239
|
+
baseUrl: "https://api.ema.co",
|
|
240
|
+
bearerToken: process.env.EMA_PROD_BEARER_TOKEN!,
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
// List AI Employees
|
|
244
|
+
const personas = await client.getPersonasForTenant();
|
|
245
|
+
|
|
246
|
+
// Get full persona with workflow
|
|
247
|
+
const persona = await client.getPersonaById("uuid");
|
|
248
|
+
|
|
249
|
+
// List available templates
|
|
250
|
+
const templates = await client.getPersonaTemplates();
|
|
251
|
+
|
|
252
|
+
// List available agents/actions
|
|
253
|
+
const actions = await client.listActions();
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
---
|
|
257
|
+
|
|
258
|
+
## Development
|
|
259
|
+
|
|
260
|
+
```bash
|
|
261
|
+
# Build
|
|
262
|
+
npm run build
|
|
263
|
+
|
|
264
|
+
# Test
|
|
265
|
+
npm test
|
|
266
|
+
|
|
267
|
+
# Type check
|
|
268
|
+
npm run typecheck
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
---
|
|
272
|
+
|
|
273
|
+
## Environment Variables
|
|
274
|
+
|
|
275
|
+
| Variable | Required | Description |
|
|
276
|
+
|----------|----------|-------------|
|
|
277
|
+
| `EMA_API_KEY` | Yes* | API key (auto-exchanges for JWT) |
|
|
278
|
+
| `EMA_BEARER_TOKEN` | Yes* | Bearer token (direct auth) |
|
|
279
|
+
| `EMA_<ENV>_API_KEY` | Yes* | API key for specific environment |
|
|
280
|
+
| `EMA_<ENV>_BEARER_TOKEN` | Yes* | Token for specific environment |
|
|
281
|
+
| `EMA_ENV_NAME` | No | Default environment name |
|
|
282
|
+
| `EMA_EMPLOYEE` | No | Set to "true" to default to demo |
|
|
283
|
+
| `EMA_AGENT_SYNC_CONFIG` | No | Path to config file |
|
|
284
|
+
|
|
285
|
+
*At least one credential required.
|
|
286
|
+
|
|
287
|
+
---
|
|
288
|
+
|
|
289
|
+
## Troubleshooting
|
|
290
|
+
|
|
291
|
+
### "Missing token for environment X"
|
|
292
|
+
|
|
293
|
+
Set the corresponding environment variable:
|
|
294
|
+
```bash
|
|
295
|
+
export EMA_X_API_KEY="your-api-key"
|
|
296
|
+
# or
|
|
297
|
+
export EMA_X_BEARER_TOKEN="your-token"
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
### "API key not yet initialized"
|
|
301
|
+
|
|
302
|
+
API keys are initialized asynchronously at startup. Either:
|
|
303
|
+
- Wait a moment and retry
|
|
304
|
+
- Use a bearer token instead
|
|
305
|
+
|
|
306
|
+
### "Token expired" / 401 errors
|
|
307
|
+
|
|
308
|
+
- **API keys**: Should auto-refresh. Check your API key is valid.
|
|
309
|
+
- **Bearer tokens**: Expire after ~1 hour. Get a fresh token or switch to API keys.
|
|
310
|
+
|
|
311
|
+
### MCP server not connecting
|
|
312
|
+
|
|
313
|
+
1. Verify credentials are set: `echo $EMA_API_KEY`
|
|
314
|
+
2. Try running manually: `npx @ema.co/mcp-toolkit`
|
|
315
|
+
3. Check MCP config path is correct
|
|
316
|
+
|
|
317
|
+
---
|
|
318
|
+
|
|
319
|
+
## License
|
|
320
|
+
|
|
321
|
+
MIT - see [LICENSE](LICENSE)
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Ema Toolkit Configuration Example
|
|
2
|
+
# Copy to ema.config.yaml and customize
|
|
3
|
+
|
|
4
|
+
environments:
|
|
5
|
+
- name: demo
|
|
6
|
+
baseUrl: https://api.demo.ema.co
|
|
7
|
+
bearerTokenEnv: EMA_DEMO_BEARER_TOKEN
|
|
8
|
+
isMaster: true
|
|
9
|
+
|
|
10
|
+
- name: dev
|
|
11
|
+
baseUrl: https://api.dev.ema.co
|
|
12
|
+
bearerTokenEnv: EMA_DEV_BEARER_TOKEN
|
|
13
|
+
|
|
14
|
+
# Add more environments as needed:
|
|
15
|
+
# - name: staging
|
|
16
|
+
# baseUrl: https://api.staging.ema.co
|
|
17
|
+
# bearerTokenEnv: EMA_STAGING_BEARER_TOKEN
|
|
18
|
+
#
|
|
19
|
+
# - name: prod
|
|
20
|
+
# baseUrl: https://api.ema.co
|
|
21
|
+
# bearerTokenEnv: EMA_PROD_BEARER_TOKEN
|
|
22
|
+
|
|
23
|
+
# Optional: Service mode settings (for central scheduler)
|
|
24
|
+
# service:
|
|
25
|
+
# stateDbPath: ./ema-state.sqlite3
|
|
26
|
+
# scheduler:
|
|
27
|
+
# intervalSeconds: 300
|
|
28
|
+
# # cron: "0 */4 * * *"
|
|
29
|
+
|
|
30
|
+
# Global settings
|
|
31
|
+
dryRun: false
|
|
32
|
+
verbose: true
|
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Ema Agent Sync CLI
|
|
4
|
+
*
|
|
5
|
+
* Commands:
|
|
6
|
+
* sync run - Run a full sync
|
|
7
|
+
* sync status [name] - Check sync status for a persona
|
|
8
|
+
* sync persona <name> - Sync a specific persona by name
|
|
9
|
+
* personas list - List all personas from master
|
|
10
|
+
* agents list - List all agents (actions)
|
|
11
|
+
* config validate - Validate config file
|
|
12
|
+
*/
|
|
13
|
+
import { loadConfig } from "../sdk/config.js";
|
|
14
|
+
import { EmaClient } from "../sdk/client.js";
|
|
15
|
+
import { SyncSDK } from "../sdk/sync.js";
|
|
16
|
+
function printUsage() {
|
|
17
|
+
console.log(`
|
|
18
|
+
Ema Agent Sync CLI
|
|
19
|
+
|
|
20
|
+
Usage: ema <command> [subcommand] [options]
|
|
21
|
+
|
|
22
|
+
Commands:
|
|
23
|
+
sync run Run a full sync from master to targets
|
|
24
|
+
sync status [persona-name] Check sync status (optionally for a specific persona)
|
|
25
|
+
sync persona <name> Sync a specific persona by name
|
|
26
|
+
|
|
27
|
+
personas list List all AI Employees from master environment
|
|
28
|
+
personas get <id> Get details of a specific AI Employee
|
|
29
|
+
|
|
30
|
+
agents list List all Agents (actions) from master environment
|
|
31
|
+
|
|
32
|
+
config validate [path] Validate a config file (default: ./config.yaml)
|
|
33
|
+
|
|
34
|
+
help Show this help message
|
|
35
|
+
|
|
36
|
+
Options:
|
|
37
|
+
--config, -c <path> Path to config file (default: ./config.yaml)
|
|
38
|
+
--dry-run Don't make actual changes
|
|
39
|
+
--json Output as JSON
|
|
40
|
+
|
|
41
|
+
Environment Variables:
|
|
42
|
+
EMA_AGENT_SYNC_CONFIG Path to config file
|
|
43
|
+
EMA_*_BEARER_TOKEN Bearer tokens for each environment (as configured)
|
|
44
|
+
|
|
45
|
+
Examples:
|
|
46
|
+
ema sync run
|
|
47
|
+
ema sync persona "My AI Employee"
|
|
48
|
+
ema personas list --json
|
|
49
|
+
ema agents list
|
|
50
|
+
ema config validate ./config.yaml
|
|
51
|
+
`);
|
|
52
|
+
}
|
|
53
|
+
function getEnvOrThrow(name) {
|
|
54
|
+
const v = process.env[name];
|
|
55
|
+
if (!v)
|
|
56
|
+
throw new Error(`Missing environment variable: ${name}`);
|
|
57
|
+
return v;
|
|
58
|
+
}
|
|
59
|
+
async function runSyncCommand(subcommand, args, options) {
|
|
60
|
+
const cfg = loadConfig(options.configPath);
|
|
61
|
+
if (options.dryRun)
|
|
62
|
+
cfg.dryRun = true;
|
|
63
|
+
const sdk = new SyncSDK(cfg);
|
|
64
|
+
try {
|
|
65
|
+
switch (subcommand) {
|
|
66
|
+
case "run": {
|
|
67
|
+
console.log("Starting sync...");
|
|
68
|
+
const result = await sdk.runSync();
|
|
69
|
+
if (options.json) {
|
|
70
|
+
console.log(JSON.stringify(result, null, 2));
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
console.log(`\nSync complete:`);
|
|
74
|
+
console.log(` Run ID: ${result.runId}`);
|
|
75
|
+
console.log(` Scanned: ${result.scanned}`);
|
|
76
|
+
console.log(` Changed: ${result.changed}`);
|
|
77
|
+
console.log(` Synced: ${result.synced}`);
|
|
78
|
+
console.log(` Skipped: ${result.skipped}`);
|
|
79
|
+
}
|
|
80
|
+
break;
|
|
81
|
+
}
|
|
82
|
+
case "status": {
|
|
83
|
+
const personaName = args[0];
|
|
84
|
+
if (personaName) {
|
|
85
|
+
const persona = await sdk.getMasterPersonaByName(personaName);
|
|
86
|
+
if (!persona) {
|
|
87
|
+
console.error(`Persona not found: ${personaName}`);
|
|
88
|
+
process.exit(1);
|
|
89
|
+
}
|
|
90
|
+
const status = await sdk.getPersonaSyncStatus(persona.id);
|
|
91
|
+
if (options.json) {
|
|
92
|
+
console.log(JSON.stringify(status, null, 2));
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
console.log(`Persona: ${status?.personaName} (${status?.personaId})`);
|
|
96
|
+
console.log(`Fingerprint: ${status?.fingerprint.substring(0, 16)}...`);
|
|
97
|
+
console.log(`In Sync: ${status?.isSynced ? "Yes" : "No"}`);
|
|
98
|
+
console.log(`\nTarget Mappings:`);
|
|
99
|
+
for (const m of status?.targetMappings ?? []) {
|
|
100
|
+
console.log(` ${m.targetEnv}: ${m.targetPersonaId} (${m.inSync ? "synced" : "out of sync"})`);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
// Show overall sync status
|
|
106
|
+
const master = sdk.getMasterEnvironment();
|
|
107
|
+
const personas = await sdk.listMasterPersonas();
|
|
108
|
+
console.log(`Master: ${master.name} (${master.baseUrl})`);
|
|
109
|
+
console.log(`Total personas: ${personas.length}`);
|
|
110
|
+
console.log(`\nTarget environments:`);
|
|
111
|
+
for (const env of sdk.getEnvironments()) {
|
|
112
|
+
if (!env.isMaster) {
|
|
113
|
+
console.log(` - ${env.name} (${env.baseUrl})`);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
break;
|
|
118
|
+
}
|
|
119
|
+
case "persona": {
|
|
120
|
+
const personaName = args[0];
|
|
121
|
+
if (!personaName) {
|
|
122
|
+
console.error("Error: Persona name required");
|
|
123
|
+
console.error("Usage: ema sync persona <name>");
|
|
124
|
+
process.exit(1);
|
|
125
|
+
}
|
|
126
|
+
console.log(`Syncing persona: ${personaName}`);
|
|
127
|
+
const result = await sdk.syncPersonaByName(personaName);
|
|
128
|
+
if (options.json) {
|
|
129
|
+
console.log(JSON.stringify(result, null, 2));
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
if (result.success) {
|
|
133
|
+
console.log(`✓ Synced to: ${result.synced.join(", ") || "already in sync"}`);
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
console.error(`✗ Failed: ${result.errors.join(", ")}`);
|
|
137
|
+
process.exit(1);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
break;
|
|
141
|
+
}
|
|
142
|
+
default:
|
|
143
|
+
console.error(`Unknown sync subcommand: ${subcommand}`);
|
|
144
|
+
process.exit(1);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
finally {
|
|
148
|
+
sdk.close();
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
async function runPersonasCommand(subcommand, args, options) {
|
|
152
|
+
const cfg = loadConfig(options.configPath);
|
|
153
|
+
const sdk = new SyncSDK(cfg);
|
|
154
|
+
try {
|
|
155
|
+
switch (subcommand) {
|
|
156
|
+
case "list": {
|
|
157
|
+
const personas = await sdk.listMasterPersonas();
|
|
158
|
+
if (options.json) {
|
|
159
|
+
console.log(JSON.stringify(personas.map(p => ({
|
|
160
|
+
id: p.id,
|
|
161
|
+
name: p.name,
|
|
162
|
+
description: p.description,
|
|
163
|
+
status: p.status,
|
|
164
|
+
template_id: p.template_id,
|
|
165
|
+
workflow_id: p.workflow_id,
|
|
166
|
+
})), null, 2));
|
|
167
|
+
}
|
|
168
|
+
else {
|
|
169
|
+
console.log(`AI Employees (${personas.length}):\n`);
|
|
170
|
+
for (const p of personas) {
|
|
171
|
+
console.log(` ${p.name || "(unnamed)"}`);
|
|
172
|
+
console.log(` ID: ${p.id}`);
|
|
173
|
+
if (p.description)
|
|
174
|
+
console.log(` Description: ${p.description.substring(0, 60)}...`);
|
|
175
|
+
console.log("");
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
break;
|
|
179
|
+
}
|
|
180
|
+
case "get": {
|
|
181
|
+
const id = args[0];
|
|
182
|
+
if (!id) {
|
|
183
|
+
console.error("Error: Persona ID required");
|
|
184
|
+
process.exit(1);
|
|
185
|
+
}
|
|
186
|
+
const persona = await sdk.getMasterPersona(id);
|
|
187
|
+
if (!persona) {
|
|
188
|
+
console.error(`Persona not found: ${id}`);
|
|
189
|
+
process.exit(1);
|
|
190
|
+
}
|
|
191
|
+
if (options.json) {
|
|
192
|
+
console.log(JSON.stringify(persona, null, 2));
|
|
193
|
+
}
|
|
194
|
+
else {
|
|
195
|
+
console.log(`Name: ${persona.name}`);
|
|
196
|
+
console.log(`ID: ${persona.id}`);
|
|
197
|
+
console.log(`Description: ${persona.description}`);
|
|
198
|
+
console.log(`Status: ${persona.status}`);
|
|
199
|
+
console.log(`Template ID: ${persona.template_id}`);
|
|
200
|
+
console.log(`Workflow ID: ${persona.workflow_id}`);
|
|
201
|
+
}
|
|
202
|
+
break;
|
|
203
|
+
}
|
|
204
|
+
default:
|
|
205
|
+
console.error(`Unknown personas subcommand: ${subcommand}`);
|
|
206
|
+
process.exit(1);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
finally {
|
|
210
|
+
sdk.close();
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
async function runAgentsCommand(subcommand, args, options) {
|
|
214
|
+
const cfg = loadConfig(options.configPath);
|
|
215
|
+
const master = cfg.environments.find((e) => e.isMaster);
|
|
216
|
+
if (!master)
|
|
217
|
+
throw new Error("No master environment configured");
|
|
218
|
+
const env = {
|
|
219
|
+
name: master.name,
|
|
220
|
+
baseUrl: master.baseUrl,
|
|
221
|
+
bearerToken: getEnvOrThrow(master.bearerTokenEnv),
|
|
222
|
+
};
|
|
223
|
+
const client = new EmaClient(env);
|
|
224
|
+
switch (subcommand) {
|
|
225
|
+
case "list": {
|
|
226
|
+
const actions = await client.listAgents();
|
|
227
|
+
if (options.json) {
|
|
228
|
+
console.log(JSON.stringify(actions, null, 2));
|
|
229
|
+
}
|
|
230
|
+
else {
|
|
231
|
+
console.log(`Agents (${actions.length}):\n`);
|
|
232
|
+
for (const a of actions) {
|
|
233
|
+
console.log(` ${a.name || a.id}`);
|
|
234
|
+
if (a.description)
|
|
235
|
+
console.log(` ${a.description.substring(0, 60)}...`);
|
|
236
|
+
if (a.category)
|
|
237
|
+
console.log(` Category: ${a.category}`);
|
|
238
|
+
console.log("");
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
break;
|
|
242
|
+
}
|
|
243
|
+
default:
|
|
244
|
+
console.error(`Unknown agents subcommand: ${subcommand}`);
|
|
245
|
+
process.exit(1);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
async function runConfigCommand(subcommand, args, options) {
|
|
249
|
+
switch (subcommand) {
|
|
250
|
+
case "validate": {
|
|
251
|
+
const path = args[0] || "./config.yaml";
|
|
252
|
+
try {
|
|
253
|
+
const cfg = loadConfig(path);
|
|
254
|
+
if (options.json) {
|
|
255
|
+
console.log(JSON.stringify({ valid: true, config: cfg }, null, 2));
|
|
256
|
+
}
|
|
257
|
+
else {
|
|
258
|
+
console.log(`✓ Config is valid: ${path}`);
|
|
259
|
+
console.log(` Master: ${cfg.environments.find((e) => e.isMaster)?.name}`);
|
|
260
|
+
console.log(` Environments: ${cfg.environments.map((e) => e.name).join(", ")}`);
|
|
261
|
+
console.log(` Routing rules: ${cfg.routing?.length ?? 0}`);
|
|
262
|
+
console.log(` Dry run: ${cfg.dryRun}`);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
catch (e) {
|
|
266
|
+
if (options.json) {
|
|
267
|
+
console.log(JSON.stringify({ valid: false, error: String(e) }, null, 2));
|
|
268
|
+
}
|
|
269
|
+
else {
|
|
270
|
+
console.error(`✗ Invalid config: ${e instanceof Error ? e.message : e}`);
|
|
271
|
+
}
|
|
272
|
+
process.exit(1);
|
|
273
|
+
}
|
|
274
|
+
break;
|
|
275
|
+
}
|
|
276
|
+
default:
|
|
277
|
+
console.error(`Unknown config subcommand: ${subcommand}`);
|
|
278
|
+
process.exit(1);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
async function main() {
|
|
282
|
+
const args = process.argv.slice(2);
|
|
283
|
+
// Parse options
|
|
284
|
+
let configPath = process.env.EMA_AGENT_SYNC_CONFIG || "./config.yaml";
|
|
285
|
+
let dryRun = false;
|
|
286
|
+
let json = false;
|
|
287
|
+
const filteredArgs = [];
|
|
288
|
+
for (let i = 0; i < args.length; i++) {
|
|
289
|
+
const arg = args[i];
|
|
290
|
+
if (arg === "--config" || arg === "-c") {
|
|
291
|
+
configPath = args[++i];
|
|
292
|
+
}
|
|
293
|
+
else if (arg === "--dry-run") {
|
|
294
|
+
dryRun = true;
|
|
295
|
+
}
|
|
296
|
+
else if (arg === "--json") {
|
|
297
|
+
json = true;
|
|
298
|
+
}
|
|
299
|
+
else {
|
|
300
|
+
filteredArgs.push(arg);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
const [command, subcommand, ...restArgs] = filteredArgs;
|
|
304
|
+
if (!command || command === "help" || command === "--help" || command === "-h") {
|
|
305
|
+
printUsage();
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
308
|
+
try {
|
|
309
|
+
switch (command) {
|
|
310
|
+
case "sync":
|
|
311
|
+
await runSyncCommand((subcommand || "run"), restArgs, { configPath, dryRun, json });
|
|
312
|
+
break;
|
|
313
|
+
case "personas":
|
|
314
|
+
await runPersonasCommand((subcommand || "list"), restArgs, { configPath, json });
|
|
315
|
+
break;
|
|
316
|
+
case "agents":
|
|
317
|
+
await runAgentsCommand((subcommand || "list"), restArgs, { configPath, json });
|
|
318
|
+
break;
|
|
319
|
+
case "config":
|
|
320
|
+
await runConfigCommand((subcommand || "validate"), restArgs, { json });
|
|
321
|
+
break;
|
|
322
|
+
default:
|
|
323
|
+
console.error(`Unknown command: ${command}`);
|
|
324
|
+
printUsage();
|
|
325
|
+
process.exit(1);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
catch (e) {
|
|
329
|
+
console.error(`Error: ${e instanceof Error ? e.message : e}`);
|
|
330
|
+
process.exit(1);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
main();
|