File size: 3,329 Bytes
c4ae742
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
#!/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 <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);
});