Template — Cloudflare Workers 部署
Cloudflare Workers 完整 starter:webhook 接入 + 异步队列 + DO 持久 per-conv 状态。 基于 部署到 Cloudflare Workers 完整教程。
目录结构
hashee-cf-workers/├── src/│ ├── worker.ts # 入口(webhook handler)│ ├── queue.ts # 队列消费者│ └── conversation-do.ts # Durable Object: per-conv 状态├── wrangler.toml├── package.json├── tsconfig.json├── .env.example # 仅供本地 dev 参考;生产用 wrangler secret└── README.mdpackage.json
{ "name": "hashee-cf-workers", "version": "0.1.0", "private": true, "type": "module", "scripts": { "dev": "wrangler dev", "deploy": "wrangler deploy", "tail": "wrangler tail", "secret:put": "wrangler secret put" }, "dependencies": { "@hasheeai/agent-sdk-ts": "^0.2.0" }, "devDependencies": { "@cloudflare/workers-types": "^4.20260317.1", "typescript": "^5.7.0", "wrangler": "^4.0.0" }}wrangler.toml
name = "my-hashee-agent"main = "src/worker.ts"compatibility_date = "2024-09-23"compatibility_flags = ["nodejs_compat"]
[observability]enabled = true
# Durable Object(per-conv 状态机;可选)[[durable_objects.bindings]]name = "CONVO"class_name = "ConversationState"
[[migrations]]tag = "v1"new_classes = ["ConversationState"]
# Queue(异步长任务;可选)[[queues.producers]]queue = "agent-jobs"binding = "JOBS"
[[queues.consumers]]queue = "agent-jobs"max_batch_size = 5max_batch_timeout = 30src/worker.ts
import { createWebhookDispatcher } from "@hasheeai/agent-sdk-ts";export { ConversationState } from "./conversation-do";import { processJob, type Job } from "./queue";
interface Env { HASHEE_AGENT_ID: string; HASHEE_AGENT_TOKEN: string; HASHEE_WEBHOOK_SECRET: string; HASHEE_X25519_PRIVATE_BASE64: string; HASHEE_ED25519_PRIVATE_BASE64: string; CONVO: DurableObjectNamespace; JOBS: Queue<Job>;}
export default { async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> { if (request.method !== "POST" || new URL(request.url).pathname !== "/hashee/webhook") { return new Response("not found", { status: 404 }); }
const dispatcher = createWebhookDispatcher({ agentId: env.HASHEE_AGENT_ID, token: env.HASHEE_AGENT_TOKEN, secret: env.HASHEE_WEBHOOK_SECRET, privateKeyBase64: env.HASHEE_X25519_PRIVATE_BASE64, signingPrivateKeyBase64: env.HASHEE_ED25519_PRIVATE_BASE64,
onMessage: async (msg) => { if (msg.payload?.type !== "text") return;
// 短消息 → 立即 ack if (msg.payload.text.length < 50) { ctx.waitUntil( dispatcher.sendReply(msg.conversation_id, { type: "text", text: "收到,正在处理...", }), ); }
// 长任务 enqueue 异步处理 await env.JOBS.send({ conversation_id: msg.conversation_id, text: msg.payload.text, sender_id: msg.sender_id, }); },
onEvent: async (event) => { if (event.type === "relation.revoked") { // DO 清理 const id = env.CONVO.idFromName(event.payload.conversation_id); await env.CONVO.get(id).fetch("/cleanup", { method: "POST" }); } }, });
try { await dispatcher.handle( Object.fromEntries(request.headers), await request.text(), ); return new Response("ok", { status: 200 }); } catch (err: any) { console.error("[hashee] dispatcher error:", err); return new Response("error", { status: err?.statusCode ?? 500 }); } },
async queue(batch: MessageBatch<Job>, env: Env, ctx: ExecutionContext): Promise<void> { for (const m of batch.messages) { try { await processJob(m.body, env); m.ack(); } catch (e) { console.error("[job] failed:", e); m.retry(); } } },};src/queue.ts
import { restRequest } from "@hasheeai/agent-sdk-ts";
export interface Job { conversation_id: string; text: string; sender_id: string;}
interface Env { HASHEE_AGENT_ID: string; HASHEE_AGENT_TOKEN: string; // ... LLM API key etc}
export async function processJob(job: Job, env: Env): Promise<void> { // 你的 LLM 调用(这里用一个虚拟 echo) const reply = `Echo: ${job.text}`;
await restRequest({ method: "POST", baseUrl: "https://api.hashee.ai", path: `/agents/${env.HASHEE_AGENT_ID}/messages`, token: env.HASHEE_AGENT_TOKEN, body: { conversation_id: job.conversation_id, payload: { type: "text", text: reply }, }, });}src/conversation-do.ts
interface Env {}
export class ConversationState { constructor(private state: DurableObjectState, private env: Env) {}
async fetch(req: Request): Promise<Response> { const url = new URL(req.url); if (url.pathname === "/get") { const data = await this.state.storage.get("data"); return Response.json(data ?? null); } if (url.pathname === "/set" && req.method === "POST") { const body = await req.json(); await this.state.storage.put("data", body); return new Response("ok"); } if (url.pathname === "/cleanup" && req.method === "POST") { await this.state.storage.deleteAll(); return new Response("ok"); } return new Response("not found", { status: 404 }); }}.env.example(仅供本地 dev)
# 生产用 wrangler secret put 设置,不存这里HASHEE_AGENT_ID=HASHEE_AGENT_TOKEN=HASHEE_WEBHOOK_SECRET=HASHEE_X25519_PRIVATE_BASE64=HASHEE_ED25519_PRIVATE_BASE64=部署步骤
# 1. 装 wrangler + 登录npm installnpx wrangler login
# 2. 创建 Queuenpx wrangler queues create agent-jobs
# 3. 设 secretnpx wrangler secret put HASHEE_AGENT_IDnpx wrangler secret put HASHEE_AGENT_TOKENnpx wrangler secret put HASHEE_WEBHOOK_SECRETnpx wrangler secret put HASHEE_X25519_PRIVATE_BASE64npx wrangler secret put HASHEE_ED25519_PRIVATE_BASE64
# 4. 部署npx wrangler deploy
# 输出 URL: https://my-hashee-agent.<subdomain>.workers.dev注册 webhook
在 Hashee app 跟系统 Agent 说:
把 DemoBot 的连接模式改成 webhookURL: https://my-hashee-agent.<subdomain>.workers.dev/hashee/webhooksecret: <你前面 wrangler secret 设的 HASHEE_WEBHOOK_SECRET 值>监控
npx wrangler tail my-hashee-agent