@cef-ai/cli 0.2.1

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.
Files changed (55) hide show
  1. package/README.md +1273 -0
  2. package/dist/cli.d.ts +3 -0
  3. package/dist/cli.js +66 -0
  4. package/dist/commands/deploy/clone.d.ts +13 -0
  5. package/dist/commands/deploy/clone.js +143 -0
  6. package/dist/commands/deploy/delete.d.ts +14 -0
  7. package/dist/commands/deploy/delete.js +134 -0
  8. package/dist/commands/deploy/deploy.d.ts +24 -0
  9. package/dist/commands/deploy/deploy.js +654 -0
  10. package/dist/commands/deploy/index.d.ts +2 -0
  11. package/dist/commands/deploy/index.js +216 -0
  12. package/dist/commands/deploy/status.d.ts +14 -0
  13. package/dist/commands/deploy/status.js +61 -0
  14. package/dist/commands/dev/index.d.ts +2 -0
  15. package/dist/commands/dev/index.js +134 -0
  16. package/dist/commands/dev/server.d.ts +14 -0
  17. package/dist/commands/dev/server.js +167 -0
  18. package/dist/commands/dev/ui.d.ts +1 -0
  19. package/dist/commands/dev/ui.js +984 -0
  20. package/dist/commands/playground/index.d.ts +2 -0
  21. package/dist/commands/playground/index.js +75 -0
  22. package/dist/commands/playground/test.d.ts +15 -0
  23. package/dist/commands/playground/test.js +166 -0
  24. package/dist/commands/service/index.d.ts +2 -0
  25. package/dist/commands/service/index.js +75 -0
  26. package/dist/lib/api.d.ts +146 -0
  27. package/dist/lib/api.js +330 -0
  28. package/dist/lib/config.d.ts +12 -0
  29. package/dist/lib/config.js +120 -0
  30. package/dist/lib/runtime/agent-router.d.ts +12 -0
  31. package/dist/lib/runtime/agent-router.js +78 -0
  32. package/dist/lib/runtime/context-builder.d.ts +27 -0
  33. package/dist/lib/runtime/context-builder.js +166 -0
  34. package/dist/lib/runtime/cubby-manager.d.ts +20 -0
  35. package/dist/lib/runtime/cubby-manager.js +161 -0
  36. package/dist/lib/runtime/index.d.ts +51 -0
  37. package/dist/lib/runtime/index.js +183 -0
  38. package/dist/lib/runtime/log-collector.d.ts +17 -0
  39. package/dist/lib/runtime/log-collector.js +61 -0
  40. package/dist/lib/runtime/stream-manager.d.ts +10 -0
  41. package/dist/lib/runtime/stream-manager.js +77 -0
  42. package/dist/lib/runtime/types.d.ts +76 -0
  43. package/dist/lib/runtime/types.js +2 -0
  44. package/dist/lib/typecheck.d.ts +31 -0
  45. package/dist/lib/typecheck.js +175 -0
  46. package/dist/lib/types.d.ts +246 -0
  47. package/dist/lib/types.js +2 -0
  48. package/dist/lib/utils.d.ts +4 -0
  49. package/dist/lib/utils.js +33 -0
  50. package/example/.env.example +6 -0
  51. package/example/agents/object-detection/tasks/yolo.ts +28 -0
  52. package/example/agents/parking-violation-detector/tasks/detect.ts +326 -0
  53. package/example/cef.config.yaml +91 -0
  54. package/example/engagements/engagement.ts +902 -0
  55. package/package.json +69 -0
