Token导航 LogoToken导航

Agent Skill · 前端设计

dead-letter-queue(死信队列)

dead-letter-queue 用于处理 GitHub 仓库、Issue、Pull Request 和代码协作信息,适合在 Codex、Claude、Cursor、Gemini CLI 中需要围绕仓库状态、代码变更或协作事项进行整理时使用。可结合来源仓库、安装命令和原始 README 继续核验具体用法。安装前建议确认权限范围、维护状态,以及是否会触发联网、命令执行或文件读写。

dead-letter-queue 是前端设计类 Agent Skill,可用宿主为 Codex、Claude、Cursor、Gemini CLI,通过GitHub 仓库获取,权限为权限未标注;许可证信息还不明确,第三方扫描已通过,按安装量在前端设计类的 10,216 个 Skill 里排第 6,094。

总安装 558周安装 23GitHub Stars 777下载 182核验 2026-05-01

安装说明

本站只整理中文说明,不托管安装包,也不代用户安装。
GitHub
许可证
unknown

安装方式

通过对话安装

复制提示词发给支持本地命令或 Skills 的 AI 助手,先确认命令和权限,再让它执行。

请帮我安装这个 Agent Skill:dead-letter-queue(死信队列)
来源仓库:https://github.com/dadbodgeoff/drift
仓库路径:skills/dead-letter-queue
安装命令:
npx skills add https://github.com/dadbodgeoff/drift --skill dead-letter-queue
安装前请先检查当前环境是否支持对应 CLI,并向我确认将要执行的命令、安装目录、联网范围和文件读写权限;确认后再执行。

命令行安装

复制命令到本机终端执行。该命令会通过 npx skills 从第三方来源获取 Skill;本站只展示命令,不托管安装包,也不自动执行。

skills.shnpx skills
npx skills add https://github.com/dadbodgeoff/drift --skill dead-letter-queue

这个 Skill 是做什么的

前端设计类 Skill 关注页面结构、视觉规范和组件写法,输出通常是可运行的界面代码。

失败任务暂存与重放系统,为消息队列故障排查提供调试支持。适合需要保留失败上下文、手动重试或分析错误模式的异步任务场景。记录完整payload、错误类型和尝试次数等关键调试信息。需配合消息中间件使用,建议设置TTL防止存储空间无限增长。

dead-letter-queue 属于前端设计类 Skill,可作为该场景下的辅助能力补充。

来源仓库内路径:skills/dead-letter-queue

上手步骤

4 步
  1. 1

    确认自己的宿主工具

    公开资料标注支持 Codex、Claude、Cursor、Gemini CLI;先确认自己用的是其中之一,再按对应工具的 Skills 目录放置。

  2. 2

    拿到安装命令

    可以直接复制的命令是 npx skills add https://github.com/dadbodgeoff/drift --skill dead-letter-queue;命令里的占位符换成自己的路径或密钥再执行。

  3. 3

    安装后重启宿主

    命令执行完要完全退出宿主工具再打开,让新装的 Skill 被重新扫描到,否则列表里可能看不到。

  4. 4

    按最小权限试用

    权限没有明确标注,第一次使用时只给一个无关紧要的任务,观察它到底读了哪些文件和网络。

常见问题

6 条

dead-letter-queue 怎么安装?

在终端执行 npx skills add https://github.com/dadbodgeoff/drift --skill dead-letter-queue。装完完全退出宿主工具再打开,让它重新扫描本地 Skill。

dead-letter-queue 支持哪些 AI 工具?

公开资料标注支持 Codex、Claude、Cursor、Gemini CLI。其他工具只要能读取本地 Skill 目录,一般也能手动放进去,但要自己确认触发方式是否一致。

dead-letter-queue 需要什么权限?

权限级别没有明确标注。第一次使用时建议只给无关紧要的任务,观察它实际读写了哪些内容。

dead-letter-queue 是免费的吗?

公开资料没有标注明确许可证,收费和商用限制都无法确认。正式使用前请到来源仓库核对授权说明。

dead-letter-queue 用的人多吗?

