@cognisos/liminal 2.5.0 → 2.6.0-beta.0
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/dist/bin.js +565 -151
- package/dist/bin.js.map +1 -1
- package/package.json +2 -1
package/dist/bin.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
4
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
4
6
|
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
5
7
|
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
6
8
|
}) : x)(function(x) {
|
|
@@ -14,6 +16,15 @@ var __export = (target, all) => {
|
|
|
14
16
|
for (var name in all)
|
|
15
17
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
16
18
|
};
|
|
19
|
+
var __copyProps = (to, from, except, desc) => {
|
|
20
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
21
|
+
for (let key of __getOwnPropNames(from))
|
|
22
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
23
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
24
|
+
}
|
|
25
|
+
return to;
|
|
26
|
+
};
|
|
27
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
17
28
|
|
|
18
29
|
// src/ui/format.ts
|
|
19
30
|
function disableColor() {
|
|
@@ -215,7 +226,7 @@ var init_version = __esm({
|
|
|
215
226
|
"src/version.ts"() {
|
|
216
227
|
"use strict";
|
|
217
228
|
init_format();
|
|
218
|
-
VERSION = true ? "2.
|
|
229
|
+
VERSION = true ? "2.6.0-beta.0" : "2.5.0";
|
|
219
230
|
BANNER_LINES = [
|
|
220
231
|
" ___ ___ _____ ______ ___ ________ ________ ___",
|
|
221
232
|
"|\\ \\ |\\ \\|\\ _ \\ _ \\|\\ \\|\\ ___ \\|\\ __ \\|\\ \\",
|
|
@@ -727,6 +738,35 @@ var init_pipeline = __esm({
|
|
|
727
738
|
}
|
|
728
739
|
});
|
|
729
740
|
|
|
741
|
+
// src/rsc/tokenizer.ts
|
|
742
|
+
var tokenizer_exports = {};
|
|
743
|
+
__export(tokenizer_exports, {
|
|
744
|
+
countTokens: () => countTokens,
|
|
745
|
+
countTokensBytes: () => countTokensBytes
|
|
746
|
+
});
|
|
747
|
+
function countTokens(text) {
|
|
748
|
+
if (!_countTokens) {
|
|
749
|
+
try {
|
|
750
|
+
const mod = __require("@anthropic-ai/tokenizer");
|
|
751
|
+
_countTokens = mod.countTokens;
|
|
752
|
+
} catch {
|
|
753
|
+
_countTokens = (t) => Math.ceil(Buffer.byteLength(t, "utf-8") / 4);
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
return _countTokens(text);
|
|
757
|
+
}
|
|
758
|
+
function countTokensBytes(bytes) {
|
|
759
|
+
const text = Buffer.from(bytes).toString("utf-8");
|
|
760
|
+
return countTokens(text);
|
|
761
|
+
}
|
|
762
|
+
var _countTokens;
|
|
763
|
+
var init_tokenizer = __esm({
|
|
764
|
+
"src/rsc/tokenizer.ts"() {
|
|
765
|
+
"use strict";
|
|
766
|
+
_countTokens = null;
|
|
767
|
+
}
|
|
768
|
+
});
|
|
769
|
+
|
|
730
770
|
// src/daemon/lifecycle.ts
|
|
731
771
|
import { readFileSync as readFileSync6, writeFileSync as writeFileSync4, unlinkSync as unlinkSync3, existsSync as existsSync8 } from "fs";
|
|
732
772
|
import { fork } from "child_process";
|
|
@@ -828,10 +868,35 @@ var init_aggregator = __esm({
|
|
|
828
868
|
tools = /* @__PURE__ */ new Map();
|
|
829
869
|
cursorMetrics = null;
|
|
830
870
|
costPerMillionTokens;
|
|
871
|
+
/** Actual input_tokens from Anthropic API responses (verified ground truth) */
|
|
872
|
+
actualInputTokensTotal = 0;
|
|
873
|
+
/** Actual output_tokens from Anthropic API responses */
|
|
874
|
+
actualOutputTokensTotal = 0;
|
|
875
|
+
/** Pre-compression input token count from tokenizer */
|
|
876
|
+
originalInputEstimateTotal = 0;
|
|
877
|
+
/** Verified tokens saved: countTokens(pre) - countTokens(post) */
|
|
878
|
+
verifiedSavedTotal = 0;
|
|
879
|
+
/** Number of API responses with verified usage data */
|
|
880
|
+
verifiedRequestCount = 0;
|
|
831
881
|
constructor(costPerMillionTokens = DEFAULT_COST_PER_MILLION_TOKENS) {
|
|
832
882
|
this.costPerMillionTokens = costPerMillionTokens;
|
|
833
883
|
}
|
|
834
884
|
// ── Recording events ──────────────────────────────────────────────
|
|
885
|
+
/**
|
|
886
|
+
* Record verified token counts from an Anthropic API response.
|
|
887
|
+
*
|
|
888
|
+
* @param actualInput - usage.input_tokens from Anthropic (post-compression ground truth)
|
|
889
|
+
* @param actualOutput - usage.output_tokens from Anthropic (generated tokens ground truth)
|
|
890
|
+
* @param verifiedSaved - tokens saved, computed as countTokens(pre) - countTokens(post) using real tokenizer
|
|
891
|
+
* @param originalInputTokens - token count of full request before compression
|
|
892
|
+
*/
|
|
893
|
+
recordApiUsage(actualInput, actualOutput, verifiedSaved, originalInputTokens) {
|
|
894
|
+
this.actualInputTokensTotal += actualInput;
|
|
895
|
+
this.actualOutputTokensTotal += actualOutput;
|
|
896
|
+
this.originalInputEstimateTotal += originalInputTokens;
|
|
897
|
+
this.verifiedSavedTotal += verifiedSaved;
|
|
898
|
+
this.verifiedRequestCount++;
|
|
899
|
+
}
|
|
835
900
|
recordCompression(toolId, tokensProcessed, tokensSaved, latencyMs) {
|
|
836
901
|
const m = this.getOrCreateTool(toolId);
|
|
837
902
|
m.calls++;
|
|
@@ -899,10 +964,18 @@ var init_aggregator = __esm({
|
|
|
899
964
|
for (const [id, m] of this.tools) {
|
|
900
965
|
byTool[id] = { ...m };
|
|
901
966
|
}
|
|
967
|
+
const verifiedInputSaved = this.verifiedSavedTotal;
|
|
968
|
+
const verifiedSavingsRate = this.originalInputEstimateTotal > 0 ? verifiedInputSaved / this.originalInputEstimateTotal : 0;
|
|
902
969
|
return {
|
|
903
970
|
sessionStartedAt: this.startedAt.toISOString(),
|
|
904
971
|
uptimeMs,
|
|
905
972
|
...totals,
|
|
973
|
+
actualInputTokens: this.actualInputTokensTotal,
|
|
974
|
+
actualOutputTokens: this.actualOutputTokensTotal,
|
|
975
|
+
originalInputEstimate: this.originalInputEstimateTotal,
|
|
976
|
+
verifiedInputSaved,
|
|
977
|
+
verifiedSavingsRate,
|
|
978
|
+
verifiedRequestCount: this.verifiedRequestCount,
|
|
906
979
|
savingsRate: rate,
|
|
907
980
|
contextExtension: this.contextExtension(rate),
|
|
908
981
|
estimatedCostSavedUsd: this.estimatedCostSaved(totals.tokensSaved),
|
|
@@ -1007,6 +1080,101 @@ var init_store = __esm({
|
|
|
1007
1080
|
}
|
|
1008
1081
|
});
|
|
1009
1082
|
|
|
1083
|
+
// src/cursor/stats.ts
|
|
1084
|
+
var stats_exports = {};
|
|
1085
|
+
__export(stats_exports, {
|
|
1086
|
+
parseCursorHookStats: () => parseCursorHookStats,
|
|
1087
|
+
readCursorLogEntries: () => readCursorLogEntries
|
|
1088
|
+
});
|
|
1089
|
+
import { existsSync as existsSync10, readFileSync as readFileSync8, readdirSync } from "fs";
|
|
1090
|
+
import { join as join7 } from "path";
|
|
1091
|
+
function parseCursorHookStats(cwd = process.cwd()) {
|
|
1092
|
+
const logPath = join7(cwd, ".fabric", "compress.log");
|
|
1093
|
+
if (!existsSync10(logPath)) return null;
|
|
1094
|
+
let compressions = 0, errors = 0;
|
|
1095
|
+
let tokensProcessed = 0, tokensSaved = 0;
|
|
1096
|
+
let apiMsSumMs = 0;
|
|
1097
|
+
const files = /* @__PURE__ */ new Set();
|
|
1098
|
+
const content = readFileSync8(logPath, "utf-8").trim();
|
|
1099
|
+
if (!content) return null;
|
|
1100
|
+
for (const line of content.split("\n")) {
|
|
1101
|
+
try {
|
|
1102
|
+
const entry = JSON.parse(line);
|
|
1103
|
+
if (entry.type === "compressed") {
|
|
1104
|
+
compressions++;
|
|
1105
|
+
tokensProcessed += entry.inputTokens ?? Math.ceil((entry.inputSize ?? 0) / 3);
|
|
1106
|
+
tokensSaved += entry.tokensSaved ?? 0;
|
|
1107
|
+
apiMsSumMs += entry.apiMs ?? 0;
|
|
1108
|
+
files.add(entry.file);
|
|
1109
|
+
} else if (entry.type === "error") {
|
|
1110
|
+
errors++;
|
|
1111
|
+
}
|
|
1112
|
+
} catch {
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
let cacheCount = 0;
|
|
1116
|
+
try {
|
|
1117
|
+
cacheCount = countFilesRecursive(join7(cwd, ".fabric", "cache"));
|
|
1118
|
+
} catch {
|
|
1119
|
+
}
|
|
1120
|
+
return {
|
|
1121
|
+
files: files.size,
|
|
1122
|
+
compressions,
|
|
1123
|
+
errors,
|
|
1124
|
+
tokensProcessed,
|
|
1125
|
+
tokensSaved,
|
|
1126
|
+
apiMsSumMs,
|
|
1127
|
+
cacheCount
|
|
1128
|
+
};
|
|
1129
|
+
}
|
|
1130
|
+
function readCursorLogEntries(maxEntries = 50, cwd = process.cwd()) {
|
|
1131
|
+
const logPath = join7(cwd, ".fabric", "compress.log");
|
|
1132
|
+
if (!existsSync10(logPath)) return [];
|
|
1133
|
+
const content = readFileSync8(logPath, "utf-8").trim();
|
|
1134
|
+
if (!content) return [];
|
|
1135
|
+
const lines = content.split("\n");
|
|
1136
|
+
const recent = lines.slice(-maxEntries);
|
|
1137
|
+
const entries = [];
|
|
1138
|
+
for (const line of recent) {
|
|
1139
|
+
try {
|
|
1140
|
+
const entry = JSON.parse(line);
|
|
1141
|
+
const ts = entry.ts ? new Date(entry.ts) : /* @__PURE__ */ new Date();
|
|
1142
|
+
const time = ts.toTimeString().slice(0, 8);
|
|
1143
|
+
if (entry.type === "compressed") {
|
|
1144
|
+
const pct = entry.savedPct ?? (entry.inputTokens && entry.tokensSaved ? +(entry.tokensSaved / entry.inputTokens * 100).toFixed(1) : 0);
|
|
1145
|
+
entries.push({
|
|
1146
|
+
ts: entry.ts,
|
|
1147
|
+
line: `[${time}] [CURSOR] ${entry.file} \u2192 ${entry.tokensSaved} tok saved (${pct}%) ${entry.apiMs}ms`
|
|
1148
|
+
});
|
|
1149
|
+
} else if (entry.type === "error") {
|
|
1150
|
+
entries.push({
|
|
1151
|
+
ts: entry.ts,
|
|
1152
|
+
line: `[${time}] [CURSOR] ${entry.file} \u2192 ERROR: ${entry.error ?? "unknown"}`
|
|
1153
|
+
});
|
|
1154
|
+
}
|
|
1155
|
+
} catch {
|
|
1156
|
+
}
|
|
1157
|
+
}
|
|
1158
|
+
return entries;
|
|
1159
|
+
}
|
|
1160
|
+
function countFilesRecursive(dir) {
|
|
1161
|
+
if (!existsSync10(dir)) return 0;
|
|
1162
|
+
let count = 0;
|
|
1163
|
+
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
1164
|
+
if (entry.isDirectory()) {
|
|
1165
|
+
count += countFilesRecursive(join7(dir, entry.name));
|
|
1166
|
+
} else {
|
|
1167
|
+
count++;
|
|
1168
|
+
}
|
|
1169
|
+
}
|
|
1170
|
+
return count;
|
|
1171
|
+
}
|
|
1172
|
+
var init_stats = __esm({
|
|
1173
|
+
"src/cursor/stats.ts"() {
|
|
1174
|
+
"use strict";
|
|
1175
|
+
}
|
|
1176
|
+
});
|
|
1177
|
+
|
|
1010
1178
|
// src/ui/screen.ts
|
|
1011
1179
|
var SEQ, Screen;
|
|
1012
1180
|
var init_screen = __esm({
|
|
@@ -1402,15 +1570,39 @@ var init_stats_view = __esm({
|
|
|
1402
1570
|
lines.push(formatRow("Savings Rate", sRate, aRate, colW));
|
|
1403
1571
|
lines.push(formatRow("Context Extension", sExt, aExt, colW));
|
|
1404
1572
|
lines.push(blank());
|
|
1573
|
+
lines.push(divider("Verified (Anthropic API)", w));
|
|
1574
|
+
lines.push(blank());
|
|
1575
|
+
if (health && health.verifiedRequestCount && health.verifiedRequestCount > 0) {
|
|
1576
|
+
const actualIn = health.actualInputTokens ?? 0;
|
|
1577
|
+
const actualOut = health.actualOutputTokens ?? 0;
|
|
1578
|
+
const origEst = health.originalInputEstimate ?? 0;
|
|
1579
|
+
const saved = health.verifiedInputSaved ?? 0;
|
|
1580
|
+
const rate = health.verifiedSavingsRate ?? 0;
|
|
1581
|
+
const reqs = health.verifiedRequestCount;
|
|
1582
|
+
lines.push(formatRow("Input (actual)", formatNum(actualIn), "\u2014", colW));
|
|
1583
|
+
lines.push(formatRow("Input (pre-comp)", formatNum(origEst), "\u2014", colW));
|
|
1584
|
+
lines.push(formatRow("Input Saved", formatNum(saved), "\u2014", colW));
|
|
1585
|
+
lines.push(formatRow("Input Savings Rate", `${(rate * 100).toFixed(1)}%`, "\u2014", colW));
|
|
1586
|
+
lines.push(blank());
|
|
1587
|
+
lines.push(formatRow("Output (actual)", formatNum(actualOut), "\u2014", colW));
|
|
1588
|
+
lines.push(formatRow("Total (in + out)", formatNum(actualIn + actualOut), "\u2014", colW));
|
|
1589
|
+
lines.push(blank());
|
|
1590
|
+
lines.push(formatRow("Verified Requests", String(reqs), "\u2014", colW));
|
|
1591
|
+
} else {
|
|
1592
|
+
lines.push(` ${c.dim}No verified data yet \u2014 waiting for API responses${c.reset}`);
|
|
1593
|
+
}
|
|
1594
|
+
lines.push(blank());
|
|
1405
1595
|
lines.push(divider("Cost Impact", w));
|
|
1406
1596
|
lines.push(blank());
|
|
1407
1597
|
lines.push(hdr);
|
|
1408
1598
|
lines.push(sep);
|
|
1409
1599
|
lines.push(formatRow("Est. Cost Saved", sCost, aCost, colW));
|
|
1410
1600
|
lines.push(blank());
|
|
1601
|
+
let byToolRendered = false;
|
|
1411
1602
|
if (health && health.sessions.length > 0) {
|
|
1412
1603
|
lines.push(divider("By Tool", w));
|
|
1413
1604
|
lines.push(blank());
|
|
1605
|
+
byToolRendered = true;
|
|
1414
1606
|
const byTool = /* @__PURE__ */ new Map();
|
|
1415
1607
|
for (const s of health.sessions) {
|
|
1416
1608
|
const existing = byTool.get(s.connector) ?? { calls: 0, compressed: 0, failed: 0, processed: 0, saved: 0, p95: null };
|
|
@@ -1432,6 +1624,19 @@ var init_stats_view = __esm({
|
|
|
1432
1624
|
lines.push(blank());
|
|
1433
1625
|
}
|
|
1434
1626
|
}
|
|
1627
|
+
if (health?.cursor) {
|
|
1628
|
+
const cur = health.cursor;
|
|
1629
|
+
if (!byToolRendered) {
|
|
1630
|
+
lines.push(divider("By Tool", w));
|
|
1631
|
+
lines.push(blank());
|
|
1632
|
+
}
|
|
1633
|
+
const curPct = cur.tokensProcessed > 0 ? `${(cur.tokensSaved / cur.tokensProcessed * 100).toFixed(1)}%` : "\u2014";
|
|
1634
|
+
const avgMs = cur.compressions > 0 ? Math.round(cur.apiMsSumMs / cur.compressions) : 0;
|
|
1635
|
+
lines.push(`${c.bold}${formatLabel("cursor")}${c.reset}`);
|
|
1636
|
+
lines.push(` Files: ${cur.files} unique (${cur.compressions} compressions${cur.errors > 0 ? `, ${c.red}${cur.errors} errors${c.reset}` : ""}) | Saved: ${formatNum(cur.tokensSaved)} tok (${curPct})`);
|
|
1637
|
+
lines.push(` Cache: ${cur.cacheCount} files | Avg API: ${avgMs}ms/file`);
|
|
1638
|
+
lines.push(blank());
|
|
1639
|
+
}
|
|
1435
1640
|
if (cum.sessionCount > 0) {
|
|
1436
1641
|
lines.push(`${c.dim}${cum.sessionCount} session${cum.sessionCount !== 1 ? "s" : ""} recorded${c.reset}`);
|
|
1437
1642
|
}
|
|
@@ -1473,6 +1678,17 @@ var init_config_view = __esm({
|
|
|
1473
1678
|
});
|
|
1474
1679
|
|
|
1475
1680
|
// src/ui/views/logs-view.ts
|
|
1681
|
+
function getCursorLogs() {
|
|
1682
|
+
if (!_readCursorLogs) {
|
|
1683
|
+
try {
|
|
1684
|
+
const mod = (init_stats(), __toCommonJS(stats_exports));
|
|
1685
|
+
_readCursorLogs = () => mod.readCursorLogEntries(50);
|
|
1686
|
+
} catch {
|
|
1687
|
+
_readCursorLogs = () => [];
|
|
1688
|
+
}
|
|
1689
|
+
}
|
|
1690
|
+
return _readCursorLogs();
|
|
1691
|
+
}
|
|
1476
1692
|
function colorizeLog(line, maxWidth) {
|
|
1477
1693
|
const display = line.length > maxWidth ? line.slice(0, maxWidth - 1) + "\u2026" : line;
|
|
1478
1694
|
const match = display.match(/^\[(\d{2}:\d{2}:\d{2}(?:\.\d+)?)\]\s*(.*)$/);
|
|
@@ -1495,21 +1711,33 @@ function colorizeLog(line, maxWidth) {
|
|
|
1495
1711
|
}
|
|
1496
1712
|
return `${c.dim}${ts} ${rest}${c.reset}`;
|
|
1497
1713
|
}
|
|
1498
|
-
var logsView;
|
|
1714
|
+
var _readCursorLogs, logsView;
|
|
1499
1715
|
var init_logs_view = __esm({
|
|
1500
1716
|
"src/ui/views/logs-view.ts"() {
|
|
1501
1717
|
"use strict";
|
|
1502
1718
|
init_format();
|
|
1503
1719
|
init_layout();
|
|
1720
|
+
_readCursorLogs = null;
|
|
1504
1721
|
logsView = {
|
|
1505
1722
|
id: "logs",
|
|
1506
1723
|
label: "Logs",
|
|
1507
1724
|
render(state, size) {
|
|
1508
1725
|
const lines = [];
|
|
1509
1726
|
const w = Math.max(40, size.cols - 6);
|
|
1510
|
-
lines.push(divider("
|
|
1727
|
+
lines.push(divider("All Logs", w));
|
|
1511
1728
|
lines.push(blank());
|
|
1512
|
-
|
|
1729
|
+
const cursorEntries = getCursorLogs();
|
|
1730
|
+
const allLogs = [];
|
|
1731
|
+
for (const line of state.recentLogs) {
|
|
1732
|
+
const match = line.match(/^\[(\d{2}:\d{2}:\d{2}(?:\.\d+)?)\]/);
|
|
1733
|
+
allLogs.push({ ts: match?.[1] ?? "99:99:99", line, source: "daemon" });
|
|
1734
|
+
}
|
|
1735
|
+
for (const entry of cursorEntries) {
|
|
1736
|
+
allLogs.push({ ts: entry.ts, line: entry.line, source: "cursor" });
|
|
1737
|
+
}
|
|
1738
|
+
const daemonLogs = state.recentLogs;
|
|
1739
|
+
const combined = [...daemonLogs, ...cursorEntries.map((e) => e.line)];
|
|
1740
|
+
if (combined.length === 0) {
|
|
1513
1741
|
if (!state.daemonRunning) {
|
|
1514
1742
|
lines.push(`${c.dim}Daemon not running \u2014 no logs to show.${c.reset}`);
|
|
1515
1743
|
} else {
|
|
@@ -1519,12 +1747,12 @@ var init_logs_view = __esm({
|
|
|
1519
1747
|
return lines;
|
|
1520
1748
|
}
|
|
1521
1749
|
const maxLines = Math.max(5, size.rows - 7);
|
|
1522
|
-
const tail =
|
|
1750
|
+
const tail = combined.slice(-maxLines);
|
|
1523
1751
|
for (const line of tail) {
|
|
1524
1752
|
lines.push(colorizeLog(line, w));
|
|
1525
1753
|
}
|
|
1526
1754
|
lines.push(blank());
|
|
1527
|
-
lines.push(`${c.dim}Showing last ${tail.length} lines \u2014 refreshes every 2s${c.reset}`);
|
|
1755
|
+
lines.push(`${c.dim}Showing last ${tail.length} lines (daemon + cursor) \u2014 refreshes every 2s${c.reset}`);
|
|
1528
1756
|
return lines;
|
|
1529
1757
|
}
|
|
1530
1758
|
};
|
|
@@ -1536,7 +1764,7 @@ var hub_exports = {};
|
|
|
1536
1764
|
__export(hub_exports, {
|
|
1537
1765
|
runHub: () => runHub
|
|
1538
1766
|
});
|
|
1539
|
-
import { existsSync as
|
|
1767
|
+
import { existsSync as existsSync16, statSync as statSync3 } from "fs";
|
|
1540
1768
|
function createInitialState() {
|
|
1541
1769
|
const state = {
|
|
1542
1770
|
health: null,
|
|
@@ -1597,7 +1825,7 @@ async function refreshState(state) {
|
|
|
1597
1825
|
}
|
|
1598
1826
|
function tailLogFile(maxLines) {
|
|
1599
1827
|
try {
|
|
1600
|
-
if (!
|
|
1828
|
+
if (!existsSync16(LOG_FILE)) return [];
|
|
1601
1829
|
const stat = statSync3(LOG_FILE);
|
|
1602
1830
|
const readSize = Math.min(stat.size, 32 * 1024);
|
|
1603
1831
|
if (readSize === 0) return [];
|
|
@@ -1907,18 +2135,37 @@ async function createApiKey(accessToken, userId, source = "cli") {
|
|
|
1907
2135
|
);
|
|
1908
2136
|
const apiKey = `${prefix}${randomBytes(32).toString("hex")}`;
|
|
1909
2137
|
const keyHash = createHash("sha256").update(apiKey).digest("hex");
|
|
2138
|
+
let projectId;
|
|
2139
|
+
let orgId;
|
|
2140
|
+
try {
|
|
2141
|
+
const projRes = await fetch(
|
|
2142
|
+
`${SUPABASE_URL}/rest/v1/projects?id=eq.${userId}&select=id,org_id&limit=1`,
|
|
2143
|
+
{ headers: supabaseHeaders(accessToken), signal: AbortSignal.timeout(1e4) }
|
|
2144
|
+
);
|
|
2145
|
+
if (projRes.ok) {
|
|
2146
|
+
const rows = await projRes.json();
|
|
2147
|
+
if (Array.isArray(rows) && rows.length > 0) {
|
|
2148
|
+
projectId = rows[0].id;
|
|
2149
|
+
orgId = rows[0].org_id;
|
|
2150
|
+
}
|
|
2151
|
+
}
|
|
2152
|
+
} catch {
|
|
2153
|
+
}
|
|
2154
|
+
const insertPayload = {
|
|
2155
|
+
user_id: userId,
|
|
2156
|
+
key_name: keyName,
|
|
2157
|
+
key_hash: keyHash,
|
|
2158
|
+
is_active: true
|
|
2159
|
+
};
|
|
2160
|
+
if (projectId) insertPayload.project_id = projectId;
|
|
2161
|
+
if (orgId) insertPayload.organization_id = orgId;
|
|
1910
2162
|
const res = await fetch(`${SUPABASE_URL}/rest/v1/user_api_keys`, {
|
|
1911
2163
|
method: "POST",
|
|
1912
2164
|
headers: {
|
|
1913
2165
|
...supabaseHeaders(accessToken),
|
|
1914
2166
|
"Prefer": "return=representation"
|
|
1915
2167
|
},
|
|
1916
|
-
body: JSON.stringify(
|
|
1917
|
-
user_id: userId,
|
|
1918
|
-
key_name: keyName,
|
|
1919
|
-
key_hash: keyHash,
|
|
1920
|
-
is_active: true
|
|
1921
|
-
})
|
|
2168
|
+
body: JSON.stringify(insertPayload)
|
|
1922
2169
|
});
|
|
1923
2170
|
if (!res.ok) {
|
|
1924
2171
|
const body = await res.json().catch(() => ({}));
|
|
@@ -2929,6 +3176,7 @@ init_loader();
|
|
|
2929
3176
|
import { RSCCircuitOpenError as RSCCircuitOpenError2 } from "@cognisos/rsc-sdk";
|
|
2930
3177
|
|
|
2931
3178
|
// src/rsc/message-compressor.ts
|
|
3179
|
+
import { createHash as createHash2 } from "crypto";
|
|
2932
3180
|
import { RSCCircuitOpenError } from "@cognisos/rsc-sdk";
|
|
2933
3181
|
|
|
2934
3182
|
// src/rsc/content-segmenter.ts
|
|
@@ -3030,6 +3278,10 @@ function isIndentedCodeLine(line) {
|
|
|
3030
3278
|
}
|
|
3031
3279
|
|
|
3032
3280
|
// src/rsc/message-compressor.ts
|
|
3281
|
+
init_tokenizer();
|
|
3282
|
+
function contentHash(text) {
|
|
3283
|
+
return createHash2("sha256").update(text).digest("hex").slice(0, 16);
|
|
3284
|
+
}
|
|
3033
3285
|
var PASSTHROUGH_BLOCK_TYPES = /* @__PURE__ */ new Set(["thinking", "tool_use", "image"]);
|
|
3034
3286
|
function sanitizeCompressedText(text) {
|
|
3035
3287
|
return text.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, "");
|
|
@@ -3097,43 +3349,90 @@ async function compressConversation(pipeline, session, plan, options = { compres
|
|
|
3097
3349
|
}
|
|
3098
3350
|
let anyCompressed = false;
|
|
3099
3351
|
let totalTokensSaved = 0;
|
|
3100
|
-
|
|
3101
|
-
|
|
3102
|
-
|
|
3103
|
-
|
|
3104
|
-
|
|
3105
|
-
|
|
3106
|
-
|
|
3107
|
-
|
|
3108
|
-
|
|
3109
|
-
|
|
3110
|
-
|
|
3111
|
-
|
|
3112
|
-
|
|
3113
|
-
const
|
|
3114
|
-
const saved = Math.max(0, result.metrics.tokensSaved);
|
|
3352
|
+
const cache = options.stickyCache;
|
|
3353
|
+
const uncached = [];
|
|
3354
|
+
for (const entry of compressible) {
|
|
3355
|
+
if (!cache) {
|
|
3356
|
+
uncached.push(entry);
|
|
3357
|
+
continue;
|
|
3358
|
+
}
|
|
3359
|
+
const hash = contentHash(entry.batchText);
|
|
3360
|
+
const cached = cache.get(hash);
|
|
3361
|
+
if (cached !== void 0) {
|
|
3362
|
+
log?.(`[STICKY] Cache hit for message #${entry.tm.index}`);
|
|
3363
|
+
const originalTokens = countTokens(entry.batchText);
|
|
3364
|
+
const compressedTokens = countTokens(cached);
|
|
3365
|
+
const saved = Math.max(0, originalTokens - compressedTokens);
|
|
3115
3366
|
if (saved > 0) {
|
|
3116
3367
|
anyCompressed = true;
|
|
3117
3368
|
totalTokensSaved += saved;
|
|
3118
3369
|
}
|
|
3119
|
-
session.recordCompression(
|
|
3120
|
-
|
|
3121
|
-
|
|
3122
|
-
|
|
3123
|
-
|
|
3124
|
-
|
|
3125
|
-
|
|
3126
|
-
|
|
3370
|
+
session.recordCompression({
|
|
3371
|
+
inputTokens: originalTokens,
|
|
3372
|
+
outputTokens: compressedTokens,
|
|
3373
|
+
tokensSaved: saved,
|
|
3374
|
+
skipped: false,
|
|
3375
|
+
fabricHits: 0,
|
|
3376
|
+
fabricMisses: 0,
|
|
3377
|
+
ratio: compressedTokens / Math.max(originalTokens, 1),
|
|
3378
|
+
deltaMdlBits: 0,
|
|
3379
|
+
processingTimeMs: 0
|
|
3380
|
+
});
|
|
3381
|
+
results[entry.planIdx] = reassembleMessage(entry.tm.message, cached, entry.batchedIndices);
|
|
3127
3382
|
} else {
|
|
3128
|
-
|
|
3383
|
+
uncached.push(entry);
|
|
3129
3384
|
}
|
|
3130
|
-
|
|
3131
|
-
|
|
3132
|
-
|
|
3385
|
+
}
|
|
3386
|
+
if (uncached.length > 0) {
|
|
3387
|
+
if (options.semaphore) await options.semaphore.acquire(options.semaphoreTimeoutMs);
|
|
3388
|
+
try {
|
|
3389
|
+
const batchSegments = uncached.map((entry) => ({
|
|
3390
|
+
index: entry.planIdx,
|
|
3391
|
+
text: entry.batchText
|
|
3392
|
+
}));
|
|
3393
|
+
const batchResults = await pipeline.normalizeBatch(batchSegments);
|
|
3394
|
+
for (const entry of uncached) {
|
|
3395
|
+
const result = batchResults.get(entry.planIdx);
|
|
3396
|
+
if (!result || result.metrics.skipped) {
|
|
3397
|
+
results[entry.planIdx] = entry.tm.message;
|
|
3398
|
+
continue;
|
|
3399
|
+
}
|
|
3400
|
+
const compressed = sanitizeCompressedText(result.text);
|
|
3401
|
+
const originalTokens = countTokens(entry.batchText);
|
|
3402
|
+
const compressedTokens = countTokens(compressed);
|
|
3403
|
+
const saved = Math.max(0, originalTokens - compressedTokens);
|
|
3404
|
+
const realMetrics = {
|
|
3405
|
+
...result.metrics,
|
|
3406
|
+
inputTokens: originalTokens,
|
|
3407
|
+
outputTokens: compressedTokens,
|
|
3408
|
+
tokensSaved: saved
|
|
3409
|
+
};
|
|
3410
|
+
if (saved > 0) {
|
|
3411
|
+
anyCompressed = true;
|
|
3412
|
+
totalTokensSaved += saved;
|
|
3413
|
+
}
|
|
3414
|
+
session.recordCompression(realMetrics);
|
|
3415
|
+
results[entry.planIdx] = reassembleMessage(entry.tm.message, compressed, entry.batchedIndices);
|
|
3416
|
+
if (cache) {
|
|
3417
|
+
cache.set(contentHash(entry.batchText), compressed);
|
|
3418
|
+
}
|
|
3419
|
+
}
|
|
3420
|
+
} catch (err) {
|
|
3421
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
3422
|
+
if (err instanceof RSCCircuitOpenError) {
|
|
3423
|
+
session.recordFailure();
|
|
3424
|
+
log?.(`[COMPRESS-ERROR] Circuit open \u2014 passing through (${errMsg})`);
|
|
3425
|
+
} else {
|
|
3426
|
+
log?.(`[COMPRESS-ERROR] ${errMsg}`);
|
|
3133
3427
|
}
|
|
3428
|
+
for (const entry of uncached) {
|
|
3429
|
+
if (!results[entry.planIdx]) {
|
|
3430
|
+
results[entry.planIdx] = entry.tm.message;
|
|
3431
|
+
}
|
|
3432
|
+
}
|
|
3433
|
+
} finally {
|
|
3434
|
+
if (options.semaphore) options.semaphore.release();
|
|
3134
3435
|
}
|
|
3135
|
-
} finally {
|
|
3136
|
-
if (options.semaphore) options.semaphore.release();
|
|
3137
3436
|
}
|
|
3138
3437
|
return { messages: results, anyCompressed, totalTokensSaved };
|
|
3139
3438
|
}
|
|
@@ -3156,6 +3455,7 @@ function extractBatchableText(msg, compressToolResults) {
|
|
|
3156
3455
|
batchedIndices.add(i);
|
|
3157
3456
|
}
|
|
3158
3457
|
if (part.type === "tool_result" && compressToolResults) {
|
|
3458
|
+
if (part.is_error) continue;
|
|
3159
3459
|
const extracted = extractToolResultText(part);
|
|
3160
3460
|
if (extracted) {
|
|
3161
3461
|
textSegments.push(extracted);
|
|
@@ -3190,7 +3490,8 @@ function reassembleMessage(msg, compressedText, batchedIndices) {
|
|
|
3190
3490
|
isFirstEligible = false;
|
|
3191
3491
|
} else {
|
|
3192
3492
|
if (parts[i].type === "tool_result") {
|
|
3193
|
-
|
|
3493
|
+
const cleared = parts[i].is_error ? parts[i] : { ...parts[i], content: "" };
|
|
3494
|
+
newParts.push(cleared);
|
|
3194
3495
|
}
|
|
3195
3496
|
}
|
|
3196
3497
|
}
|
|
@@ -3272,7 +3573,7 @@ async function compressArrayContent(msg, pipeline, session, record, options = {
|
|
|
3272
3573
|
return part;
|
|
3273
3574
|
}
|
|
3274
3575
|
}
|
|
3275
|
-
if (part.type === "tool_result" && options.compressToolResults) {
|
|
3576
|
+
if (part.type === "tool_result" && options.compressToolResults && !part.is_error) {
|
|
3276
3577
|
return compressToolResult(part, pipeline, session, record);
|
|
3277
3578
|
}
|
|
3278
3579
|
return part;
|
|
@@ -3331,8 +3632,11 @@ async function compressTextWithSegmentation(text, pipeline, session, record, sem
|
|
|
3331
3632
|
if (semaphore) await semaphore.acquire(semaphoreTimeoutMs);
|
|
3332
3633
|
try {
|
|
3333
3634
|
const result = await pipeline.compressForLLM(text);
|
|
3334
|
-
|
|
3335
|
-
const
|
|
3635
|
+
const originalTok = countTokens(text);
|
|
3636
|
+
const compressedTok = countTokens(sanitizeCompressedText(result.text));
|
|
3637
|
+
const saved = Math.max(0, originalTok - compressedTok);
|
|
3638
|
+
const realMetrics = { ...result.metrics, inputTokens: originalTok, outputTokens: compressedTok, tokensSaved: saved };
|
|
3639
|
+
session.recordCompression(realMetrics);
|
|
3336
3640
|
record(!result.metrics.skipped, saved);
|
|
3337
3641
|
return sanitizeCompressedText(result.text);
|
|
3338
3642
|
} finally {
|
|
@@ -3346,8 +3650,11 @@ async function compressTextWithSegmentation(text, pipeline, session, record, sem
|
|
|
3346
3650
|
if (semaphore) await semaphore.acquire(semaphoreTimeoutMs);
|
|
3347
3651
|
try {
|
|
3348
3652
|
const result = await pipeline.compressForLLM(seg.text);
|
|
3349
|
-
|
|
3350
|
-
const
|
|
3653
|
+
const originalTok = countTokens(seg.text);
|
|
3654
|
+
const compressedTok = countTokens(sanitizeCompressedText(result.text));
|
|
3655
|
+
const saved = Math.max(0, originalTok - compressedTok);
|
|
3656
|
+
const realMetrics = { ...result.metrics, inputTokens: originalTok, outputTokens: compressedTok, tokensSaved: saved };
|
|
3657
|
+
session.recordCompression(realMetrics);
|
|
3351
3658
|
record(!result.metrics.skipped, saved);
|
|
3352
3659
|
return sanitizeCompressedText(result.text);
|
|
3353
3660
|
} catch (err) {
|
|
@@ -3372,7 +3679,7 @@ function estimateBlockTokens(block, compressToolResults) {
|
|
|
3372
3679
|
if (block.type === "text" && typeof block.text === "string") {
|
|
3373
3680
|
return estimateTokens(block.text);
|
|
3374
3681
|
}
|
|
3375
|
-
if (block.type === "tool_result" && compressToolResults) {
|
|
3682
|
+
if (block.type === "tool_result" && compressToolResults && !block.is_error) {
|
|
3376
3683
|
if (typeof block.content === "string") {
|
|
3377
3684
|
return estimateTokens(block.content);
|
|
3378
3685
|
}
|
|
@@ -3586,7 +3893,7 @@ function extractBearerToken(req) {
|
|
|
3586
3893
|
if (!auth || !auth.startsWith("Bearer ")) return null;
|
|
3587
3894
|
return auth.slice(7);
|
|
3588
3895
|
}
|
|
3589
|
-
async function handleChatCompletions(req, res, body, pipeline, config, logger, semaphore, latencyMonitor, sessionKey) {
|
|
3896
|
+
async function handleChatCompletions(req, res, body, pipeline, config, logger, semaphore, latencyMonitor, sessionKey, stickyCache) {
|
|
3590
3897
|
const request = body;
|
|
3591
3898
|
if (!request.messages || !Array.isArray(request.messages)) {
|
|
3592
3899
|
sendJSON(res, 400, {
|
|
@@ -3645,7 +3952,8 @@ async function handleChatCompletions(req, res, body, pipeline, config, logger, s
|
|
|
3645
3952
|
compressionThreshold: config.compressionThreshold,
|
|
3646
3953
|
logFn: blockLogFn,
|
|
3647
3954
|
semaphore,
|
|
3648
|
-
semaphoreTimeoutMs: config.concurrencyTimeoutMs
|
|
3955
|
+
semaphoreTimeoutMs: config.concurrencyTimeoutMs,
|
|
3956
|
+
stickyCache
|
|
3649
3957
|
}
|
|
3650
3958
|
);
|
|
3651
3959
|
const totalBlocks = batchedCount + skippedCount + hotCount;
|
|
@@ -3696,7 +4004,7 @@ async function handleChatCompletions(req, res, body, pipeline, config, logger, s
|
|
|
3696
4004
|
return;
|
|
3697
4005
|
}
|
|
3698
4006
|
if (request.stream && upstreamResponse.body) {
|
|
3699
|
-
const learningBuffer =
|
|
4007
|
+
const learningBuffer = createStreamLearningBuffer(pipeline.pipeline);
|
|
3700
4008
|
logger.log(formatResponseLog(request.model, totalTokensSaved, true));
|
|
3701
4009
|
await pipeSSEResponse(
|
|
3702
4010
|
upstreamResponse,
|
|
@@ -3727,7 +4035,7 @@ async function handleChatCompletions(req, res, body, pipeline, config, logger, s
|
|
|
3727
4035
|
setCORSHeaders(res);
|
|
3728
4036
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
3729
4037
|
res.end(finalBody);
|
|
3730
|
-
|
|
4038
|
+
{
|
|
3731
4039
|
try {
|
|
3732
4040
|
const parsed = JSON.parse(responseBody);
|
|
3733
4041
|
const content = parsed?.choices?.[0]?.message?.content;
|
|
@@ -3752,18 +4060,33 @@ async function handleChatCompletions(req, res, body, pipeline, config, logger, s
|
|
|
3752
4060
|
import { RSCCircuitOpenError as RSCCircuitOpenError3 } from "@cognisos/rsc-sdk";
|
|
3753
4061
|
|
|
3754
4062
|
// src/proxy/anthropic-streaming.ts
|
|
3755
|
-
function
|
|
4063
|
+
function parseMessageStart(dataLine, tokensSaved) {
|
|
4064
|
+
try {
|
|
4065
|
+
const json = JSON.parse(dataLine.slice(6));
|
|
4066
|
+
const usage2 = json?.message?.usage;
|
|
4067
|
+
if (usage2?.input_tokens != null) {
|
|
4068
|
+
const actual = usage2.input_tokens + (usage2.cache_creation_input_tokens ?? 0) + (usage2.cache_read_input_tokens ?? 0);
|
|
4069
|
+
if (tokensSaved > 0) {
|
|
4070
|
+
json.message.usage.input_tokens += tokensSaved;
|
|
4071
|
+
return [`data: ${JSON.stringify(json)}`, actual];
|
|
4072
|
+
}
|
|
4073
|
+
return [null, actual];
|
|
4074
|
+
}
|
|
4075
|
+
} catch {
|
|
4076
|
+
}
|
|
4077
|
+
return [null, null];
|
|
4078
|
+
}
|
|
4079
|
+
function parseMessageDelta(dataLine) {
|
|
3756
4080
|
try {
|
|
3757
4081
|
const json = JSON.parse(dataLine.slice(6));
|
|
3758
|
-
if (json?.
|
|
3759
|
-
json.
|
|
3760
|
-
return `data: ${JSON.stringify(json)}`;
|
|
4082
|
+
if (json?.usage?.output_tokens != null) {
|
|
4083
|
+
return json.usage.output_tokens;
|
|
3761
4084
|
}
|
|
3762
4085
|
} catch {
|
|
3763
4086
|
}
|
|
3764
4087
|
return null;
|
|
3765
4088
|
}
|
|
3766
|
-
async function pipeAnthropicSSEResponse(upstreamResponse, clientRes, onContentDelta, onComplete, totalTokensSaved = 0) {
|
|
4089
|
+
async function pipeAnthropicSSEResponse(upstreamResponse, clientRes, onContentDelta, onComplete, totalTokensSaved = 0, onUsage) {
|
|
3767
4090
|
clientRes.writeHead(200, {
|
|
3768
4091
|
"Content-Type": "text/event-stream",
|
|
3769
4092
|
"Cache-Control": "no-cache",
|
|
@@ -3774,14 +4097,19 @@ async function pipeAnthropicSSEResponse(upstreamResponse, clientRes, onContentDe
|
|
|
3774
4097
|
const decoder = new TextDecoder();
|
|
3775
4098
|
let lineBuf = "";
|
|
3776
4099
|
let currentEvent = "";
|
|
3777
|
-
let
|
|
4100
|
+
let capturedInputTokens = null;
|
|
4101
|
+
let capturedOutputTokens = null;
|
|
4102
|
+
let inputAdjusted = false;
|
|
3778
4103
|
const needsAdjustment = totalTokensSaved > 0;
|
|
4104
|
+
const needsCapture = !!onUsage;
|
|
3779
4105
|
try {
|
|
3780
4106
|
while (true) {
|
|
3781
4107
|
const { done, value } = await reader.read();
|
|
3782
4108
|
if (done) break;
|
|
3783
4109
|
const chunk = decoder.decode(value, { stream: true });
|
|
3784
|
-
|
|
4110
|
+
const inputDone = !needsAdjustment || inputAdjusted;
|
|
4111
|
+
const captureDone = !needsCapture || capturedInputTokens != null && capturedOutputTokens != null;
|
|
4112
|
+
if (inputDone && captureDone) {
|
|
3785
4113
|
clientRes.write(chunk);
|
|
3786
4114
|
lineBuf += chunk;
|
|
3787
4115
|
const lines2 = lineBuf.split("\n");
|
|
@@ -3804,21 +4132,32 @@ async function pipeAnthropicSSEResponse(upstreamResponse, clientRes, onContentDe
|
|
|
3804
4132
|
lineBuf += chunk;
|
|
3805
4133
|
const lines = lineBuf.split("\n");
|
|
3806
4134
|
lineBuf = lines.pop() || "";
|
|
3807
|
-
let
|
|
4135
|
+
let lineModified = false;
|
|
3808
4136
|
const outputLines = [];
|
|
3809
4137
|
for (const line of lines) {
|
|
3810
4138
|
if (line.startsWith("event: ")) {
|
|
3811
4139
|
currentEvent = line.slice(7).trim();
|
|
3812
4140
|
outputLines.push(line);
|
|
3813
|
-
} else if (line.startsWith("data: ") && currentEvent === "message_start" &&
|
|
3814
|
-
const
|
|
3815
|
-
if (
|
|
3816
|
-
|
|
3817
|
-
|
|
3818
|
-
adjusted
|
|
4141
|
+
} else if (line.startsWith("data: ") && currentEvent === "message_start" && capturedInputTokens == null) {
|
|
4142
|
+
const [adjusted, actual] = parseMessageStart(line, totalTokensSaved);
|
|
4143
|
+
if (actual != null) {
|
|
4144
|
+
capturedInputTokens = actual;
|
|
4145
|
+
inputAdjusted = true;
|
|
4146
|
+
if (adjusted && needsAdjustment) {
|
|
4147
|
+
outputLines.push(adjusted);
|
|
4148
|
+
lineModified = true;
|
|
4149
|
+
} else {
|
|
4150
|
+
outputLines.push(line);
|
|
4151
|
+
}
|
|
3819
4152
|
} else {
|
|
3820
4153
|
outputLines.push(line);
|
|
3821
4154
|
}
|
|
4155
|
+
} else if (line.startsWith("data: ") && currentEvent === "message_delta" && capturedOutputTokens == null) {
|
|
4156
|
+
const outputTok = parseMessageDelta(line);
|
|
4157
|
+
if (outputTok != null) {
|
|
4158
|
+
capturedOutputTokens = outputTok;
|
|
4159
|
+
}
|
|
4160
|
+
outputLines.push(line);
|
|
3822
4161
|
} else {
|
|
3823
4162
|
outputLines.push(line);
|
|
3824
4163
|
if (line.startsWith("data: ") && currentEvent === "content_block_delta") {
|
|
@@ -3832,14 +4171,20 @@ async function pipeAnthropicSSEResponse(upstreamResponse, clientRes, onContentDe
|
|
|
3832
4171
|
}
|
|
3833
4172
|
}
|
|
3834
4173
|
}
|
|
3835
|
-
if (
|
|
3836
|
-
const reconstructed = outputLines.join("\n") + "\n"
|
|
4174
|
+
if (lineModified) {
|
|
4175
|
+
const reconstructed = outputLines.join("\n") + "\n";
|
|
3837
4176
|
clientRes.write(reconstructed);
|
|
3838
4177
|
} else {
|
|
3839
4178
|
clientRes.write(chunk);
|
|
3840
4179
|
}
|
|
3841
4180
|
}
|
|
3842
4181
|
} finally {
|
|
4182
|
+
if (onUsage && (capturedInputTokens != null || capturedOutputTokens != null)) {
|
|
4183
|
+
onUsage({
|
|
4184
|
+
inputTokens: capturedInputTokens ?? 0,
|
|
4185
|
+
outputTokens: capturedOutputTokens ?? 0
|
|
4186
|
+
});
|
|
4187
|
+
}
|
|
3843
4188
|
clientRes.end();
|
|
3844
4189
|
onComplete();
|
|
3845
4190
|
}
|
|
@@ -3880,7 +4225,7 @@ function convertCompressedToAnthropic(messages) {
|
|
|
3880
4225
|
content: msg.content
|
|
3881
4226
|
}));
|
|
3882
4227
|
}
|
|
3883
|
-
async function handleAnthropicMessages(req, res, body, pipeline, config, logger, semaphore, latencyMonitor, sessionKey) {
|
|
4228
|
+
async function handleAnthropicMessages(req, res, body, pipeline, config, logger, semaphore, latencyMonitor, sessionKey, onUsage, stickyCache) {
|
|
3884
4229
|
const request = body;
|
|
3885
4230
|
if (!request.messages || !Array.isArray(request.messages)) {
|
|
3886
4231
|
sendAnthropicError(res, 400, "invalid_request_error", "messages is required and must be an array");
|
|
@@ -3895,6 +4240,8 @@ async function handleAnthropicMessages(req, res, body, pipeline, config, logger,
|
|
|
3895
4240
|
sendAnthropicError(res, 401, "authentication_error", "Authentication required (x-api-key or Authorization header)");
|
|
3896
4241
|
return;
|
|
3897
4242
|
}
|
|
4243
|
+
const { countTokens: countTok } = await Promise.resolve().then(() => (init_tokenizer(), tokenizer_exports));
|
|
4244
|
+
const originalInputTokens = countTok(JSON.stringify(request));
|
|
3898
4245
|
let messages = request.messages;
|
|
3899
4246
|
let anyCompressed = false;
|
|
3900
4247
|
let totalTokensSaved = 0;
|
|
@@ -3940,7 +4287,8 @@ async function handleAnthropicMessages(req, res, body, pipeline, config, logger,
|
|
|
3940
4287
|
compressionThreshold: config.compressionThreshold,
|
|
3941
4288
|
logFn: blockLogFn,
|
|
3942
4289
|
semaphore,
|
|
3943
|
-
semaphoreTimeoutMs: config.concurrencyTimeoutMs
|
|
4290
|
+
semaphoreTimeoutMs: config.concurrencyTimeoutMs,
|
|
4291
|
+
stickyCache
|
|
3944
4292
|
}
|
|
3945
4293
|
);
|
|
3946
4294
|
const totalBlocks = batchedCount + skippedCount + hotCount;
|
|
@@ -3968,6 +4316,8 @@ async function handleAnthropicMessages(req, res, body, pipeline, config, logger,
|
|
|
3968
4316
|
}
|
|
3969
4317
|
const upstreamUrl = `${config.anthropicUpstreamUrl}/v1/messages`;
|
|
3970
4318
|
const upstreamBody = { ...request, messages };
|
|
4319
|
+
const compressedInputTokens = countTok(JSON.stringify(upstreamBody));
|
|
4320
|
+
const verifiedTokensSaved = Math.max(0, originalInputTokens - compressedInputTokens);
|
|
3971
4321
|
const upstreamHeaders = {
|
|
3972
4322
|
...authHeaders,
|
|
3973
4323
|
"anthropic-version": req.headers["anthropic-version"] || "2023-06-01",
|
|
@@ -3996,35 +4346,44 @@ async function handleAnthropicMessages(req, res, body, pipeline, config, logger,
|
|
|
3996
4346
|
return;
|
|
3997
4347
|
}
|
|
3998
4348
|
if (request.stream && upstreamResponse.body) {
|
|
3999
|
-
const learningBuffer =
|
|
4349
|
+
const learningBuffer = createStreamLearningBuffer(pipeline.pipeline);
|
|
4000
4350
|
logger.log(formatResponseLog(request.model, totalTokensSaved, true));
|
|
4001
4351
|
await pipeAnthropicSSEResponse(
|
|
4002
4352
|
upstreamResponse,
|
|
4003
4353
|
res,
|
|
4004
4354
|
(text) => learningBuffer?.append(text),
|
|
4005
4355
|
() => learningBuffer?.flush(),
|
|
4006
|
-
totalTokensSaved
|
|
4356
|
+
totalTokensSaved,
|
|
4357
|
+
onUsage ? (usage2) => {
|
|
4358
|
+
onUsage(usage2, verifiedTokensSaved, originalInputTokens);
|
|
4359
|
+
logger.log(`[TOKENS] input: ${usage2.inputTokens} | output: ${usage2.outputTokens} | verified_saved: ${verifiedTokensSaved} | orig_tok: ${originalInputTokens} | comp_tok: ${compressedInputTokens}`);
|
|
4360
|
+
} : void 0
|
|
4007
4361
|
);
|
|
4008
4362
|
return;
|
|
4009
4363
|
}
|
|
4010
4364
|
const responseBody = await upstreamResponse.text();
|
|
4011
4365
|
logger.log(formatResponseLog(request.model, totalTokensSaved));
|
|
4012
4366
|
let finalBody = responseBody;
|
|
4013
|
-
|
|
4014
|
-
|
|
4015
|
-
|
|
4016
|
-
|
|
4367
|
+
try {
|
|
4368
|
+
const parsed = JSON.parse(responseBody);
|
|
4369
|
+
if (parsed?.usage) {
|
|
4370
|
+
const actualInput = (parsed.usage.input_tokens ?? 0) + (parsed.usage.cache_creation_input_tokens ?? 0) + (parsed.usage.cache_read_input_tokens ?? 0);
|
|
4371
|
+
const actualOutput = parsed.usage.output_tokens ?? 0;
|
|
4372
|
+
if (onUsage) {
|
|
4373
|
+
onUsage({ inputTokens: actualInput, outputTokens: actualOutput }, verifiedTokensSaved, originalInputTokens);
|
|
4374
|
+
logger.log(`[TOKENS] input: ${actualInput} | output: ${actualOutput} | verified_saved: ${verifiedTokensSaved} | orig_tok: ${originalInputTokens} | comp_tok: ${compressedInputTokens}`);
|
|
4375
|
+
}
|
|
4376
|
+
if (totalTokensSaved > 0 && parsed.usage.input_tokens != null) {
|
|
4017
4377
|
parsed.usage.input_tokens += totalTokensSaved;
|
|
4018
4378
|
finalBody = JSON.stringify(parsed);
|
|
4019
|
-
logger.log(`[TOKENS] Adjusted input_tokens by +${totalTokensSaved}`);
|
|
4020
4379
|
}
|
|
4021
|
-
} catch {
|
|
4022
4380
|
}
|
|
4381
|
+
} catch {
|
|
4023
4382
|
}
|
|
4024
4383
|
setCORSHeaders2(res);
|
|
4025
4384
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
4026
4385
|
res.end(finalBody);
|
|
4027
|
-
|
|
4386
|
+
{
|
|
4028
4387
|
try {
|
|
4029
4388
|
const parsed = JSON.parse(responseBody);
|
|
4030
4389
|
const textBlocks = parsed?.content?.filter(
|
|
@@ -4317,7 +4676,7 @@ async function handleResponses(req, res, body, pipeline, config, logger, semapho
|
|
|
4317
4676
|
return;
|
|
4318
4677
|
}
|
|
4319
4678
|
if (request.stream && upstreamResponse.body) {
|
|
4320
|
-
const learningBuffer =
|
|
4679
|
+
const learningBuffer = createStreamLearningBuffer(pipeline.pipeline);
|
|
4321
4680
|
logger.log(formatResponseLog(request.model, totalTokensSaved, true));
|
|
4322
4681
|
await pipeResponsesSSE(
|
|
4323
4682
|
upstreamResponse,
|
|
@@ -4348,7 +4707,7 @@ async function handleResponses(req, res, body, pipeline, config, logger, semapho
|
|
|
4348
4707
|
setCORSHeaders3(res);
|
|
4349
4708
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
4350
4709
|
res.end(finalBody);
|
|
4351
|
-
|
|
4710
|
+
{
|
|
4352
4711
|
try {
|
|
4353
4712
|
const parsed = JSON.parse(responseBody);
|
|
4354
4713
|
if (parsed?.output) {
|
|
@@ -4604,12 +4963,14 @@ function createRequestHandler(deps) {
|
|
|
4604
4963
|
calls_failed: s.failedCalls,
|
|
4605
4964
|
p95_latency_ms: latencyMonitor.getSessionP95(s.key),
|
|
4606
4965
|
last_active_ago_ms: Date.now() - s.lastAccessedAt
|
|
4607
|
-
}))
|
|
4966
|
+
})),
|
|
4967
|
+
...deps.getVerifiedTokens ? deps.getVerifiedTokens() : {},
|
|
4968
|
+
...deps.getCursorMetrics ? { cursor: deps.getCursorMetrics() } : {}
|
|
4608
4969
|
});
|
|
4609
4970
|
return;
|
|
4610
4971
|
}
|
|
4611
4972
|
const sessionKey = identifySession(req, url);
|
|
4612
|
-
const pipeline = sessions.getOrCreate(sessionKey);
|
|
4973
|
+
const { pipeline, stickyCache } = sessions.getOrCreate(sessionKey);
|
|
4613
4974
|
if (method === "POST" && (url === "/v1/chat/completions" || url === "/chat/completions")) {
|
|
4614
4975
|
const body = await readBody(req);
|
|
4615
4976
|
let parsed;
|
|
@@ -4621,7 +4982,7 @@ function createRequestHandler(deps) {
|
|
|
4621
4982
|
});
|
|
4622
4983
|
return;
|
|
4623
4984
|
}
|
|
4624
|
-
await handleChatCompletions(req, res, parsed, pipeline, config, logger, semaphore, latencyMonitor, sessionKey.raw);
|
|
4985
|
+
await handleChatCompletions(req, res, parsed, pipeline, config, logger, semaphore, latencyMonitor, sessionKey.raw, stickyCache);
|
|
4625
4986
|
return;
|
|
4626
4987
|
}
|
|
4627
4988
|
if (method === "POST" && (url === "/v1/responses" || url === "/responses")) {
|
|
@@ -4650,7 +5011,7 @@ function createRequestHandler(deps) {
|
|
|
4650
5011
|
});
|
|
4651
5012
|
return;
|
|
4652
5013
|
}
|
|
4653
|
-
await handleAnthropicMessages(req, res, parsed, pipeline, config, logger, semaphore, latencyMonitor, sessionKey.raw);
|
|
5014
|
+
await handleAnthropicMessages(req, res, parsed, pipeline, config, logger, semaphore, latencyMonitor, sessionKey.raw, deps.onUsage, stickyCache);
|
|
4654
5015
|
return;
|
|
4655
5016
|
}
|
|
4656
5017
|
await passthroughToUpstream(req, res, fullUrl, config, logger);
|
|
@@ -4814,7 +5175,7 @@ var SessionManager = class {
|
|
|
4814
5175
|
if (existing) {
|
|
4815
5176
|
existing.lastAccessedAt = Date.now();
|
|
4816
5177
|
existing.requestCount++;
|
|
4817
|
-
return existing.pipeline;
|
|
5178
|
+
return { pipeline: existing.pipeline, stickyCache: existing.stickyCache };
|
|
4818
5179
|
}
|
|
4819
5180
|
if (this.sessions.size >= this.config.maxSessions) {
|
|
4820
5181
|
this.evictLRU();
|
|
@@ -4823,14 +5184,16 @@ var SessionManager = class {
|
|
|
4823
5184
|
...this.config.pipelineConfig,
|
|
4824
5185
|
sessionId: key.raw
|
|
4825
5186
|
});
|
|
5187
|
+
const stickyCache = /* @__PURE__ */ new Map();
|
|
4826
5188
|
this.sessions.set(key.raw, {
|
|
4827
5189
|
pipeline,
|
|
5190
|
+
stickyCache,
|
|
4828
5191
|
lastAccessedAt: Date.now(),
|
|
4829
5192
|
requestCount: 1,
|
|
4830
5193
|
connector: key.connector
|
|
4831
5194
|
});
|
|
4832
5195
|
this.config.onSessionCreated?.(key.raw, pipeline);
|
|
4833
|
-
return pipeline;
|
|
5196
|
+
return { pipeline, stickyCache };
|
|
4834
5197
|
}
|
|
4835
5198
|
getAllSummaries() {
|
|
4836
5199
|
const entries = [];
|
|
@@ -5409,6 +5772,13 @@ async function startCommand(flags) {
|
|
|
5409
5772
|
logger.log(`[LIMINAL] [${key}] Error: ${event.error.message}`);
|
|
5410
5773
|
statsAggregator.recordFailure(connector);
|
|
5411
5774
|
});
|
|
5775
|
+
pipeline.events.on("learning", (event) => {
|
|
5776
|
+
if (event.error) {
|
|
5777
|
+
logger.log(`[LIMINAL] [${key}] Learning error: ${event.error}`);
|
|
5778
|
+
} else if (event.tokensIngested > 0) {
|
|
5779
|
+
logger.log(`[LIMINAL] [${key}] Learning: ${event.tokensIngested} tokens ingested (deferred:${event.deferred})`);
|
|
5780
|
+
}
|
|
5781
|
+
});
|
|
5412
5782
|
pipeline.events.on("degradation", (event) => {
|
|
5413
5783
|
logger.log(`[LIMINAL] [${key}] Circuit ${event.circuitState}: ${event.reason}`);
|
|
5414
5784
|
});
|
|
@@ -5435,7 +5805,50 @@ async function startCommand(flags) {
|
|
|
5435
5805
|
logger.log(`[LATENCY] ${alert.type.toUpperCase()}: ${alert.message} (${alert.activeSessions} sessions) \u2014 ${alert.suggestion}`);
|
|
5436
5806
|
});
|
|
5437
5807
|
const mitmStats = new MitmStats();
|
|
5438
|
-
const deps = {
|
|
5808
|
+
const deps = {
|
|
5809
|
+
sessions,
|
|
5810
|
+
semaphore,
|
|
5811
|
+
latencyMonitor,
|
|
5812
|
+
mitmStats,
|
|
5813
|
+
config: resolvedConfig,
|
|
5814
|
+
logger,
|
|
5815
|
+
onUsage: (usage2, verifiedSaved, originalInputTokens) => {
|
|
5816
|
+
statsAggregator.recordApiUsage(usage2.inputTokens, usage2.outputTokens, verifiedSaved, originalInputTokens);
|
|
5817
|
+
if (resolvedConfig.rscApiKey && resolvedConfig.rscBaseUrl) {
|
|
5818
|
+
const body = JSON.stringify({
|
|
5819
|
+
model: "cli",
|
|
5820
|
+
actual_input_tokens: usage2.inputTokens,
|
|
5821
|
+
actual_output_tokens: usage2.outputTokens,
|
|
5822
|
+
original_input_bytes: originalInputTokens * 4
|
|
5823
|
+
});
|
|
5824
|
+
fetch(`${resolvedConfig.rscBaseUrl}/api/v1/usage/verified`, {
|
|
5825
|
+
method: "POST",
|
|
5826
|
+
headers: {
|
|
5827
|
+
"Content-Type": "application/json",
|
|
5828
|
+
"Authorization": `Bearer ${resolvedConfig.rscApiKey}`
|
|
5829
|
+
},
|
|
5830
|
+
body,
|
|
5831
|
+
signal: AbortSignal.timeout(5e3)
|
|
5832
|
+
}).catch(() => {
|
|
5833
|
+
});
|
|
5834
|
+
}
|
|
5835
|
+
},
|
|
5836
|
+
getVerifiedTokens: () => {
|
|
5837
|
+
const snap = statsAggregator.snapshot();
|
|
5838
|
+
return {
|
|
5839
|
+
actualInputTokens: snap.actualInputTokens,
|
|
5840
|
+
actualOutputTokens: snap.actualOutputTokens,
|
|
5841
|
+
originalInputEstimate: snap.originalInputEstimate,
|
|
5842
|
+
verifiedInputSaved: snap.verifiedInputSaved,
|
|
5843
|
+
verifiedSavingsRate: snap.verifiedSavingsRate,
|
|
5844
|
+
verifiedRequestCount: snap.verifiedRequestCount
|
|
5845
|
+
};
|
|
5846
|
+
},
|
|
5847
|
+
getCursorMetrics: () => {
|
|
5848
|
+
const { parseCursorHookStats: parseCursorHookStats3 } = (init_stats(), __toCommonJS(stats_exports));
|
|
5849
|
+
return parseCursorHookStats3();
|
|
5850
|
+
}
|
|
5851
|
+
};
|
|
5439
5852
|
const handler = createRequestHandler(deps);
|
|
5440
5853
|
let mitmHandler;
|
|
5441
5854
|
const connectHandler = createConnectHandler({
|
|
@@ -5640,8 +6053,8 @@ async function stopCommand() {
|
|
|
5640
6053
|
init_lifecycle();
|
|
5641
6054
|
init_loader();
|
|
5642
6055
|
init_format();
|
|
5643
|
-
import { existsSync as
|
|
5644
|
-
import { join as
|
|
6056
|
+
import { existsSync as existsSync11, readFileSync as readFileSync9 } from "fs";
|
|
6057
|
+
import { join as join8 } from "path";
|
|
5645
6058
|
async function statusCommand() {
|
|
5646
6059
|
if (!isConfigured()) {
|
|
5647
6060
|
console.log(error(
|
|
@@ -5709,27 +6122,27 @@ async function statusCommand() {
|
|
|
5709
6122
|
}
|
|
5710
6123
|
}
|
|
5711
6124
|
function printCursorHookStats() {
|
|
5712
|
-
const logPath =
|
|
5713
|
-
const hooksPath =
|
|
6125
|
+
const logPath = join8(process.cwd(), ".fabric", "compress.log");
|
|
6126
|
+
const hooksPath = join8(process.cwd(), ".cursor", "hooks.json");
|
|
5714
6127
|
let hooksActive = false;
|
|
5715
6128
|
try {
|
|
5716
|
-
if (
|
|
5717
|
-
const data = JSON.parse(
|
|
6129
|
+
if (existsSync11(hooksPath)) {
|
|
6130
|
+
const data = JSON.parse(readFileSync9(hooksPath, "utf-8"));
|
|
5718
6131
|
const entries = data?.hooks?.preToolUse ?? [];
|
|
5719
6132
|
hooksActive = entries.some((e) => e.command?.includes("fabric-compress"));
|
|
5720
6133
|
}
|
|
5721
6134
|
} catch {
|
|
5722
6135
|
}
|
|
5723
|
-
if (!hooksActive && !
|
|
6136
|
+
if (!hooksActive && !existsSync11(logPath)) return;
|
|
5724
6137
|
console.log(sectionHeader("Cursor Hooks"));
|
|
5725
6138
|
console.log();
|
|
5726
6139
|
console.log(label("Status", hooksActive ? `${c.green}active${c.reset}` : `${c.dim}not installed${c.reset}`));
|
|
5727
|
-
if (!
|
|
6140
|
+
if (!existsSync11(logPath)) {
|
|
5728
6141
|
console.log(label("Stats", `${c.dim}no compression data yet${c.reset}`));
|
|
5729
6142
|
console.log();
|
|
5730
6143
|
return;
|
|
5731
6144
|
}
|
|
5732
|
-
const lines =
|
|
6145
|
+
const lines = readFileSync9(logPath, "utf-8").trim().split("\n");
|
|
5733
6146
|
let compressed = 0;
|
|
5734
6147
|
let errors = 0;
|
|
5735
6148
|
let totalInputSize = 0;
|
|
@@ -5763,7 +6176,7 @@ function printCursorHookStats() {
|
|
|
5763
6176
|
const avgApiMs = compressed > 0 ? Math.round(totalApiMs / compressed) : 0;
|
|
5764
6177
|
let cacheCount = 0;
|
|
5765
6178
|
try {
|
|
5766
|
-
cacheCount = countFiles(
|
|
6179
|
+
cacheCount = countFiles(join8(process.cwd(), ".fabric", "cache"));
|
|
5767
6180
|
} catch {
|
|
5768
6181
|
}
|
|
5769
6182
|
console.log(label("Files", `${files.size} unique ${c.dim}(${compressed} compressions${errors > 0 ? `, ${c.red}${errors} errors${c.reset}${c.dim}` : ""})${c.reset}`));
|
|
@@ -5776,12 +6189,12 @@ function printCursorHookStats() {
|
|
|
5776
6189
|
console.log();
|
|
5777
6190
|
}
|
|
5778
6191
|
function countFiles(dir) {
|
|
5779
|
-
if (!
|
|
5780
|
-
const { readdirSync:
|
|
6192
|
+
if (!existsSync11(dir)) return 0;
|
|
6193
|
+
const { readdirSync: readdirSync3, statSync: statSync4 } = __require("fs");
|
|
5781
6194
|
let count = 0;
|
|
5782
|
-
for (const entry of
|
|
6195
|
+
for (const entry of readdirSync3(dir, { withFileTypes: true })) {
|
|
5783
6196
|
if (entry.isDirectory()) {
|
|
5784
|
-
count += countFiles(
|
|
6197
|
+
count += countFiles(join8(dir, entry.name));
|
|
5785
6198
|
} else {
|
|
5786
6199
|
count++;
|
|
5787
6200
|
}
|
|
@@ -5864,17 +6277,17 @@ async function configCommand(flags) {
|
|
|
5864
6277
|
|
|
5865
6278
|
// src/commands/logs.ts
|
|
5866
6279
|
init_paths();
|
|
5867
|
-
import { readFileSync as
|
|
6280
|
+
import { readFileSync as readFileSync10, existsSync as existsSync12, statSync as statSync2, createReadStream } from "fs";
|
|
5868
6281
|
import { watchFile, unwatchFile } from "fs";
|
|
5869
6282
|
async function logsCommand(flags) {
|
|
5870
6283
|
const follow = flags.has("follow") || flags.has("f");
|
|
5871
6284
|
const linesFlag = flags.get("lines") ?? flags.get("n");
|
|
5872
6285
|
const lines = typeof linesFlag === "string" ? parseInt(linesFlag, 10) : 50;
|
|
5873
|
-
if (!
|
|
6286
|
+
if (!existsSync12(LOG_FILE)) {
|
|
5874
6287
|
console.log('No log file found. Start the daemon with "liminal start" to generate logs.');
|
|
5875
6288
|
return;
|
|
5876
6289
|
}
|
|
5877
|
-
const content =
|
|
6290
|
+
const content = readFileSync10(LOG_FILE, "utf-8");
|
|
5878
6291
|
const allLines = content.split("\n");
|
|
5879
6292
|
const tail = allLines.slice(-lines - 1);
|
|
5880
6293
|
process.stdout.write(tail.join("\n"));
|
|
@@ -5900,7 +6313,7 @@ async function logsCommand(flags) {
|
|
|
5900
6313
|
}
|
|
5901
6314
|
|
|
5902
6315
|
// src/commands/uninstall.ts
|
|
5903
|
-
import { existsSync as
|
|
6316
|
+
import { existsSync as existsSync13, rmSync, readFileSync as readFileSync11 } from "fs";
|
|
5904
6317
|
init_paths();
|
|
5905
6318
|
init_lifecycle();
|
|
5906
6319
|
init_prompts();
|
|
@@ -5910,9 +6323,9 @@ var GREEN = "\x1B[32m";
|
|
|
5910
6323
|
var YELLOW = "\x1B[33m";
|
|
5911
6324
|
var RESET = "\x1B[0m";
|
|
5912
6325
|
function loadConfiguredTools() {
|
|
5913
|
-
if (!
|
|
6326
|
+
if (!existsSync13(CONFIG_FILE)) return [];
|
|
5914
6327
|
try {
|
|
5915
|
-
const raw =
|
|
6328
|
+
const raw = readFileSync11(CONFIG_FILE, "utf-8");
|
|
5916
6329
|
const config = JSON.parse(raw);
|
|
5917
6330
|
if (Array.isArray(config.tools)) return config.tools;
|
|
5918
6331
|
} catch {
|
|
@@ -6000,7 +6413,7 @@ async function uninstallCommand() {
|
|
|
6000
6413
|
}
|
|
6001
6414
|
}
|
|
6002
6415
|
}
|
|
6003
|
-
if (
|
|
6416
|
+
if (existsSync13(LIMINAL_DIR)) {
|
|
6004
6417
|
console.log();
|
|
6005
6418
|
const removeData = await selectPrompt({
|
|
6006
6419
|
message: "Remove ~/.liminal/ directory? (config, logs, PID file)",
|
|
@@ -6120,9 +6533,9 @@ async function untrustCACommand() {
|
|
|
6120
6533
|
// src/commands/setup-cursor.ts
|
|
6121
6534
|
init_loader();
|
|
6122
6535
|
init_version();
|
|
6123
|
-
import { existsSync as
|
|
6536
|
+
import { existsSync as existsSync14, readFileSync as readFileSync12, writeFileSync as writeFileSync6, mkdirSync as mkdirSync5, unlinkSync as unlinkSync4, readdirSync as readdirSync2, rmdirSync, appendFileSync as appendFileSync3 } from "fs";
|
|
6124
6537
|
import { homedir as homedir5 } from "os";
|
|
6125
|
-
import { join as
|
|
6538
|
+
import { join as join9 } from "path";
|
|
6126
6539
|
|
|
6127
6540
|
// src/cursor/hook-template.ts
|
|
6128
6541
|
function getHookScript() {
|
|
@@ -6375,10 +6788,11 @@ async function main() {
|
|
|
6375
6788
|
if (!fs.existsSync(cacheDir)) fs.mkdirSync(cacheDir, { recursive: true });
|
|
6376
6789
|
fs.writeFileSync(cachePath, result.text, "utf-8");
|
|
6377
6790
|
|
|
6378
|
-
// Use server-side token counts
|
|
6379
|
-
// Fall back to ~
|
|
6380
|
-
const inputTokens = result.inputTokens || Math.ceil(result.inputSize /
|
|
6381
|
-
const outputTokens = result.outputTokens || Math.ceil(result.outputSize /
|
|
6791
|
+
// Use server-side token counts from tiktoken-rs (cl100k_base BPE tokenizer)
|
|
6792
|
+
// Fall back to ~3 chars/token estimate if server doesn't return counts
|
|
6793
|
+
const inputTokens = result.inputTokens || Math.ceil(result.inputSize / 3);
|
|
6794
|
+
const outputTokens = result.outputTokens || Math.ceil(result.outputSize / 3);
|
|
6795
|
+
const tokensSaved = Math.max(0, inputTokens - outputTokens);
|
|
6382
6796
|
logEntry({
|
|
6383
6797
|
type: "compressed",
|
|
6384
6798
|
file: relativePath,
|
|
@@ -6386,8 +6800,8 @@ async function main() {
|
|
|
6386
6800
|
outputSize: result.outputSize,
|
|
6387
6801
|
inputTokens: inputTokens,
|
|
6388
6802
|
outputTokens: outputTokens,
|
|
6389
|
-
tokensSaved:
|
|
6390
|
-
savedPct: +((
|
|
6803
|
+
tokensSaved: tokensSaved,
|
|
6804
|
+
savedPct: inputTokens > 0 ? +((tokensSaved / inputTokens) * 100).toFixed(1) : 0,
|
|
6391
6805
|
apiMs: elapsed,
|
|
6392
6806
|
});
|
|
6393
6807
|
|
|
@@ -6410,26 +6824,26 @@ var HOOK_SCRIPT_NAME = "fabric-compress.js";
|
|
|
6410
6824
|
var HOOK_COMMAND = `node .cursor/hooks/${HOOK_SCRIPT_NAME}`;
|
|
6411
6825
|
function isCursorInstalled2() {
|
|
6412
6826
|
if (process.platform === "darwin") {
|
|
6413
|
-
return
|
|
6827
|
+
return existsSync14("/Applications/Cursor.app");
|
|
6414
6828
|
}
|
|
6415
6829
|
if (process.platform === "win32") {
|
|
6416
|
-
const localAppData = process.env.LOCALAPPDATA ||
|
|
6417
|
-
return
|
|
6830
|
+
const localAppData = process.env.LOCALAPPDATA || join9(homedir5(), "AppData", "Local");
|
|
6831
|
+
return existsSync14(join9(localAppData, "Programs", "Cursor", "Cursor.exe"));
|
|
6418
6832
|
}
|
|
6419
|
-
return
|
|
6833
|
+
return existsSync14("/usr/bin/cursor");
|
|
6420
6834
|
}
|
|
6421
6835
|
function readHooksJson(workspaceRoot) {
|
|
6422
|
-
const hooksPath =
|
|
6836
|
+
const hooksPath = join9(workspaceRoot, ".cursor", "hooks.json");
|
|
6423
6837
|
try {
|
|
6424
|
-
return JSON.parse(
|
|
6838
|
+
return JSON.parse(readFileSync12(hooksPath, "utf-8"));
|
|
6425
6839
|
} catch {
|
|
6426
6840
|
return {};
|
|
6427
6841
|
}
|
|
6428
6842
|
}
|
|
6429
6843
|
function writeHooksJson(workspaceRoot, data) {
|
|
6430
|
-
const dir =
|
|
6431
|
-
if (!
|
|
6432
|
-
writeFileSync6(
|
|
6844
|
+
const dir = join9(workspaceRoot, ".cursor");
|
|
6845
|
+
if (!existsSync14(dir)) mkdirSync5(dir, { recursive: true });
|
|
6846
|
+
writeFileSync6(join9(dir, "hooks.json"), JSON.stringify(data, null, 2) + "\n");
|
|
6433
6847
|
}
|
|
6434
6848
|
function hasOurHook(hooks) {
|
|
6435
6849
|
const entries = hooks.hooks?.preToolUse ?? [];
|
|
@@ -6462,10 +6876,10 @@ function removeOurHook(hooks) {
|
|
|
6462
6876
|
return hooks;
|
|
6463
6877
|
}
|
|
6464
6878
|
function ensureGitignore(workspaceRoot) {
|
|
6465
|
-
const gitignorePath =
|
|
6879
|
+
const gitignorePath = join9(workspaceRoot, ".gitignore");
|
|
6466
6880
|
const entry = ".fabric/";
|
|
6467
|
-
if (
|
|
6468
|
-
const content =
|
|
6881
|
+
if (existsSync14(gitignorePath)) {
|
|
6882
|
+
const content = readFileSync12(gitignorePath, "utf-8");
|
|
6469
6883
|
if (content.includes(entry)) return;
|
|
6470
6884
|
appendFileSync3(gitignorePath, `
|
|
6471
6885
|
# Liminal compression cache
|
|
@@ -6479,7 +6893,7 @@ ${entry}
|
|
|
6479
6893
|
}
|
|
6480
6894
|
function rmdirIfEmpty(dir) {
|
|
6481
6895
|
try {
|
|
6482
|
-
if (
|
|
6896
|
+
if (existsSync14(dir) && readdirSync2(dir).length === 0) {
|
|
6483
6897
|
rmdirSync(dir);
|
|
6484
6898
|
}
|
|
6485
6899
|
} catch {
|
|
@@ -6502,20 +6916,20 @@ async function setupCursorCommand(flags) {
|
|
|
6502
6916
|
writeHooksJson(workspaceRoot, updated2);
|
|
6503
6917
|
console.log(success("Removed hook entry from .cursor/hooks.json"));
|
|
6504
6918
|
} else {
|
|
6505
|
-
const hooksJsonPath =
|
|
6919
|
+
const hooksJsonPath = join9(workspaceRoot, ".cursor", "hooks.json");
|
|
6506
6920
|
try {
|
|
6507
6921
|
unlinkSync4(hooksJsonPath);
|
|
6508
6922
|
} catch {
|
|
6509
6923
|
}
|
|
6510
6924
|
console.log(success("Deleted .cursor/hooks.json", "no remaining hooks"));
|
|
6511
6925
|
}
|
|
6512
|
-
const scriptPath2 =
|
|
6926
|
+
const scriptPath2 = join9(workspaceRoot, ".cursor", "hooks", HOOK_SCRIPT_NAME);
|
|
6513
6927
|
try {
|
|
6514
6928
|
unlinkSync4(scriptPath2);
|
|
6515
6929
|
console.log(success(`Deleted .cursor/hooks/${HOOK_SCRIPT_NAME}`));
|
|
6516
6930
|
} catch {
|
|
6517
6931
|
}
|
|
6518
|
-
rmdirIfEmpty(
|
|
6932
|
+
rmdirIfEmpty(join9(workspaceRoot, ".cursor", "hooks"));
|
|
6519
6933
|
console.log();
|
|
6520
6934
|
console.log(success("Hooks removed. Reload Cursor to deactivate."));
|
|
6521
6935
|
console.log();
|
|
@@ -6538,9 +6952,9 @@ async function setupCursorCommand(flags) {
|
|
|
6538
6952
|
console.log(success("API key configured"));
|
|
6539
6953
|
console.log();
|
|
6540
6954
|
console.log(` ${c.dim}[3/4]${c.reset} Installing compression hooks...`);
|
|
6541
|
-
const hooksDir =
|
|
6542
|
-
if (!
|
|
6543
|
-
const scriptPath =
|
|
6955
|
+
const hooksDir = join9(workspaceRoot, ".cursor", "hooks");
|
|
6956
|
+
if (!existsSync14(hooksDir)) mkdirSync5(hooksDir, { recursive: true });
|
|
6957
|
+
const scriptPath = join9(hooksDir, HOOK_SCRIPT_NAME);
|
|
6544
6958
|
writeFileSync6(scriptPath, getHookScript(), { mode: 493 });
|
|
6545
6959
|
console.log(success(`Wrote .cursor/hooks/${HOOK_SCRIPT_NAME}`));
|
|
6546
6960
|
const hooks = readHooksJson(workspaceRoot);
|
|
@@ -6569,8 +6983,8 @@ init_loader();
|
|
|
6569
6983
|
init_store();
|
|
6570
6984
|
init_aggregator();
|
|
6571
6985
|
init_format();
|
|
6572
|
-
import { existsSync as
|
|
6573
|
-
import { join as
|
|
6986
|
+
import { existsSync as existsSync15, readFileSync as readFileSync13 } from "fs";
|
|
6987
|
+
import { join as join10 } from "path";
|
|
6574
6988
|
async function statsCommand(flags) {
|
|
6575
6989
|
if (!isConfigured()) {
|
|
6576
6990
|
console.log(error(
|
|
@@ -6606,7 +7020,7 @@ async function statsCommand(flags) {
|
|
|
6606
7020
|
agg.recordSkipped(s.connector, 0);
|
|
6607
7021
|
}
|
|
6608
7022
|
}
|
|
6609
|
-
const cursorMetrics =
|
|
7023
|
+
const cursorMetrics = parseCursorHookStats2();
|
|
6610
7024
|
if (cursorMetrics) {
|
|
6611
7025
|
agg.setCursorMetrics(cursorMetrics);
|
|
6612
7026
|
}
|
|
@@ -6759,14 +7173,14 @@ async function statsCommand(flags) {
|
|
|
6759
7173
|
console.log(` ${c.dim}\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500${c.reset}`);
|
|
6760
7174
|
console.log();
|
|
6761
7175
|
}
|
|
6762
|
-
function
|
|
6763
|
-
const logPath =
|
|
6764
|
-
if (!
|
|
7176
|
+
function parseCursorHookStats2() {
|
|
7177
|
+
const logPath = join10(process.cwd(), ".fabric", "compress.log");
|
|
7178
|
+
if (!existsSync15(logPath)) return null;
|
|
6765
7179
|
let compressions = 0, errors = 0;
|
|
6766
7180
|
let tokensProcessed = 0, tokensSaved = 0;
|
|
6767
7181
|
let apiMsSumMs = 0;
|
|
6768
7182
|
const files = /* @__PURE__ */ new Set();
|
|
6769
|
-
const lines =
|
|
7183
|
+
const lines = readFileSync13(logPath, "utf-8").trim().split("\n");
|
|
6770
7184
|
for (const line of lines) {
|
|
6771
7185
|
try {
|
|
6772
7186
|
const entry = JSON.parse(line);
|
|
@@ -6786,7 +7200,7 @@ function parseCursorHookStats() {
|
|
|
6786
7200
|
}
|
|
6787
7201
|
let cacheCount = 0;
|
|
6788
7202
|
try {
|
|
6789
|
-
cacheCount =
|
|
7203
|
+
cacheCount = countFilesRecursive2(join10(process.cwd(), ".fabric", "cache"));
|
|
6790
7204
|
} catch {
|
|
6791
7205
|
}
|
|
6792
7206
|
return {
|
|
@@ -6799,13 +7213,13 @@ function parseCursorHookStats() {
|
|
|
6799
7213
|
cacheCount
|
|
6800
7214
|
};
|
|
6801
7215
|
}
|
|
6802
|
-
function
|
|
6803
|
-
if (!
|
|
6804
|
-
const { readdirSync:
|
|
7216
|
+
function countFilesRecursive2(dir) {
|
|
7217
|
+
if (!existsSync15(dir)) return 0;
|
|
7218
|
+
const { readdirSync: readdirSync3 } = __require("fs");
|
|
6805
7219
|
let count = 0;
|
|
6806
|
-
for (const entry of
|
|
7220
|
+
for (const entry of readdirSync3(dir, { withFileTypes: true })) {
|
|
6807
7221
|
if (entry.isDirectory()) {
|
|
6808
|
-
count +=
|
|
7222
|
+
count += countFilesRecursive2(join10(dir, entry.name));
|
|
6809
7223
|
} else {
|
|
6810
7224
|
count++;
|
|
6811
7225
|
}
|