package/README.md ADDED
@@ -0,0 +1,1273 @@
1
+ # CEF AI CLI
2
+
3
+ Build, test, and deploy CEF AI agent topologies from the command line.
4
+
5
+ Write agent handlers locally, test them with a real dev server (SQLite cubbies, streams, execution tracing), then deploy to the platform from a single YAML config.
6
+
7
+ **Package:** `@cef-ai/cli` v0.2.0
8
+ **Binaries:** `cef`, `cef-deploy`
9
+ **Requirements:** Node.js 18+
10
+
11
+ ---
12
+
13
+ ## Table of Contents
14
+
15
+ - [Quick Start](#quick-start)
16
+ - [Developer Workflow](#developer-workflow)
17
+ - [Commands](#commands)
18
+ - [cef dev - Local Development Server](#cef-dev---local-development-server)
19
+ - [Dev UI](#dev-ui)
20
+ - [HTTP API Endpoints](#http-api-endpoints)
21
+ - [WebSocket API](#websocket-api)
22
+ - [Data Directory (.cef-dev/)](#data-directory-cef-dev)
23
+ - [Hot Reload](#hot-reload)
24
+ - [cef deploy - Deploy Topology](#cef-deploy---deploy-topology)
25
+ - [Deploy Order](#deploy-order)
26
+ - [ID Writeback](#id-writeback)
27
+ - [Stale ID Detection](#stale-id-detection)
28
+ - [Dry Run](#dry-run)
29
+ - [Partial Deploy](#partial-deploy)
30
+ - [cef deploy status - Compare Local vs Deployed](#cef-deploy-status---compare-local-vs-deployed)
31
+ - [cef deploy clone - Download Existing Service](#cef-deploy-clone---download-existing-service)
32
+ - [cef deploy delete - Delete Deployed Entities](#cef-deploy-delete---delete-deployed-entities)
33
+ - [cef service list - List Agent Services](#cef-service-list---list-agent-services)
34
+ - [cef service create - Create New Agent Service](#cef-service-create---create-new-agent-service)
35
+ - [cef playground test - Test Agent Tools via MCP](#cef-playground-test---test-agent-tools-via-mcp)
36
+ - [Single Test](#single-test)
37
+ - [List Tools](#list-tools)
38
+ - [Batch Tests](#batch-tests)
39
+ - [Test File Format (cef.tests.yaml)](#test-file-format-ceftestsyaml)
40
+ - [Configuration (cef.config.yaml)](#configuration-cefconfigyaml)
41
+ - [Top-Level Fields](#top-level-fields)
42
+ - [Engagements](#engagements)
43
+ - [Agents](#agents)
44
+ - [Cubbies](#cubbies)
45
+ - [Workspaces](#workspaces)
46
+ - [Full Example Config](#full-example-config)
47
+ - [Agent Sandbox](#agent-sandbox)
48
+ - [Handler Signature](#handler-signature)
49
+ - [Available Globals](#available-globals)
50
+ - [Restrictions](#restrictions)
51
+ - [Context API Reference](#context-api-reference)
52
+ - [Logging](#logging)
53
+ - [Event Emission](#event-emission)
54
+ - [HTTP Fetch](#http-fetch)
55
+ - [Agent-to-Agent Calls](#agent-to-agent-calls)
56
+ - [Cubby Operations](#cubby-operations)
57
+ - [Stream Subscriptions](#stream-subscriptions)
58
+ - [Raft Queries](#raft-queries)
59
+ - [Environment Variables](#environment-variables)
60
+ - [Options Reference](#options-reference)
61
+ - [Examples](#examples)
62
+ - [Hello World](#hello-world)
63
+ - [Drone Telemetry (Workspaces + Streams)](#drone-telemetry-workspaces--streams)
64
+ - [Wiki Knowledge Graph (Graph Viz + Batch)](#wiki-knowledge-graph-graph-viz--batch)
65
+ - [Testing](#testing)
66
+ - [Troubleshooting](#troubleshooting)
67
+
68
+ ---
69
+
70
+ ## Quick Start
71
+
72
+ ```bash
73
+ # Install globally
74
+ npm install -g @cef-ai/cli
75
+
76
+ # Or use npx
77
+ npx @cef-ai/cli dev
78
+
79
+ # Start local dev server (no auth needed)
80
+ cef dev
81
+
82
+ # Start with a specific config
83
+ cef dev --config ./my-agent/cef.config.yaml
84
+
85
+ # Deploy to platform (requires .env with auth tokens)
86
+ cef deploy --config ./cef.config.yaml
87
+ ```
88
+
89
+ ---
90
+
91
+ ## Developer Workflow
92
+
93
+ The recommended flow from idea to production:
94
+
95
+ ```
96
+ 1. Scaffold cef service create --name my-agent
97
+ Set up cef.config.yaml with agents, cubbies, engagements
98
+
99
+ 2. Write agents Create handler .ts files (fully inline, no imports)
100
+ Define cubby migrations in config for any SQL state
101
+
102
+ 3. Dev server cef dev
103
+ Opens http://localhost:8787 with:
104
+ - Topology sidebar, agent/engagement panels
105
+ - Stream push (single, bulk, file upload)
106
+ - Live execution logs and call chain tracing
107
+
108
+ 4. Test agents POST /api/agents/:alias/:task with payloads
109
+ Trigger full engagements via POST /api/trigger
110
+ Push stream packets via POST /api/streams/:id/push
111
+
112
+ 5. Inspect data Use the SQL editor in the dev UI
113
+ Or: POST /api/cubbies/:alias/query with {"sql": "SELECT ..."}
114
+ Cubbies are real SQLite with vector support (sqlite-vec)
115
+
116
+ 6. Iterate Edit agent code; changes hot-reload automatically
117
+ .cef-dev/ is cleared on shutdown (use --persist to keep)
118
+
119
+ 7. Deploy Create .env with CEF_AUTH_TOKEN, CEF_ORCHESTRATOR_URL
120
+ cef deploy
121
+ IDs are written back into cef.config.yaml
122
+
123
+ 8. Verify cef deploy status (compares local vs deployed)
124
+ Redeploy after changes: cef deploy
125
+ ```
126
+
127
+ ---
128
+
129
+ ## Commands
130
+
131
+ | Command | Description | Auth Required |
132
+ |-|-|-|
133
+ | `cef dev` | Start local dev server with UI, hot-reload, real SQLite cubbies | No |
134
+ | `cef deploy` | Deploy topology to orchestrator from cef.config.yaml | Yes |
135
+ | `cef deploy status` | Compare local config vs deployed state | Yes |
136
+ | `cef deploy clone` | Download an existing service into a directory | Yes |
137
+ | `cef deploy delete` | Delete deployed entities (reverse dependency order) | Yes |
138
+ | `cef service list` | List agent services from ROB | Yes |
139
+ | `cef service create` | Create a new agent service on ROB + orchestrator | Yes |
140
+ | `cef playground test` | Run playground tests against deployed agent tools | Yes |
141
+
142
+ ---
143
+
144
+ ### cef dev - Local Development Server
145
+
146
+ Starts a local dev server that emulates the CEF platform. Loads `cef.config.yaml`, runs agent code in sandboxed V8 contexts, and provides an HTTP API and web UI.
147
+
148
+ No auth token or orchestrator connection required. Runs as a persistent process until Ctrl+C.
149
+
150
+ ```bash
151
+ cef dev # Default: localhost:8787
152
+ cef dev --config ./my-agent/cef.config.yaml
153
+ cef dev --port 3000
154
+ cef dev --host 0.0.0.0
155
+ cef dev --persist # Keep .cef-dev/ data after shutdown
156
+ ```
157
+
158
+ | Option | Default | Description |
159
+ |-|-|-|
160
+ | `--config <path>` | `./cef.config.yaml` | Path to config file |
161
+ | `--port <port>` | `8787` | Server port |
162
+ | `--host <host>` | `localhost` | Server host |
163
+ | `--persist` | `false` | Keep `.cef-dev/` data after shutdown |
164
+
165
+ #### Dev UI
166
+
167
+ Open `http://localhost:<port>` to access the built-in web UI:
168
+
169
+ - **Topology sidebar** listing all engagements, agents, cubbies, and workspaces
170
+ - **Engagement trigger panel** with payload editor and stream push (single, bulk JSON Lines, file upload for .json/.jsonl)
171
+ - **Engagement blast mode** with repeat count, delay, and JSON file upload for burst testing
172
+ - **Agent task panels** with pre-filled payloads based on `parameters` JSON Schema from config
173
+ - **Run Batch** button on agent panels; queries a cubby for input rows via `batch_file` SQL, then calls the agent once per row
174
+ - **Cubby table browser** with SQL editor for ad-hoc queries
175
+ - **Saved queries** for the SQL editor; save, load, and delete named queries from a dropdown
176
+ - **Graph visualization** tab on cubby panels using vis.js; color-coded nodes by type (artifact, work, operator, eval), click-to-inspect detail panel, legend, and stats
177
+ - **Live execution logs** with filtering by execution ID
178
+ - **Call chain tracing** visualizing engagement -> agent -> cubby execution flows
179
+ - **Real-time updates** via WebSocket
180
+
181
+ **Config hint:** Add `parameters` and `returns` to agent tasks in cef.config.yaml (JSON Schema format). The dev UI uses these to pre-fill payloads and show schema hints. These fields are local-only and not sent to the orchestrator.
182
+
183
+ #### HTTP API Endpoints
184
+
185
+ All endpoints are available for scripting with curl or any HTTP client.
186
+
187
+ | Method | Path | Body | Description |
188
+ |-|-|-|-|
189
+ | `GET` | `/` | - | Dev UI |
190
+ | `GET` | `/api/topology` | - | Returns loaded agents, engagements, cubbies, workspaces |
191
+ | `GET` | `/api/executions` | - | Returns execution logs (optional `?executionId=` filter) |
192
+ | `POST` | `/api/trigger` | `{"engagement": "Name", "payload": {...}}` | Start an engagement |
193
+ | `POST` | `/api/agents/:alias/:task` | `{"payload": {...}}` | Call an agent task directly |
194
+ | `POST` | `/api/streams/:id/push` | `{"data": {"sequenceNum": 0, "payload": {...}}}` | Push a packet to a running stream |
195
+ | `POST` | `/api/cubbies/:alias/query` | `{"sql": "SELECT ...", "params": [], "instanceId": "default"}` | SQL read query |
196
+ | `POST` | `/api/cubbies/:alias/exec` | `{"sql": "INSERT ...", "params": []}` | SQL write operation |
197
+ | `GET` | `/api/cubbies/:alias/instances` | - | List all cubby instances |
198
+
199
+ Example: call an agent task directly:
200
+
201
+ ```bash
202
+ curl -X POST http://localhost:8787/api/agents/greeter/greet \
203
+ -H "Content-Type: application/json" \
204
+ -d '{"payload": {"name": "Alice"}}'
205
+ ```
206
+
207
+ Example: query a cubby:
208
+
209
+ ```bash
210
+ curl -X POST http://localhost:8787/api/cubbies/greetings/query \
211
+ -H "Content-Type: application/json" \
212
+ -d '{"sql": "SELECT * FROM greetings"}'
213
+ ```
214
+
215
+ Example: trigger an engagement:
216
+
217
+ ```bash
218
+ curl -X POST http://localhost:8787/api/trigger \
219
+ -H "Content-Type: application/json" \
220
+ -d '{"engagement": "Hello World", "payload": {"streamId": "test-stream-1"}}'
221
+ ```
222
+
223
+ #### WebSocket API
224
+
225
+ Connect to `ws://localhost:<port>/ws` for real-time streaming of logs and trace events.
226
+
227
+ Messages are JSON with two types:
228
+
229
+ **Log entries:**
230
+
231
+ ```json
232
+ {
233
+ "type": "log",
234
+ "data": {
235
+ "timestamp": 1712345678000,
236
+ "executionId": "exec-abc123",
237
+ "agentAlias": "greeter",
238
+ "taskAlias": "greet",
239
+ "level": "log",
240
+ "args": ["Greeter agent called with name: Alice"]
241
+ }
242
+ }
243
+ ```
244
+
245
+ **Trace events:**
246
+
247
+ ```json
248
+ {
249
+ "type": "trace",
250
+ "data": {
251
+ "timestamp": 1712345678000,
252
+ "executionId": "exec-abc123",
253
+ "type": "agent:call",
254
+ "caller": "engagement:Hello World",
255
+ "callee": "greeter.greet",
256
+ "durationMs": 12,
257
+ "success": true
258
+ }
259
+ }
260
+ ```
261
+
262
+ Trace event types: `engagement:start`, `agent:call`, `agent:return`, `cubby:query`, `cubby:exec`
263
+
264
+ #### Data Directory (.cef-dev/)
265
+
266
+ The dev server stores SQLite databases in `.cef-dev/cubbies/{alias}/{instanceId}.db` relative to the config directory.
267
+
268
+ ```
269
+ .cef-dev/
270
+ cubbies/
271
+ greetings/
272
+ default.db
273
+ syncMission/
274
+ default.db
275
+ drone-001.db
276
+ ```
277
+
278
+ By default, `.cef-dev/` is deleted on shutdown. Use `--persist` to keep data between runs.
279
+
280
+ #### Hot Reload
281
+
282
+ The dev server watches all agent task and engagement handler files. On save:
283
+
284
+ 1. New TypeScript code is read from disk
285
+ 2. Code is transpiled (ES2022 target) and cached
286
+ 3. Next call to that agent/task uses the updated code
287
+ 4. A `[reload] path/to/file` message appears in the console
288
+
289
+ In-flight executions continue with the old code; only new calls use the updated version. No server restart needed.
290
+
291
+ **What runs locally:**
292
+
293
+ - **Agents** execute in sandboxed V8 contexts (see [Agent Sandbox](#agent-sandbox))
294
+ - **Cubbies** are real SQLite databases (better-sqlite3 + sqlite-vec for vector ops)
295
+ - **Streams** are in-memory async iterators; push packets via UI or API
296
+ - **Execution tracing** tracks engagement -> agent -> cubby call chains in real-time
297
+
298
+ ---
299
+
300
+ ### cef deploy - Deploy Topology
301
+
302
+ Reads `cef.config.yaml`, creates or updates each entity on the orchestrator, and writes returned IDs back into the config.
303
+
304
+ ```bash
305
+ cef deploy
306
+ cef deploy --config ./app/cef.config.yaml
307
+ cef deploy --dry-run
308
+ cef deploy --only agent
309
+ ```
310
+
311
+ | Option | Default | Description |
312
+ |-|-|-|
313
+ | `--config <path>` | `./cef.config.yaml` | Path to config file |
314
+ | `--dry-run` | `false` | Show planned operations without calling the API |
315
+ | `--only <type>` | (all) | Deploy only one entity type |
316
+
317
+ Valid `--only` values: `engagement`, `agent`, `cubby`, `workspace`, `stream`, `deployment`, `raft`
318
+
319
+ #### Deploy Order
320
+
321
+ Entities are created in dependency order:
322
+
323
+ ```
324
+ Engagements -> Agents -> Cubbies -> Workspaces -> Streams -> Deployments -> Rafts
325
+ ```
326
+
327
+ Each entity type completes before the next begins. If an entity already has an ID in config, the CLI issues an update instead of a create.
328
+
329
+ #### ID Writeback
330
+
331
+ After each entity type is created, the returned IDs are written back into `cef.config.yaml`. For example, after creating an engagement:
332
+
333
+ ```yaml
334
+ engagements:
335
+ - name: Hello World
336
+ file: ./engagements/hello-engagement.ts
337
+ engagementId: eng-2f8b9517 # written by CLI
338
+ ```
339
+
340
+ If the deploy fails partway through, already-created IDs are preserved in config. Subsequent runs detect existing IDs and issue updates instead of creates. The final `agentServiceId` resolved from ROB is also written back.
341
+
342
+ Commit the updated config after a successful deploy.
343
+
344
+ #### Stale ID Detection
345
+
346
+ If `agentServiceId` in config does not match the resolved service from ROB (e.g. you copied a config from another service and changed `agentServicePubKey`), the CLI warns that IDs may belong to a different service. It prompts to clear all entity IDs and deploy fresh.
347
+
348
+ #### Dry Run
349
+
350
+ ```bash
351
+ cef deploy --dry-run
352
+ ```
353
+
354
+ Logs what would be created or updated without making any API calls.
355
+
356
+ #### Partial Deploy
357
+
358
+ Deploy a single entity type:
359
+
360
+ ```bash
361
+ cef deploy --only agent
362
+ cef deploy --only cubby
363
+ cef deploy --only workspace
364
+ ```
365
+
366
+ ---
367
+
368
+ ### cef deploy status - Compare Local vs Deployed
369
+
370
+ Compares local handler code against the deployed version on the orchestrator. Checks engagements and agents (task code).
371
+
372
+ ```bash
373
+ cef deploy status
374
+ cef deploy status --config ./app/cef.config.yaml
375
+ ```
376
+
377
+ | Option | Default | Description |
378
+ |-|-|-|
379
+ | `--config <path>` | `./cef.config.yaml` | Path to config file |
380
+
381
+ Status values:
382
+
383
+ | Status | Meaning |
384
+ |-|-|
385
+ | `up-to-date` | Local code matches deployed version byte-for-byte |
386
+ | `outdated` | Deployed code differs from local |
387
+ | `not-deployed` | Entity has no ID in config (never deployed) |
388
+ | `not-found` | Entity ID exists in config but not found on orchestrator |
389
+
390
+ Example output:
391
+
392
+ ```
393
+ [OK] engagement: Hello World (eng-2f8b9517)
394
+ [OUTDATED] agent: Object Detection (agent-628677bd)
395
+ task: Yolo [OUTDATED]
396
+ task: Detect [OK]
397
+
398
+ 1 outdated, 0 not deployed.
399
+ ```
400
+
401
+ For agents, the status is aggregated from all tasks. If any task is outdated, the agent is reported as outdated.
402
+
403
+ ---
404
+
405
+ ### cef deploy clone - Download Existing Service
406
+
407
+ Downloads all code and config from a deployed agent service and writes local files.
408
+
409
+ ```bash
410
+ cef deploy clone --pubkey 0x... --output-dir ./my-agent
411
+ cef deploy clone --pubkey 0x... --output-dir ./my-agent --force
412
+ cef deploy clone --config ./other/cef.config.yaml
413
+ ```
414
+
415
+ | Option | Default | Description |
416
+ |-|-|-|
417
+ | `--config <path>` | `./cef.config.yaml` | Path to config file |
418
+ | `--pubkey <key>` | (from config) | Agent service public key (`0x...`) |
419
+ | `--output-dir <dir>` | (config directory) | Directory to write files into |
420
+ | `--force` | `false` | Overwrite existing files |
421
+
422
+ Downloads in parallel:
423
+ - Engagement handler code -> `./engagements/{name}.ts`
424
+ - Agent task code -> `./agents/{alias}/tasks/{taskAlias}.ts`
425
+ - Full ontology (workspaces, streams, deployments, rafts)
426
+ - Cubby configs with migrations
427
+
428
+ Produces a complete `cef.config.yaml` with all IDs populated, ready for `cef deploy status` or further edits. Without `--force`, existing files are not overwritten.
429
+
430
+ ---
431
+
432
+ ### cef deploy delete - Delete Deployed Entities
433
+
434
+ Deletes deployed entities in reverse dependency order and removes the corresponding IDs from config.
435
+
436
+ ```bash
437
+ cef deploy delete
438
+ cef deploy delete --only agent
439
+ cef deploy delete --force # Skip confirmation prompt
440
+ ```
441
+
442
+ | Option | Default | Description |
443
+ |-|-|-|
444
+ | `--config <path>` | `./cef.config.yaml` | Path to config file |
445
+ | `--only <type>` | (all) | Delete only one entity type |
446
+ | `--force` | `false` | Skip confirmation prompt |
447
+
448
+ Deletion order (reverse of deploy):
449
+
450
+ ```
451
+ Deployments -> Rafts -> Streams -> Workspaces -> Agents -> Engagements
452
+ ```
453
+
454
+ After successful deletion, the corresponding IDs are cleared from `cef.config.yaml`.
455
+
456
+ **Note:** Cubbies cannot be deleted via the CLI. A warning is shown for cubbies with IDs.
457
+
458
+ Without `--force`, the CLI lists all entities to be deleted and asks for confirmation. In non-interactive terminals, `--force` is required.
459
+
460
+ ---
461
+
462
+ ### cef service list - List Agent Services
463
+
464
+ Lists all active (non-archived) agent services from ROB.
465
+
466
+ ```bash
467
+ cef service list
468
+ cef service list --config ./app/cef.config.yaml
469
+ ```
470
+
471
+ | Option | Default | Description |
472
+ |-|-|-|
473
+ | `--config <path>` | `./cef.config.yaml` | Path to config file (finds `.env` in same dir) |
474
+
475
+ Requires `CEF_ROB_API_URL` in `.env`.
476
+
477
+ Output:
478
+
479
+ ```
480
+ Agent Services (3):
481
+ Parking Detection
482
+ agentServiceId: 2620
483
+ agentServicePubKey: 0x788b3863...
484
+ created: 2024-01-15T10:30:00Z
485
+
486
+ Hello World Agent
487
+ agentServiceId: 2621
488
+ agentServicePubKey: 0xbbccddee...
489
+ created: 2024-02-01T08:00:00Z
490
+ ```
491
+
492
+ ---
493
+
494
+ ### cef service create - Create New Agent Service
495
+
496
+ Creates a new agent service on both ROB and the orchestrator.
497
+
498
+ ```bash
499
+ cef service create --name my-agent
500
+ cef service create --name my-agent --bucket-id 1505
501
+ ```
502
+
503
+ | Option | Default | Required | Description |
504
+ |-|-|-|-|
505
+ | `--name <name>` | - | Yes | Name of the new agent service |
506
+ | `--config <path>` | `./cef.config.yaml` | No | Path to config file |
507
+ | `--bucket-id <id>` | `0` | No | DDC bucket ID |
508
+
509
+ Requires `CEF_ROB_API_URL` in `.env`.
510
+
511
+ Returns the service name, `agentServiceId`, and `agentServicePubKey`. Use the pubkey in your `cef.config.yaml`.
512
+
513
+ ---
514
+
515
+ ### cef playground test - Test Agent Tools via MCP
516
+
517
+ Tests deployed agent tools using the MCP (Model Context Protocol) playground API. Agents must have an `agentId` (deployed via `cef deploy`).
518
+
519
+ ```bash
520
+ cef playground test [options]
521
+ ```
522
+
523
+ | Option | Default | Description |
524
+ |-|-|-|
525
+ | `--config <path>` | `./cef.config.yaml` | Path to config file |
526
+ | `--agent <name>` | - | Agent name, alias, or agentId |
527
+ | `--tool <alias>` | - | Task alias (tool name) to call |
528
+ | `--args <json>` | - | JSON arguments to pass to the tool |
529
+ | `--test-file <path>` | - | Path to `cef.tests.yaml` for batch testing |
530
+ | `--list-tools` | `false` | List available tools on an agent |
531
+
532
+ Agent resolution: matches by name, alias, or agentId from config.
533
+
534
+ #### Single Test
535
+
536
+ ```bash
537
+ cef playground test \
538
+ --agent objectDetection \
539
+ --tool yolo \
540
+ --args '{"image": "https://example.com/photo.jpg"}'
541
+ ```
542
+
543
+ #### List Tools
544
+
545
+ ```bash
546
+ cef playground test --agent objectDetection --list-tools
547
+ ```
548
+
549
+ Lists all available task aliases (tools) on the specified agent.
550
+
551
+ #### Batch Tests
552
+
553
+ ```bash
554
+ cef playground test --test-file ./cef.tests.yaml
555
+ ```
556
+
557
+ Runs all tests defined in the file. Exits with code 1 if any test fails.
558
+
559
+ #### Test File Format (cef.tests.yaml)
560
+
561
+ ```yaml
562
+ tests:
563
+ - agent: objectDetection
564
+ tool: yolo
565
+ arguments:
566
+ image: "https://example.com/photo.jpg"
567
+ expect:
568
+ success: true
569
+ result:
570
+ totalDetections: 3
571
+
572
+ - agent: parkingViolationDetector
573
+ tool: detect
574
+ arguments:
575
+ timestamp: 1712345678
576
+ detections: []
577
+ telemetry: {}
578
+ expect:
579
+ success: true
580
+ ```
581
+
582
+ Expectations use partial matching; the response only needs to contain the specified fields. Omit `expect` to just verify the call succeeds without error.
583
+
584
+ Output:
585
+
586
+ ```
587
+ [PASS] objectDetection > yolo (123ms)
588
+ {"totalDetections": 3, "detections": [...]}
589
+ [FAIL] parkingViolationDetector > detect (456ms)
590
+ - expected totalDetections: 3, got: 0
591
+ [ERROR] someAgent > someTask (789ms)
592
+ Agent not found
593
+
594
+ Results: 1 passed, 1 failed, 1 error
595
+ ```
596
+
597
+ ---
598
+
599
+ ## Configuration (cef.config.yaml)
600
+
601
+ The config file defines your entire agent topology. All file paths are resolved relative to the config file location. By default the CLI looks for `./cef.config.yaml` in the current directory.
602
+
603
+ ### Top-Level Fields
604
+
605
+ ```yaml
606
+ agentServicePubKey: "0x..." # Required. 64-char hex string with 0x prefix.
607
+ agentServiceId: "2620" # Optional. Set by CLI on first deploy.
608
+
609
+ engagements: [...] # Optional. Engagement definitions.
610
+ agents: [...] # Optional. Agent definitions.
611
+ cubbies: [...] # Optional. Cubby (database) definitions.
612
+ workspaces: [...] # Optional. Workspace/stream/deployment definitions.
613
+ ```
614
+
615
+ Get `agentServicePubKey` from `cef service list` or `cef service create`.
616
+
617
+ ### Engagements
618
+
619
+ Engagements are the entry points that subscribe to streams and orchestrate agent calls.
620
+
621
+ ```yaml
622
+ engagements:
623
+ - name: Hello World # Required. Display name.
624
+ description: Greets incoming events # Optional.
625
+ file: ./engagements/hello.ts # Required. Path to handler file.
626
+ version: 1.0.0 # Optional. Semantic version.
627
+ engagementId: eng-2f8b9517 # Set by CLI on deploy. Do not set manually.
628
+ parameters: # Optional. JSON Schema for dev UI hints.
629
+ type: object
630
+ properties:
631
+ streamId:
632
+ type: string
633
+ required: [streamId]
634
+ ```
635
+
636
+ The `parameters` field is local-only (not sent to orchestrator). It drives input hints in the dev UI.
637
+
638
+ ### Agents
639
+
640
+ Agents contain one or more tasks (tools). Each task has its own handler file.
641
+
642
+ ```yaml
643
+ agents:
644
+ - name: Object Detection # Required. Display name.
645
+ alias: objectDetection # Required. Used in context.agents.{alias}.
646
+ description: Runs YOLO detection # Optional.
647
+ version: 1.0.0 # Optional.
648
+ agentId: agent-628677bd # Set by CLI on deploy. Do not set manually.
649
+ tasks:
650
+ - name: Yolo # Required. Display name.
651
+ alias: yolo # Required. Used in context.agents.{alias}.{taskAlias}().
652
+ file: ./agents/od/tasks/yolo.ts # Required. Path to handler file.
653
+ parameters: # Optional. JSON Schema for input.
654
+ type: object
655
+ properties:
656
+ image:
657
+ type: string
658
+ description: URL of image to analyze
659
+ required: []
660
+ returns: # Optional. JSON Schema for output.
661
+ type: object
662
+ properties:
663
+ totalDetections:
664
+ type: number
665
+ detections:
666
+ type: array
667
+ batch_file: ./agents/od/eval.sql # Optional. SQL file for Run Batch (dev only).
668
+ batch_cubby: detection_store # Optional. Cubby alias to run batch_file against.
669
+ ```
670
+
671
+ `parameters` and `returns` are local-only. They drive dev UI input hints and output schema display. The MCP playground also uses `parameters` for tool argument schema.
672
+
673
+ `batch_file` and `batch_cubby` are local dev only. When both are set, the dev UI shows a "Run Batch" button on the agent task panel. Clicking it runs the SQL from `batch_file` against `batch_cubby`, then calls the agent once per result row.
674
+
675
+ ### Cubbies
676
+
677
+ Cubbies are per-agent SQLite databases with versioned schema migrations.
678
+
679
+ ```yaml
680
+ cubbies:
681
+ - alias: greetings # Required. Used in context.cubbies.{alias}.
682
+ name: Greetings Store # Required. Display name.
683
+ description: Stores greeting records # Optional.
684
+ cubbyId: 53d8630e-... # Set by CLI on deploy. Do not set manually.
685
+ maxSizeBytes: 10485760 # Optional. Max DB size in bytes.
686
+ idleTimeout: 24h # Optional. Instance idle timeout.
687
+ migrations:
688
+ - version: 1 # Required. Integer, applied in order.
689
+ up: | # Required. SQL DDL statements.
690
+ CREATE TABLE greetings (
691
+ id INTEGER PRIMARY KEY,
692
+ name TEXT,
693
+ message TEXT,
694
+ created_at TEXT DEFAULT (datetime('now'))
695
+ );
696
+ CREATE INDEX idx_greetings_name ON greetings(name);
697
+ - version: 2
698
+ up: |
699
+ ALTER TABLE greetings ADD COLUMN source TEXT DEFAULT 'manual';
700
+ ```
701
+
702
+ Migrations run in version order. Each `up` field can contain multiple SQL statements separated by semicolons. A `schema_migrations` table tracks applied versions.
703
+
704
+ In the local dev server, cubbies are backed by real SQLite with `better-sqlite3` and `sqlite-vec` (vector operations). Pragmas applied: WAL journal mode, 64MB cache, 5s busy timeout, NORMAL synchronous mode.
705
+
706
+ ### Workspaces
707
+
708
+ Workspaces group related streams. Streams contain deployments (which bind engagements to event flows) and rafts (query interfaces).
709
+
710
+ ```yaml
711
+ workspaces:
712
+ - name: Freemont # Required.
713
+ description: Freemont zone # Optional.
714
+ workspaceId: "2198" # Set by CLI on deploy.
715
+ streams:
716
+ - name: DSC 142 # Required.
717
+ description: Camera feed DSC 142 # Optional.
718
+ streamId: stream-f5213550 # Set by CLI on deploy.
719
+ selectors: # Optional. Event type filters.
720
+ - name: illegalParkingDetection
721
+ conditions:
722
+ - event_type:illegalParking
723
+ deployments:
724
+ - name: Illegal Parking Detection # Required.
725
+ description: Detects violations # Optional.
726
+ engagement: Illegal Parking detection # Required. References engagement by name.
727
+ isActive: true # Optional. Default true.
728
+ deploymentId: dep-21e72017 # Set by CLI on deploy.
729
+ triggers: # Optional. Event matching rules.
730
+ - name: all-events
731
+ conditions:
732
+ - "*"
733
+ engagementRules: [] # Optional.
734
+ rafts:
735
+ - name: Query Interface # Required.
736
+ description: Raft for queries # Optional.
737
+ file: ./queries/raft.ts # Optional. Path to raft handler.
738
+ raftId: raft-abc123 # Set by CLI on deploy.
739
+ dataSourcesIds: [] # Optional.
740
+ queryOperations: [] # Optional.
741
+ ```
742
+
743
+ Raft handlers export `onInit()`, `onData()`, `query()`, `onCleanup()` functions.
744
+
745
+ ### Full Example Config
746
+
747
+ A minimal working config (from `example/hello-world/cef.config.yaml`):
748
+
749
+ ```yaml
750
+ agentServicePubKey: "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
751
+
752
+ workspaces:
753
+ - name: Local Dev
754
+ workspaceId: "local-ws-1"
755
+
756
+ engagements:
757
+ - name: Hello World
758
+ file: ./engagements/hello-engagement.ts
759
+ parameters:
760
+ type: object
761
+ properties:
762
+ streamId:
763
+ type: string
764
+ description: Stream to subscribe to
765
+ required: [streamId]
766
+
767
+ agents:
768
+ - name: Greeter
769
+ alias: greeter
770
+ tasks:
771
+ - name: Greet
772
+ alias: greet
773
+ file: ./agents/greeter.ts
774
+ parameters:
775
+ type: object
776
+ properties:
777
+ name:
778
+ type: string
779
+ description: Name of the person to greet
780
+ required: [name]
781
+ returns:
782
+ type: object
783
+ properties:
784
+ greeting:
785
+ type: string
786
+ timestamp:
787
+ type: number
788
+
789
+ - name: Summarizer
790
+ alias: summarizer
791
+ tasks:
792
+ - name: Summarize
793
+ alias: summarize
794
+ file: ./agents/summarizer.ts
795
+ parameters:
796
+ type: object
797
+ properties: {}
798
+ returns:
799
+ type: object
800
+ properties:
801
+ summary:
802
+ type: string
803
+ totalGreetings:
804
+ type: number
805
+
806
+ cubbies:
807
+ - alias: greetings
808
+ name: Greetings Store
809
+ migrations:
810
+ - version: 1
811
+ up: "CREATE TABLE greetings (id INTEGER PRIMARY KEY, name TEXT, message TEXT, created_at TEXT DEFAULT (datetime('now')))"
812
+ ```
813
+
814
+ A full reference config with workspaces, streams, deployments, and rafts is in the `example/` directory.
815
+
816
+ ---
817
+
818
+ ## Agent Sandbox
819
+
820
+ Agent and engagement handlers run in isolated V8 sandboxes using Node.js `vm` module. Each execution gets its own context with no access to the host environment.
821
+
822
+ ### Handler Signature
823
+
824
+ Every handler file must define an `async function handle`:
825
+
826
+ ```typescript
827
+ async function handle(event: any, context: any) {
828
+ // event.payload contains the input data
829
+ const name = event.payload.name;
830
+
831
+ // Use context to interact with the platform
832
+ context.log('Processing: ' + name);
833
+
834
+ // Return a result
835
+ return { greeting: `Hello, ${name}!` };
836
+ }
837
+ ```
838
+
839
+ The function receives:
840
+ - `event` with `event.payload` containing the input data
841
+ - `context` providing platform APIs (logging, agents, cubbies, streams, fetch)
842
+
843
+ The return value is sent back to the caller (another agent, the playground, or the dev UI).
844
+
845
+ ### Available Globals
846
+
847
+ The sandbox provides these globals and nothing else:
848
+
849
+ | Global | Description |
850
+ |-|-|
851
+ | `event` | Input payload object (`event.payload` has the data) |
852
+ | `context` | Platform API (see [Context API Reference](#context-api-reference)) |
853
+ | `console` | `log`, `warn`, `error` (proxied to context logging) |
854
+ | `Promise` | Standard Promise |
855
+ | `JSON` | Standard JSON |
856
+ | `TextDecoder` | Standard TextDecoder |
857
+ | `TextEncoder` | Standard TextEncoder |
858
+ | `Uint8Array` | Standard Uint8Array |
859
+
860
+ ### Restrictions
861
+
862
+ The following are **not available** in the sandbox:
863
+
864
+ - `process` - no access to Node.js process
865
+ - `require` / `import` - no module loading
866
+ - `Buffer` - use `Uint8Array` and `TextEncoder`/`TextDecoder` instead
867
+ - `fs` - no filesystem access
868
+ - `global` / `globalThis` - isolated scope
869
+ - Any Node.js built-in modules (`http`, `path`, `crypto`, etc.)
870
+
871
+ All handler code must be fully self-contained. No `import` or `require` statements. This is a V8 isolate constraint; the same constraint applies to deployed handlers on the platform.
872
+
873
+ ---
874
+
875
+ ## Context API Reference
876
+
877
+ The `context` object is injected into every handler and provides all platform interactions.
878
+
879
+ ### Logging
880
+
881
+ ```typescript
882
+ context.log('info message', extraData);
883
+ context.warn('warning message');
884
+ context.error('error message');
885
+ ```
886
+
887
+ Logs appear in the dev server console, the dev UI live log panel, and are broadcast via WebSocket. Each log entry captures the execution ID, agent/task alias, timestamp, and level.
888
+
889
+ `console.log`, `console.warn`, and `console.error` are proxied to the corresponding context methods.
890
+
891
+ ### Event Emission
892
+
893
+ ```typescript
894
+ context.emit(eventType: string, payload: any, targetId?: string);
895
+ ```
896
+
897
+ Emits a custom event that appears in the trace log. Use for signaling between components or for custom instrumentation.
898
+
899
+ ### HTTP Fetch
900
+
901
+ ```typescript
902
+ const response = await context.fetch('https://api.example.com/data', {
903
+ method: 'POST',
904
+ headers: { 'Content-Type': 'application/json' },
905
+ body: JSON.stringify({ key: 'value' })
906
+ });
907
+ const data = await response.json();
908
+ ```
909
+
910
+ Standard fetch API. Available in both local dev and deployed environments. Use this for calling external APIs from within agent handlers.
911
+
912
+ ### Agent-to-Agent Calls
913
+
914
+ ```typescript
915
+ const result = await context.agents.greeter.greet({ name: 'Alice' });
916
+ const summary = await context.agents.summarizer.summarize({});
917
+ ```
918
+
919
+ Call any agent task by alias. The pattern is `context.agents.{agentAlias}.{taskAlias}(payload)`.
920
+
921
+ Returns the handler's return value directly. Each call emits `agent:call` and `agent:return` trace events with timing information.
922
+
923
+ ### Cubby Operations
924
+
925
+ **Read query:**
926
+
927
+ ```typescript
928
+ // Default instance
929
+ const result = await context.cubbies.greetings.query(
930
+ 'SELECT name, message FROM greetings WHERE name = ?',
931
+ ['Alice']
932
+ );
933
+ // result.columns = ['name', 'message']
934
+ // result.rows = [['Alice', 'Hello, Alice!']]
935
+ // result.meta = { duration: 2, rowsRead: 1 }
936
+ ```
937
+
938
+ **Write operation:**
939
+
940
+ ```typescript
941
+ const result = await context.cubbies.greetings.exec(
942
+ 'INSERT INTO greetings (name, message) VALUES (?, ?)',
943
+ ['Alice', 'Hello, Alice!']
944
+ );
945
+ // result.rowsAffected = 1
946
+ // result.lastInsertId = 1
947
+ // result.meta = { duration: 1 }
948
+ ```
949
+
950
+ **Named instances:**
951
+
952
+ ```typescript
953
+ // Query a specific instance
954
+ const result = await context.cubbies.greetings.query(
955
+ 'user-123',
956
+ 'SELECT * FROM greetings',
957
+ []
958
+ );
959
+
960
+ // Write to a specific instance
961
+ await context.cubbies.greetings.exec(
962
+ 'user-123',
963
+ 'INSERT INTO greetings (name, message) VALUES (?, ?)',
964
+ ['Bob', 'Hello, Bob!']
965
+ );
966
+ ```
967
+
968
+ Both `query` and `exec` support an optional first argument for the instance ID. If the first argument contains a space, it is treated as SQL (using the `"default"` instance). Otherwise it is treated as an instance ID.
969
+
970
+ Each operation emits `cubby:query` or `cubby:exec` trace events with timing and row counts.
971
+
972
+ ### Stream Subscriptions
973
+
974
+ Used in engagements to consume stream events:
975
+
976
+ ```typescript
977
+ async function handle(event: any, context: any) {
978
+ const stream = await context.streams.subscribe(event.payload.streamId);
979
+
980
+ for await (const packet of stream) {
981
+ const payload = JSON.parse(new TextDecoder().decode(packet.payload));
982
+ context.log('Received: ' + payload.event_type);
983
+
984
+ // Process packet and call agents...
985
+ }
986
+ }
987
+ ```
988
+
989
+ Each packet has:
990
+ - `sequenceNum` (number) - packet sequence number
991
+ - `payload` (Uint8Array) - packet data, decode with `TextDecoder`
992
+ - `metadata` (object, optional) - additional metadata
993
+
994
+ ### Raft Queries
995
+
996
+ ```typescript
997
+ const data = await context.rafts.myRaft.query();
998
+ ```
999
+
1000
+ In local dev mode, raft queries throw `"not available in local dev mode"`. In deployed environments, rafts execute against their configured data sources and query operations.
1001
+
1002
+ ---
1003
+
1004
+ ## Environment Variables
1005
+
1006
+ Create a `.env` file in the same directory as your `cef.config.yaml`:
1007
+
1008
+ ```bash
1009
+ CEF_AUTH_TOKEN=your-jwt-token-here
1010
+ CEF_ORCHESTRATOR_URL=https://compute-1.devnet.ddc-dragon.com/orchestrator
1011
+ CEF_ROB_API_URL=https://api.rob.dev.cere.io/rms-node-backend
1012
+ ```
1013
+
1014
+ | Variable | Required For | Description |
1015
+ |-|-|-|
1016
+ | `CEF_AUTH_TOKEN` | deploy, status, clone, delete, service | JWT bearer token (same as ROB UI) |
1017
+ | `CEF_ORCHESTRATOR_URL` | deploy, status, clone, delete | Orchestrator base URL |
1018
+ | `CEF_ROB_API_URL` | service list/create, workspace registration | ROB API base URL |
1019
+ | `CEF_ROB_ORIGIN` | (optional) | Override Origin/Referer header for ROB requests |
1020
+
1021
+ `cef dev` does **not** require any environment variables.
1022
+
1023
+ Copy from `example/.env.example` and fill in your credentials.
1024
+
1025
+ ---
1026
+
1027
+ ## Options Reference
1028
+
1029
+ | Option | Commands | Default | Description |
1030
+ |-|-|-|-|
1031
+ | `--config <path>` | all | `./cef.config.yaml` | Config file path |
1032
+ | `--dry-run` | deploy | `false` | Show planned operations without calling the API |
1033
+ | `--only <type>` | deploy, delete | (all) | Act on one entity type only |
1034
+ | `--pubkey <key>` | clone | (from config) | Agent service public key (`0x...`) |
1035
+ | `--output-dir <dir>` | clone | (config dir) | Directory to write cloned files into |
1036
+ | `--force` | clone, delete | `false` | Skip confirmation / overwrite existing files |
1037
+ | `--name <name>` | service create | - | Name of the new agent service (required) |
1038
+ | `--bucket-id <id>` | service create | `0` | DDC bucket ID |
1039
+ | `--port <port>` | dev | `8787` | Server port |
1040
+ | `--host <host>` | dev | `localhost` | Server host |
1041
+ | `--persist` | dev | `false` | Keep `.cef-dev/` data after shutdown |
1042
+ | `--agent <name>` | playground test | - | Agent name, alias, or agentId |
1043
+ | `--tool <alias>` | playground test | - | Task alias (tool name) to call |
1044
+ | `--args <json>` | playground test | - | JSON arguments for tool |
1045
+ | `--test-file <path>` | playground test | - | Path to cef.tests.yaml |
1046
+ | `--list-tools` | playground test | `false` | List available tools on an agent |
1047
+ | `-V, --version` | (global) | - | Show CLI version |
1048
+ | `--help` | (global) | - | Show help |
1049
+
1050
+ ---
1051
+
1052
+ ## Examples
1053
+
1054
+ ### Hello World
1055
+
1056
+ A minimal topology with two agents sharing a cubby, driven by a stream-based engagement.
1057
+
1058
+ **Engagement handler** (`engagements/hello-engagement.ts`):
1059
+
1060
+ ```typescript
1061
+ async function handle(event: any, context: any) {
1062
+ const streamId = event.payload.streamId;
1063
+ context.log('Hello engagement started, subscribing to stream: ' + streamId);
1064
+
1065
+ const stream = await context.streams.subscribe(streamId);
1066
+
1067
+ for await (const packet of stream) {
1068
+ const data = JSON.parse(new TextDecoder().decode(packet.payload));
1069
+ context.log('Received event: ' + data.event_type);
1070
+
1071
+ if (data.event_type === 'GREET') {
1072
+ const greetResult = await context.agents.greeter.greet({
1073
+ name: data.name,
1074
+ timestamp: data.timestamp,
1075
+ });
1076
+ context.log('Greet result: ' + JSON.stringify(greetResult));
1077
+
1078
+ } else if (data.event_type === 'SUMMARIZE') {
1079
+ const summaryResult = await context.agents.summarizer.summarize({});
1080
+ context.log('Summary result: ' + JSON.stringify(summaryResult));
1081
+ }
1082
+ }
1083
+ }
1084
+ ```
1085
+
1086
+ **Greeter agent** (`agents/greeter.ts`):
1087
+
1088
+ ```typescript
1089
+ async function handle(event: any, context: any) {
1090
+ const name = event.payload.name || 'World';
1091
+ context.log(`Greeter agent called with name: ${name}`);
1092
+
1093
+ // Write to cubby
1094
+ await context.cubbies.greetings.exec(
1095
+ 'INSERT INTO greetings (name, message) VALUES (?, ?)',
1096
+ [name, `Hello, ${name}!`]
1097
+ );
1098
+
1099
+ return { greeting: `Hello, ${name}!`, timestamp: Date.now() };
1100
+ }
1101
+ ```
1102
+
1103
+ **Summarizer agent** (`agents/summarizer.ts`):
1104
+
1105
+ ```typescript
1106
+ async function handle(event: any, context: any) {
1107
+ context.log('Summarizer agent called');
1108
+
1109
+ // Read all greetings from cubby (cross-agent shared state)
1110
+ const result = await context.cubbies.greetings.query(
1111
+ 'SELECT name, message FROM greetings ORDER BY id'
1112
+ );
1113
+
1114
+ const names = result.rows.map((r: any) => r[0]);
1115
+ const summary = `Greeted ${result.rows.length} people: ${names.join(', ')}`;
1116
+
1117
+ context.log(`Summary: ${summary}`);
1118
+ return { summary, totalGreetings: result.rows.length };
1119
+ }
1120
+ ```
1121
+
1122
+ **Run it:**
1123
+
1124
+ ```bash
1125
+ # Start dev server
1126
+ cef dev --config ./example/hello-world/cef.config.yaml
1127
+
1128
+ # Call the greeter directly
1129
+ curl -s -X POST http://localhost:8787/api/agents/greeter/greet \
1130
+ -H "Content-Type: application/json" \
1131
+ -d '{"payload": {"name": "Alice"}}'
1132
+ # -> {"greeting":"Hello, Alice!","timestamp":1712345678000}
1133
+
1134
+ # Greet again
1135
+ curl -s -X POST http://localhost:8787/api/agents/greeter/greet \
1136
+ -H "Content-Type: application/json" \
1137
+ -d '{"payload": {"name": "Bob"}}'
1138
+
1139
+ # Get summary (reads from same cubby)
1140
+ curl -s -X POST http://localhost:8787/api/agents/summarizer/summarize \
1141
+ -H "Content-Type: application/json" \
1142
+ -d '{"payload": {}}'
1143
+ # -> {"summary":"Greeted 2 people: Alice, Bob","totalGreetings":2}
1144
+
1145
+ # Query the cubby directly
1146
+ curl -s -X POST http://localhost:8787/api/cubbies/greetings/query \
1147
+ -H "Content-Type: application/json" \
1148
+ -d '{"sql": "SELECT * FROM greetings"}'
1149
+ ```
1150
+
1151
+ See `example/hello-world/` for the complete project.
1152
+
1153
+ ### Drone Telemetry (Workspaces + Streams)
1154
+
1155
+ A more complex topology with workspaces, streams, selectors, deployments, and triggers. This pattern is used when you need:
1156
+
1157
+ - **Workspaces** to group related streams by location or domain
1158
+ - **Streams** with **selectors** to filter incoming events by type
1159
+ - **Deployments** that bind engagements to streams with trigger conditions
1160
+ - **Rafts** for query interfaces over collected data
1161
+
1162
+ ```yaml
1163
+ workspaces:
1164
+ - name: Freemont
1165
+ streams:
1166
+ - name: DSC 142
1167
+ selectors:
1168
+ - name: illegalParkingDetection
1169
+ conditions:
1170
+ - event_type:illegalParking
1171
+ deployments:
1172
+ - name: Illegal Parking Detection
1173
+ engagement: Illegal Parking detection # references engagement by name
1174
+ isActive: true
1175
+ triggers:
1176
+ - name: all-events
1177
+ conditions:
1178
+ - "*"
1179
+
1180
+ agents:
1181
+ - name: Object Detection
1182
+ alias: objectDetection
1183
+ tasks:
1184
+ - name: Yolo
1185
+ alias: yolo
1186
+ file: ./agents/object-detection/tasks/yolo.ts
1187
+ - name: Detect
1188
+ alias: detect
1189
+ file: ./agents/parking-violation-detector/tasks/detect.ts
1190
+
1191
+ cubbies:
1192
+ - alias: syncMission
1193
+ name: Sync Mission
1194
+ migrations:
1195
+ - version: 1
1196
+ up: |
1197
+ CREATE TABLE missions (id TEXT PRIMARY KEY, status TEXT NOT NULL, drone_id TEXT, started_at TEXT, ended_at TEXT, created_at TEXT DEFAULT (datetime('now')));
1198
+ CREATE TABLE violations (id TEXT PRIMARY KEY, mission_id TEXT NOT NULL, type TEXT NOT NULL, latitude REAL, longitude REAL, confidence REAL, detected_at TEXT NOT NULL, FOREIGN KEY (mission_id) REFERENCES missions(id));
1199
+ CREATE INDEX idx_violations_mission ON violations(mission_id);
1200
+ ```
1201
+
1202
+ See `example/cef.config.yaml` for the complete drone telemetry config.
1203
+
1204
+ ### Wiki Knowledge Graph (Graph Viz + Batch)
1205
+
1206
+ A knowledge graph pipeline that ingests Notion pages as versioned artifact nodes, evaluates changes with an LLM, and visualizes the result as a graph. Demonstrates graph visualization, saved queries, and batch execution.
1207
+
1208
+ ```bash
1209
+ cd example/wiki-graph
1210
+ cef dev --config cef.config.yaml
1211
+
1212
+ # Seed saved queries (first time)
1213
+ node connector/seed-queries.js
1214
+
1215
+ # Ingest test data via the UI (Graph Ingest Handler -> blast mode with test-events.json)
1216
+ # Or ingest a real Notion page:
1217
+ NOTION_API_KEY=... node connector/ingest-graph.js --page-id <notion_id>
1218
+
1219
+ # Run evals via UI (Run Batch on graph-eval-agent.eval-page) or CLI:
1220
+ node connector/run-evals.js
1221
+ ```
1222
+
1223
+ Graph schema: `artifact`, `work`, `operator`, `eval` nodes with relationship edges (`ADDED`, `CHANGED`, `OWNS`, `HAS_EVAL`, etc.). See `example/wiki-graph/README.md` for full details.
1224
+
1225
+ ---
1226
+
1227
+ ## Testing
1228
+
1229
+ ```bash
1230
+ npm test # Unit tests (Vitest)
1231
+ npm run test:watch # Watch mode
1232
+ npm run test:coverage # Coverage report
1233
+ npm run test:integration # Integration tests (real API calls)
1234
+ npm run test:e2e # E2E tests (full deploy cycle, slow)
1235
+ npm run lint # ESLint
1236
+ npm run build # Build TypeScript
1237
+ ```
1238
+
1239
+ | Suite | When to use | Auth needed | Speed |
1240
+ |-|-|-|-|
1241
+ | Unit (`npm test`) | Always; validates logic without external deps | No | Fast |
1242
+ | Integration | Verify API client against dev environment | Yes (`.env`) | Medium |
1243
+ | E2E | Full lifecycle: deploy, test, delete | Yes (`.env` + `CEF_E2E_AGENT_SERVICE_PUB_KEY`) | Slow (120s timeout) |
1244
+
1245
+ For integration/E2E: copy `example/.env.example` to `.env` and set credentials.
1246
+
1247
+ ---
1248
+
1249
+ ## Troubleshooting
1250
+
1251
+ **"Missing CEF_AUTH_TOKEN" on deploy**
1252
+ Create a `.env` file next to your `cef.config.yaml` with auth tokens. See [Environment Variables](#environment-variables).
1253
+
1254
+ **Stale IDs after copying config between services**
1255
+ If you copied a `cef.config.yaml` from another service, the embedded IDs point to the wrong service. Run `cef deploy` and accept the prompt to clear all IDs, or manually remove all `*Id` fields from the config.
1256
+
1257
+ **"require is not defined" or "import is not supported" in agent code**
1258
+ Agent handlers run in a V8 sandbox with no module system. All code must be self-contained in a single file with no imports. See [Agent Sandbox](#agent-sandbox).
1259
+
1260
+ **Cubby data disappears after restarting dev server**
1261
+ By default, `.cef-dev/` is deleted on shutdown. Use `cef dev --persist` to keep data between runs.
1262
+
1263
+ **"Not available in local dev mode" for raft queries**
1264
+ Raft queries are not emulated locally. Test rafts by deploying to the platform and using `cef playground test`.
1265
+
1266
+ **Agent code changes not taking effect**
1267
+ The dev server hot-reloads on file save. Check that the file path in `cef.config.yaml` matches the file you are editing. In-flight executions continue with old code; only new calls use the updated version.
1268
+
1269
+ **Deploy fails with 404 on update**
1270
+ The entity ID in config points to something that no longer exists on the orchestrator. Clear the ID field for that entity and re-run `cef deploy` to create it fresh.
1271
+
1272
+ **Non-interactive terminal and delete/deploy prompts**
1273
+ Some operations prompt for confirmation. In CI or non-interactive environments, use `--force` to skip prompts.