ema-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/README.md +338 -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 +4585 -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 +95 -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/README.md
ADDED
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
# Ema MCP Toolkit
|
|
2
|
+
|
|
3
|
+
A comprehensive toolkit for managing Ema AI Employees via **MCP Server** (for AI assistants like Cursor/Claude), **CLI**, and **SDK**.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
### Option 1: npx (Recommended - No Installation)
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
# Run directly without installing
|
|
11
|
+
npx ema-mcp-toolkit
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
### Option 2: Global Install
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npm install -g ema-mcp-toolkit
|
|
18
|
+
ema-mcp # Start MCP server
|
|
19
|
+
ema # CLI
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
### Option 3: Clone & Build (Development)
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
git clone https://github.com/ema-co/ema-mcp-toolkit.git
|
|
26
|
+
cd ema-mcp-toolkit
|
|
27
|
+
npm install
|
|
28
|
+
npm run build
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## Quick Start
|
|
34
|
+
|
|
35
|
+
### 1. Set Your Token
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
# Minimal setup - just one token for demo environment
|
|
39
|
+
export EMA_BEARER_TOKEN="your-token-here"
|
|
40
|
+
|
|
41
|
+
# Or use environment-specific naming (auto-detected)
|
|
42
|
+
export EMA_DEMO_BEARER_TOKEN="your-demo-token"
|
|
43
|
+
export EMA_DEV_BEARER_TOKEN="your-dev-token"
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### 2. Add to Your AI Assistant
|
|
47
|
+
|
|
48
|
+
**Cursor** (`~/.cursor/mcp.json`):
|
|
49
|
+
|
|
50
|
+
```json
|
|
51
|
+
{
|
|
52
|
+
"mcpServers": {
|
|
53
|
+
"ema": {
|
|
54
|
+
"command": "npx",
|
|
55
|
+
"args": ["ema-mcp-toolkit"],
|
|
56
|
+
"env": {
|
|
57
|
+
"EMA_DEMO_BEARER_TOKEN": "your-demo-token",
|
|
58
|
+
"EMA_DEV_BEARER_TOKEN": "your-dev-token"
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
**Claude Desktop** (`~/Library/Application Support/Claude/claude_desktop_config.json`):
|
|
66
|
+
|
|
67
|
+
```json
|
|
68
|
+
{
|
|
69
|
+
"mcpServers": {
|
|
70
|
+
"ema": {
|
|
71
|
+
"command": "npx",
|
|
72
|
+
"args": ["ema-mcp-toolkit"],
|
|
73
|
+
"env": {
|
|
74
|
+
"EMA_DEMO_BEARER_TOKEN": "your-demo-token"
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### 3. Start Using
|
|
82
|
+
|
|
83
|
+
Ask your AI assistant:
|
|
84
|
+
- "List my AI Employees"
|
|
85
|
+
- "Analyze the workflow for Customer Support Bot"
|
|
86
|
+
- "Create a new Voice AI for appointment scheduling"
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## Configuration
|
|
91
|
+
|
|
92
|
+
### Zero-Config Mode (Recommended)
|
|
93
|
+
|
|
94
|
+
No config file needed! The toolkit auto-detects environments from environment variables:
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
# Pattern: EMA_<ENV>_BEARER_TOKEN
|
|
98
|
+
export EMA_DEMO_BEARER_TOKEN="..." # → creates "demo" environment
|
|
99
|
+
export EMA_DEV_BEARER_TOKEN="..." # → creates "dev" environment
|
|
100
|
+
export EMA_STAGING_BEARER_TOKEN="..."# → creates "staging" environment
|
|
101
|
+
export EMA_PROD_BEARER_TOKEN="..." # → creates "prod" environment
|
|
102
|
+
|
|
103
|
+
# Optional: Custom URLs (auto-detected if not set)
|
|
104
|
+
export EMA_DEV_BASE_URL="https://api.custom-dev.ema.co"
|
|
105
|
+
|
|
106
|
+
# Optional: Set default environment
|
|
107
|
+
export EMA_ENV_NAME="dev"
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
**Well-known URLs** (automatic):
|
|
111
|
+
| Environment | URL |
|
|
112
|
+
|-------------|-----|
|
|
113
|
+
| `demo` | `https://api.demo.ema.co` |
|
|
114
|
+
| `dev` | `https://api.dev.ema.co` |
|
|
115
|
+
| `staging` | `https://api.staging.ema.co` |
|
|
116
|
+
| `prod` | `https://api.ema.co` |
|
|
117
|
+
|
|
118
|
+
### Config File (Advanced)
|
|
119
|
+
|
|
120
|
+
For complex setups, create `ema.config.yaml`:
|
|
121
|
+
|
|
122
|
+
```yaml
|
|
123
|
+
environments:
|
|
124
|
+
- name: demo
|
|
125
|
+
baseUrl: https://api.demo.ema.co
|
|
126
|
+
bearerTokenEnv: EMA_DEMO_BEARER_TOKEN # References env var
|
|
127
|
+
isMaster: true
|
|
128
|
+
|
|
129
|
+
- name: dev
|
|
130
|
+
baseUrl: https://api.dev.ema.co
|
|
131
|
+
bearerTokenEnv: EMA_DEV_BEARER_TOKEN
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Then point to it:
|
|
135
|
+
```bash
|
|
136
|
+
export EMA_AGENT_SYNC_CONFIG=./ema.config.yaml
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### JSON Config via Environment (CI/CD)
|
|
140
|
+
|
|
141
|
+
For CI/CD, pass config as JSON:
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
export EMA_CONFIG_JSON='{"environments":[{"name":"demo","baseUrl":"https://api.demo.ema.co","bearerTokenEnv":"EMA_DEMO_BEARER_TOKEN","isMaster":true}]}'
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
---
|
|
148
|
+
|
|
149
|
+
## Getting Your Token
|
|
150
|
+
|
|
151
|
+
1. Log in to [Ema Platform](https://app.ema.co)
|
|
152
|
+
2. Open browser DevTools → Network tab
|
|
153
|
+
3. Make any API request and find the `Authorization: Bearer ...` header
|
|
154
|
+
4. Copy the token (without "Bearer " prefix)
|
|
155
|
+
|
|
156
|
+
**Note**: Tokens expire. The toolkit supports automatic token refresh if you configure a refresh callback in the SDK.
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
## Usage
|
|
161
|
+
|
|
162
|
+
### MCP Server (AI Assistants)
|
|
163
|
+
|
|
164
|
+
The MCP server provides **9 consolidated tools**, **15 prompts**, and **dynamic resources**.
|
|
165
|
+
|
|
166
|
+
#### Tools
|
|
167
|
+
|
|
168
|
+
| Tool | Purpose |
|
|
169
|
+
|------|---------|
|
|
170
|
+
| `env` | List available environments |
|
|
171
|
+
| `persona` | AI Employee management (get/list/create/update/compare) |
|
|
172
|
+
| `workflow` | Generate/analyze/deploy/optimize/explain/extend workflows |
|
|
173
|
+
| `action` | Agent lookup, docs, and recommendations |
|
|
174
|
+
| `template` | Patterns, widgets, qualifying questions |
|
|
175
|
+
| `knowledge` | Data sources + embedding (upload/list/delete/toggle) |
|
|
176
|
+
| `reference` | Concepts, guidance, validation, common mistakes |
|
|
177
|
+
| `sync` | Sync across environments |
|
|
178
|
+
| `demo` | Demo/RAG document utilities |
|
|
179
|
+
|
|
180
|
+
#### Resources (Dynamic)
|
|
181
|
+
|
|
182
|
+
| Resource | Source |
|
|
183
|
+
|----------|--------|
|
|
184
|
+
| `ema://catalog/agents` | Live from API |
|
|
185
|
+
| `ema://catalog/templates` | Live from API |
|
|
186
|
+
| `ema://catalog/patterns` | Workflow patterns |
|
|
187
|
+
| `ema://rules/anti-patterns` | Validation rules |
|
|
188
|
+
|
|
189
|
+
### CLI
|
|
190
|
+
|
|
191
|
+
```bash
|
|
192
|
+
# List personas
|
|
193
|
+
ema personas list
|
|
194
|
+
|
|
195
|
+
# Sync a persona
|
|
196
|
+
ema sync persona "My Bot" --target dev --dry-run
|
|
197
|
+
|
|
198
|
+
# Check sync status
|
|
199
|
+
ema sync status
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
### SDK (Programmatic)
|
|
203
|
+
|
|
204
|
+
```typescript
|
|
205
|
+
import { EmaClient } from "ema-mcp-toolkit";
|
|
206
|
+
|
|
207
|
+
const client = new EmaClient({
|
|
208
|
+
name: "demo",
|
|
209
|
+
baseUrl: "https://api.demo.ema.co",
|
|
210
|
+
bearerToken: process.env.EMA_DEMO_BEARER_TOKEN!,
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
// List AI Employees
|
|
214
|
+
const personas = await client.getPersonasForTenant();
|
|
215
|
+
|
|
216
|
+
// Get full persona with workflow
|
|
217
|
+
const persona = await client.getPersonaById("uuid");
|
|
218
|
+
|
|
219
|
+
// List available templates
|
|
220
|
+
const templates = await client.getPersonaTemplates();
|
|
221
|
+
|
|
222
|
+
// List available agents/actions
|
|
223
|
+
const actions = await client.listActions();
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
---
|
|
227
|
+
|
|
228
|
+
## Testing
|
|
229
|
+
|
|
230
|
+
```bash
|
|
231
|
+
# Run all tests
|
|
232
|
+
npm test
|
|
233
|
+
|
|
234
|
+
# Run specific test
|
|
235
|
+
npm test -- --run workflow
|
|
236
|
+
|
|
237
|
+
# Type check
|
|
238
|
+
npm run typecheck
|
|
239
|
+
|
|
240
|
+
# Build
|
|
241
|
+
npm run build
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
---
|
|
245
|
+
|
|
246
|
+
## Development
|
|
247
|
+
|
|
248
|
+
### Project Structure
|
|
249
|
+
|
|
250
|
+
```
|
|
251
|
+
src/
|
|
252
|
+
├── mcp/
|
|
253
|
+
│ ├── server.ts # MCP server entry
|
|
254
|
+
│ ├── prompts.ts # Prompt definitions
|
|
255
|
+
│ └── resources.ts # Resource registry
|
|
256
|
+
├── sdk/
|
|
257
|
+
│ ├── client.ts # EmaClient API wrapper
|
|
258
|
+
│ ├── config.ts # Config loading
|
|
259
|
+
│ ├── models.ts # TypeScript types
|
|
260
|
+
│ └── knowledge.ts # Agent catalog, patterns
|
|
261
|
+
└── cli/
|
|
262
|
+
└── index.ts # CLI entry
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
### Adding Tools/Prompts
|
|
266
|
+
|
|
267
|
+
See `docs/naming-conventions.md` for naming rules.
|
|
268
|
+
|
|
269
|
+
```typescript
|
|
270
|
+
// In src/mcp/prompts.ts
|
|
271
|
+
my_thing_action: {
|
|
272
|
+
definition: {
|
|
273
|
+
name: "my_thing_action", // {noun}_{action} pattern
|
|
274
|
+
description: "...",
|
|
275
|
+
arguments: [...],
|
|
276
|
+
},
|
|
277
|
+
render: (args) => [...],
|
|
278
|
+
},
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
### Auto-Fix Capabilities
|
|
282
|
+
|
|
283
|
+
The `workflow(mode="optimize")` can automatically fix:
|
|
284
|
+
|
|
285
|
+
| Issue | Fix |
|
|
286
|
+
|-------|-----|
|
|
287
|
+
| `wrong_input_source` (email_to) | Adds `entity_extraction`, wires email |
|
|
288
|
+
| `wrong_input_source` (query) | Rewires to correct trigger output |
|
|
289
|
+
| `missing_workflow_output` | Adds results mapping |
|
|
290
|
+
|
|
291
|
+
---
|
|
292
|
+
|
|
293
|
+
## Environment Variables Reference
|
|
294
|
+
|
|
295
|
+
| Variable | Required | Description |
|
|
296
|
+
|----------|----------|-------------|
|
|
297
|
+
| `EMA_BEARER_TOKEN` | Yes* | Default token (if no env-specific tokens) |
|
|
298
|
+
| `EMA_<ENV>_BEARER_TOKEN` | Yes* | Token for specific environment |
|
|
299
|
+
| `EMA_<ENV>_BASE_URL` | No | Custom URL (auto-detected) |
|
|
300
|
+
| `EMA_ENV_NAME` | No | Default environment name |
|
|
301
|
+
| `EMA_AGENT_SYNC_CONFIG` | No | Path to config file |
|
|
302
|
+
| `EMA_CONFIG_JSON` | No | Config as JSON string |
|
|
303
|
+
| `EMA_ENABLE_LEGACY_TOOLS` | No | Enable deprecated tools |
|
|
304
|
+
|
|
305
|
+
*At least one token required.
|
|
306
|
+
|
|
307
|
+
---
|
|
308
|
+
|
|
309
|
+
## Troubleshooting
|
|
310
|
+
|
|
311
|
+
### "Missing token for environment X"
|
|
312
|
+
|
|
313
|
+
Set the corresponding environment variable:
|
|
314
|
+
```bash
|
|
315
|
+
export EMA_X_BEARER_TOKEN="your-token"
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
### "Token expired" / 401 errors
|
|
319
|
+
|
|
320
|
+
Tokens expire after ~1 hour. Get a fresh token from the Ema dashboard.
|
|
321
|
+
|
|
322
|
+
### MCP server not connecting
|
|
323
|
+
|
|
324
|
+
1. Check your MCP config path is correct
|
|
325
|
+
2. Verify the token env vars are set
|
|
326
|
+
3. Try running manually: `npx ema-mcp-toolkit`
|
|
327
|
+
|
|
328
|
+
### Tests failing
|
|
329
|
+
|
|
330
|
+
```bash
|
|
331
|
+
npm run build && npm test
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
---
|
|
335
|
+
|
|
336
|
+
## License
|
|
337
|
+
|
|
338
|
+
Proprietary - Ema Inc.
|
|
@@ -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();
|