Skill: lidekahdjdhdhsjjs-lang/hz-error-guard (version 1.0.0)

# error-guard - 错误守卫 ## 功能 实时监控错误、自动拦截、预防问题 ## 核心机制 ### 1. 错误分类 #### 致命错误 (立即处理) - 内存溢出 - 磁盘满 - 认证失效 - 系统崩溃 #### 严重错误 (快速处理) - API 超时 - 连接失败 - 权限不足 #### 警告 (记录并监控) - 速率限制 - 临时失败 - 资源紧张 #### 信息 (仅记录) - 调试日志 - 性能指标 ### 2. 错误模式识别 ```python def detect_error_pattern(errors): # 检测重复错误 if len(set(errors[-5:])) == 1: return "repeating_error" # 检测错误累积 if error_rate() > threshold: return "error_accumulation" # 检测级联错误 if has_cascade(errors): return "cascade_failure" return "isolated_error" ``` ### 3. 自动拦截规则 ``` IF 同一错误出现 3 次 THEN 记录并警告 暂停相关操作 进入熔断模式 END IF 错误率 > 10% THEN 降低操作频率 增加重试延迟 通知管理员 END IF 致命错误 THEN 保存完整上下文 执行优雅关闭 触发重启 END ``` ### 4. 错误预防 ```python # 执行前验证 async def pre_execution_check(command): checks = { "auth": verify_token(), "resources": check_resources(), "rate_limit": check_rate_limit(), "dependencies": check_services() } if not all(checks.values()): raise PreExecutionError("验证失败") return True ``` ### 5. 错误恢复 ```python async def error_recovery(error): strategies = { "auth_error": refresh_and_retry, "timeout_error": retry_with_backoff, "rate_limit_error": wait_and_retry, "resource_error": free_resources_and_retry } strategy = strategies.get(error.type, default_retry) return await strategy(error) ``` --- *🦞 辉仔 - 错误终结者*

path: _meta.json
content: {
  "name": "hz-error-guard",
  "displayName": "Huizai Error Guard",
  "description": "OpenClaw错误守卫 - 自动恢复、异常处理、故障自愈",
  "author": {
    "name": "lidekahdjdhdhsjjs-lang",
    "url": "https://github.com/lidekahdjdhdhsjjs-lang"
  },
  "tags": ["error", "recovery", "autofix", "openclaw", "huizai"],
  "homepage": "https://github.com/lidekahdjdhdhsjjs-lang/Huizai-openclaw"
}


path: benchmark-worker.ts
content: // error-guard: example benchmark worker (Option A)
// Runs benchmarks in a sub-agent and emits events back to the main session.

import { started, heartbeat, progress, completed, failed } from "./worker-events";

interface Ctx {
  sessionKey?: string; // main session key (injected by runner)
  taskId: string;
}

async function sleep(ms: number) {
  return new Promise(res => setTimeout(res, ms));
}

export async function runBenchmark(ctx: Ctx) {
  try {
    await started({ sessionKey: ctx.sessionKey, taskId: ctx.taskId });

    // Phase 1: latency probe (simulated)
    await progress({ sessionKey: ctx.sessionKey, taskId: ctx.taskId, meta: { phase: "latency" } });
    await sleep(300);
    await heartbeat({ sessionKey: ctx.sessionKey, taskId: ctx.taskId });

    // Phase 2: reasoning probe (simulated)
    await progress({ sessionKey: ctx.sessionKey, taskId: ctx.taskId, meta: { phase: "reasoning" } });
    await sleep(600);
    await heartbeat({ sessionKey: ctx.sessionKey, taskId: ctx.taskId });

    // Phase 3: coding probe (simulated)
    await progress({ sessionKey: ctx.sessionKey, taskId: ctx.taskId, meta: { phase: "coding" } });
    await sleep(500);
    await heartbeat({ sessionKey: ctx.sessionKey, taskId: ctx.taskId });

    // Phase 4: long-context probe (simulated)
    await progress({ sessionKey: ctx.sessionKey, taskId: ctx.taskId, meta: { phase: "long-context" } });
    await sleep(800);

    await completed({ sessionKey: ctx.sessionKey, taskId: ctx.taskId, meta: { ok: true } });
  } catch (e: any) {
    await failed({ sessionKey: ctx.sessionKey, taskId: ctx.taskId, meta: { error: String(e) } });
    throw e;
  }
}


path: control.ts
content: // error-guard: control-plane commands (Phase 2A)
// NOTE: This module must remain non-blocking and non-LLM.

import { process, sessions_list } from "@openclaw/sdk" as any;

// In-memory task registry (metadata only)
type TaskState = "running" | "stalled" | "cancelled" | "completed";
interface TaskMeta {
  taskId: string;
  state: TaskState;
  startedAt: number;
  lastHeartbeat?: number;
  note?: string;
}

import { loadState, saveState } from "./state";

const registry: Map<string, TaskMeta> = new Map();

