| #!/usr/bin/env node |
| |
| |
| |
|
|
| 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 (_) { |
| |
| } |
| } |
|
|
| 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 <recording.zip> [--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); |
| }); |
|
|