公开数据是累计安装 558 次,近一周安装 23 次,下载 182 次,GitHub 777 星。这些数字来自第三方目录统计,只能当作热度参考,不代表质量或安全性。

dead-letter-queue 安全吗?

第三方扫描结果为通过,但这只覆盖已知的静态检查项,不能替代自己读一遍源码和安装命令。

值得选的地方和要先想清楚的地方

值得选的地方

  • 同时适配 4 种工具,换客户端不用重新找替代品。
  • 第三方扫描已通过,常见的高风险行为没有命中。

先想清楚的地方

  • 许可证没有明确标注,能不能商用、能不能再分发都不确定。
  • 第三方目录整理,版本更新和行为细节以来源仓库为准。

适不适合你

更适合

  • 已经在用 Codex,想直接扩展它能力的人。
  • 需要处理前端设计类任务、又不想每次都手写提示词的人。

不太适合

  • 需要明确授权才能用到公司项目里的场景。
  • 需要厂商级技术支持和 SLA 的生产系统。

前端设计类 Skill 对比

6 个同类
Skill总安装GitHub 星权限许可证扫描
frontend-design

前端设计 · Codex、Claude

8,823,360126,072只读MIT通过
vercel-composition-patterns

Vercel 组合模式 · Codex、Claude

3,858,81625,893需要联网MIT通过
vercel-react-native-skills

Vercel React native skills 前端 · Codex、Claude

2,468,66425,878只读MIT通过
adapt

adapt 搜索 · Codex、Claude

2,027,04023,125操作浏览器未明确通过
bolder

bolder 分析 · Codex、Claude

2,004,28823,130只读未明确通过
dead-letter-queue

死信队列 · 本页

558777权限未标注未明确通过

按安装量取同分类前几名;数字来自第三方目录统计,只作热度参考,不代表质量结论。

SKILL.md 原文

Dead Letter Queue

Store failed jobs for replay and debugging.

When to Use This Skill

  • Jobs fail after max retries
  • Need visibility into failure patterns
  • Want to replay failed jobs manually
  • Can't afford to lose failed work

Core Concepts

  1. Capture context - Store enough info to replay
  2. Track attempts - Record all error messages
  3. Enable replay - Allow manual re-processing
  4. Enforce limits - Prevent unbounded growth

TypeScript Implementation

// dead-letter-queue.ts
interface DeadLetterJob {
  id: string;
  workerName: string;
  payload: Record<string, unknown>;
  errorMessage: string;
  errorType: string;
  stackTrace?: string;
  attempts: number;
  attemptErrors: string[];
  firstAttemptAt: Date;
  lastAttemptAt: Date;
  createdAt: Date;
  resolvedAt?: Date;
  resolution?: string;
}

class DeadLetterQueue {
  private jobs = new Map<string, DeadLetterJob>();
  private maxSize = 1000;
  private counter = 0;

  add(
    workerName: string,
    payload: Record<string, unknown>,
    errorMessage: string,
    errorType: string,
    attempts: number,
    stackTrace?: string
  ): DeadLetterJob {
    const id = `dlq_${++this.counter}_${Date.now()}`;
    const now = new Date();

    const job: DeadLetterJob = {
      id,
      workerName,
      payload,
      errorMessage,
      errorType,
      stackTrace,
      attempts,
      attemptErrors: [errorMessage],
      firstAttemptAt: now,
      lastAttemptAt: now,
      createdAt: now,
    };

    this.jobs.set(id, job);
    this.enforceMaxSize();

    console.log(`[DLQ] Added: ${id} (${workerName})`);
    return job;
  }

  recordAttempt(jobId: string, errorMessage: string): boolean {
    const job = this.jobs.get(jobId);
    if (!job) return false;

    job.attempts++;
    job.lastAttemptAt = new Date();
    job.attemptErrors.push(errorMessage);
    job.errorMessage = errorMessage;
    return true;
  }

  resolve(jobId: string, resolution: string): boolean {
    const job = this.jobs.get(jobId);
    if (!job) return false;

    job.resolvedAt = new Date();
    job.resolution = resolution;
    return true;
  }

  discard(jobId: string): boolean {
    return this.jobs.delete(jobId);
  }