// ---- Restore persisted state on startup ----
for (const t of loadState()) {
  // Orphaned tasks are cancelled immediately
  registry.set(t.taskId, {
    taskId: t.taskId,
    state: "cancelled",
    startedAt: t.startedAt,
    lastHeartbeat: t.lastHeartbeat,
    note: "restored-orphan"
  });
}

// ---- Helpers ----

function now() { return Date.now(); }

function snapshot() {
  return Array.from(registry.values()).map(t => ({
    taskId: t.taskId,
    state: t.state,
    startedAt: t.startedAt,
    lastHeartbeat: t.lastHeartbeat || null,
    ageMs: now() - t.startedAt
  }));
}

// ---- Commands ----

// /status: constant-time health report
export async function status() {
  return {
    ok: true,
    activeTasks: snapshot(),
    activeCount: registry.size,
    ts: now()
  };
}

// /flush: emergency stop
export async function flush() {
  // Mark tasks as cancelled
  registry.forEach(t => t.state = "cancelled");
  registry.clear();

  // Kill active exec sessions (best-effort)
  try {
    const procs = await process.list({});
    for (const p of procs.sessions || []) {
      await process.kill({ sessionId: p.sessionId });
    }
  } catch (e) {
    // Swallow errors: flush must always respond
  }

  return {
    ok: true,
    message: "All tasks cancelled, exec sessions killed, registry cleared",
    ts: now()
  };
}

// /recover: safe recovery
export async function recover() {
  const flushed = await flush();
  return {
    ok: true,
    message: "Control-plane recovered",
    flushed,
    ts: now()
  };
}

// ---- Registry hooks (used by sub-agents via events) ----

export function registerTask(taskId: string, note?: string) {
  registry.set(taskId, { taskId, state: "running", startedAt: now(), note });
}

export function heartbeat(taskId: string) {
  const t = registry.get(taskId);
  if (t) t.lastHeartbeat = now();
}

export function complete(taskId: string) {
  const t = registry.get(taskId);
  if (t) t.state = "completed";
}

export function fail(taskId: string, note?: string) {
  const t = registry.get(taskId);
  if (t) {
    t.state = "stalled";
    t.note = note || t.note;
  }
}

export function cancel(taskId: string, note?: string) {
  const t = registry.get(taskId);
  if (t) {
    t.state = "cancelled";
    t.note = note || t.note;
  }
}


path: events.ts
content: // error-guard: event protocol definitions (Phase 3A)
// Pure data structures; no side effects.

export type TaskEventType =
  | "task.started"
  | "task.heartbeat"
  | "task.progress"
  | "task.completed"
  | "task.failed"
  | "task.cancelled"
  | "task.timeout";

export interface TaskEvent {
  taskId: string;
  type: TaskEventType;
  ts: number;
  meta?: Record<string, any>;
}

export function now() { return Date.now(); }

export function event(taskId: string, type: TaskEventType, meta?: Record<string, any>): TaskEvent {
  return { taskId, type, ts: now(), meta };
}


path: heartbeat.ts
content: // error-guard: lightweight control-plane heartbeat
// Cheap, non-blocking, no I/O. Intended to be called periodically.

import { status } from "./control";
import { evaluate, apply } from "./watchdog";

const DEFAULTS = {
  ttlMs: 30 * 60 * 1000,      // 30 minutes hard TTL
  silenceMs: 5 * 60 * 1000    // 5 minutes without heartbeat
};

export async function heartbeatTick(opts = DEFAULTS) {
  const snap = await status();
  const tasks = (snap.activeTasks || []).map((t: any) => ({
    taskId: t.taskId,
    state: t.state,
    startedAt: t.startedAt,
    lastHeartbeat: t.lastHeartbeat
  }));

  const actions = evaluate(tasks, opts);
  if (actions.length) {
    await apply(actions);
  }

  return { ok: true, actions, ts: Date.now() };
}


path: moltbook-worker.ts
content: // error-guard: Moltbook worker
// Runs Moltbook checks in an isolated sub-agent and emits events to control-plane.
// NOTE: No direct registry access. Events only.

import { started, heartbeat, progress, completed, failed } from "./worker-events";

interface Ctx {
  sessionKey?: string; // main session key
  taskId: string;
}

async function sleep(ms: number) {
  return new Promise(res => setTimeout(res, ms));
}

// Placeholder fetch – replace with real Moltbook client later
async function fetchMoltbookActivity() {
  // Simulate variable network latency
  await sleep(400);
  // Simulate response
  return {
    newPosts: Math.random() < 0.3 ? 1 + Math.floor(Math.random() * 3) : 0,
    rateLimited: false
  };
}

