Skill: aaronstuart/tracker-latest-run-monitor (version 1.0.0)

--- name: tracker-latest-run-monitor description: Monitor the most recent run result of a configured OpenClaw cron job and send a compact Feishu private message with the latest execution time, status, and detail. Use when a user wants a standalone skill for latest-run monitoring, cron status notifications, daily status pings, or Feishu alerts for a tracker/scheduled job regardless of success or failure. --- # Tracker Latest Run Monitor Use this skill when you need a standalone latest-run status notifier for a cron job. ## Core files - `scripts/monitor-tracker-runs.js`: Read the target cron job's most recent `finished` run record and send a compact Feishu DM. - `references/configuration.md`: Explain what the script reads and what notification it sends. ## Workflow 1. Ensure the target cron job id and Feishu config are correct in the script or referenced config. 2. Run `scripts/monitor-tracker-runs.js` directly with Node. 3. Let the script read the target cron run JSONL file. 4. Send exactly one Feishu private message for the latest finished run, whether it succeeded or failed. ## Command ```bash node scripts/monitor-tracker-runs.js ``` ## Output behavior The script sends a compact mobile-friendly Feishu text with only: - 最近一次执行时间 - 状态 - 详情 If no finished run exists yet, the script exits quietly.

path: references/configuration.md
content: # Configuration

`monitor-tracker-runs.js` currently targets the Beijing tracker cron job by reading:

- Cron runs file: `/home/SENSETIME/zhangjiazhao/.openclaw/cron/runs/407511f7-5f9f-4a1e-aee9-c2e0764fb5e4.jsonl`
- Feishu config: `/home/SENSETIME/zhangjiazhao/.openclaw/workspace/skills/beijing-signed-price-tracker/projects.json`

The script sends a Feishu private message to `feishu.notifyUserOpenId` using `feishu.appId` and `feishu.appSecret`.

Current notification format is mobile-first and includes only:

- 最近一次执行时间
- 状态
- 详情

If you repoint this skill to a different cron job, update:

- `TARGET_JOB_ID`
- `TARGET_JOB_NAME`
- `RUNS_PATH`
- any config path assumptions


path: scripts/monitor-tracker-runs.js
content: #!/usr/bin/env node

const fs = require('fs');
const path = require('path');

const FEISHU_BASE_URL = 'https://open.feishu.cn';
const CONFIG_PATH = '/home/SENSETIME/zhangjiazhao/.openclaw/workspace/skills/beijing-signed-price-tracker/projects.json';
const TARGET_JOB_ID = '407511f7-5f9f-4a1e-aee9-c2e0764fb5e4';
const TARGET_JOB_NAME = 'beijing-signed-price-tracker-hourly';
const RUNS_PATH = '/home/SENSETIME/zhangjiazhao/.openclaw/cron/runs/407511f7-5f9f-4a1e-aee9-c2e0764fb5e4.jsonl';
const TZ = 'Asia/Shanghai';

function formatShanghaiDateTime(ms) {
  if (!Number.isFinite(ms)) return '';
  const parts = new Intl.DateTimeFormat('en-CA', {
    timeZone: TZ,
    year: 'numeric',
    month: '2-digit',
    day: '2-digit',
    hour: '2-digit',
    minute: '2-digit',
    second: '2-digit',
    hour12: false
  }).formatToParts(new Date(ms));
  const map = Object.fromEntries(parts.filter(part => part.type !== 'literal').map(part => [part.type, part.value]));
  return `${map.year}-${map.month}-${map.day} ${map.hour}:${map.minute}:${map.second}`;
}

function readJson(filePath) {
  return JSON.parse(fs.readFileSync(filePath, 'utf8'));
}

function readJsonLines(filePath) {
  if (!fs.existsSync(filePath)) return [];
  return fs.readFileSync(filePath, 'utf8')
    .split(/\r?\n/)
    .map(line => line.trim())
    .filter(Boolean)
    .map(line => JSON.parse(line));
}

function getLatestFinishedRun(entries) {
  return entries
    .filter(entry => entry?.action === 'finished')
    .sort((a, b) => Number(b?.runAtMs || b?.ts || 0) - Number(a?.runAtMs || a?.ts || 0))[0] || null;
}

async function fetchJson(url, options = {}) {
  const response = await fetch(url, options);
  const text = await response.text();
  let json;
  try {
    json = text ? JSON.parse(text) : {};
  } catch {
    json = { raw: text };
  }
  if (!response.ok || json?.code) {
    throw new Error(json?.msg || json?.message || `HTTP ${response.status}`);
  }
  return json;
}

async function getFeishuAccessToken(appId, appSecret) {
  const json = await fetchJson(`${FEISHU_BASE_URL}/open-apis/auth/v3/tenant_access_token/internal`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json; charset=utf-8' },
    body: JSON.stringify({ app_id: appId, app_secret: appSecret })
  });
  return json.tenant_access_token;
}

async function sendFeishuTextMessage(accessToken, receiveId, text) {
  return fetchJson(`${FEISHU_BASE_URL}/open-apis/im/v1/messages?receive_id_type=open_id`, {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${accessToken}`,
      'Content-Type': 'application/json; charset=utf-8'
    },
    body: JSON.stringify({
      receive_id: receiveId,
      msg_type: 'text',
      content: JSON.stringify({ text })
    })
  });
}

function buildStatusText(latestRun) {
  const time = formatShanghaiDateTime(Number(latestRun?.runAtMs || latestRun?.ts || 0));
  const status = String(latestRun?.status || 'unknown');
  const detail = String(latestRun?.error || latestRun?.summary || '无详细信息');
  const title = status.toLowerCase() === 'ok'
    ? '北京签约跟踪最新状态'
    : '北京签约跟踪执行告警';
  return [
    title,
    `最近一次执行时间：${time}`,
    `状态：${status}`,
    `详情：${detail}`
  ].join('\n');
}

async function main() {
  const config = readJson(CONFIG_PATH);
  const feishu = config?.feishu || {};
  if (!feishu.appId || !feishu.appSecret || !feishu.notifyUserOpenId) {
    throw new Error('projects.json 缺少飞书通知所需配置（appId/appSecret/notifyUserOpenId）');
  }

  const entries = readJsonLines(RUNS_PATH);
  const latestRun = getLatestFinishedRun(entries);

  if (!latestRun) {
    console.log(JSON.stringify({ ok: true, notified: false, reason: 'no-finished-runs', targetJobId: TARGET_JOB_ID, targetJobName: TARGET_JOB_NAME }));
    return;
  }

  const accessToken = await getFeishuAccessToken(feishu.appId, feishu.appSecret);
  const text = buildStatusText(latestRun);
  await sendFeishuTextMessage(accessToken, feishu.notifyUserOpenId, text);
  console.log(JSON.stringify({ ok: true, notified: true, latestRunAt: latestRun.runAtMs || latestRun.ts || null, latestStatus: latestRun.status || 'unknown', targetJobId: TARGET_JOB_ID, targetJobName: TARGET_JOB_NAME }));
}

main().catch(error => {
  console.error(error?.stack || String(error));
  process.exitCode = 1;
});


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