aiopt 0.3.8 → 0.3.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -18
- package/dist/cli.js +239 -179
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -330,6 +330,11 @@ var init_code_scan = __esm({
|
|
|
330
330
|
|
|
331
331
|
// src/sarif.ts
|
|
332
332
|
function toUri(p) {
|
|
333
|
+
try {
|
|
334
|
+
const rel = import_path4.default.relative(process.cwd(), import_path4.default.resolve(p)).replace(/\\/g, "/");
|
|
335
|
+
if (rel && !rel.startsWith("..")) return rel;
|
|
336
|
+
} catch {
|
|
337
|
+
}
|
|
333
338
|
const abs = import_path4.default.resolve(p);
|
|
334
339
|
const u = abs.replace(/\\/g, "/");
|
|
335
340
|
return u.match(/^[A-Za-z]:\//) ? `file:///${u}` : `file://${u}`;
|
|
@@ -656,7 +661,7 @@ var require_package = __commonJS({
|
|
|
656
661
|
"package.json"(exports2, module2) {
|
|
657
662
|
module2.exports = {
|
|
658
663
|
name: "aiopt",
|
|
659
|
-
version: "0.3.
|
|
664
|
+
version: "0.3.10",
|
|
660
665
|
description: "Pre-deploy LLM cost accident guardrail (serverless local CLI)",
|
|
661
666
|
bin: {
|
|
662
667
|
aiopt: "dist/cli.js"
|
|
@@ -697,19 +702,19 @@ __export(install_exports, {
|
|
|
697
702
|
runInstall: () => runInstall
|
|
698
703
|
});
|
|
699
704
|
function ensureDir2(p) {
|
|
700
|
-
|
|
705
|
+
import_fs6.default.mkdirSync(p, { recursive: true });
|
|
701
706
|
}
|
|
702
707
|
function writeFile(filePath, content, force) {
|
|
703
|
-
if (!force &&
|
|
704
|
-
ensureDir2(
|
|
705
|
-
|
|
708
|
+
if (!force && import_fs6.default.existsSync(filePath)) return { wrote: false, reason: "exists" };
|
|
709
|
+
ensureDir2(import_path7.default.dirname(filePath));
|
|
710
|
+
import_fs6.default.writeFileSync(filePath, content);
|
|
706
711
|
return { wrote: true };
|
|
707
712
|
}
|
|
708
713
|
function runInstall(cwd, opts) {
|
|
709
714
|
const force = Boolean(opts.force);
|
|
710
|
-
const aioptDir =
|
|
711
|
-
const policiesDir =
|
|
712
|
-
const outDir =
|
|
715
|
+
const aioptDir = import_path7.default.join(cwd, "aiopt");
|
|
716
|
+
const policiesDir = import_path7.default.join(aioptDir, "policies");
|
|
717
|
+
const outDir = import_path7.default.join(cwd, "aiopt-output");
|
|
713
718
|
ensureDir2(aioptDir);
|
|
714
719
|
ensureDir2(policiesDir);
|
|
715
720
|
ensureDir2(outDir);
|
|
@@ -765,7 +770,7 @@ return {
|
|
|
765
770
|
};
|
|
766
771
|
\`\`\`
|
|
767
772
|
`;
|
|
768
|
-
const r1 = writeFile(
|
|
773
|
+
const r1 = writeFile(import_path7.default.join(aioptDir, "README.md"), readme, force);
|
|
769
774
|
created.push({ path: "aiopt/README.md", status: r1.wrote ? "created" : "skipped" });
|
|
770
775
|
const config = {
|
|
771
776
|
version: 1,
|
|
@@ -775,7 +780,7 @@ return {
|
|
|
775
780
|
policies_dir: "./aiopt/policies",
|
|
776
781
|
rate_table: { path: "./rates/rate_table.json" }
|
|
777
782
|
};
|
|
778
|
-
const r2 = writeFile(
|
|
783
|
+
const r2 = writeFile(import_path7.default.join(aioptDir, "aiopt.config.json"), JSON.stringify(config, null, 2) + "\n", force);
|
|
779
784
|
created.push({ path: "aiopt/aiopt.config.json", status: r2.wrote ? "created" : "skipped" });
|
|
780
785
|
const routing = {
|
|
781
786
|
version: 1,
|
|
@@ -806,15 +811,15 @@ return {
|
|
|
806
811
|
reduce_top_percentile: 0.2,
|
|
807
812
|
assumed_reduction_ratio: 0.25
|
|
808
813
|
};
|
|
809
|
-
const p1 = writeFile(
|
|
810
|
-
const p2 = writeFile(
|
|
811
|
-
const p3 = writeFile(
|
|
812
|
-
const p4 = writeFile(
|
|
814
|
+
const p1 = writeFile(import_path7.default.join(policiesDir, "routing.json"), JSON.stringify(routing, null, 2) + "\n", force);
|
|
815
|
+
const p2 = writeFile(import_path7.default.join(policiesDir, "retry.json"), JSON.stringify(retry, null, 2) + "\n", force);
|
|
816
|
+
const p3 = writeFile(import_path7.default.join(policiesDir, "output.json"), JSON.stringify(output, null, 2) + "\n", force);
|
|
817
|
+
const p4 = writeFile(import_path7.default.join(policiesDir, "context.json"), JSON.stringify(context, null, 2) + "\n", force);
|
|
813
818
|
created.push({ path: "aiopt/policies/routing.json", status: p1.wrote ? "created" : "skipped" });
|
|
814
819
|
created.push({ path: "aiopt/policies/retry.json", status: p2.wrote ? "created" : "skipped" });
|
|
815
820
|
created.push({ path: "aiopt/policies/output.json", status: p3.wrote ? "created" : "skipped" });
|
|
816
821
|
created.push({ path: "aiopt/policies/context.json", status: p4.wrote ? "created" : "skipped" });
|
|
817
|
-
const wrapperPath =
|
|
822
|
+
const wrapperPath = import_path7.default.join(aioptDir, "aiopt-wrapper.js");
|
|
818
823
|
const wrapper = `// AIOpt Wrapper (guardrails) \u2014 local-only (CommonJS)
|
|
819
824
|
|
|
820
825
|
const fs = require('fs');
|
|
@@ -968,9 +973,9 @@ module.exports = { guardedCall };
|
|
|
968
973
|
;
|
|
969
974
|
const w = writeFile(wrapperPath, wrapper, force);
|
|
970
975
|
created.push({ path: "aiopt/aiopt-wrapper.js", status: w.wrote ? "created" : "skipped" });
|
|
971
|
-
const usagePath =
|
|
972
|
-
if (force || !
|
|
973
|
-
|
|
976
|
+
const usagePath = import_path7.default.join(outDir, "usage.jsonl");
|
|
977
|
+
if (force || !import_fs6.default.existsSync(usagePath)) {
|
|
978
|
+
import_fs6.default.writeFileSync(usagePath, "");
|
|
974
979
|
created.push({ path: "aiopt-output/usage.jsonl", status: "created" });
|
|
975
980
|
if (opts.seedSample) {
|
|
976
981
|
const sample = {
|
|
@@ -990,19 +995,19 @@ module.exports = { guardedCall };
|
|
|
990
995
|
latency_ms: 1,
|
|
991
996
|
meta: { feature_tag: "demo", routed_from: null, policy_hits: ["install-sample"] }
|
|
992
997
|
};
|
|
993
|
-
|
|
998
|
+
import_fs6.default.appendFileSync(usagePath, JSON.stringify(sample) + "\n");
|
|
994
999
|
}
|
|
995
1000
|
} else {
|
|
996
1001
|
created.push({ path: "aiopt-output/usage.jsonl", status: "skipped" });
|
|
997
1002
|
}
|
|
998
1003
|
return { created };
|
|
999
1004
|
}
|
|
1000
|
-
var
|
|
1005
|
+
var import_fs6, import_path7;
|
|
1001
1006
|
var init_install = __esm({
|
|
1002
1007
|
"src/install.ts"() {
|
|
1003
1008
|
"use strict";
|
|
1004
|
-
|
|
1005
|
-
|
|
1009
|
+
import_fs6 = __toESM(require("fs"));
|
|
1010
|
+
import_path7 = __toESM(require("path"));
|
|
1006
1011
|
}
|
|
1007
1012
|
});
|
|
1008
1013
|
|
|
@@ -1013,10 +1018,10 @@ __export(doctor_exports, {
|
|
|
1013
1018
|
});
|
|
1014
1019
|
function canWrite(dir) {
|
|
1015
1020
|
try {
|
|
1016
|
-
|
|
1017
|
-
const p =
|
|
1018
|
-
|
|
1019
|
-
|
|
1021
|
+
import_fs7.default.mkdirSync(dir, { recursive: true });
|
|
1022
|
+
const p = import_path8.default.join(dir, `.aiopt-write-test-${Date.now()}`);
|
|
1023
|
+
import_fs7.default.writeFileSync(p, "ok");
|
|
1024
|
+
import_fs7.default.unlinkSync(p);
|
|
1020
1025
|
return true;
|
|
1021
1026
|
} catch {
|
|
1022
1027
|
return false;
|
|
@@ -1024,7 +1029,7 @@ function canWrite(dir) {
|
|
|
1024
1029
|
}
|
|
1025
1030
|
function tailLines(filePath, n) {
|
|
1026
1031
|
try {
|
|
1027
|
-
const raw =
|
|
1032
|
+
const raw = import_fs7.default.readFileSync(filePath, "utf8");
|
|
1028
1033
|
const lines = raw.split(/\r?\n/).filter((l) => l.trim().length > 0);
|
|
1029
1034
|
return lines.slice(Math.max(0, lines.length - n));
|
|
1030
1035
|
} catch {
|
|
@@ -1032,15 +1037,15 @@ function tailLines(filePath, n) {
|
|
|
1032
1037
|
}
|
|
1033
1038
|
}
|
|
1034
1039
|
function runDoctor(cwd) {
|
|
1035
|
-
const aioptDir =
|
|
1036
|
-
const policiesDir =
|
|
1037
|
-
const outDir =
|
|
1038
|
-
const usagePath =
|
|
1040
|
+
const aioptDir = import_path8.default.join(cwd, "aiopt");
|
|
1041
|
+
const policiesDir = import_path8.default.join(aioptDir, "policies");
|
|
1042
|
+
const outDir = import_path8.default.join(cwd, "aiopt-output");
|
|
1043
|
+
const usagePath = import_path8.default.join(outDir, "usage.jsonl");
|
|
1039
1044
|
const checks = [];
|
|
1040
|
-
checks.push({ name: "aiopt/ exists", ok:
|
|
1041
|
-
checks.push({ name: "aiopt/policies exists", ok:
|
|
1045
|
+
checks.push({ name: "aiopt/ exists", ok: import_fs7.default.existsSync(aioptDir) });
|
|
1046
|
+
checks.push({ name: "aiopt/policies exists", ok: import_fs7.default.existsSync(policiesDir) });
|
|
1042
1047
|
checks.push({ name: "aiopt-output/ writable", ok: canWrite(outDir) });
|
|
1043
|
-
checks.push({ name: "usage.jsonl exists", ok:
|
|
1048
|
+
checks.push({ name: "usage.jsonl exists", ok: import_fs7.default.existsSync(usagePath), detail: usagePath });
|
|
1044
1049
|
const last5raw = tailLines(usagePath, 5);
|
|
1045
1050
|
const last5 = last5raw.length === 0 ? [{ status: "(empty usage.jsonl)" }] : last5raw.map((l) => {
|
|
1046
1051
|
try {
|
|
@@ -1078,12 +1083,12 @@ function runDoctor(cwd) {
|
|
|
1078
1083
|
const ok = checks.every((c) => c.ok);
|
|
1079
1084
|
return { ok, checks, last5 };
|
|
1080
1085
|
}
|
|
1081
|
-
var
|
|
1086
|
+
var import_fs7, import_path8;
|
|
1082
1087
|
var init_doctor = __esm({
|
|
1083
1088
|
"src/doctor.ts"() {
|
|
1084
1089
|
"use strict";
|
|
1085
|
-
|
|
1086
|
-
|
|
1090
|
+
import_fs7 = __toESM(require("fs"));
|
|
1091
|
+
import_path8 = __toESM(require("path"));
|
|
1087
1092
|
}
|
|
1088
1093
|
});
|
|
1089
1094
|
|
|
@@ -1138,7 +1143,7 @@ function verifyLicenseKey(key, publicKeyPem) {
|
|
|
1138
1143
|
}
|
|
1139
1144
|
}
|
|
1140
1145
|
function defaultLicensePath(cwd) {
|
|
1141
|
-
return
|
|
1146
|
+
return import_path9.default.join(cwd, "aiopt", "license.json");
|
|
1142
1147
|
}
|
|
1143
1148
|
function writeLicenseFile(p, key, payload, verified) {
|
|
1144
1149
|
const out = {
|
|
@@ -1147,19 +1152,19 @@ function writeLicenseFile(p, key, payload, verified) {
|
|
|
1147
1152
|
verified,
|
|
1148
1153
|
verified_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
1149
1154
|
};
|
|
1150
|
-
|
|
1151
|
-
|
|
1155
|
+
import_fs8.default.mkdirSync(import_path9.default.dirname(p), { recursive: true });
|
|
1156
|
+
import_fs8.default.writeFileSync(p, JSON.stringify(out, null, 2));
|
|
1152
1157
|
}
|
|
1153
1158
|
function readLicenseFile(p) {
|
|
1154
|
-
return JSON.parse(
|
|
1159
|
+
return JSON.parse(import_fs8.default.readFileSync(p, "utf8"));
|
|
1155
1160
|
}
|
|
1156
|
-
var import_crypto,
|
|
1161
|
+
var import_crypto, import_fs8, import_path9, DEFAULT_PUBLIC_KEY_PEM;
|
|
1157
1162
|
var init_license = __esm({
|
|
1158
1163
|
"src/license.ts"() {
|
|
1159
1164
|
"use strict";
|
|
1160
1165
|
import_crypto = __toESM(require("crypto"));
|
|
1161
|
-
|
|
1162
|
-
|
|
1166
|
+
import_fs8 = __toESM(require("fs"));
|
|
1167
|
+
import_path9 = __toESM(require("path"));
|
|
1163
1168
|
DEFAULT_PUBLIC_KEY_PEM = `-----BEGIN PUBLIC KEY-----
|
|
1164
1169
|
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAz1LLE/pXIx5TloDa0LAf
|
|
1165
1170
|
jg9NSIW6STWhsAFP2ZzXgpWoQ3cCmW6xcB/4QNEmPpGlfMWhyRfkxsdKuhnjUMTg
|
|
@@ -1189,13 +1194,13 @@ function parseFileUri(uri) {
|
|
|
1189
1194
|
}
|
|
1190
1195
|
}
|
|
1191
1196
|
function runGate(outDir, cwd) {
|
|
1192
|
-
const sarifPath =
|
|
1193
|
-
if (!
|
|
1197
|
+
const sarifPath = import_path10.default.join(outDir, "aiopt.sarif");
|
|
1198
|
+
if (!import_fs9.default.existsSync(sarifPath)) {
|
|
1194
1199
|
return { violations: 0, top3: [] };
|
|
1195
1200
|
}
|
|
1196
1201
|
let sarif;
|
|
1197
1202
|
try {
|
|
1198
|
-
sarif = JSON.parse(
|
|
1203
|
+
sarif = JSON.parse(import_fs9.default.readFileSync(sarifPath, "utf8"));
|
|
1199
1204
|
} catch {
|
|
1200
1205
|
return { violations: 0, top3: [] };
|
|
1201
1206
|
}
|
|
@@ -1211,8 +1216,8 @@ function runGate(outDir, cwd) {
|
|
|
1211
1216
|
const line = Number(loc?.region?.startLine || 1);
|
|
1212
1217
|
let file = parseFileUri(uri);
|
|
1213
1218
|
try {
|
|
1214
|
-
const abs =
|
|
1215
|
-
file =
|
|
1219
|
+
const abs = import_path10.default.isAbsolute(file) ? file : import_path10.default.resolve(file);
|
|
1220
|
+
file = import_path10.default.relative(cwd, abs) || file;
|
|
1216
1221
|
} catch {
|
|
1217
1222
|
}
|
|
1218
1223
|
locs.push({ file, line: Number.isFinite(line) && line > 0 ? line : 1 });
|
|
@@ -1224,7 +1229,7 @@ function formatGateStdout(r, outDir) {
|
|
|
1224
1229
|
const lines = [];
|
|
1225
1230
|
if (r.violations <= 0) {
|
|
1226
1231
|
lines.push("OK: no policy violations");
|
|
1227
|
-
lines.push(`Artifacts: ${
|
|
1232
|
+
lines.push(`Artifacts: ${import_path10.default.join(outDir, "report.md")} | ${import_path10.default.join(outDir, "aiopt.sarif")}`);
|
|
1228
1233
|
return { text: lines.join("\n"), exitCode: 0 };
|
|
1229
1234
|
}
|
|
1230
1235
|
lines.push(`FAIL: policy violations=${r.violations}`);
|
|
@@ -1234,12 +1239,12 @@ function formatGateStdout(r, outDir) {
|
|
|
1234
1239
|
const text = lines.slice(0, 10).join("\n");
|
|
1235
1240
|
return { text, exitCode: 1 };
|
|
1236
1241
|
}
|
|
1237
|
-
var
|
|
1242
|
+
var import_fs9, import_path10;
|
|
1238
1243
|
var init_gate = __esm({
|
|
1239
1244
|
"src/gate.ts"() {
|
|
1240
1245
|
"use strict";
|
|
1241
|
-
|
|
1242
|
-
|
|
1246
|
+
import_fs9 = __toESM(require("fs"));
|
|
1247
|
+
import_path10 = __toESM(require("path"));
|
|
1243
1248
|
}
|
|
1244
1249
|
});
|
|
1245
1250
|
|
|
@@ -1249,15 +1254,15 @@ __export(fix_exports, {
|
|
|
1249
1254
|
runFix: () => runFix
|
|
1250
1255
|
});
|
|
1251
1256
|
function isTextLike2(p) {
|
|
1252
|
-
const ext =
|
|
1257
|
+
const ext = import_path11.default.extname(p).toLowerCase();
|
|
1253
1258
|
return [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"].includes(ext);
|
|
1254
1259
|
}
|
|
1255
1260
|
function walk2(root, out) {
|
|
1256
|
-
const items =
|
|
1261
|
+
const items = import_fs10.default.readdirSync(root, { withFileTypes: true });
|
|
1257
1262
|
for (const it of items) {
|
|
1258
1263
|
if (it.name.startsWith(".")) continue;
|
|
1259
1264
|
if (DEFAULT_EXCLUDES2.has(it.name)) continue;
|
|
1260
|
-
const full =
|
|
1265
|
+
const full = import_path11.default.join(root, it.name);
|
|
1261
1266
|
if (it.isDirectory()) walk2(full, out);
|
|
1262
1267
|
else out.push(full);
|
|
1263
1268
|
}
|
|
@@ -1326,9 +1331,9 @@ function applyModelRouting(content) {
|
|
|
1326
1331
|
return { changed, next };
|
|
1327
1332
|
}
|
|
1328
1333
|
function tmpFilePath(original) {
|
|
1329
|
-
const base =
|
|
1334
|
+
const base = import_path11.default.basename(original);
|
|
1330
1335
|
const rand = Math.random().toString(16).slice(2);
|
|
1331
|
-
return
|
|
1336
|
+
return import_path11.default.join(import_os2.default.tmpdir(), `aiopt-fix-${base}-${rand}`);
|
|
1332
1337
|
}
|
|
1333
1338
|
function diffNoIndex(oldPath, newPath) {
|
|
1334
1339
|
try {
|
|
@@ -1372,7 +1377,7 @@ function runFix(cwd, opts) {
|
|
|
1372
1377
|
if (!isTextLike2(file)) continue;
|
|
1373
1378
|
let st;
|
|
1374
1379
|
try {
|
|
1375
|
-
st =
|
|
1380
|
+
st = import_fs10.default.statSync(file);
|
|
1376
1381
|
} catch {
|
|
1377
1382
|
continue;
|
|
1378
1383
|
}
|
|
@@ -1380,7 +1385,7 @@ function runFix(cwd, opts) {
|
|
|
1380
1385
|
if (st.size > 1024 * 1024) continue;
|
|
1381
1386
|
let content = "";
|
|
1382
1387
|
try {
|
|
1383
|
-
content =
|
|
1388
|
+
content = import_fs10.default.readFileSync(file, "utf8");
|
|
1384
1389
|
} catch {
|
|
1385
1390
|
continue;
|
|
1386
1391
|
}
|
|
@@ -1389,8 +1394,8 @@ function runFix(cwd, opts) {
|
|
|
1389
1394
|
const next = r2.next;
|
|
1390
1395
|
if (next === content) continue;
|
|
1391
1396
|
const tmp = tmpFilePath(file);
|
|
1392
|
-
|
|
1393
|
-
const rel =
|
|
1397
|
+
import_fs10.default.writeFileSync(tmp, next);
|
|
1398
|
+
const rel = import_path11.default.relative(cwd, file).replace(/\\/g, "/");
|
|
1394
1399
|
const d0 = diffNoIndex(file, tmp);
|
|
1395
1400
|
const d = normalizePatchPaths(d0, rel);
|
|
1396
1401
|
if (d && d.trim().length > 0) {
|
|
@@ -1398,20 +1403,20 @@ function runFix(cwd, opts) {
|
|
|
1398
1403
|
changedFiles.push(rel);
|
|
1399
1404
|
}
|
|
1400
1405
|
try {
|
|
1401
|
-
|
|
1406
|
+
import_fs10.default.unlinkSync(tmp);
|
|
1402
1407
|
} catch {
|
|
1403
1408
|
}
|
|
1404
1409
|
if (patches.join("\n").length > 5e5) break;
|
|
1405
1410
|
}
|
|
1406
|
-
|
|
1407
|
-
const patchPath =
|
|
1411
|
+
import_fs10.default.mkdirSync(opts.outDir, { recursive: true });
|
|
1412
|
+
const patchPath = import_path11.default.join(opts.outDir, "aiopt.patch");
|
|
1408
1413
|
const header = [
|
|
1409
1414
|
"# AIOpt patch (generated)",
|
|
1410
1415
|
"# - retry cap: reduce high retry/attempt counts to 3",
|
|
1411
1416
|
"# - model routing: cheap default via AIOPT_MODEL env override",
|
|
1412
1417
|
""
|
|
1413
1418
|
].join("\n");
|
|
1414
|
-
|
|
1419
|
+
import_fs10.default.writeFileSync(patchPath, header + patches.join("\n"));
|
|
1415
1420
|
if (opts.apply) {
|
|
1416
1421
|
try {
|
|
1417
1422
|
const inside = (0, import_child_process.execSync)("git rev-parse --is-inside-work-tree", { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
|
|
@@ -1430,13 +1435,13 @@ function runFix(cwd, opts) {
|
|
|
1430
1435
|
}
|
|
1431
1436
|
return { ok: true, applied: false, patchPath, changedFiles };
|
|
1432
1437
|
}
|
|
1433
|
-
var
|
|
1438
|
+
var import_fs10, import_path11, import_os2, import_child_process, DEFAULT_EXCLUDES2;
|
|
1434
1439
|
var init_fix = __esm({
|
|
1435
1440
|
"src/fix.ts"() {
|
|
1436
1441
|
"use strict";
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1442
|
+
import_fs10 = __toESM(require("fs"));
|
|
1443
|
+
import_path11 = __toESM(require("path"));
|
|
1444
|
+
import_os2 = __toESM(require("os"));
|
|
1440
1445
|
import_child_process = require("child_process");
|
|
1441
1446
|
DEFAULT_EXCLUDES2 = /* @__PURE__ */ new Set([
|
|
1442
1447
|
".git",
|
|
@@ -1669,7 +1674,7 @@ var init_guard = __esm({
|
|
|
1669
1674
|
// src/collect.ts
|
|
1670
1675
|
function exists(p) {
|
|
1671
1676
|
try {
|
|
1672
|
-
return
|
|
1677
|
+
return import_fs11.default.existsSync(p);
|
|
1673
1678
|
} catch {
|
|
1674
1679
|
return false;
|
|
1675
1680
|
}
|
|
@@ -1677,7 +1682,7 @@ function exists(p) {
|
|
|
1677
1682
|
function safeReadJsonl(p) {
|
|
1678
1683
|
const out = [];
|
|
1679
1684
|
try {
|
|
1680
|
-
const txt =
|
|
1685
|
+
const txt = import_fs11.default.readFileSync(p, "utf8");
|
|
1681
1686
|
for (const line of txt.split(/\r?\n/)) {
|
|
1682
1687
|
if (!line.trim()) continue;
|
|
1683
1688
|
try {
|
|
@@ -1692,11 +1697,11 @@ function safeReadJsonl(p) {
|
|
|
1692
1697
|
function listJsonlFiles(dir) {
|
|
1693
1698
|
const out = [];
|
|
1694
1699
|
try {
|
|
1695
|
-
for (const name of
|
|
1696
|
-
const full =
|
|
1700
|
+
for (const name of import_fs11.default.readdirSync(dir)) {
|
|
1701
|
+
const full = import_path12.default.join(dir, name);
|
|
1697
1702
|
let st;
|
|
1698
1703
|
try {
|
|
1699
|
-
st =
|
|
1704
|
+
st = import_fs11.default.statSync(full);
|
|
1700
1705
|
} catch {
|
|
1701
1706
|
continue;
|
|
1702
1707
|
}
|
|
@@ -1707,18 +1712,18 @@ function listJsonlFiles(dir) {
|
|
|
1707
1712
|
return out;
|
|
1708
1713
|
}
|
|
1709
1714
|
function findOpenClawSessionLogs() {
|
|
1710
|
-
const home =
|
|
1711
|
-
const root =
|
|
1715
|
+
const home = import_os3.default.homedir();
|
|
1716
|
+
const root = import_path12.default.join(home, ".openclaw", "agents");
|
|
1712
1717
|
if (!exists(root)) return [];
|
|
1713
1718
|
const found = [];
|
|
1714
1719
|
let agents = [];
|
|
1715
1720
|
try {
|
|
1716
|
-
agents =
|
|
1721
|
+
agents = import_fs11.default.readdirSync(root);
|
|
1717
1722
|
} catch {
|
|
1718
1723
|
agents = [];
|
|
1719
1724
|
}
|
|
1720
1725
|
for (const a of agents) {
|
|
1721
|
-
const sessDir =
|
|
1726
|
+
const sessDir = import_path12.default.join(root, a, "sessions");
|
|
1722
1727
|
if (!exists(sessDir)) continue;
|
|
1723
1728
|
for (const f of listJsonlFiles(sessDir)) found.push(f);
|
|
1724
1729
|
}
|
|
@@ -1790,18 +1795,18 @@ function collectToUsageJsonl(outPath) {
|
|
|
1790
1795
|
uniq.push(e);
|
|
1791
1796
|
}
|
|
1792
1797
|
uniq.sort((a, b) => Date.parse(a.ts) - Date.parse(b.ts));
|
|
1793
|
-
|
|
1798
|
+
import_fs11.default.mkdirSync(import_path12.default.dirname(outPath), { recursive: true });
|
|
1794
1799
|
const lines = uniq.map((e) => JSON.stringify(e)).join("\n") + (uniq.length ? "\n" : "");
|
|
1795
|
-
|
|
1800
|
+
import_fs11.default.writeFileSync(outPath, lines);
|
|
1796
1801
|
return { outPath, sources, eventsWritten: uniq.length };
|
|
1797
1802
|
}
|
|
1798
|
-
var
|
|
1803
|
+
var import_fs11, import_path12, import_os3;
|
|
1799
1804
|
var init_collect = __esm({
|
|
1800
1805
|
"src/collect.ts"() {
|
|
1801
1806
|
"use strict";
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1807
|
+
import_fs11 = __toESM(require("fs"));
|
|
1808
|
+
import_path12 = __toESM(require("path"));
|
|
1809
|
+
import_os3 = __toESM(require("os"));
|
|
1805
1810
|
}
|
|
1806
1811
|
});
|
|
1807
1812
|
|
|
@@ -1813,8 +1818,8 @@ __export(dashboard_exports, {
|
|
|
1813
1818
|
async function startDashboard(cwd, opts) {
|
|
1814
1819
|
const host = "127.0.0.1";
|
|
1815
1820
|
const port = opts.port || 3010;
|
|
1816
|
-
const outDir =
|
|
1817
|
-
const file = (name) =>
|
|
1821
|
+
const outDir = import_path13.default.join(cwd, "aiopt-output");
|
|
1822
|
+
const file = (name) => import_path13.default.join(outDir, name);
|
|
1818
1823
|
let lastCollect = null;
|
|
1819
1824
|
let lastCollectError = null;
|
|
1820
1825
|
let lastAutoGen = null;
|
|
@@ -1822,7 +1827,7 @@ async function startDashboard(cwd, opts) {
|
|
|
1822
1827
|
function ensureUsageFile() {
|
|
1823
1828
|
try {
|
|
1824
1829
|
const usagePath = file("usage.jsonl");
|
|
1825
|
-
if (
|
|
1830
|
+
if (import_fs12.default.existsSync(usagePath)) return;
|
|
1826
1831
|
const r = collectToUsageJsonl(usagePath);
|
|
1827
1832
|
lastCollect = { ts: (/* @__PURE__ */ new Date()).toISOString(), outPath: r.outPath, sources: r.sources, eventsWritten: r.eventsWritten };
|
|
1828
1833
|
lastCollectError = null;
|
|
@@ -1831,11 +1836,11 @@ async function startDashboard(cwd, opts) {
|
|
|
1831
1836
|
}
|
|
1832
1837
|
}
|
|
1833
1838
|
function loadRateTable2() {
|
|
1834
|
-
const p =
|
|
1835
|
-
return JSON.parse(
|
|
1839
|
+
const p = import_path13.default.join(__dirname, "..", "rates", "rate_table.json");
|
|
1840
|
+
return JSON.parse(import_fs12.default.readFileSync(p, "utf8"));
|
|
1836
1841
|
}
|
|
1837
1842
|
function readEvents(usagePath) {
|
|
1838
|
-
if (!
|
|
1843
|
+
if (!import_fs12.default.existsSync(usagePath)) return [];
|
|
1839
1844
|
return isCsvPath(usagePath) ? readCsv(usagePath) : readJsonl(usagePath);
|
|
1840
1845
|
}
|
|
1841
1846
|
function ensureScanAndGuard() {
|
|
@@ -1843,7 +1848,7 @@ async function startDashboard(cwd, opts) {
|
|
|
1843
1848
|
try {
|
|
1844
1849
|
const usagePath = file("usage.jsonl");
|
|
1845
1850
|
const rt = loadRateTable2();
|
|
1846
|
-
const needScan = !
|
|
1851
|
+
const needScan = !import_fs12.default.existsSync(file("report.json")) || !import_fs12.default.existsSync(file("report.md"));
|
|
1847
1852
|
if (needScan) {
|
|
1848
1853
|
const events = readEvents(usagePath);
|
|
1849
1854
|
const { analysis, savings, policy, meta } = analyze(rt, events);
|
|
@@ -1851,14 +1856,14 @@ async function startDashboard(cwd, opts) {
|
|
|
1851
1856
|
writeOutputs(outDir, analysis, savings, policy, { ...meta, cwd, cliVersion: "dashboard" });
|
|
1852
1857
|
did.push("scan");
|
|
1853
1858
|
}
|
|
1854
|
-
const needGuard = !
|
|
1859
|
+
const needGuard = !import_fs12.default.existsSync(file("guard-last.txt")) || !import_fs12.default.existsSync(file("guard-last.json"));
|
|
1855
1860
|
if (needGuard) {
|
|
1856
1861
|
const events = readEvents(usagePath);
|
|
1857
1862
|
const r = runGuard(rt, { baselineEvents: events, candidate: {} });
|
|
1858
1863
|
const ts = (/* @__PURE__ */ new Date()).toISOString();
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1864
|
+
import_fs12.default.writeFileSync(file("guard-last.txt"), r.message);
|
|
1865
|
+
import_fs12.default.writeFileSync(file("guard-last.json"), JSON.stringify({ ts, exitCode: r.exitCode }, null, 2));
|
|
1866
|
+
import_fs12.default.appendFileSync(file("guard-history.jsonl"), JSON.stringify({ ts, exitCode: r.exitCode, mode: "dashboard", baseline: usagePath, candidate: null }) + "\n");
|
|
1862
1867
|
did.push("guard");
|
|
1863
1868
|
}
|
|
1864
1869
|
if (did.length) lastAutoGen = { ts: (/* @__PURE__ */ new Date()).toISOString(), did };
|
|
@@ -1871,15 +1876,15 @@ async function startDashboard(cwd, opts) {
|
|
|
1871
1876
|
ensureScanAndGuard();
|
|
1872
1877
|
function readOrNull(p) {
|
|
1873
1878
|
try {
|
|
1874
|
-
if (!
|
|
1875
|
-
return
|
|
1879
|
+
if (!import_fs12.default.existsSync(p)) return null;
|
|
1880
|
+
return import_fs12.default.readFileSync(p, "utf8");
|
|
1876
1881
|
} catch {
|
|
1877
1882
|
return null;
|
|
1878
1883
|
}
|
|
1879
1884
|
}
|
|
1880
1885
|
function statOrNull(p) {
|
|
1881
1886
|
try {
|
|
1882
|
-
const st =
|
|
1887
|
+
const st = import_fs12.default.statSync(p);
|
|
1883
1888
|
return { size: st.size, mtimeMs: st.mtimeMs };
|
|
1884
1889
|
} catch {
|
|
1885
1890
|
return null;
|
|
@@ -1958,6 +1963,8 @@ async function startDashboard(cwd, opts) {
|
|
|
1958
1963
|
<span class="muted">\xB7</span>
|
|
1959
1964
|
<button id="btnRefresh" style="all:unset; cursor:pointer; padding:2px 8px; border:1px solid rgba(255,255,255,.16); border-radius:999px; background:rgba(255,255,255,.04); font-size:12px">Refresh</button>
|
|
1960
1965
|
<button id="btnLive" style="all:unset; cursor:pointer; padding:2px 8px; border:1px solid rgba(255,255,255,.16); border-radius:999px; background:rgba(255,255,255,.04); font-size:12px">Live: Off</button>
|
|
1966
|
+
<span class="muted">\xB7</span>
|
|
1967
|
+
<a href="https://github.com/tkddlr0716-collab/aiopt/issues/new?template=dashboard_feedback.yml" target="_blank">Feedback</a>
|
|
1961
1968
|
</div>
|
|
1962
1969
|
</div>
|
|
1963
1970
|
|
|
@@ -2272,7 +2279,7 @@ load();
|
|
|
2272
2279
|
ensureUsageFile();
|
|
2273
2280
|
ensureScanAndGuard();
|
|
2274
2281
|
const expected = ["guard-last.txt", "guard-last.json", "report.json", "report.md", "usage.jsonl", "guard-history.jsonl"];
|
|
2275
|
-
const missing = expected.filter((f) => !
|
|
2282
|
+
const missing = expected.filter((f) => !import_fs12.default.existsSync(file(f)));
|
|
2276
2283
|
res.writeHead(200, { "content-type": "application/json; charset=utf-8" });
|
|
2277
2284
|
res.end(JSON.stringify({ baseDir: cwd, outDir, missing, collect: lastCollect, collectError: lastCollectError, autoGen: lastAutoGen, autoGenError: lastAutoGenError }, null, 2));
|
|
2278
2285
|
return;
|
|
@@ -2305,7 +2312,7 @@ load();
|
|
|
2305
2312
|
globalThis.__aioptDashCache = globalThis.__aioptDashCache || {};
|
|
2306
2313
|
const cache = globalThis.__aioptDashCache;
|
|
2307
2314
|
if (!cache.usage || cache.usage.mtimeMs !== st.mtimeMs) {
|
|
2308
|
-
const txt2 =
|
|
2315
|
+
const txt2 = import_fs12.default.readFileSync(usagePath, "utf8");
|
|
2309
2316
|
const now = Date.now();
|
|
2310
2317
|
const liveBins = Array.from({ length: 60 }, () => ({ cost: 0, calls: 0 }));
|
|
2311
2318
|
const dayBins = Array.from({ length: 7 }, () => ({ cost: 0, calls: 0 }));
|
|
@@ -2370,13 +2377,13 @@ load();
|
|
|
2370
2377
|
await new Promise(() => {
|
|
2371
2378
|
});
|
|
2372
2379
|
}
|
|
2373
|
-
var import_http,
|
|
2380
|
+
var import_http, import_fs12, import_path13;
|
|
2374
2381
|
var init_dashboard = __esm({
|
|
2375
2382
|
"src/dashboard.ts"() {
|
|
2376
2383
|
"use strict";
|
|
2377
2384
|
import_http = __toESM(require("http"));
|
|
2378
|
-
|
|
2379
|
-
|
|
2385
|
+
import_fs12 = __toESM(require("fs"));
|
|
2386
|
+
import_path13 = __toESM(require("path"));
|
|
2380
2387
|
init_collect();
|
|
2381
2388
|
init_scan();
|
|
2382
2389
|
init_io();
|
|
@@ -2390,55 +2397,55 @@ __export(find_output_exports, {
|
|
|
2390
2397
|
findAioptOutputDir: () => findAioptOutputDir
|
|
2391
2398
|
});
|
|
2392
2399
|
function findAioptOutputDir(startCwd) {
|
|
2393
|
-
let cur =
|
|
2400
|
+
let cur = import_path14.default.resolve(startCwd);
|
|
2394
2401
|
while (true) {
|
|
2395
|
-
const outDir =
|
|
2396
|
-
if (
|
|
2402
|
+
const outDir = import_path14.default.join(cur, "aiopt-output");
|
|
2403
|
+
if (import_fs13.default.existsSync(outDir)) {
|
|
2397
2404
|
try {
|
|
2398
|
-
if (
|
|
2405
|
+
if (import_fs13.default.statSync(outDir).isDirectory()) return { cwd: cur, outDir };
|
|
2399
2406
|
} catch {
|
|
2400
2407
|
}
|
|
2401
2408
|
}
|
|
2402
|
-
const parent =
|
|
2409
|
+
const parent = import_path14.default.dirname(cur);
|
|
2403
2410
|
if (parent === cur) break;
|
|
2404
2411
|
cur = parent;
|
|
2405
2412
|
}
|
|
2406
2413
|
try {
|
|
2407
|
-
const base =
|
|
2408
|
-
const children =
|
|
2414
|
+
const base = import_path14.default.resolve(startCwd);
|
|
2415
|
+
const children = import_fs13.default.readdirSync(base, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => import_path14.default.join(base, d.name));
|
|
2409
2416
|
for (const child of children) {
|
|
2410
|
-
const outDir =
|
|
2411
|
-
if (
|
|
2417
|
+
const outDir = import_path14.default.join(child, "aiopt-output");
|
|
2418
|
+
if (import_fs13.default.existsSync(outDir)) {
|
|
2412
2419
|
try {
|
|
2413
|
-
if (
|
|
2420
|
+
if (import_fs13.default.statSync(outDir).isDirectory()) return { cwd: child, outDir };
|
|
2414
2421
|
} catch {
|
|
2415
2422
|
}
|
|
2416
2423
|
}
|
|
2417
2424
|
}
|
|
2418
2425
|
} catch {
|
|
2419
2426
|
}
|
|
2420
|
-
return { cwd:
|
|
2427
|
+
return { cwd: import_path14.default.resolve(startCwd), outDir: import_path14.default.join(import_path14.default.resolve(startCwd), "aiopt-output") };
|
|
2421
2428
|
}
|
|
2422
|
-
var
|
|
2429
|
+
var import_fs13, import_path14;
|
|
2423
2430
|
var init_find_output = __esm({
|
|
2424
2431
|
"src/find-output.ts"() {
|
|
2425
2432
|
"use strict";
|
|
2426
|
-
|
|
2427
|
-
|
|
2433
|
+
import_fs13 = __toESM(require("fs"));
|
|
2434
|
+
import_path14 = __toESM(require("path"));
|
|
2428
2435
|
}
|
|
2429
2436
|
});
|
|
2430
2437
|
|
|
2431
2438
|
// src/rates-util.ts
|
|
2432
2439
|
function loadRateTableFromDistPath() {
|
|
2433
|
-
const p =
|
|
2434
|
-
return JSON.parse(
|
|
2440
|
+
const p = import_path15.default.join(__dirname, "..", "rates", "rate_table.json");
|
|
2441
|
+
return JSON.parse(import_fs14.default.readFileSync(p, "utf8"));
|
|
2435
2442
|
}
|
|
2436
|
-
var
|
|
2443
|
+
var import_fs14, import_path15;
|
|
2437
2444
|
var init_rates_util = __esm({
|
|
2438
2445
|
"src/rates-util.ts"() {
|
|
2439
2446
|
"use strict";
|
|
2440
|
-
|
|
2441
|
-
|
|
2447
|
+
import_fs14 = __toESM(require("fs"));
|
|
2448
|
+
import_path15 = __toESM(require("path"));
|
|
2442
2449
|
}
|
|
2443
2450
|
});
|
|
2444
2451
|
|
|
@@ -2449,8 +2456,8 @@ __export(quickstart_exports, {
|
|
|
2449
2456
|
seedDemoUsage: () => seedDemoUsage
|
|
2450
2457
|
});
|
|
2451
2458
|
function seedDemoUsage(outDir) {
|
|
2452
|
-
|
|
2453
|
-
const p =
|
|
2459
|
+
import_fs15.default.mkdirSync(outDir, { recursive: true });
|
|
2460
|
+
const p = import_path16.default.join(outDir, "usage.jsonl");
|
|
2454
2461
|
const now = Date.now();
|
|
2455
2462
|
const lines = [];
|
|
2456
2463
|
for (let i = 0; i < 60; i++) {
|
|
@@ -2467,18 +2474,18 @@ function seedDemoUsage(outDir) {
|
|
|
2467
2474
|
meta: { feature_tag: i % 2 ? "summarize" : "coding" }
|
|
2468
2475
|
});
|
|
2469
2476
|
}
|
|
2470
|
-
|
|
2477
|
+
import_fs15.default.writeFileSync(p, lines.map((x) => JSON.stringify(x)).join("\n") + "\n");
|
|
2471
2478
|
return p;
|
|
2472
2479
|
}
|
|
2473
2480
|
function runQuickstart(cwd, opts) {
|
|
2474
|
-
const outDir =
|
|
2481
|
+
const outDir = import_path16.default.join(cwd, "aiopt-output");
|
|
2475
2482
|
const usagePath = seedDemoUsage(outDir);
|
|
2476
2483
|
const rt = loadRateTableFromDistPath();
|
|
2477
2484
|
const { readJsonl: readJsonl2 } = (init_io(), __toCommonJS(io_exports));
|
|
2478
2485
|
const events = readJsonl2(usagePath);
|
|
2479
2486
|
const { analysis, savings, policy, meta } = analyze(rt, events);
|
|
2480
|
-
|
|
2481
|
-
|
|
2487
|
+
import_fs15.default.writeFileSync(import_path16.default.join(outDir, "analysis.json"), JSON.stringify(analysis, null, 2));
|
|
2488
|
+
import_fs15.default.writeFileSync(import_path16.default.join(outDir, "report.json"), JSON.stringify({
|
|
2482
2489
|
version: 3,
|
|
2483
2490
|
generated_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2484
2491
|
confidence: analysis.unknown_models?.length ? "MEDIUM" : "HIGH",
|
|
@@ -2498,8 +2505,8 @@ function runQuickstart(cwd, opts) {
|
|
|
2498
2505
|
unknown_models: analysis.unknown_models || [],
|
|
2499
2506
|
notes: []
|
|
2500
2507
|
}, null, 2));
|
|
2501
|
-
|
|
2502
|
-
|
|
2508
|
+
import_fs15.default.writeFileSync(import_path16.default.join(outDir, "cost-policy.json"), JSON.stringify(policy, null, 2));
|
|
2509
|
+
import_fs15.default.writeFileSync(import_path16.default.join(outDir, "report.md"), "# AIOpt quickstart demo\n\nThis is a demo report generated by `aiopt quickstart --demo`.\n");
|
|
2503
2510
|
const r = runGuard(rt, {
|
|
2504
2511
|
baselineEvents: events,
|
|
2505
2512
|
candidate: {
|
|
@@ -2510,12 +2517,12 @@ function runQuickstart(cwd, opts) {
|
|
|
2510
2517
|
});
|
|
2511
2518
|
return { usagePath, outDir, guard: r, port: opts.port };
|
|
2512
2519
|
}
|
|
2513
|
-
var
|
|
2520
|
+
var import_fs15, import_path16;
|
|
2514
2521
|
var init_quickstart = __esm({
|
|
2515
2522
|
"src/quickstart.ts"() {
|
|
2516
2523
|
"use strict";
|
|
2517
|
-
|
|
2518
|
-
|
|
2524
|
+
import_fs15 = __toESM(require("fs"));
|
|
2525
|
+
import_path16 = __toESM(require("path"));
|
|
2519
2526
|
init_scan();
|
|
2520
2527
|
init_rates_util();
|
|
2521
2528
|
init_guard();
|
|
@@ -2523,26 +2530,54 @@ var init_quickstart = __esm({
|
|
|
2523
2530
|
});
|
|
2524
2531
|
|
|
2525
2532
|
// src/cli.ts
|
|
2526
|
-
var
|
|
2527
|
-
var
|
|
2533
|
+
var import_fs16 = __toESM(require("fs"));
|
|
2534
|
+
var import_path17 = __toESM(require("path"));
|
|
2528
2535
|
var import_commander = require("commander");
|
|
2529
2536
|
init_io();
|
|
2530
2537
|
init_scan();
|
|
2538
|
+
|
|
2539
|
+
// src/usage-path.ts
|
|
2540
|
+
var import_fs5 = __toESM(require("fs"));
|
|
2541
|
+
var import_path6 = __toESM(require("path"));
|
|
2542
|
+
var import_os = __toESM(require("os"));
|
|
2543
|
+
function homeAioptUsagePath() {
|
|
2544
|
+
return import_path6.default.join(import_os.default.homedir(), ".aiopt", "aiopt-output", "usage.jsonl");
|
|
2545
|
+
}
|
|
2546
|
+
function resolveUsagePath(preferred) {
|
|
2547
|
+
const tried = [];
|
|
2548
|
+
const push = (p) => {
|
|
2549
|
+
if (!p) return;
|
|
2550
|
+
if (tried.includes(p)) return;
|
|
2551
|
+
tried.push(p);
|
|
2552
|
+
};
|
|
2553
|
+
push(preferred);
|
|
2554
|
+
push(homeAioptUsagePath());
|
|
2555
|
+
push("./aiopt-output/usage.jsonl");
|
|
2556
|
+
for (const p of tried) {
|
|
2557
|
+
try {
|
|
2558
|
+
if (import_fs5.default.existsSync(p)) return { path: p, tried };
|
|
2559
|
+
} catch {
|
|
2560
|
+
}
|
|
2561
|
+
}
|
|
2562
|
+
return { path: preferred, tried };
|
|
2563
|
+
}
|
|
2564
|
+
|
|
2565
|
+
// src/cli.ts
|
|
2531
2566
|
var program = new import_commander.Command();
|
|
2532
2567
|
var DEFAULT_INPUT = "./aiopt-output/usage.jsonl";
|
|
2533
2568
|
var DEFAULT_OUTPUT_DIR = "./aiopt-output";
|
|
2534
2569
|
function loadRateTable() {
|
|
2535
|
-
const p =
|
|
2536
|
-
return JSON.parse(
|
|
2570
|
+
const p = import_path17.default.join(__dirname, "..", "rates", "rate_table.json");
|
|
2571
|
+
return JSON.parse(import_fs16.default.readFileSync(p, "utf8"));
|
|
2537
2572
|
}
|
|
2538
2573
|
program.name("aiopt").description("AI \uBE44\uC6A9 \uC790\uB3D9 \uC808\uAC10 \uC778\uD504\uB77C \u2014 \uC11C\uBC84 \uC5C6\uB294 \uB85C\uCEEC CLI MVP").version(require_package().version);
|
|
2539
2574
|
program.command("init").description("aiopt-input/ \uBC0F \uC0D8\uD50C usage.jsonl, aiopt-output/ \uC0DD\uC131").action(() => {
|
|
2540
2575
|
ensureDir("./aiopt-input");
|
|
2541
2576
|
ensureDir("./aiopt-output");
|
|
2542
|
-
const sampleSrc =
|
|
2543
|
-
const dst =
|
|
2544
|
-
if (!
|
|
2545
|
-
|
|
2577
|
+
const sampleSrc = import_path17.default.join(__dirname, "..", "samples", "sample_usage.jsonl");
|
|
2578
|
+
const dst = import_path17.default.join("./aiopt-input", "usage.jsonl");
|
|
2579
|
+
if (!import_fs16.default.existsSync(dst)) {
|
|
2580
|
+
import_fs16.default.copyFileSync(sampleSrc, dst);
|
|
2546
2581
|
console.log("Created ./aiopt-input/usage.jsonl (sample)");
|
|
2547
2582
|
} else {
|
|
2548
2583
|
console.log("Exists ./aiopt-input/usage.jsonl (skip)");
|
|
@@ -2552,7 +2587,7 @@ program.command("init").description("aiopt-input/ \uBC0F \uC0D8\uD50C usage.json
|
|
|
2552
2587
|
program.command("scan").description("\uC785\uB825 \uB85C\uADF8(JSONL/CSV)\uB97C \uBD84\uC11D\uD558\uACE0 report.md/report.json + patches\uAE4C\uC9C0 \uC0DD\uC131").option("--input <path>", "input file path (default: ./aiopt-output/usage.jsonl)", DEFAULT_INPUT).option("--out <dir>", "output dir (default: ./aiopt-output)", DEFAULT_OUTPUT_DIR).option("--json", "print machine-readable JSON to stdout").action(async (opts) => {
|
|
2553
2588
|
const inputPath = String(opts.input);
|
|
2554
2589
|
const outDir = String(opts.out);
|
|
2555
|
-
if (!
|
|
2590
|
+
if (!import_fs16.default.existsSync(inputPath)) {
|
|
2556
2591
|
console.error(`Input not found: ${inputPath}`);
|
|
2557
2592
|
process.exit(1);
|
|
2558
2593
|
}
|
|
@@ -2569,10 +2604,10 @@ program.command("scan").description("\uC785\uB825 \uB85C\uADF8(JSONL/CSV)\uB97C
|
|
|
2569
2604
|
outDir,
|
|
2570
2605
|
input: inputPath,
|
|
2571
2606
|
report: {
|
|
2572
|
-
report_md:
|
|
2573
|
-
report_json:
|
|
2574
|
-
cost_policy_json:
|
|
2575
|
-
sarif:
|
|
2607
|
+
report_md: import_path17.default.join(outDir, "report.md"),
|
|
2608
|
+
report_json: import_path17.default.join(outDir, "report.json"),
|
|
2609
|
+
cost_policy_json: import_path17.default.join(outDir, "cost-policy.json"),
|
|
2610
|
+
sarif: import_path17.default.join(outDir, "aiopt.sarif")
|
|
2576
2611
|
},
|
|
2577
2612
|
summary: {
|
|
2578
2613
|
total_cost_usd: analysis.total_cost,
|
|
@@ -2588,7 +2623,7 @@ program.command("scan").description("\uC785\uB825 \uB85C\uADF8(JSONL/CSV)\uB97C
|
|
|
2588
2623
|
const tag = f.status === "no-issue" ? "(no issue detected)" : `($${Math.round(f.impact_usd * 100) / 100})`;
|
|
2589
2624
|
console.log(`${i + 1}) ${f.title} ${tag}`);
|
|
2590
2625
|
});
|
|
2591
|
-
console.log(`Report: ${
|
|
2626
|
+
console.log(`Report: ${import_path17.default.join(outDir, "report.md")}`);
|
|
2592
2627
|
});
|
|
2593
2628
|
program.command("policy").description("\uB9C8\uC9C0\uB9C9 scan \uACB0\uACFC \uAE30\uBC18\uC73C\uB85C cost-policy.json\uB9CC \uC7AC\uC0DD\uC131 (MVP: scan\uACFC \uB3D9\uC77C \uB85C\uC9C1)").option("--input <path>", "input file path (default: ./aiopt-input/usage.jsonl)", DEFAULT_INPUT).option("--out <dir>", "output dir (default: ./aiopt-output)", DEFAULT_OUTPUT_DIR).action((opts) => {
|
|
2594
2629
|
const inputPath = String(opts.input);
|
|
@@ -2598,7 +2633,7 @@ program.command("policy").description("\uB9C8\uC9C0\uB9C9 scan \uACB0\uACFC \uAE
|
|
|
2598
2633
|
const { policy } = analyze(rt, events);
|
|
2599
2634
|
policy.generated_from.input = inputPath;
|
|
2600
2635
|
ensureDir(outDir);
|
|
2601
|
-
|
|
2636
|
+
import_fs16.default.writeFileSync(import_path17.default.join(outDir, "cost-policy.json"), JSON.stringify(policy, null, 2));
|
|
2602
2637
|
console.log(`OK: ${outDir}/cost-policy.json`);
|
|
2603
2638
|
});
|
|
2604
2639
|
program.command("install").description("Install AIOpt guardrails: create aiopt/ + policies + usage.jsonl").option("--force", "overwrite existing files").option("--seed-sample", "seed 1 sample line into aiopt-output/usage.jsonl").action(async (opts) => {
|
|
@@ -2638,7 +2673,7 @@ licenseCmd.command("verify").option("--path <path>", "license.json path (default
|
|
|
2638
2673
|
const { DEFAULT_PUBLIC_KEY_PEM: DEFAULT_PUBLIC_KEY_PEM2, defaultLicensePath: defaultLicensePath2, readLicenseFile: readLicenseFile2, verifyLicenseKey: verifyLicenseKey2 } = await Promise.resolve().then(() => (init_license(), license_exports));
|
|
2639
2674
|
const p = opts.path ? String(opts.path) : defaultLicensePath2(process.cwd());
|
|
2640
2675
|
const pub = process.env.AIOPT_LICENSE_PUBKEY || DEFAULT_PUBLIC_KEY_PEM2;
|
|
2641
|
-
if (!
|
|
2676
|
+
if (!import_fs16.default.existsSync(p)) {
|
|
2642
2677
|
console.error(`FAIL: license file not found: ${p}`);
|
|
2643
2678
|
process.exit(3);
|
|
2644
2679
|
}
|
|
@@ -2655,7 +2690,7 @@ licenseCmd.command("status").option("--path <path>", "license.json path (default
|
|
|
2655
2690
|
const { DEFAULT_PUBLIC_KEY_PEM: DEFAULT_PUBLIC_KEY_PEM2, defaultLicensePath: defaultLicensePath2, readLicenseFile: readLicenseFile2, verifyLicenseKey: verifyLicenseKey2 } = await Promise.resolve().then(() => (init_license(), license_exports));
|
|
2656
2691
|
const p = opts.path ? String(opts.path) : defaultLicensePath2(process.cwd());
|
|
2657
2692
|
const pub = process.env.AIOPT_LICENSE_PUBKEY || DEFAULT_PUBLIC_KEY_PEM2;
|
|
2658
|
-
if (!
|
|
2693
|
+
if (!import_fs16.default.existsSync(p)) {
|
|
2659
2694
|
console.log("NO_LICENSE");
|
|
2660
2695
|
process.exit(2);
|
|
2661
2696
|
}
|
|
@@ -2669,19 +2704,44 @@ licenseCmd.command("status").option("--path <path>", "license.json path (default
|
|
|
2669
2704
|
process.exit(3);
|
|
2670
2705
|
});
|
|
2671
2706
|
program.command("gate").description("Merge gate (CI-friendly): fail (exit 1) when policy violations are detected; prints <=10 lines").option("--input <path>", "input usage jsonl/csv (default: ./aiopt-output/usage.jsonl)", DEFAULT_INPUT).option("--out <dir>", "output dir (default: ./aiopt-output)", DEFAULT_OUTPUT_DIR).option("--json", "print machine-readable JSON to stdout").action(async (opts) => {
|
|
2672
|
-
const
|
|
2707
|
+
const preferredInput = String(opts.input);
|
|
2673
2708
|
const outDir = String(opts.out);
|
|
2674
|
-
|
|
2675
|
-
|
|
2709
|
+
const resolved = resolveUsagePath(preferredInput);
|
|
2710
|
+
const inputPath = resolved.path;
|
|
2711
|
+
const defaultOut = "./aiopt-output";
|
|
2712
|
+
let finalOutDir = outDir;
|
|
2713
|
+
if (outDir === defaultOut) {
|
|
2714
|
+
try {
|
|
2715
|
+
const os4 = require("os");
|
|
2716
|
+
finalOutDir = import_path17.default.join(os4.homedir(), ".aiopt", "aiopt-output");
|
|
2717
|
+
} catch {
|
|
2718
|
+
finalOutDir = outDir;
|
|
2719
|
+
}
|
|
2720
|
+
}
|
|
2721
|
+
if (!import_fs16.default.existsSync(inputPath)) {
|
|
2722
|
+
if (opts.json) {
|
|
2723
|
+
console.log(JSON.stringify({
|
|
2724
|
+
ok: false,
|
|
2725
|
+
exitCode: 1,
|
|
2726
|
+
error: "input_not_found",
|
|
2727
|
+
message: `FAIL: input not found: ${preferredInput}`,
|
|
2728
|
+
tried: resolved.tried,
|
|
2729
|
+
hint: "Run: aiopt dashboard (auto-collects OpenClaw usage) or pass --input <usage.jsonl>"
|
|
2730
|
+
}, null, 2));
|
|
2731
|
+
} else {
|
|
2732
|
+
console.error(`FAIL: input not found: ${preferredInput}`);
|
|
2733
|
+
console.error(`Tried: ${resolved.tried.join(", ")}`);
|
|
2734
|
+
console.error("Hint: run `aiopt dashboard` first (auto-collects OpenClaw usage)");
|
|
2735
|
+
}
|
|
2676
2736
|
process.exit(1);
|
|
2677
2737
|
}
|
|
2678
2738
|
const rt = loadRateTable();
|
|
2679
2739
|
const events = isCsvPath(inputPath) ? readCsv(inputPath) : readJsonl(inputPath);
|
|
2680
2740
|
const { analysis, savings, policy, meta } = analyze(rt, events);
|
|
2681
2741
|
policy.generated_from.input = inputPath;
|
|
2682
|
-
writeOutputs(
|
|
2742
|
+
writeOutputs(finalOutDir, analysis, savings, policy, { ...meta, cwd: process.cwd(), cliVersion: program.version() });
|
|
2683
2743
|
const { runGate: runGate2, formatGateStdout: formatGateStdout2 } = await Promise.resolve().then(() => (init_gate(), gate_exports));
|
|
2684
|
-
const r = runGate2(
|
|
2744
|
+
const r = runGate2(finalOutDir, process.cwd());
|
|
2685
2745
|
if (opts.json) {
|
|
2686
2746
|
const payload = {
|
|
2687
2747
|
ok: r.violations <= 0,
|
|
@@ -2689,15 +2749,15 @@ program.command("gate").description("Merge gate (CI-friendly): fail (exit 1) whe
|
|
|
2689
2749
|
violations: r.violations,
|
|
2690
2750
|
top3: r.top3,
|
|
2691
2751
|
artifacts: {
|
|
2692
|
-
report_md:
|
|
2693
|
-
sarif:
|
|
2694
|
-
patches_dir:
|
|
2752
|
+
report_md: import_path17.default.join(finalOutDir, "report.md"),
|
|
2753
|
+
sarif: import_path17.default.join(finalOutDir, "aiopt.sarif"),
|
|
2754
|
+
patches_dir: import_path17.default.join(finalOutDir, "patches")
|
|
2695
2755
|
}
|
|
2696
2756
|
};
|
|
2697
2757
|
console.log(JSON.stringify(payload, null, 2));
|
|
2698
2758
|
process.exit(payload.exitCode);
|
|
2699
2759
|
}
|
|
2700
|
-
const out = formatGateStdout2(r,
|
|
2760
|
+
const out = formatGateStdout2(r, finalOutDir);
|
|
2701
2761
|
console.log(out.text);
|
|
2702
2762
|
process.exit(out.exitCode);
|
|
2703
2763
|
});
|
|
@@ -2741,11 +2801,11 @@ program.command("guard").description("Pre-deploy guardrail: compare baseline usa
|
|
|
2741
2801
|
console.error("FAIL: diff mode requires both --baseline and --candidate");
|
|
2742
2802
|
process.exit(3);
|
|
2743
2803
|
}
|
|
2744
|
-
if (!
|
|
2804
|
+
if (!import_fs16.default.existsSync(baselinePath)) {
|
|
2745
2805
|
console.error(`FAIL: baseline not found: ${baselinePath}`);
|
|
2746
2806
|
process.exit(3);
|
|
2747
2807
|
}
|
|
2748
|
-
if (candidatePath && !
|
|
2808
|
+
if (candidatePath && !import_fs16.default.existsSync(candidatePath)) {
|
|
2749
2809
|
console.error(`FAIL: candidate not found: ${candidatePath}`);
|
|
2750
2810
|
process.exit(3);
|
|
2751
2811
|
}
|
|
@@ -2773,10 +2833,10 @@ program.command("guard").description("Pre-deploy guardrail: compare baseline usa
|
|
|
2773
2833
|
baseline: baselinePath,
|
|
2774
2834
|
candidate: candidatePath,
|
|
2775
2835
|
artifacts: {
|
|
2776
|
-
outDir:
|
|
2777
|
-
guard_last_txt:
|
|
2778
|
-
guard_last_json:
|
|
2779
|
-
guard_history_jsonl:
|
|
2836
|
+
outDir: import_path17.default.resolve(DEFAULT_OUTPUT_DIR),
|
|
2837
|
+
guard_last_txt: import_path17.default.join(import_path17.default.resolve(DEFAULT_OUTPUT_DIR), "guard-last.txt"),
|
|
2838
|
+
guard_last_json: import_path17.default.join(import_path17.default.resolve(DEFAULT_OUTPUT_DIR), "guard-last.json"),
|
|
2839
|
+
guard_history_jsonl: import_path17.default.join(import_path17.default.resolve(DEFAULT_OUTPUT_DIR), "guard-history.jsonl")
|
|
2780
2840
|
}
|
|
2781
2841
|
};
|
|
2782
2842
|
console.log(JSON.stringify(payload, null, 2));
|
|
@@ -2784,21 +2844,21 @@ program.command("guard").description("Pre-deploy guardrail: compare baseline usa
|
|
|
2784
2844
|
console.log(r.message);
|
|
2785
2845
|
}
|
|
2786
2846
|
try {
|
|
2787
|
-
const outDir =
|
|
2788
|
-
|
|
2847
|
+
const outDir = import_path17.default.resolve(DEFAULT_OUTPUT_DIR);
|
|
2848
|
+
import_fs16.default.mkdirSync(outDir, { recursive: true });
|
|
2789
2849
|
const ts = (/* @__PURE__ */ new Date()).toISOString();
|
|
2790
|
-
|
|
2791
|
-
|
|
2850
|
+
import_fs16.default.writeFileSync(import_path17.default.join(outDir, "guard-last.txt"), r.message);
|
|
2851
|
+
import_fs16.default.writeFileSync(import_path17.default.join(outDir, "guard-last.json"), JSON.stringify({ ts, exitCode: r.exitCode }, null, 2));
|
|
2792
2852
|
const histLine = JSON.stringify({ ts, exitCode: r.exitCode, mode: candidateEvents ? "diff" : "transform", baseline: baselinePath, candidate: candidatePath }) + "\n";
|
|
2793
|
-
|
|
2853
|
+
import_fs16.default.appendFileSync(import_path17.default.join(outDir, "guard-history.jsonl"), histLine);
|
|
2794
2854
|
} catch {
|
|
2795
2855
|
}
|
|
2796
2856
|
process.exit(r.exitCode);
|
|
2797
2857
|
});
|
|
2798
2858
|
program.command("dashboard").description("Local dashboard (localhost only): view usage + scan + guard (auto-collects OpenClaw usage)").option("--port <n>", "port (default: 3010)", (v) => Number(v), 3010).option("--dir <path>", "base directory (default: ~/.aiopt). Use this to pin a consistent data source.").option("--auto", "auto-detect by searching parents (and one-level children) for aiopt-output (use only if you really want project-local)").action(async (opts) => {
|
|
2799
2859
|
const { startDashboard: startDashboard2 } = await Promise.resolve().then(() => (init_dashboard(), dashboard_exports));
|
|
2800
|
-
const
|
|
2801
|
-
const defaultBase = require("path").join(
|
|
2860
|
+
const os4 = await import("os");
|
|
2861
|
+
const defaultBase = require("path").join(os4.homedir(), ".aiopt");
|
|
2802
2862
|
const base = opts.dir ? String(opts.dir) : defaultBase;
|
|
2803
2863
|
if (opts.auto && !opts.dir) {
|
|
2804
2864
|
const { findAioptOutputDir: findAioptOutputDir2 } = await Promise.resolve().then(() => (init_find_output(), find_output_exports));
|
|
@@ -2820,14 +2880,14 @@ program.command("quickstart").description("1-minute demo: generate sample usage,
|
|
|
2820
2880
|
console.log("--- guard ---");
|
|
2821
2881
|
console.log(r.guard.message);
|
|
2822
2882
|
try {
|
|
2823
|
-
const
|
|
2824
|
-
const
|
|
2825
|
-
|
|
2883
|
+
const fs17 = await import("fs");
|
|
2884
|
+
const path18 = await import("path");
|
|
2885
|
+
fs17.mkdirSync(r.outDir, { recursive: true });
|
|
2826
2886
|
const ts = (/* @__PURE__ */ new Date()).toISOString();
|
|
2827
|
-
|
|
2828
|
-
|
|
2887
|
+
fs17.writeFileSync(path18.join(r.outDir, "guard-last.txt"), r.guard.message);
|
|
2888
|
+
fs17.writeFileSync(path18.join(r.outDir, "guard-last.json"), JSON.stringify({ ts, exitCode: r.guard.exitCode }, null, 2));
|
|
2829
2889
|
const histLine = JSON.stringify({ ts, exitCode: r.guard.exitCode, mode: "quickstart", baseline: r.usagePath, candidate: null }) + "\n";
|
|
2830
|
-
|
|
2890
|
+
fs17.appendFileSync(path18.join(r.outDir, "guard-history.jsonl"), histLine);
|
|
2831
2891
|
} catch {
|
|
2832
2892
|
}
|
|
2833
2893
|
console.log("--- next ---");
|