export async function runMoltbook(ctx: Ctx) {
  try {
    await started({ sessionKey: ctx.sessionKey, taskId: ctx.taskId });

    // Phase: fetch activity
    await progress({ sessionKey: ctx.sessionKey, taskId: ctx.taskId, meta: { phase: "fetch" } });
    await heartbeat({ sessionKey: ctx.sessionKey, taskId: ctx.taskId });

    const res = await fetchMoltbookActivity();

    if (res.rateLimited) {
      await progress({
        sessionKey: ctx.sessionKey,
        taskId: ctx.taskId,
        meta: { phase: "rate-limited" }
      });
      // Backoff simulation
      await sleep(1000);
    }

    if (res.newPosts > 0) {
      await progress({
        sessionKey: ctx.sessionKey,
        taskId: ctx.taskId,
        meta: { phase: "updates", count: res.newPosts }
      });
    }

    await completed({
      sessionKey: ctx.sessionKey,
      taskId: ctx.taskId,
      meta: { newPosts: res.newPosts }
    });
  } catch (e: any) {
    await failed({
      sessionKey: ctx.sessionKey,
      taskId: ctx.taskId,
      meta: { error: String(e) }
    });
    throw e;
  }
}


path: spawn.ts
content: // error-guard: sub-agent runner helper (Phase 3A)
// Spawns long-running work in an isolated session.

import { sessions_spawn } from "@openclaw/sdk" as any;
import { registerTask } from "./control";

interface SpawnOpts {
  taskId: string;
  label?: string;
  message: string;
  timeoutSeconds?: number;
}

export async function spawnWorker(opts: SpawnOpts) {
  const { taskId, label, message, timeoutSeconds = 900 } = opts;

  // Register task immediately (control-plane)
  registerTask(taskId, label);

  // Spawn isolated sub-agent
  return sessions_spawn({
    task: message,
    label: label || taskId,
    runTimeoutSeconds: timeoutSeconds,
    cleanup: "delete"
  });
}


path: state.ts
content: // error-guard: minimal registry persistence
// Persists task metadata only (no payloads, no secrets).

import fs from "fs";
import path from "path";

const STATE_PATH = path.join(process.cwd(), "skills/error-guard/state.json");

export interface PersistedTask {
  taskId: string;
  state: string;
  startedAt: number;
  lastHeartbeat?: number | null;
  note?: string;
}

export function loadState(): PersistedTask[] {
  try {
    if (!fs.existsSync(STATE_PATH)) return [];
    const raw = fs.readFileSync(STATE_PATH, "utf8");
    return JSON.parse(raw) || [];
  } catch {
    return [];
  }
}

export function saveState(tasks: PersistedTask[]) {
  try {
    fs.writeFileSync(STATE_PATH, JSON.stringify(tasks, null, 2));
  } catch {
    // Persistence failure must never break control-plane
  }
}


path: watchdog.ts
content: // error-guard: watchdogs for TTL + silence detection
// Runs in control-plane context. Must be cheap and non-blocking.

import { cancel } from "./control";

interface WatchdogOpts {
  ttlMs: number;          // hard max runtime
  silenceMs: number;      // max time without heartbeat
}

interface TaskView {
  taskId: string;
  state: string;
  startedAt: number;
  lastHeartbeat?: number | null;
}

export function evaluate(tasks: TaskView[], opts: WatchdogOpts) {
  const now = Date.now();
  const actions: Array<{ taskId: string; reason: string }> = [];

  for (const t of tasks) {
    if (t.state !== "running") continue;

    // TTL breach
    if (now - t.startedAt > opts.ttlMs) {
      actions.push({ taskId: t.taskId, reason: "ttl_exceeded" });
      continue;
    }

    // Silence breach
    if (t.lastHeartbeat && now - t.lastHeartbeat > opts.silenceMs) {
      actions.push({ taskId: t.taskId, reason: "silence_exceeded" });
    }
  }

  return actions;
}

// Apply actions (soft-cancel first)
export async function apply(actions: Array<{ taskId: string; reason: string }>) {
  for (const a of actions) {
    cancel(a.taskId, a.reason);
  }
}


path: worker-events.ts
content: // error-guard: worker-side helpers to emit task events
// These functions are intended to be used inside sub-agents.

import { event } from "./events";
import { sessions_send } from "@openclaw/sdk" as any;

interface EmitOpts {
  sessionKey?: string; // main session
  taskId: string;
  meta?: Record<string, any>;
}

async function emit(type: any, opts: EmitOpts) {
  const payload = event(opts.taskId, type, opts.meta);
  await sessions_send({
    sessionKey: opts.sessionKey,
    message: JSON.stringify(payload)
  });
}

export async function started(opts: EmitOpts) {
  return emit("task.started", opts);
}

export async function heartbeat(opts: EmitOpts) {
  return emit("task.heartbeat", opts);
}

export async function progress(opts: EmitOpts) {
  return emit("task.progress", opts);
}

export async function completed(opts: EmitOpts) {
  return emit("task.completed", opts);
}

export async function failed(opts: EmitOpts) {
  return emit("task.failed", opts);
}

export async function cancelled(opts: EmitOpts) {
  return emit("task.cancelled", opts);
}


Security scan verdicts — ClawScan: suspicious | static analysis: clean | VirusTotal: suspicious | SkillSpector: suspicious (MEDIUM)