  getUnresolved(): DeadLetterJob[] {
    return Array.from(this.jobs.values())
      .filter(j => !j.resolvedAt)
      .sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());
  }

  getReplayable(maxAttempts = 5): DeadLetterJob[] {
    return this.getUnresolved().filter(j => j.attempts < maxAttempts);
  }

  getByWorker(workerName: string): DeadLetterJob[] {
    return this.getUnresolved().filter(j => j.workerName === workerName);
  }

  getStats() {
    const jobs = Array.from(this.jobs.values());
    const unresolved = jobs.filter(j => !j.resolvedAt);

    const byWorker: Record<string, number> = {};
    const byErrorType: Record<string, number> = {};

    for (const job of unresolved) {
      byWorker[job.workerName] = (byWorker[job.workerName] || 0) + 1;
      byErrorType[job.errorType] = (byErrorType[job.errorType] || 0) + 1;
    }

    return { total: jobs.length, unresolved: unresolved.length, byWorker, byErrorType };
  }

  cleanupResolved(olderThanHours = 24): number {
    const cutoff = Date.now() - olderThanHours * 60 * 60 * 1000;
    let deleted = 0;

    for (const [id, job] of this.jobs) {
      if (job.resolvedAt && job.resolvedAt.getTime() < cutoff) {
        this.jobs.delete(id);
        deleted++;
      }
    }
    return deleted;
  }

  private enforceMaxSize(): void {
    if (this.jobs.size <= this.maxSize) return;

    // Remove oldest resolved first, then oldest unresolved
    const sorted = Array.from(this.jobs.entries())
      .sort((a, b) => {
        if (a[1].resolvedAt && !b[1].resolvedAt) return -1;
        return a[1].createdAt.getTime() - b[1].createdAt.getTime();
      });

    while (sorted.length > this.maxSize) {
      const [id] = sorted.shift()!;
      this.jobs.delete(id);
    }
  }
}

// Singleton
let dlq: DeadLetterQueue | null = null;
export function getDeadLetterQueue(): DeadLetterQueue {
  if (!dlq) dlq = new DeadLetterQueue();
  return dlq;
}

Python Implementation

# dead_letter_queue.py
from dataclasses import dataclass, field
from datetime import datetime
from typing import Dict, List, Optional, Any

@dataclass
class DeadLetterJob:
    id: str
    worker_name: str
    payload: Dict[str, Any]
    error_message: str
    error_type: str
    attempts: int
    attempt_errors: List[str]
    first_attempt_at: datetime
    last_attempt_at: datetime
    created_at: datetime
    stack_trace: Optional[str] = None
    resolved_at: Optional[datetime] = None
    resolution: Optional[str] = None

class DeadLetterQueue:
    def __init__(self, max_size: int = 1000):
        self._jobs: Dict[str, DeadLetterJob] = {}
        self._max_size = max_size
        self._counter = 0

    def add(
        self,
        worker_name: str,
        payload: Dict[str, Any],
        error_message: str,
        error_type: str,
        attempts: int,
        stack_trace: Optional[str] = None,
    ) -> DeadLetterJob:
        self._counter += 1
        job_id = f"dlq_{self._counter}_{int(datetime.now().timestamp())}"
        now = datetime.now()

        job = DeadLetterJob(
            id=job_id,
            worker_name=worker_name,
            payload=payload,
            error_message=error_message,
            error_type=error_type,
            stack_trace=stack_trace,
            attempts=attempts,
            attempt_errors=[error_message],
            first_attempt_at=now,
            last_attempt_at=now,
            created_at=now,
        )

        self._jobs[job_id] = job
        self._enforce_max_size()
        return job

    def record_attempt(self, job_id: str, error_message: str) -> bool:
        job = self._jobs.get(job_id)
        if not job:
            return False

        job.attempts += 1
        job.last_attempt_at = datetime.now()
        job.attempt_errors.append(error_message)
        job.error_message = error_message
        return True

    def resolve(self, job_id: str, resolution: str) -> bool:
        job = self._jobs.get(job_id)
        if not job:
            return False

        job.resolved_at = datetime.now()
        job.resolution = resolution
        return True

    def get_unresolved(self) -> List[DeadLetterJob]:
        return sorted(
            [j for j in self._jobs.values() if not j.resolved_at],
            key=lambda j: j.created_at,
            reverse=True,
        )

    def get_replayable(self, max_attempts: int = 5) -> List[DeadLetterJob]:
        return [j for j in self.get_unresolved() if j.attempts < max_attempts]

    def get_stats(self) -> Dict[str, Any]:
        unresolved = self.get_unresolved()
        by_worker: Dict[str, int] = {}
        by_error: Dict[str, int] = {}

        for job in unresolved:
            by_worker[job.worker_name] = by_worker.get(job.worker_name, 0) + 1
            by_error[job.error_type] = by_error.get(job.error_type, 0) + 1

        return {
            "total": len(self._jobs),
            "unresolved": len(unresolved),
            "by_worker": by_worker,
            "by_error_type": by_error,
        }

    def _enforce_max_size(self):
        if len(self._jobs) <= self._max_size:
            return

        # Sort: resolved first, then by age
        sorted_jobs = sorted(
            self._jobs.items(),
            key=lambda x: (x[1].resolved_at is None, x[1].created_at),
        )

        while len(sorted_jobs) > self._max_size:
            job_id, _ = sorted_jobs.pop(0)
            del self._jobs[job_id]

