cmdc-sdk
v0.1.0
Published
CMDC Agent Kernel Node.js/Browser SDK — connect to cmdc_gateway via HTTP + SSE + WebSocket
Readme
cmdc (Node.js / Browser SDK)
CMDC Agent Kernel TypeScript/JavaScript SDK — 通过 HTTP + SSE + WebSocket 连接 cmdc_gateway,在 Node.js 或浏览器中使用 CMDC Agent 能力。
安装
npm install cmdc
# 或
yarn add cmdc
# 或
pnpm add cmdc依赖要求:
- Node.js 18+(使用原生 Fetch API)
- 浏览器:支持 Fetch + ReadableStream + WebSocket 的现代浏览器
快速开始(5 分钟)
1. 启动 cmdc_gateway
cd cmdc_gateway
CMDC_API_KEYS=sk-abc123 mix run --no-halt2. Node.js(ESM)
import { CMDCClient } from 'cmdc';
const client = new CMDCClient({
baseUrl: 'http://localhost:4000',
apiKey: 'sk-abc123',
});
const session = await client.createSession({
model: 'deepseek:deepseek-chat',
systemPrompt: '你是一个专业的编程助手',
tools: ['CMDC.Tool.Shell', 'CMDC.Tool.ReadFile'],
});
try {
for await (const event of session.prompt('帮我写一个快速排序')) {
if (event.type === 'message_delta') {
process.stdout.write(event.data.delta);
}
}
} finally {
await session.stop();
}3. 自动管理 Session 生命周期
await client.withSession({ model: 'deepseek:deepseek-chat' }, async (session) => {
const reply = await session.promptSync('1+1=?');
console.log(reply);
}); // session 自动 stop4. 浏览器(<script> 标签)
<script src="https://unpkg.com/cmdc/dist/index.iife.global.js"></script>
<script>
const client = new CMDC.CMDCClient({
baseUrl: 'http://localhost:4000',
apiKey: 'sk-abc123',
});
async function run() {
const session = await client.createSession({ model: 'deepseek:deepseek-chat' });
for await (const event of session.prompt('你好')) {
if (event.type === 'message_delta') {
document.getElementById('output').textContent += event.data.delta;
}
}
await session.stop();
}
run();
</script>API 参考
CMDCClient
import { CMDCClient } from 'cmdc';
const client = new CMDCClient({
baseUrl: 'http://localhost:4000', // Gateway 地址
apiKey: 'sk-abc123', // API Key
});方法
| 方法 | 说明 |
|------|------|
| createSession(options) | 创建 Session,返回 Session 实例 |
| withSession(options, fn) | 创建 Session,回调结束后自动 stop |
| health() | 检查 Gateway 健康状态 |
SessionOptions
const session = await client.createSession({
model: 'deepseek:deepseek-chat', // 必填
sessionId: 'my-session-001', // 自定义 ID(可选)
systemPrompt: '你是助手', // 系统提示词
workingDir: '/home/user/project', // 工具工作目录
tools: ['CMDC.Tool.Shell'], // 启用的工具模块
plugins: ['CMDC.Plugin.Builtin.SecurityGuard'], // 插件
blueprint: 'CMDC.Blueprint.Base', // 蓝图模块
maxTurns: 50, // 最大轮次
maxTokens: 4096, // 最大输出 token
skillsDirs: ['/skills'], // Skill 目录
providerOpts: { temperature: 0.7 }, // Provider 额外参数
});Session
发送消息
// 方式一:AsyncGenerator 流式迭代
for await (const event of session.prompt('你好')) {
if (event.type === 'message_delta') {
process.stdout.write(event.data.delta);
}
}
// 方式二:阻塞等待完整回复
const reply = await session.promptSync('简单回答:1+1=?');
console.log(reply); // "2"
// 方式三:仅投递,不监听
const { requestId } = await session.sendPrompt('开始任务');EventEmitter 模式
// 注册 token 事件处理器
session.on('token', (delta) => {
process.stdout.write(delta as string);
});
// 注册所有事件
session.on('event', (event) => {
console.log((event as Event).type);
});
// 注册特定事件类型
session.on('agent_end', (event) => {
const e = event as AgentEndEvent;
console.log(`完成,共 ${e.data.tokenUsage.totalTokens} tokens`);
});
// 触发 EventEmitter 的同时迭代
for await (const event of session.prompt('你好')) {
// token 事件已通过 on() 处理
}控制操作
await session.approve('apr_x7y8z9'); // 审批通过
await session.reject('apr_x7y8z9'); // 审批拒绝
await session.respond('ask_001', '快速排序'); // 回答提问
await session.stop(); // 停止 Agent查询
const info = await session.getInfo(); // SessionInfo
const stats = await session.getStats(); // Stats(含 meter 用量)
const messages = await session.getMessages(); // Message[]事件类型(15 种)
import type { Event, MessageDeltaEvent, AgentEndEvent, ApprovalRequiredEvent } from 'cmdc';
for await (const event of session.prompt('你好')) {
switch (event.type) {
case 'agent_start':
console.log('Agent 开始处理');
break;
case 'message_delta':
process.stdout.write(event.data.delta);
break;
case 'thinking_delta':
console.log('[思考]', event.data.delta);
break;
case 'tool_execution_start':
console.log(`工具 ${event.data.toolName} 开始执行`);
break;
case 'tool_execution_end':
console.log(`工具完成: ${event.data.status}`);
break;
case 'approval_required':
await session.approve(event.data.approvalId);
break;
case 'ask_user':
await session.respond(event.data.ref, '我的回答');
break;
case 'agent_end':
console.log(`\n完成,共 ${event.data.tokenUsage.totalTokens} tokens`);
break;
case 'error':
console.error('错误:', event.data.reason);
break;
}
}| 事件类型 | data 字段 |
|---------|----------|
| agent_start | {} |
| agent_end | messageCount, lastMessage, tokenUsage |
| agent_abort | reason |
| prompt_received | text |
| message_start | {} |
| message_delta | delta |
| thinking_start | {} |
| thinking_delta | delta |
| tool_calls | count |
| tool_execution_start | toolName, callId, args |
| tool_execution_end | toolName, callId, status, result |
| approval_required | approvalId, toolName, args, hint |
| approval_resolved | approvalId, status |
| ask_user | ref, question, options |
| error | reason |
HITL 审批
// 内置策略
for await (const event of session.prompt('执行任务', {
approvalPolicy: 'auto_approve', // 或 'auto_reject'
})) { ... }
// 自定义策略(callable)
for await (const event of session.prompt('执行任务', {
approvalPolicy: (e) => e.data.toolName !== 'Shell',
})) { ... }
// 异步策略
for await (const event of session.prompt('执行任务', {
approvalPolicy: async (e) => {
const confirmed = await askUser(`允许执行 ${e.data.toolName}?`);
return confirmed;
},
})) { ... }WebSocket 双向连接
const ws = session.connectWs();
await ws.connect();
// 发送 prompt
await ws.sendPrompt('执行危险操作');
// 迭代接收所有事件
for await (const event of ws.iterEvents()) {
switch (event.type) {
case 'approval_required':
console.log(`需要审批: ${event.data.toolName}`);
await ws.approve(event.data.approvalId);
break;
case 'message_delta':
process.stdout.write(event.data.delta);
break;
case 'agent_end':
ws.close();
break;
}
}本地工具注册
await session.registerTool({
name: 'search_database',
description: '在本地数据库中搜索信息',
parameters: {
type: 'object',
properties: {
query: { type: 'string', description: '搜索关键词' },
},
required: ['query'],
},
callback: async (args) => {
// 本地执行逻辑
return `查询结果: ${args.query}`;
},
callbackUrl: 'http://my-service:8080/tools/search_database',
});错误处理
import {
CMDCError,
AuthError,
SessionNotFoundError,
RateLimitError,
SessionCreateError,
} from 'cmdc';
try {
const session = await client.createSession({ model: 'unknown:model' });
} catch (err) {
if (err instanceof AuthError) {
console.error('API Key 无效');
} else if (err instanceof SessionCreateError) {
console.error('创建失败:', err.message);
} else if (err instanceof RateLimitError) {
console.error(`超出限流,${err.retryAfter}s 后重试`);
} else if (err instanceof CMDCError) {
console.error(`错误 [${err.statusCode}]: ${err.message}`);
}
}构建产物
| 文件 | 格式 | 用途 |
|------|------|------|
| dist/index.js | ESM | Node.js import / 现代打包工具 |
| dist/index.cjs | CJS | Node.js require / CommonJS |
| dist/index.iife.global.js | IIFE | 浏览器 <script> 标签直接使用 |
| dist/index.d.ts | TypeScript 类型 | IDE 自动补全 |
开发
npm install
npm run build # 构建三格式产物
npm test # 运行测试(44 个用例)
npm run typecheck # TypeScript 类型检查环境变量
const client = new CMDCClient({
baseUrl: process.env.CMDC_BASE_URL ?? 'http://localhost:4000',
apiKey: process.env.CMDC_API_KEY!,
});