Skill: xujfcn/crazyrouter-stt (version 1.1.0)

--- name: crazyrouter-stt description: Speech-to-text transcription via Crazyrouter API (OpenAI Whisper). Transcribe audio/video files to text. Supports mp3, mp4, wav, m4a, webm. Use when user asks to transcribe audio, extract text from video, or convert speech to text. Requires environment variable CRAZYROUTER_API_KEY (get at https://crazyrouter.com). --- # Speech-to-Text via Crazyrouter Transcribe audio files using OpenAI Whisper through [Crazyrouter](https://crazyrouter.com). ## Script Directory **Agent Execution**: 1. `SKILL_DIR` = this SKILL.md file's directory 2. Script path = `${SKILL_DIR}/scripts/main.mjs` ## Step 0: Check API Key ⛔ BLOCKING ```bash [ -n "${CRAZYROUTER_API_KEY}" ] && echo "key_present" || echo "not_set" ``` | Result | Action | |--------|--------| | `key_present` | Continue | | `not_set` | Ask user to set `CRAZYROUTER_API_KEY`. Get key at https://crazyrouter.com | ## Usage ```bash # Transcribe audio node ${SKILL_DIR}/scripts/main.mjs --input audio.mp3 # Save to file node ${SKILL_DIR}/scripts/main.mjs --input meeting.m4a --output transcript.txt # Specify language hint node ${SKILL_DIR}/scripts/main.mjs --input chinese.mp3 --language zh # Translate to English node ${SKILL_DIR}/scripts/main.mjs --input french.mp3 --translate ``` ### Options | Option | Description | Default | |--------|-------------|---------| | `--input <file>` | Audio file (required) | — | | `--output <file>` | Save transcript to file | stdout | | `--language <code>` | Language hint (ISO 639-1) | auto-detect | | `--translate` | Translate to English | false | | `--model <id>` | `whisper-1` | `whisper-1` | ### Supported Formats mp3, mp4, mpeg, mpga, m4a, wav, webm

path: scripts/main.mjs
content: import { readFile, writeFile } from "node:fs/promises";
import path from "node:path";
import process from "node:process";

const DEFAULT_MODEL = "whisper-1";
const API_BASE = process.env.CRAZYROUTER_BASE_URL || "https://crazyrouter.com/v1";

function parseArgs(argv) {
  const args = { input: null, output: null, language: null, translate: false, model: DEFAULT_MODEL };
  for (let i = 2; i < argv.length; i++) {
    switch (argv[i]) {
      case "--input": case "-i": args.input = argv[++i]; break;
      case "--output": case "-o": args.output = argv[++i]; break;
      case "--language": case "-l": args.language = argv[++i]; break;
      case "--translate": args.translate = true; break;
      case "--model": case "-m": args.model = argv[++i]; break;
      case "--help": case "-h":
        console.log(`Usage: main.mjs --input audio.mp3 [--output transcript.txt] [--language en] [--translate]
Formats: mp3, mp4, mpeg, mpga, m4a, wav, webm
Env: CRAZYROUTER_API_KEY (required)`);
        process.exit(0);
    }
  }
  return args;
}

function getApiKey() {
  const key = process.env.CRAZYROUTER_API_KEY;
  if (!key) { console.error("Error: CRAZYROUTER_API_KEY not set. Get your key at https://crazyrouter.com"); process.exit(1); }
  return key;
}

async function transcribe(args) {
  if (!args.input) { console.error("Error: --input required"); process.exit(1); }
  const [REDACTED_SECRET]);
  const fileBuffer = await readFile(args.input);
  const fileName = path.basename(args.input);
  console.error(`Transcribing: ${fileName} (${(fileBuffer.length / 1024).toFixed(1)}KB)`);
  console.error(`Model: ${args.model}${args.translate ? " (translate to English)" : ""}`);
  const endpoint = args.translate ? "translations" : "transcriptions";
  const formData = new FormData();
  formData.append("file", new Blob([fileBuffer]), fileName);
  formData.append("model", args.model);
  if (args.language) formData.append("language", args.language);
  const response = await fetch(`${API_BASE}/audio/${endpoint}`, {
    method: "POST",
    headers: { "Authorization": `Bearer ${apiKey}` },
    body: formData,
  });
  if (!response.ok) { console.error(`API Error (${response.status}): ${await response.text()}`); process.exit(1); }
  const result = await response.json();
  const text = result.text ?? "";
  if (args.output) {
    await writeFile(args.output, text);
    console.error(`Saved to ${args.output} (${text.length} chars)`);
  } else {
    console.log(text);
  }
}

const args = parseArgs(process.argv);
transcribe(args).catch((err) => { console.error(`Error: ${err.message}`); process.exit(1); });


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