# Singleton
_dlq: Optional[DeadLetterQueue] = None

def get_dead_letter_queue() -> DeadLetterQueue:
    global _dlq
    if _dlq is None:
        _dlq = DeadLetterQueue()
    return _dlq

Usage Examples

Worker Integration

const dlq = getDeadLetterQueue();
const MAX_RETRIES = 3;

async function processJob(job: Job) {
  try {
    await doWork(job.payload);
  } catch (error) {
    if (job.attempts >= MAX_RETRIES) {
      dlq.add(
        'my-worker',
        job.payload,
        error.message,
        error.name,
        job.attempts,
        error.stack
      );
    } else {
      throw error; // Let retry mechanism handle it
    }
  }
}

Admin Replay

async function replayFailedJobs() {
  const dlq = getDeadLetterQueue();
  const replayable = dlq.getReplayable();

  for (const job of replayable) {
    try {
      await processJob({ payload: job.payload, attempts: 0 });
      dlq.resolve(job.id, 'Replayed successfully');
    } catch (e) {
      dlq.recordAttempt(job.id, e.message);
    }
  }
}

Monitoring Endpoint

app.get('/admin/dlq/stats', (req, res) => {
  const dlq = getDeadLetterQueue();
  res.json(dlq.getStats());
});

app.get('/admin/dlq/jobs', (req, res) => {
  const dlq = getDeadLetterQueue();
  res.json(dlq.getUnresolved());
});

app.post('/admin/dlq/jobs/:id/replay', async (req, res) => {
  const dlq = getDeadLetterQueue();
  const job = dlq.getUnresolved().find(j => j.id === req.params.id);

  if (!job) {
    return res.status(404).json({ error: 'Job not found' });
  }

  // Replay logic...
});

Best Practices

  1. Store full context - Include everything needed to replay
  2. Track all errors - Keep history of attempt failures
  3. Enforce size limits - Prevent memory exhaustion
  4. Expose stats - Monitor failure patterns
  5. Cleanup resolved - Don't keep forever

Common Mistakes

  • Not storing enough context to replay
  • Unbounded queue growth
  • No visibility into failure patterns
  • Forgetting to cleanup old resolved jobs
  • Not tracking attempt history

Related Skills

平台分布

Codex

34.58%
按下载量换算63

Claude

29.26%
按下载量换算53

Cursor

20.6%
按下载量换算37

Gemini CLI

9.27%
按下载量换算17

安全审计

Gen Agent Trust Hub

通过

Socket

通过

Snyk

通过

权限和风险

权限未标注

当前来源未能明确判断权限范围,默认进入异常复核队列。

安装前确认

本站仅展示第三方公开信息,不托管安装包,不提供自动安装或运行环境。安装前应自行审查源码、依赖和命令行为。目前只有一个公开来源,建议同时核对源仓库。

继续浏览同类 Skills