Files
Arcrun/cypher-executor/src/actions/webhook-handlers.ts
T
Leo d04e34ea35 feat(cypher-executor): implicit telemetry (LI SDD M1.2)
對應 .agents/specs/llm-interface/ Milestone 1.2。

新增 lib/telemetry.ts:
- hashApiKey(): SHA-256 截 16 hex 字元(不可逆,可聚合)
- recordTelemetry(): fetch fire-and-forget 寫 KBDB type=agent-telemetry block
- 設計:不阻擋主流程,錯誤 console.warn 不 throw
- 用 ctx.waitUntil 確保即使主 request 已回,背景仍會跑完

寫入點 3 處:
1. routes/webhooks-named.ts POST /webhooks/named (deploy) → deploy_success
2. routes/webhooks-named.ts POST /webhooks/named/:name/trigger →
   executeWebhookGraph 帶 ctx + userAgent,內部記 run_success / run_fail
3. routes/validate.ts POST /validate → validation_error (含 schema_failed / edge_node_missing)

executeWebhookGraph 簽名擴張:可選 ctx + userAgent,舊 caller (scheduled /
trigger_workflow / anonymous webhook) 不傳也 OK(telemetry 仍寫但無 ctx 加持)。

paused (workflow 因 claude_api 等等 callback resume) 算 run_success,
不污染 fail metric。

types.ts: 加 PLATFORM_API_KEY env (可選) + re-export ExecutionContext

不違反「業務邏輯走 WASM」鐵律:telemetry 是 orchestrator 觀測自身的能力,
跟 trigger_workflow / scheduled() 同類。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-16 15:33:35 +08:00

92 lines
3.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import type { Bindings, ExecutionGraph, ExecutionContext } from '../types';
import { ExecutionError } from '../types';
import { GraphExecutor } from '../graph-executor';
import { graphSchema } from '../lib/schemas';
import { createComponentLoader } from '../lib/component-loader';
import { recordTelemetry } from '../lib/telemetry';
type WebhookRecord = {
graph: Record<string, unknown>;
description: string;
created_at: string;
};
export function generateToken(): string {
const tokenBytes = crypto.getRandomValues(new Uint8Array(16));
return Array.from(tokenBytes).map(b => b.toString(16).padStart(2, '0')).join('');
}
export async function validateAndParseWebhook(raw: string): Promise<WebhookRecord | null> {
try {
return JSON.parse(raw) as WebhookRecord;
} catch {
return null;
}
}
export async function executeWebhookGraph(
env: Bindings,
graph: Record<string, unknown>,
triggerContext: Record<string, unknown>,
token: string,
apiKey?: string,
ctx?: ExecutionContext, // 可選 — 用 waitUntil 把 telemetry 推到背景
userAgent?: string, // MCP / SDK client 帶過來
): Promise<{ success: boolean; data?: unknown; error?: string; trace?: unknown; duration_ms: number }> {
const parsed = graphSchema.safeParse(graph);
if (!parsed.success) {
return { success: false, error: '圖定義已失效', duration_ms: 0 };
}
const loader = createComponentLoader(env);
const executor = new GraphExecutor(loader, undefined, env, apiKey);
const start = Date.now();
try {
const result = await executor.execute(
parsed.data as ExecutionGraph,
{ ...triggerContext, _webhook_token: token },
env.EXEC_CONTEXT,
);
const duration_ms = Date.now() - start;
// Implicit telemetry:成功 run(含 paused 也算「成功啟動」由 trigger_workflow 那層分類)
recordTelemetry(env, apiKey, {
event_type: 'run_success',
workflow_name: token,
duration_ms,
agent_user_agent: userAgent,
}, ctx);
return { success: true, data: result.data, duration_ms };
} catch (err) {
const duration_ms = Date.now() - start;
const errMsg = err instanceof Error ? err.message : String(err);
const isPaused = /workflow paused/i.test(errMsg);
// Implicit telemetrypaused 算 run_success;真錯才 run_fail
recordTelemetry(env, apiKey, {
event_type: isPaused ? 'run_success' : 'run_fail',
workflow_name: token,
error_code: isPaused ? 'paused_awaiting_resume' : 'execution_error',
duration_ms,
agent_user_agent: userAgent,
}, ctx);
if (err instanceof ExecutionError) {
const traceFormatted = err.trace.map(s => ({
node: s.nodeId,
status: s.error ? 'failed' : 'success',
...(s.error ? { error: s.error } : {}),
}));
return {
success: false,
error: errMsg,
trace: traceFormatted,
duration_ms,
};
}
return { success: false, error: errMsg, duration_ms };
}
}