@cef-ai/cli 1.4.0 → 1.6.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 +5 -1
- package/dist/cli-program.d.ts +8 -1
- package/dist/cli-program.d.ts.map +1 -1
- package/dist/cli-program.js +129 -6
- package/dist/cli-program.js.map +1 -1
- package/dist/commands/deploy.d.ts +38 -0
- package/dist/commands/deploy.d.ts.map +1 -0
- package/dist/commands/deploy.js +317 -0
- package/dist/commands/deploy.js.map +1 -0
- package/dist/commands/init.d.ts +41 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +191 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/publish.d.ts.map +1 -1
- package/dist/commands/publish.js +4 -1
- package/dist/commands/publish.js.map +1 -1
- package/dist/lib/ddc.d.ts.map +1 -1
- package/dist/lib/ddc.js +18 -1
- package/dist/lib/ddc.js.map +1 -1
- package/dist/lib/env.d.ts +38 -0
- package/dist/lib/env.d.ts.map +1 -0
- package/dist/lib/env.js +45 -0
- package/dist/lib/env.js.map +1 -0
- package/dist/lib.d.ts +4 -0
- package/dist/lib.d.ts.map +1 -1
- package/dist/lib.js +2 -0
- package/dist/lib.js.map +1 -1
- package/package.json +3 -2
- package/templates/hello-world/AGENTS.md +41 -0
- package/templates/hello-world/CLAUDE.md +9 -0
- package/templates/hello-world/README.md +55 -0
- package/templates/hello-world/cef.config.ts +47 -0
- package/templates/hello-world/claude/settings.json +23 -0
- package/templates/hello-world/deployments/default.jsonc +9 -0
- package/templates/hello-world/deployments/sandbox-canary.jsonc +12 -0
- package/templates/hello-world/gitignore +3 -0
- package/templates/hello-world/migrations/history/001-init.sql +5 -0
- package/templates/hello-world/package.json +23 -0
- package/templates/hello-world/src/agent.ts +57 -0
- package/templates/hello-world/test/agent.test.ts +41 -0
- package/templates/hello-world/tsconfig.json +19 -0
- package/templates/hello-world/widgets/hello/app.js +8 -0
- package/templates/hello-world/widgets/hello/index.html +5 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# {{projectName}} — agent context
|
|
2
|
+
|
|
3
|
+
This is a **CEF agent** project. Agents are authored with `@cef-ai/agent-sdk`,
|
|
4
|
+
built/shipped with the `cef` CLI, and run on the Cere Agent Runtime.
|
|
5
|
+
|
|
6
|
+
## Dev loop
|
|
7
|
+
|
|
8
|
+
- `pnpm test` — run the agent test (`@cef-ai/testing`).
|
|
9
|
+
- `pnpm run build` — `cef build`: lint + bundle to `dist/<agentId>/`.
|
|
10
|
+
- `pnpm run push` — `cef push`: upload bundle + widgets to DDC. Needs a DDC bucket id,
|
|
11
|
+
a **registry access token minted in the ROC UI** (`--access-token` / `$CEF_DDC_ACCESS_TOKEN`),
|
|
12
|
+
and your AS pubkey (`--as-pubkey`) — both from ROC.
|
|
13
|
+
- `pnpm run deploy` — `cef deploy`: apply the `deployments/` folder to make a
|
|
14
|
+
version live/testable (incl. sandbox). Needs the platform `--endpoint`
|
|
15
|
+
(defaults to dev) + AS pubkey. **This — not publish — is what makes an agent run.**
|
|
16
|
+
- `pnpm run publish` — `cef publish`: **optional, last.** Lists the card in the
|
|
17
|
+
marketplace for discovery (needs AS credentials). Skip for debug/test.
|
|
18
|
+
|
|
19
|
+
## Structure
|
|
20
|
+
|
|
21
|
+
- `cef.config.ts` — declares id, cubbies, widgets.
|
|
22
|
+
- `src/agent.ts` — default-exported class; `@Engagement`, `@OnEvent`, `@OnStart`, `@OnClose`.
|
|
23
|
+
- `migrations/history/*.sql` — schema for the `history` cubby (used via `ctx.cubby`).
|
|
24
|
+
- `widgets/hello/` — a self-contained static widget.
|
|
25
|
+
- `deployments/*.jsonc` — deployment records (one per file, filename = name)
|
|
26
|
+
applied by `cef deploy`; `default.jsonc` is the required catch-all. `targeting`
|
|
27
|
+
(CEL over `vault.*`/`event.type`) + `priority`/`weight` express audiences / A-B.
|
|
28
|
+
- `test/agent.test.ts` — `testAgent` → dispatch an event → assert output.
|
|
29
|
+
|
|
30
|
+
## Runtime constraints (important)
|
|
31
|
+
|
|
32
|
+
- No Node built-ins (`fs`, `net`, `http`, `child_process`, …). Use the global
|
|
33
|
+
`fetch` for HTTP and `ctx.cubby` for state.
|
|
34
|
+
- Use `globalThis.crypto` (WebCrypto), not `node:crypto`.
|
|
35
|
+
- `@OnEvent("...")` and `ctx.publish("...")` take **string literals**.
|
|
36
|
+
- `ctx.cubby("alias")` must reference a cubby declared in `cef.config.ts`.
|
|
37
|
+
|
|
38
|
+
## Deeper guidance
|
|
39
|
+
|
|
40
|
+
The full authoring playbook lives in the `cef` Claude Code plugin
|
|
41
|
+
(`CEF-AI/cef-plugins`) — see `.claude/settings.json`, which auto-wires it.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
@AGENTS.md
|
|
2
|
+
|
|
3
|
+
## Claude Code notes
|
|
4
|
+
|
|
5
|
+
- This project auto-wires the **`cef`** authoring plugin via
|
|
6
|
+
`.claude/settings.json`. When available, the `cef:develop` skill (authoring)
|
|
7
|
+
and `cef:deploy` skill (build → push → deploy) load automatically.
|
|
8
|
+
- If the plugin isn't installed, add it once:
|
|
9
|
+
`/plugin marketplace add CEF-AI/cef-plugins` then `/plugin install cef@cef`.
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# {{projectName}}
|
|
2
|
+
|
|
3
|
+
A hello-world **CEF agent** scaffolded by `cef init`. It has one engagement,
|
|
4
|
+
one cubby (SQLite state), one widget, and a passing test.
|
|
5
|
+
|
|
6
|
+
## Layout
|
|
7
|
+
|
|
8
|
+
- `cef.config.ts` — agent id, cubbies, widgets (read by the `cef` CLI).
|
|
9
|
+
- `src/agent.ts` — the agent class (`@Engagement` / `@OnEvent` / lifecycle).
|
|
10
|
+
- `migrations/history/` — SQL migrations for the `history` cubby.
|
|
11
|
+
- `widgets/hello/` — a self-contained widget.
|
|
12
|
+
- `deployments/` — deployment records (which version runs for which audience),
|
|
13
|
+
applied by `cef deploy`. `default.jsonc` is the required catch-all.
|
|
14
|
+
- `test/agent.test.ts` — a `@cef-ai/testing` test.
|
|
15
|
+
|
|
16
|
+
## Develop
|
|
17
|
+
|
|
18
|
+
```sh
|
|
19
|
+
pnpm test # run the agent test
|
|
20
|
+
pnpm run build # cef build → dist/<agentId>/{bundle.js,manifest.json}
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Ship (build → push → deploy)
|
|
24
|
+
|
|
25
|
+
Each step and the external inputs it needs:
|
|
26
|
+
|
|
27
|
+
1. `pnpm run build` — no external inputs.
|
|
28
|
+
2. `pnpm run push` — uploads the bundle + widgets to DDC. Needs a **DDC bucket
|
|
29
|
+
id**, a **registry access token you mint in the ROC UI** (ROC → your Agent
|
|
30
|
+
Service → **Access** → generate token), and your **AS pubkey** (also shown
|
|
31
|
+
in ROC):
|
|
32
|
+
`cef push --bucket <id> --access-token <token> --as-pubkey <hex>`
|
|
33
|
+
(or set `$CEF_DDC_ACCESS_TOKEN`; see `cef push --help`).
|
|
34
|
+
3. `pnpm run deploy` — **makes a version live so you can run and test the agent**
|
|
35
|
+
(including in a sandbox). Applies the `deployments/` folder; edit those files
|
|
36
|
+
to target audiences or run A/B splits. Needs the platform `--endpoint`
|
|
37
|
+
(defaults to dev; `$CEF_ENDPOINT` to override) + your AS pubkey.
|
|
38
|
+
4. `pnpm run publish` — **optional, and last.** Lists the agent's card in the
|
|
39
|
+
marketplace for discovery (needs your **AS Ed25519 credentials** —
|
|
40
|
+
`cef keypair generate` if you don't have them). Skip it for debug/test/sandbox;
|
|
41
|
+
an agent runs because it's *deployed*, not because it's published.
|
|
42
|
+
|
|
43
|
+
## AI-assisted development (Claude Code)
|
|
44
|
+
|
|
45
|
+
This project auto-wires the **`cef`** authoring plugin via
|
|
46
|
+
`.claude/settings.json` — trust the folder and Claude Code installs it and
|
|
47
|
+
keeps it updated. To install it manually / at user scope:
|
|
48
|
+
|
|
49
|
+
```
|
|
50
|
+
/plugin marketplace add CEF-AI/cef-plugins
|
|
51
|
+
/plugin install cef@cef
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Then just ask Claude to help build the agent — the `cef:develop` skill loads
|
|
55
|
+
automatically; `cef:deploy` walks you through shipping.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { defineAgent } from "@cef-ai/agent-sdk/config";
|
|
2
|
+
|
|
3
|
+
export default defineAgent({
|
|
4
|
+
id: "{{agentId}}",
|
|
5
|
+
version: "0.1.0",
|
|
6
|
+
entry: "./src/agent.ts",
|
|
7
|
+
// Terminate an idle Job after 30s (default is "30m"). Fine for a test agent;
|
|
8
|
+
// raise it for long-lived / real-time agents.
|
|
9
|
+
idleTimeout: "30s",
|
|
10
|
+
card: {
|
|
11
|
+
name: "{{agentId}}",
|
|
12
|
+
description: "A hello-world CEF agent scaffolded by `cef init`.",
|
|
13
|
+
},
|
|
14
|
+
// One cubby (SQLite) for durable per-stream state.
|
|
15
|
+
cubbies: [{ alias: "history", migrations: "./migrations/history" }],
|
|
16
|
+
// One self-contained widget shipped with the agent.
|
|
17
|
+
widgets: [
|
|
18
|
+
{
|
|
19
|
+
id: "hello",
|
|
20
|
+
name: "Hello",
|
|
21
|
+
description: "Minimal hello-world widget.",
|
|
22
|
+
cubbyAlias: "history",
|
|
23
|
+
dir: "./widgets/hello",
|
|
24
|
+
// index.html so the CDN serves it at the widget's directory URL
|
|
25
|
+
// (…/widgets/hello) without naming the file.
|
|
26
|
+
entry: "index.html",
|
|
27
|
+
},
|
|
28
|
+
],
|
|
29
|
+
// Schemas for events this agent owns (handled + published). `cef build` folds
|
|
30
|
+
// these into the manifest so the platform and any peer agent that imports
|
|
31
|
+
// this one get the event contract. (Note: `cef typegen` currently types only
|
|
32
|
+
// *peer* events locally — it does not yet type your own events from these.)
|
|
33
|
+
eventSchemas: {
|
|
34
|
+
user_message: {
|
|
35
|
+
type: "object",
|
|
36
|
+
properties: { text: { type: "string" } },
|
|
37
|
+
required: ["text"],
|
|
38
|
+
additionalProperties: false,
|
|
39
|
+
},
|
|
40
|
+
reply: {
|
|
41
|
+
type: "object",
|
|
42
|
+
properties: { text: { type: "string" } },
|
|
43
|
+
required: ["text"],
|
|
44
|
+
additionalProperties: false,
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
});
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"permissions": {
|
|
3
|
+
"allow": [
|
|
4
|
+
"Bash(cef build)",
|
|
5
|
+
"Bash(cef typegen)",
|
|
6
|
+
"Bash(cef inspect:*)",
|
|
7
|
+
"Bash(vitest run)",
|
|
8
|
+
"Bash(pnpm test)",
|
|
9
|
+
"Bash(pnpm run build)",
|
|
10
|
+
"Bash(git status)",
|
|
11
|
+
"Bash(git diff:*)"
|
|
12
|
+
]
|
|
13
|
+
},
|
|
14
|
+
"extraKnownMarketplaces": {
|
|
15
|
+
"cef": {
|
|
16
|
+
"source": { "source": "github", "repo": "CEF-AI/cef-plugins" },
|
|
17
|
+
"autoUpdate": true
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"enabledPlugins": {
|
|
21
|
+
"cef@cef": true
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// The mandatory catch-all deployment. Exactly one file must have empty
|
|
2
|
+
// targeting; a HIGH priority number keeps it the lowest-priority fallthrough,
|
|
3
|
+
// so more specific deployments you add later win for their audience.
|
|
4
|
+
{
|
|
5
|
+
"priority": 99,
|
|
6
|
+
"targeting": "", // empty => always eligible (the one required default)
|
|
7
|
+
"version": "latest", // simplest "go live"; pin a semver for production
|
|
8
|
+
"weight": 1
|
|
9
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// Example audience deployment: only sandbox vaults get this pinned build;
|
|
2
|
+
// everyone else falls through to default.jsonc. Edit or delete freely.
|
|
3
|
+
//
|
|
4
|
+
// targeting is a CEL expression over vault.id / vault.scope / event.type.
|
|
5
|
+
// Among matching deployments the LOWEST priority wins; within a tier, traffic
|
|
6
|
+
// splits by weight (that's how you run an A/B test).
|
|
7
|
+
{
|
|
8
|
+
"priority": 10, // lower than the default (99) => wins for matching vaults
|
|
9
|
+
"targeting": "vault.scope == 'sandbox'",
|
|
10
|
+
"version": "0.1.0",
|
|
11
|
+
"weight": 1
|
|
12
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "{{projectName}}",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"build": "cef build",
|
|
8
|
+
"typegen": "cef typegen",
|
|
9
|
+
"test": "vitest run",
|
|
10
|
+
"push": "cef push",
|
|
11
|
+
"deploy": "cef deploy",
|
|
12
|
+
"publish": "cef publish"
|
|
13
|
+
},
|
|
14
|
+
"dependencies": {
|
|
15
|
+
"@cef-ai/agent-sdk": "^1.4.0"
|
|
16
|
+
},
|
|
17
|
+
"devDependencies": {
|
|
18
|
+
"@cef-ai/cli": "^1.4.0",
|
|
19
|
+
"@cef-ai/testing": "^1.1.9",
|
|
20
|
+
"typescript": "^5.4.0",
|
|
21
|
+
"vitest": "^1.5.0"
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Engagement,
|
|
3
|
+
OnEvent,
|
|
4
|
+
OnStart,
|
|
5
|
+
OnClose,
|
|
6
|
+
type Context,
|
|
7
|
+
type Event,
|
|
8
|
+
} from "@cef-ai/agent-sdk";
|
|
9
|
+
|
|
10
|
+
interface UserMessage {
|
|
11
|
+
text: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
@Engagement({
|
|
15
|
+
id: "default",
|
|
16
|
+
goal: "reply to a user message and remember it",
|
|
17
|
+
})
|
|
18
|
+
export default class HelloAgent {
|
|
19
|
+
@OnStart
|
|
20
|
+
async onStart() {
|
|
21
|
+
// Plain logging goes to the sandbox console; `ctx` is only for
|
|
22
|
+
// CEF-specific calls (cubby / publish / models / close).
|
|
23
|
+
console.info("hello-world agent started");
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
@OnEvent("user_message")
|
|
27
|
+
async onMessage(event: Event<UserMessage>, ctx: Context) {
|
|
28
|
+
// Never assume a well-formed event. A missing/empty payload must NOT throw
|
|
29
|
+
// — an unhandled error in a handler can leave the Job stuck. Guard first.
|
|
30
|
+
const text =
|
|
31
|
+
typeof event.payload?.text === "string" ? event.payload.text.trim() : "";
|
|
32
|
+
if (!text) {
|
|
33
|
+
await ctx.publish("reply", { text: 'no text received — send { "text": "..." }' });
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
try {
|
|
38
|
+
await ctx
|
|
39
|
+
.cubby("history")
|
|
40
|
+
.exec("INSERT INTO messages(id, text, ts) VALUES (?, ?, ?)", [
|
|
41
|
+
Date.now(),
|
|
42
|
+
text,
|
|
43
|
+
event.ts,
|
|
44
|
+
]);
|
|
45
|
+
} catch (err) {
|
|
46
|
+
// A storage hiccup shouldn't crash the handler; log and still reply.
|
|
47
|
+
console.error("failed to persist message", err);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
await ctx.publish("reply", { text: `hello — you said: ${text}` });
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
@OnClose
|
|
54
|
+
async onClose(_ctx: Context, reason: string) {
|
|
55
|
+
console.info("hello-world agent closing", { reason });
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { describe, it, expect, afterEach } from "vitest";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { testAgent, type TestHarness } from "@cef-ai/testing";
|
|
5
|
+
import HelloAgent from "../src/agent.js";
|
|
6
|
+
|
|
7
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
8
|
+
const HISTORY_MIGRATIONS = path.resolve(__dirname, "../migrations/history");
|
|
9
|
+
|
|
10
|
+
describe("hello-world agent", () => {
|
|
11
|
+
let h: TestHarness | undefined;
|
|
12
|
+
afterEach(() => h?.dispose());
|
|
13
|
+
|
|
14
|
+
it("replies to user_message and persists to history", async () => {
|
|
15
|
+
h = testAgent(HelloAgent, {
|
|
16
|
+
cubbies: [{ alias: "history", migrations: HISTORY_MIGRATIONS }],
|
|
17
|
+
});
|
|
18
|
+
const events = await h.dispatch({
|
|
19
|
+
type: "user_message",
|
|
20
|
+
payload: { text: "hi" },
|
|
21
|
+
});
|
|
22
|
+
expect(events.map((e) => e.type)).toContain("reply");
|
|
23
|
+
const rows = await h
|
|
24
|
+
.cubby("history")
|
|
25
|
+
.query<{ text: string }>("SELECT text FROM messages");
|
|
26
|
+
expect(rows.map((r) => r.text)).toEqual(["hi"]);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it("handles an empty payload without throwing or persisting", async () => {
|
|
30
|
+
h = testAgent(HelloAgent, {
|
|
31
|
+
cubbies: [{ alias: "history", migrations: HISTORY_MIGRATIONS }],
|
|
32
|
+
});
|
|
33
|
+
// An event with no `text` must not crash the handler.
|
|
34
|
+
const events = await h.dispatch({ type: "user_message", payload: {} });
|
|
35
|
+
expect(events.map((e) => e.type)).toContain("reply");
|
|
36
|
+
const rows = await h
|
|
37
|
+
.cubby("history")
|
|
38
|
+
.query<{ text: string }>("SELECT text FROM messages");
|
|
39
|
+
expect(rows).toEqual([]); // nothing persisted for an empty payload
|
|
40
|
+
});
|
|
41
|
+
});
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "NodeNext",
|
|
5
|
+
"moduleResolution": "NodeNext",
|
|
6
|
+
"lib": ["ES2022", "WebWorker"],
|
|
7
|
+
"strict": true,
|
|
8
|
+
"esModuleInterop": true,
|
|
9
|
+
"skipLibCheck": true,
|
|
10
|
+
"forceConsistentCasingInFileNames": true,
|
|
11
|
+
"isolatedModules": true,
|
|
12
|
+
"resolveJsonModule": true,
|
|
13
|
+
"experimentalDecorators": true,
|
|
14
|
+
"emitDecoratorMetadata": false,
|
|
15
|
+
"noEmit": true
|
|
16
|
+
},
|
|
17
|
+
"include": ["src/**/*", "test/**/*", "cef.config.ts"],
|
|
18
|
+
"exclude": ["dist", ".cef", "node_modules"]
|
|
19
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// A minimal, self-contained widget (plain HTML + JS — no build step).
|
|
2
|
+
//
|
|
3
|
+
// When the @cef-ai/widget-runtime ships, you can read this agent's cubby
|
|
4
|
+
// and publish events from here, e.g.:
|
|
5
|
+
// const { rows } = await window.WidgetRuntime.query("SELECT text FROM messages");
|
|
6
|
+
// await window.WidgetRuntime.publish("user_message", { text: "hi" });
|
|
7
|
+
document.getElementById("root").textContent =
|
|
8
|
+
"hello from {{agentId}} — this is your widget";
|