#!/usr/bin/env node // Slack analyzer CLI. Usage: // node index.mjs path/to/recording.zip --out result.json // env: OPENAI_API_KEY (required), OPENAI_BASE_URL, OPENAI_MODEL import fs from "node:fs/promises"; import path from "node:path"; import { createRequire } from "node:module"; const require = createRequire(import.meta.url); const importerMod = require("../scripts/slack/importer.js"); const rulesMod = require("../scripts/slack/rules.js"); const clientMod = require("../scripts/slack/client.js"); function loadJSZip() { const candidates = [ "jszip", "../vendor/jszip.min.js", "../discord-cli/node_modules/jszip" ]; for (const candidate of candidates) { try { const mod = require(candidate); return mod && mod.default ? mod.default : mod; } catch (_) { // Try the next fallback. } } throw new Error( "Unable to load JSZip. Run npm install in slack-cli, or keep ../vendor/jszip.min.js available." ); } const JSZip = loadJSZip(); function parseArgs(argv) { const args = { _: [] }; for (let i = 2; i < argv.length; i += 1) { const a = argv[i]; if (a === "--out") args.out = argv[++i]; else if (a === "--help" || a === "-h") args.help = true; else args._.push(a); } return args; } function usage() { process.stdout.write("Usage: node index.mjs [--out result.json]\n"); process.stdout.write("Env: OPENAI_API_KEY (required), OPENAI_BASE_URL, OPENAI_MODEL\n"); } async function main() { const args = parseArgs(process.argv); if (args.help || args._.length < 1) { usage(); process.exit(args.help ? 0 : 1); } const zipPath = path.resolve(args._[0]); const outPath = args.out ? path.resolve(args.out) : null; const apiKey = process.env.OPENAI_API_KEY; if (!apiKey) { process.stderr.write("OPENAI_API_KEY is required\n"); process.exit(2); } const baseURL = process.env.OPENAI_BASE_URL || "https://api.openai.com/v1/responses"; const model = process.env.OPENAI_MODEL || "gpt-5.4"; const buffer = await fs.readFile(zipPath); const parsed = await importerMod.readZip({ arrayBuffer: () => Promise.resolve(buffer), name: path.basename(zipPath) }, JSZip); const evidence = importerMod.buildEvidence(parsed); const flags = rulesMod.runLocalRules(evidence); process.stderr.write(`[info] requests=${evidence.requests.length} endpoints=${Object.keys(evidence.endpoints).length} flags=${flags.length}\n`); const { result } = await clientMod.analyze(evidence, flags, { apiKey, baseURL, apiMode: /chat\/completions/.test(baseURL) ? "chat" : "responses", model }); const optimizedResult = rulesMod.optimizeResult ? rulesMod.optimizeResult(result, evidence) : result; const validated = (optimizedResult.recommended_rubrics || []).map((r) => ({ ...r, _validation: rulesMod.validateCheckerKey(r.checker_key, evidence) })); const finalOut = { ...optimizedResult, recommended_rubrics: validated, local_flags: flags }; const json = JSON.stringify(finalOut, null, 2); if (outPath) { await fs.writeFile(outPath, json); process.stderr.write(`[info] wrote ${outPath}\n`); } else { process.stdout.write(json + "\n"); } } main().catch((e) => { process.stderr.write("[error] " + (e && e.stack ? e.stack : e) + "\n"); process.exit(1); });