eminiar 0.9.1 → 0.10.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/.claude-plugin/plugin.json +1 -1
- package/README.md +15 -4
- package/dist/cli.js +3507 -317
- package/dist/hook.js +2189 -237
- package/dist/server.js +2938 -231
- package/dist/ui/app.global.js +60 -60
- package/dist/ui/styles.css +91 -0
- package/eminiar.config.schema.json +64 -0
- package/package.json +1 -1
- package/skills/em-purge/SKILL.md +2 -2
- package/skills/em-usage/SKILL.md +21 -0
package/dist/server.js
CHANGED
|
@@ -116,7 +116,7 @@ var require_code = __commonJS({
|
|
|
116
116
|
}
|
|
117
117
|
exports._ = _;
|
|
118
118
|
var plus = new _Code("+");
|
|
119
|
-
function
|
|
119
|
+
function str3(strs, ...args) {
|
|
120
120
|
const expr = [safeStringify(strs[0])];
|
|
121
121
|
let i = 0;
|
|
122
122
|
while (i < args.length) {
|
|
@@ -127,7 +127,7 @@ var require_code = __commonJS({
|
|
|
127
127
|
optimize(expr);
|
|
128
128
|
return new _Code(expr);
|
|
129
129
|
}
|
|
130
|
-
exports.str =
|
|
130
|
+
exports.str = str3;
|
|
131
131
|
function addCodeArg(code, arg) {
|
|
132
132
|
if (arg instanceof _Code)
|
|
133
133
|
code.push(...arg._items);
|
|
@@ -170,7 +170,7 @@ var require_code = __commonJS({
|
|
|
170
170
|
return;
|
|
171
171
|
}
|
|
172
172
|
function strConcat(c1, c2) {
|
|
173
|
-
return c2.emptyStr() ? c1 : c1.emptyStr() ? c2 :
|
|
173
|
+
return c2.emptyStr() ? c1 : c1.emptyStr() ? c2 : str3`${c1}${c2}`;
|
|
174
174
|
}
|
|
175
175
|
exports.strConcat = strConcat;
|
|
176
176
|
function interpolate(x) {
|
|
@@ -1135,22 +1135,22 @@ var require_util = __commonJS({
|
|
|
1135
1135
|
return (0, codegen_1._)`${topSchemaRef}${schemaPath}${(0, codegen_1.getProperty)(keyword)}`;
|
|
1136
1136
|
}
|
|
1137
1137
|
exports.schemaRefOrVal = schemaRefOrVal;
|
|
1138
|
-
function unescapeFragment(
|
|
1139
|
-
return unescapeJsonPointer(decodeURIComponent(
|
|
1138
|
+
function unescapeFragment(str3) {
|
|
1139
|
+
return unescapeJsonPointer(decodeURIComponent(str3));
|
|
1140
1140
|
}
|
|
1141
1141
|
exports.unescapeFragment = unescapeFragment;
|
|
1142
|
-
function escapeFragment(
|
|
1143
|
-
return encodeURIComponent(escapeJsonPointer(
|
|
1142
|
+
function escapeFragment(str3) {
|
|
1143
|
+
return encodeURIComponent(escapeJsonPointer(str3));
|
|
1144
1144
|
}
|
|
1145
1145
|
exports.escapeFragment = escapeFragment;
|
|
1146
|
-
function escapeJsonPointer(
|
|
1147
|
-
if (typeof
|
|
1148
|
-
return `${
|
|
1149
|
-
return
|
|
1146
|
+
function escapeJsonPointer(str3) {
|
|
1147
|
+
if (typeof str3 == "number")
|
|
1148
|
+
return `${str3}`;
|
|
1149
|
+
return str3.replace(/~/g, "~0").replace(/\//g, "~1");
|
|
1150
1150
|
}
|
|
1151
1151
|
exports.escapeJsonPointer = escapeJsonPointer;
|
|
1152
|
-
function unescapeJsonPointer(
|
|
1153
|
-
return
|
|
1152
|
+
function unescapeJsonPointer(str3) {
|
|
1153
|
+
return str3.replace(/~1/g, "/").replace(/~0/g, "~");
|
|
1154
1154
|
}
|
|
1155
1155
|
exports.unescapeJsonPointer = unescapeJsonPointer;
|
|
1156
1156
|
function eachItem(xs, f) {
|
|
@@ -2187,8 +2187,8 @@ var require_json_schema_traverse = __commonJS({
|
|
|
2187
2187
|
post(schema, jsonPtr, rootSchema, parentJsonPtr, parentKeyword, parentSchema, keyIndex);
|
|
2188
2188
|
}
|
|
2189
2189
|
}
|
|
2190
|
-
function escapeJsonPtr(
|
|
2191
|
-
return
|
|
2190
|
+
function escapeJsonPtr(str3) {
|
|
2191
|
+
return str3.replace(/~/g, "~0").replace(/\//g, "~1");
|
|
2192
2192
|
}
|
|
2193
2193
|
}
|
|
2194
2194
|
});
|
|
@@ -3019,7 +3019,7 @@ var require_compile = __commonJS({
|
|
|
3019
3019
|
const schOrFunc = root.refs[ref];
|
|
3020
3020
|
if (schOrFunc)
|
|
3021
3021
|
return schOrFunc;
|
|
3022
|
-
let _sch =
|
|
3022
|
+
let _sch = resolve9.call(this, root, ref);
|
|
3023
3023
|
if (_sch === void 0) {
|
|
3024
3024
|
const schema = (_a3 = root.localRefs) === null || _a3 === void 0 ? void 0 : _a3[ref];
|
|
3025
3025
|
const { schemaId } = this.opts;
|
|
@@ -3046,7 +3046,7 @@ var require_compile = __commonJS({
|
|
|
3046
3046
|
function sameSchemaEnv(s1, s2) {
|
|
3047
3047
|
return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
|
|
3048
3048
|
}
|
|
3049
|
-
function
|
|
3049
|
+
function resolve9(root, ref) {
|
|
3050
3050
|
let sch;
|
|
3051
3051
|
while (typeof (sch = this.refs[ref]) == "string")
|
|
3052
3052
|
ref = sch;
|
|
@@ -3291,10 +3291,10 @@ var require_utils = __commonJS({
|
|
|
3291
3291
|
isIPV6: true
|
|
3292
3292
|
};
|
|
3293
3293
|
}
|
|
3294
|
-
function findToken(
|
|
3294
|
+
function findToken(str3, token) {
|
|
3295
3295
|
let ind = 0;
|
|
3296
|
-
for (let i = 0; i <
|
|
3297
|
-
if (
|
|
3296
|
+
for (let i = 0; i < str3.length; i++) {
|
|
3297
|
+
if (str3[i] === token) ind++;
|
|
3298
3298
|
}
|
|
3299
3299
|
return ind;
|
|
3300
3300
|
}
|
|
@@ -3879,7 +3879,7 @@ var require_fast_uri = __commonJS({
|
|
|
3879
3879
|
}
|
|
3880
3880
|
return uri;
|
|
3881
3881
|
}
|
|
3882
|
-
function
|
|
3882
|
+
function resolve9(baseURI, relativeURI, options) {
|
|
3883
3883
|
const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
|
|
3884
3884
|
const {
|
|
3885
3885
|
parsed: baseParsed,
|
|
@@ -4248,7 +4248,7 @@ var require_fast_uri = __commonJS({
|
|
|
4248
4248
|
var fastUri = {
|
|
4249
4249
|
SCHEMES,
|
|
4250
4250
|
normalize: normalize7,
|
|
4251
|
-
resolve:
|
|
4251
|
+
resolve: resolve9,
|
|
4252
4252
|
resolveComponent,
|
|
4253
4253
|
equal,
|
|
4254
4254
|
serialize,
|
|
@@ -4312,7 +4312,7 @@ var require_core = __commonJS({
|
|
|
4312
4312
|
var util_1 = require_util();
|
|
4313
4313
|
var $dataRefSchema = require_data();
|
|
4314
4314
|
var uri_1 = require_uri();
|
|
4315
|
-
var defaultRegExp = (
|
|
4315
|
+
var defaultRegExp = (str3, flags) => new RegExp(str3, flags);
|
|
4316
4316
|
defaultRegExp.code = "new RegExp";
|
|
4317
4317
|
var META_IGNORE_OPTIONS = ["removeAdditional", "useDefaults", "coerceTypes"];
|
|
4318
4318
|
var EXT_SCOPE_NAMES = /* @__PURE__ */ new Set([
|
|
@@ -5113,16 +5113,16 @@ var require_ucs2length = __commonJS({
|
|
|
5113
5113
|
"use strict";
|
|
5114
5114
|
init_esm_shims();
|
|
5115
5115
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5116
|
-
function ucs2length(
|
|
5117
|
-
const len =
|
|
5116
|
+
function ucs2length(str3) {
|
|
5117
|
+
const len = str3.length;
|
|
5118
5118
|
let length = 0;
|
|
5119
5119
|
let pos = 0;
|
|
5120
5120
|
let value;
|
|
5121
5121
|
while (pos < len) {
|
|
5122
5122
|
length++;
|
|
5123
|
-
value =
|
|
5123
|
+
value = str3.charCodeAt(pos++);
|
|
5124
5124
|
if (value >= 55296 && value <= 56319 && pos < len) {
|
|
5125
|
-
value =
|
|
5125
|
+
value = str3.charCodeAt(pos);
|
|
5126
5126
|
if ((value & 64512) === 56320)
|
|
5127
5127
|
pos++;
|
|
5128
5128
|
}
|
|
@@ -7040,8 +7040,8 @@ var require_formats = __commonJS({
|
|
|
7040
7040
|
}
|
|
7041
7041
|
var DATE = /^(\d\d\d\d)-(\d\d)-(\d\d)$/;
|
|
7042
7042
|
var DAYS = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
|
|
7043
|
-
function date5(
|
|
7044
|
-
const matches2 = DATE.exec(
|
|
7043
|
+
function date5(str3) {
|
|
7044
|
+
const matches2 = DATE.exec(str3);
|
|
7045
7045
|
if (!matches2)
|
|
7046
7046
|
return false;
|
|
7047
7047
|
const year = +matches2[1];
|
|
@@ -7060,8 +7060,8 @@ var require_formats = __commonJS({
|
|
|
7060
7060
|
}
|
|
7061
7061
|
var TIME = /^(\d\d):(\d\d):(\d\d(?:\.\d+)?)(z|([+-])(\d\d)(?::?(\d\d))?)?$/i;
|
|
7062
7062
|
function getTime(strictTimeZone) {
|
|
7063
|
-
return function time3(
|
|
7064
|
-
const matches2 = TIME.exec(
|
|
7063
|
+
return function time3(str3) {
|
|
7064
|
+
const matches2 = TIME.exec(str3);
|
|
7065
7065
|
if (!matches2)
|
|
7066
7066
|
return false;
|
|
7067
7067
|
const hr = +matches2[1];
|
|
@@ -7107,9 +7107,9 @@ var require_formats = __commonJS({
|
|
|
7107
7107
|
var DATE_TIME_SEPARATOR = /t|\s/i;
|
|
7108
7108
|
function getDateTime(strictTimeZone) {
|
|
7109
7109
|
const time3 = getTime(strictTimeZone);
|
|
7110
|
-
return function date_time(
|
|
7111
|
-
const
|
|
7112
|
-
return
|
|
7110
|
+
return function date_time(str3) {
|
|
7111
|
+
const dateTime2 = str3.split(DATE_TIME_SEPARATOR);
|
|
7112
|
+
return dateTime2.length === 2 && date5(dateTime2[0]) && time3(dateTime2[1]);
|
|
7113
7113
|
};
|
|
7114
7114
|
}
|
|
7115
7115
|
function compareDateTime(dt1, dt2) {
|
|
@@ -7133,13 +7133,13 @@ var require_formats = __commonJS({
|
|
|
7133
7133
|
}
|
|
7134
7134
|
var NOT_URI_FRAGMENT = /\/|:/;
|
|
7135
7135
|
var URI = /^(?:[a-z][a-z0-9+\-.]*:)(?:\/?\/(?:(?:[a-z0-9\-._~!$&'()*+,;=:]|%[0-9a-f]{2})*@)?(?:\[(?:(?:(?:(?:[0-9a-f]{1,4}:){6}|::(?:[0-9a-f]{1,4}:){5}|(?:[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){4}|(?:(?:[0-9a-f]{1,4}:){0,1}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){3}|(?:(?:[0-9a-f]{1,4}:){0,2}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){2}|(?:(?:[0-9a-f]{1,4}:){0,3}[0-9a-f]{1,4})?::[0-9a-f]{1,4}:|(?:(?:[0-9a-f]{1,4}:){0,4}[0-9a-f]{1,4})?::)(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?))|(?:(?:[0-9a-f]{1,4}:){0,5}[0-9a-f]{1,4})?::[0-9a-f]{1,4}|(?:(?:[0-9a-f]{1,4}:){0,6}[0-9a-f]{1,4})?::)|[Vv][0-9a-f]+\.[a-z0-9\-._~!$&'()*+,;=:]+)\]|(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)|(?:[a-z0-9\-._~!$&'()*+,;=]|%[0-9a-f]{2})*)(?::\d*)?(?:\/(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})*)*|\/(?:(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})+(?:\/(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})*)*)?|(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})+(?:\/(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})*)*)(?:\?(?:[a-z0-9\-._~!$&'()*+,;=:@/?]|%[0-9a-f]{2})*)?(?:#(?:[a-z0-9\-._~!$&'()*+,;=:@/?]|%[0-9a-f]{2})*)?$/i;
|
|
7136
|
-
function uri(
|
|
7137
|
-
return NOT_URI_FRAGMENT.test(
|
|
7136
|
+
function uri(str3) {
|
|
7137
|
+
return NOT_URI_FRAGMENT.test(str3) && URI.test(str3);
|
|
7138
7138
|
}
|
|
7139
7139
|
var BYTE = /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/gm;
|
|
7140
|
-
function byte(
|
|
7140
|
+
function byte(str3) {
|
|
7141
7141
|
BYTE.lastIndex = 0;
|
|
7142
|
-
return BYTE.test(
|
|
7142
|
+
return BYTE.test(str3);
|
|
7143
7143
|
}
|
|
7144
7144
|
var MIN_INT32 = -(2 ** 31);
|
|
7145
7145
|
var MAX_INT32 = 2 ** 31 - 1;
|
|
@@ -7153,11 +7153,11 @@ var require_formats = __commonJS({
|
|
|
7153
7153
|
return true;
|
|
7154
7154
|
}
|
|
7155
7155
|
var Z_ANCHOR = /[^\\]\\Z/;
|
|
7156
|
-
function regex(
|
|
7157
|
-
if (Z_ANCHOR.test(
|
|
7156
|
+
function regex(str3) {
|
|
7157
|
+
if (Z_ANCHOR.test(str3))
|
|
7158
7158
|
return false;
|
|
7159
7159
|
try {
|
|
7160
|
-
new RegExp(
|
|
7160
|
+
new RegExp(str3);
|
|
7161
7161
|
return true;
|
|
7162
7162
|
} catch (e) {
|
|
7163
7163
|
return false;
|
|
@@ -7644,14 +7644,14 @@ var require_is_extglob = __commonJS({
|
|
|
7644
7644
|
"node_modules/is-extglob/index.js"(exports, module) {
|
|
7645
7645
|
"use strict";
|
|
7646
7646
|
init_esm_shims();
|
|
7647
|
-
module.exports = function isExtglob(
|
|
7648
|
-
if (typeof
|
|
7647
|
+
module.exports = function isExtglob(str3) {
|
|
7648
|
+
if (typeof str3 !== "string" || str3 === "") {
|
|
7649
7649
|
return false;
|
|
7650
7650
|
}
|
|
7651
7651
|
var match;
|
|
7652
|
-
while (match = /(\\).|([@?!+*]\(.*\))/g.exec(
|
|
7652
|
+
while (match = /(\\).|([@?!+*]\(.*\))/g.exec(str3)) {
|
|
7653
7653
|
if (match[2]) return true;
|
|
7654
|
-
|
|
7654
|
+
str3 = str3.slice(match.index + match[0].length);
|
|
7655
7655
|
}
|
|
7656
7656
|
return false;
|
|
7657
7657
|
};
|
|
@@ -7665,8 +7665,8 @@ var require_is_glob = __commonJS({
|
|
|
7665
7665
|
init_esm_shims();
|
|
7666
7666
|
var isExtglob = require_is_extglob();
|
|
7667
7667
|
var chars = { "{": "}", "(": ")", "[": "]" };
|
|
7668
|
-
var strictCheck = function(
|
|
7669
|
-
if (
|
|
7668
|
+
var strictCheck = function(str3) {
|
|
7669
|
+
if (str3[0] === "!") {
|
|
7670
7670
|
return true;
|
|
7671
7671
|
}
|
|
7672
7672
|
var index = 0;
|
|
@@ -7675,70 +7675,70 @@ var require_is_glob = __commonJS({
|
|
|
7675
7675
|
var closeCurlyIndex = -2;
|
|
7676
7676
|
var closeParenIndex = -2;
|
|
7677
7677
|
var backSlashIndex = -2;
|
|
7678
|
-
while (index <
|
|
7679
|
-
if (
|
|
7678
|
+
while (index < str3.length) {
|
|
7679
|
+
if (str3[index] === "*") {
|
|
7680
7680
|
return true;
|
|
7681
7681
|
}
|
|
7682
|
-
if (
|
|
7682
|
+
if (str3[index + 1] === "?" && /[\].+)]/.test(str3[index])) {
|
|
7683
7683
|
return true;
|
|
7684
7684
|
}
|
|
7685
|
-
if (closeSquareIndex !== -1 &&
|
|
7685
|
+
if (closeSquareIndex !== -1 && str3[index] === "[" && str3[index + 1] !== "]") {
|
|
7686
7686
|
if (closeSquareIndex < index) {
|
|
7687
|
-
closeSquareIndex =
|
|
7687
|
+
closeSquareIndex = str3.indexOf("]", index);
|
|
7688
7688
|
}
|
|
7689
7689
|
if (closeSquareIndex > index) {
|
|
7690
7690
|
if (backSlashIndex === -1 || backSlashIndex > closeSquareIndex) {
|
|
7691
7691
|
return true;
|
|
7692
7692
|
}
|
|
7693
|
-
backSlashIndex =
|
|
7693
|
+
backSlashIndex = str3.indexOf("\\", index);
|
|
7694
7694
|
if (backSlashIndex === -1 || backSlashIndex > closeSquareIndex) {
|
|
7695
7695
|
return true;
|
|
7696
7696
|
}
|
|
7697
7697
|
}
|
|
7698
7698
|
}
|
|
7699
|
-
if (closeCurlyIndex !== -1 &&
|
|
7700
|
-
closeCurlyIndex =
|
|
7699
|
+
if (closeCurlyIndex !== -1 && str3[index] === "{" && str3[index + 1] !== "}") {
|
|
7700
|
+
closeCurlyIndex = str3.indexOf("}", index);
|
|
7701
7701
|
if (closeCurlyIndex > index) {
|
|
7702
|
-
backSlashIndex =
|
|
7702
|
+
backSlashIndex = str3.indexOf("\\", index);
|
|
7703
7703
|
if (backSlashIndex === -1 || backSlashIndex > closeCurlyIndex) {
|
|
7704
7704
|
return true;
|
|
7705
7705
|
}
|
|
7706
7706
|
}
|
|
7707
7707
|
}
|
|
7708
|
-
if (closeParenIndex !== -1 &&
|
|
7709
|
-
closeParenIndex =
|
|
7708
|
+
if (closeParenIndex !== -1 && str3[index] === "(" && str3[index + 1] === "?" && /[:!=]/.test(str3[index + 2]) && str3[index + 3] !== ")") {
|
|
7709
|
+
closeParenIndex = str3.indexOf(")", index);
|
|
7710
7710
|
if (closeParenIndex > index) {
|
|
7711
|
-
backSlashIndex =
|
|
7711
|
+
backSlashIndex = str3.indexOf("\\", index);
|
|
7712
7712
|
if (backSlashIndex === -1 || backSlashIndex > closeParenIndex) {
|
|
7713
7713
|
return true;
|
|
7714
7714
|
}
|
|
7715
7715
|
}
|
|
7716
7716
|
}
|
|
7717
|
-
if (pipeIndex !== -1 &&
|
|
7717
|
+
if (pipeIndex !== -1 && str3[index] === "(" && str3[index + 1] !== "|") {
|
|
7718
7718
|
if (pipeIndex < index) {
|
|
7719
|
-
pipeIndex =
|
|
7719
|
+
pipeIndex = str3.indexOf("|", index);
|
|
7720
7720
|
}
|
|
7721
|
-
if (pipeIndex !== -1 &&
|
|
7722
|
-
closeParenIndex =
|
|
7721
|
+
if (pipeIndex !== -1 && str3[pipeIndex + 1] !== ")") {
|
|
7722
|
+
closeParenIndex = str3.indexOf(")", pipeIndex);
|
|
7723
7723
|
if (closeParenIndex > pipeIndex) {
|
|
7724
|
-
backSlashIndex =
|
|
7724
|
+
backSlashIndex = str3.indexOf("\\", pipeIndex);
|
|
7725
7725
|
if (backSlashIndex === -1 || backSlashIndex > closeParenIndex) {
|
|
7726
7726
|
return true;
|
|
7727
7727
|
}
|
|
7728
7728
|
}
|
|
7729
7729
|
}
|
|
7730
7730
|
}
|
|
7731
|
-
if (
|
|
7732
|
-
var open3 =
|
|
7731
|
+
if (str3[index] === "\\") {
|
|
7732
|
+
var open3 = str3[index + 1];
|
|
7733
7733
|
index += 2;
|
|
7734
7734
|
var close = chars[open3];
|
|
7735
7735
|
if (close) {
|
|
7736
|
-
var n =
|
|
7736
|
+
var n = str3.indexOf(close, index);
|
|
7737
7737
|
if (n !== -1) {
|
|
7738
7738
|
index = n + 1;
|
|
7739
7739
|
}
|
|
7740
7740
|
}
|
|
7741
|
-
if (
|
|
7741
|
+
if (str3[index] === "!") {
|
|
7742
7742
|
return true;
|
|
7743
7743
|
}
|
|
7744
7744
|
} else {
|
|
@@ -7747,26 +7747,26 @@ var require_is_glob = __commonJS({
|
|
|
7747
7747
|
}
|
|
7748
7748
|
return false;
|
|
7749
7749
|
};
|
|
7750
|
-
var relaxedCheck = function(
|
|
7751
|
-
if (
|
|
7750
|
+
var relaxedCheck = function(str3) {
|
|
7751
|
+
if (str3[0] === "!") {
|
|
7752
7752
|
return true;
|
|
7753
7753
|
}
|
|
7754
7754
|
var index = 0;
|
|
7755
|
-
while (index <
|
|
7756
|
-
if (/[*?{}()[\]]/.test(
|
|
7755
|
+
while (index < str3.length) {
|
|
7756
|
+
if (/[*?{}()[\]]/.test(str3[index])) {
|
|
7757
7757
|
return true;
|
|
7758
7758
|
}
|
|
7759
|
-
if (
|
|
7760
|
-
var open3 =
|
|
7759
|
+
if (str3[index] === "\\") {
|
|
7760
|
+
var open3 = str3[index + 1];
|
|
7761
7761
|
index += 2;
|
|
7762
7762
|
var close = chars[open3];
|
|
7763
7763
|
if (close) {
|
|
7764
|
-
var n =
|
|
7764
|
+
var n = str3.indexOf(close, index);
|
|
7765
7765
|
if (n !== -1) {
|
|
7766
7766
|
index = n + 1;
|
|
7767
7767
|
}
|
|
7768
7768
|
}
|
|
7769
|
-
if (
|
|
7769
|
+
if (str3[index] === "!") {
|
|
7770
7770
|
return true;
|
|
7771
7771
|
}
|
|
7772
7772
|
} else {
|
|
@@ -7775,18 +7775,18 @@ var require_is_glob = __commonJS({
|
|
|
7775
7775
|
}
|
|
7776
7776
|
return false;
|
|
7777
7777
|
};
|
|
7778
|
-
module.exports = function isGlob(
|
|
7779
|
-
if (typeof
|
|
7778
|
+
module.exports = function isGlob(str3, options) {
|
|
7779
|
+
if (typeof str3 !== "string" || str3 === "") {
|
|
7780
7780
|
return false;
|
|
7781
7781
|
}
|
|
7782
|
-
if (isExtglob(
|
|
7782
|
+
if (isExtglob(str3)) {
|
|
7783
7783
|
return true;
|
|
7784
7784
|
}
|
|
7785
7785
|
var check3 = strictCheck;
|
|
7786
7786
|
if (options && options.strict === false) {
|
|
7787
7787
|
check3 = relaxedCheck;
|
|
7788
7788
|
}
|
|
7789
|
-
return check3(
|
|
7789
|
+
return check3(str3);
|
|
7790
7790
|
};
|
|
7791
7791
|
}
|
|
7792
7792
|
});
|
|
@@ -7804,19 +7804,19 @@ var require_glob_parent = __commonJS({
|
|
|
7804
7804
|
var enclosure = /[\{\[].*[\}\]]$/;
|
|
7805
7805
|
var globby = /(^|[^\\])([\{\[]|\([^\)]+$)/;
|
|
7806
7806
|
var escaped = /\\([\!\*\?\|\[\]\(\)\{\}])/g;
|
|
7807
|
-
module.exports = function globParent(
|
|
7807
|
+
module.exports = function globParent(str3, opts) {
|
|
7808
7808
|
var options = Object.assign({ flipBackslashes: true }, opts);
|
|
7809
|
-
if (options.flipBackslashes && isWin32 &&
|
|
7810
|
-
|
|
7809
|
+
if (options.flipBackslashes && isWin32 && str3.indexOf(slash) < 0) {
|
|
7810
|
+
str3 = str3.replace(backslash, slash);
|
|
7811
7811
|
}
|
|
7812
|
-
if (enclosure.test(
|
|
7813
|
-
|
|
7812
|
+
if (enclosure.test(str3)) {
|
|
7813
|
+
str3 += slash;
|
|
7814
7814
|
}
|
|
7815
|
-
|
|
7815
|
+
str3 += "a";
|
|
7816
7816
|
do {
|
|
7817
|
-
|
|
7818
|
-
} while (isGlob(
|
|
7819
|
-
return
|
|
7817
|
+
str3 = pathPosixDirname(str3);
|
|
7818
|
+
} while (isGlob(str3) || globby.test(str3));
|
|
7819
|
+
return str3.replace(escaped, "$1");
|
|
7820
7820
|
};
|
|
7821
7821
|
}
|
|
7822
7822
|
});
|
|
@@ -7826,12 +7826,12 @@ var require_utils2 = __commonJS({
|
|
|
7826
7826
|
"node_modules/braces/lib/utils.js"(exports) {
|
|
7827
7827
|
"use strict";
|
|
7828
7828
|
init_esm_shims();
|
|
7829
|
-
exports.isInteger = (
|
|
7830
|
-
if (typeof
|
|
7831
|
-
return Number.isInteger(
|
|
7829
|
+
exports.isInteger = (num5) => {
|
|
7830
|
+
if (typeof num5 === "number") {
|
|
7831
|
+
return Number.isInteger(num5);
|
|
7832
7832
|
}
|
|
7833
|
-
if (typeof
|
|
7834
|
-
return Number.isInteger(Number(
|
|
7833
|
+
if (typeof num5 === "string" && num5.trim() !== "") {
|
|
7834
|
+
return Number.isInteger(Number(num5));
|
|
7835
7835
|
}
|
|
7836
7836
|
return false;
|
|
7837
7837
|
};
|
|
@@ -7941,12 +7941,12 @@ var require_is_number = __commonJS({
|
|
|
7941
7941
|
"node_modules/is-number/index.js"(exports, module) {
|
|
7942
7942
|
"use strict";
|
|
7943
7943
|
init_esm_shims();
|
|
7944
|
-
module.exports = function(
|
|
7945
|
-
if (typeof
|
|
7946
|
-
return
|
|
7944
|
+
module.exports = function(num5) {
|
|
7945
|
+
if (typeof num5 === "number") {
|
|
7946
|
+
return num5 - num5 === 0;
|
|
7947
7947
|
}
|
|
7948
|
-
if (typeof
|
|
7949
|
-
return Number.isFinite ? Number.isFinite(+
|
|
7948
|
+
if (typeof num5 === "string" && num5.trim() !== "") {
|
|
7949
|
+
return Number.isFinite ? Number.isFinite(+num5) : isFinite(+num5);
|
|
7950
7950
|
}
|
|
7951
7951
|
return false;
|
|
7952
7952
|
};
|
|
@@ -8070,7 +8070,7 @@ var require_to_regex_range = __commonJS({
|
|
|
8070
8070
|
}
|
|
8071
8071
|
return { pattern: pattern2, count: [count], digits };
|
|
8072
8072
|
}
|
|
8073
|
-
function splitToPatterns(min, max,
|
|
8073
|
+
function splitToPatterns(min, max, tok2, options) {
|
|
8074
8074
|
let ranges = splitToRanges(min, max);
|
|
8075
8075
|
let tokens = [];
|
|
8076
8076
|
let start = min;
|
|
@@ -8079,7 +8079,7 @@ var require_to_regex_range = __commonJS({
|
|
|
8079
8079
|
let max2 = ranges[i];
|
|
8080
8080
|
let obj = rangeToPattern(String(start), String(max2), options);
|
|
8081
8081
|
let zeros = "";
|
|
8082
|
-
if (!
|
|
8082
|
+
if (!tok2.isPadded && prev && prev.pattern === obj.pattern) {
|
|
8083
8083
|
if (prev.count.length > 1) {
|
|
8084
8084
|
prev.count.pop();
|
|
8085
8085
|
}
|
|
@@ -8088,8 +8088,8 @@ var require_to_regex_range = __commonJS({
|
|
|
8088
8088
|
start = max2 + 1;
|
|
8089
8089
|
continue;
|
|
8090
8090
|
}
|
|
8091
|
-
if (
|
|
8092
|
-
zeros = padZeros(max2,
|
|
8091
|
+
if (tok2.isPadded) {
|
|
8092
|
+
zeros = padZeros(max2, tok2, options);
|
|
8093
8093
|
}
|
|
8094
8094
|
obj.string = zeros + obj.pattern + toQuantifier(obj.count);
|
|
8095
8095
|
tokens.push(obj);
|
|
@@ -8138,14 +8138,14 @@ var require_to_regex_range = __commonJS({
|
|
|
8138
8138
|
function toCharacterClass(a, b, options) {
|
|
8139
8139
|
return `[${a}${b - a === 1 ? "" : "-"}${b}]`;
|
|
8140
8140
|
}
|
|
8141
|
-
function hasPadding(
|
|
8142
|
-
return /^-?(0+)\d/.test(
|
|
8141
|
+
function hasPadding(str3) {
|
|
8142
|
+
return /^-?(0+)\d/.test(str3);
|
|
8143
8143
|
}
|
|
8144
|
-
function padZeros(value,
|
|
8145
|
-
if (!
|
|
8144
|
+
function padZeros(value, tok2, options) {
|
|
8145
|
+
if (!tok2.isPadded) {
|
|
8146
8146
|
return value;
|
|
8147
8147
|
}
|
|
8148
|
-
let diff = Math.abs(
|
|
8148
|
+
let diff = Math.abs(tok2.maxLen - String(value).length);
|
|
8149
8149
|
let relax = options.relaxZeros !== false;
|
|
8150
8150
|
switch (diff) {
|
|
8151
8151
|
case 0:
|
|
@@ -8179,7 +8179,7 @@ var require_fill_range = __commonJS({
|
|
|
8179
8179
|
var isValidValue = (value) => {
|
|
8180
8180
|
return typeof value === "number" || typeof value === "string" && value !== "";
|
|
8181
8181
|
};
|
|
8182
|
-
var isNumber = (
|
|
8182
|
+
var isNumber = (num5) => Number.isInteger(+num5);
|
|
8183
8183
|
var zeros = (input2) => {
|
|
8184
8184
|
let value = `${input2}`;
|
|
8185
8185
|
let index = -1;
|
|
@@ -8289,7 +8289,7 @@ var require_fill_range = __commonJS({
|
|
|
8289
8289
|
return toRange(toMaxLen(start, maxLen), toMaxLen(end, maxLen), true, options);
|
|
8290
8290
|
}
|
|
8291
8291
|
let parts = { negatives: [], positives: [] };
|
|
8292
|
-
let push = (
|
|
8292
|
+
let push = (num5) => parts[num5 < 0 ? "negatives" : "positives"].push(Math.abs(num5));
|
|
8293
8293
|
let range = [];
|
|
8294
8294
|
let index = 0;
|
|
8295
8295
|
while (descending ? a >= b : a <= b) {
|
|
@@ -9129,12 +9129,12 @@ var require_utils3 = __commonJS({
|
|
|
9129
9129
|
REGEX_SPECIAL_CHARS_GLOBAL
|
|
9130
9130
|
} = require_constants2();
|
|
9131
9131
|
exports.isObject = (val) => val !== null && typeof val === "object" && !Array.isArray(val);
|
|
9132
|
-
exports.hasRegexChars = (
|
|
9133
|
-
exports.isRegexChar = (
|
|
9134
|
-
exports.escapeRegex = (
|
|
9135
|
-
exports.toPosixSlashes = (
|
|
9136
|
-
exports.removeBackslashes = (
|
|
9137
|
-
return
|
|
9132
|
+
exports.hasRegexChars = (str3) => REGEX_SPECIAL_CHARS.test(str3);
|
|
9133
|
+
exports.isRegexChar = (str3) => str3.length === 1 && exports.hasRegexChars(str3);
|
|
9134
|
+
exports.escapeRegex = (str3) => str3.replace(REGEX_SPECIAL_CHARS_GLOBAL, "\\$1");
|
|
9135
|
+
exports.toPosixSlashes = (str3) => str3.replace(REGEX_BACKSLASH, "/");
|
|
9136
|
+
exports.removeBackslashes = (str3) => {
|
|
9137
|
+
return str3.replace(REGEX_REMOVE_BACKSLASH, (match) => {
|
|
9138
9138
|
return match === "\\" ? "" : match;
|
|
9139
9139
|
});
|
|
9140
9140
|
};
|
|
@@ -9230,7 +9230,7 @@ var require_scan = __commonJS({
|
|
|
9230
9230
|
const slashes = [];
|
|
9231
9231
|
const tokens = [];
|
|
9232
9232
|
const parts = [];
|
|
9233
|
-
let
|
|
9233
|
+
let str3 = input2;
|
|
9234
9234
|
let index = -1;
|
|
9235
9235
|
let start = 0;
|
|
9236
9236
|
let lastIndex = 0;
|
|
@@ -9249,10 +9249,10 @@ var require_scan = __commonJS({
|
|
|
9249
9249
|
let code;
|
|
9250
9250
|
let token = { value: "", depth: 0, isGlob: false };
|
|
9251
9251
|
const eos = () => index >= length;
|
|
9252
|
-
const peek = () =>
|
|
9252
|
+
const peek = () => str3.charCodeAt(index + 1);
|
|
9253
9253
|
const advance = () => {
|
|
9254
9254
|
prev = code;
|
|
9255
|
-
return
|
|
9255
|
+
return str3.charCodeAt(++index);
|
|
9256
9256
|
};
|
|
9257
9257
|
while (index < length) {
|
|
9258
9258
|
code = advance();
|
|
@@ -9420,24 +9420,24 @@ var require_scan = __commonJS({
|
|
|
9420
9420
|
isExtglob = false;
|
|
9421
9421
|
isGlob = false;
|
|
9422
9422
|
}
|
|
9423
|
-
let base =
|
|
9423
|
+
let base = str3;
|
|
9424
9424
|
let prefix = "";
|
|
9425
9425
|
let glob2 = "";
|
|
9426
9426
|
if (start > 0) {
|
|
9427
|
-
prefix =
|
|
9428
|
-
|
|
9427
|
+
prefix = str3.slice(0, start);
|
|
9428
|
+
str3 = str3.slice(start);
|
|
9429
9429
|
lastIndex -= start;
|
|
9430
9430
|
}
|
|
9431
9431
|
if (base && isGlob === true && lastIndex > 0) {
|
|
9432
|
-
base =
|
|
9433
|
-
glob2 =
|
|
9432
|
+
base = str3.slice(0, lastIndex);
|
|
9433
|
+
glob2 = str3.slice(lastIndex);
|
|
9434
9434
|
} else if (isGlob === true) {
|
|
9435
9435
|
base = "";
|
|
9436
|
-
glob2 =
|
|
9436
|
+
glob2 = str3;
|
|
9437
9437
|
} else {
|
|
9438
|
-
base =
|
|
9438
|
+
base = str3;
|
|
9439
9439
|
}
|
|
9440
|
-
if (base && base !== "" && base !== "/" && base !==
|
|
9440
|
+
if (base && base !== "" && base !== "/" && base !== str3) {
|
|
9441
9441
|
if (isPathSeparator(base.charCodeAt(base.length - 1))) {
|
|
9442
9442
|
base = base.slice(0, -1);
|
|
9443
9443
|
}
|
|
@@ -9816,9 +9816,9 @@ var require_parse2 = __commonJS({
|
|
|
9816
9816
|
const peek = state.peek = (n = 1) => input2[state.index + n];
|
|
9817
9817
|
const advance = state.advance = () => input2[++state.index] || "";
|
|
9818
9818
|
const remaining = () => input2.slice(state.index + 1);
|
|
9819
|
-
const consume = (value2 = "",
|
|
9819
|
+
const consume = (value2 = "", num5 = 0) => {
|
|
9820
9820
|
state.consumed += value2;
|
|
9821
|
-
state.index +=
|
|
9821
|
+
state.index += num5;
|
|
9822
9822
|
};
|
|
9823
9823
|
const append = (token) => {
|
|
9824
9824
|
state.output += token.output != null ? token.output : token.value;
|
|
@@ -9846,11 +9846,11 @@ var require_parse2 = __commonJS({
|
|
|
9846
9846
|
state[type]--;
|
|
9847
9847
|
stack.pop();
|
|
9848
9848
|
};
|
|
9849
|
-
const push = (
|
|
9849
|
+
const push = (tok2) => {
|
|
9850
9850
|
if (prev.type === "globstar") {
|
|
9851
|
-
const isBrace = state.braces > 0 && (
|
|
9852
|
-
const isExtglob =
|
|
9853
|
-
if (
|
|
9851
|
+
const isBrace = state.braces > 0 && (tok2.type === "comma" || tok2.type === "brace");
|
|
9852
|
+
const isExtglob = tok2.extglob === true || extglobs.length && (tok2.type === "pipe" || tok2.type === "paren");
|
|
9853
|
+
if (tok2.type !== "slash" && tok2.type !== "paren" && !isBrace && !isExtglob) {
|
|
9854
9854
|
state.output = state.output.slice(0, -prev.output.length);
|
|
9855
9855
|
prev.type = "star";
|
|
9856
9856
|
prev.value = "*";
|
|
@@ -9858,18 +9858,18 @@ var require_parse2 = __commonJS({
|
|
|
9858
9858
|
state.output += prev.output;
|
|
9859
9859
|
}
|
|
9860
9860
|
}
|
|
9861
|
-
if (extglobs.length &&
|
|
9862
|
-
extglobs[extglobs.length - 1].inner +=
|
|
9861
|
+
if (extglobs.length && tok2.type !== "paren") {
|
|
9862
|
+
extglobs[extglobs.length - 1].inner += tok2.value;
|
|
9863
9863
|
}
|
|
9864
|
-
if (
|
|
9865
|
-
if (prev && prev.type === "text" &&
|
|
9866
|
-
prev.value +=
|
|
9867
|
-
prev.output = (prev.output || "") +
|
|
9864
|
+
if (tok2.value || tok2.output) append(tok2);
|
|
9865
|
+
if (prev && prev.type === "text" && tok2.type === "text") {
|
|
9866
|
+
prev.value += tok2.value;
|
|
9867
|
+
prev.output = (prev.output || "") + tok2.value;
|
|
9868
9868
|
return;
|
|
9869
9869
|
}
|
|
9870
|
-
|
|
9871
|
-
tokens.push(
|
|
9872
|
-
prev =
|
|
9870
|
+
tok2.prev = prev;
|
|
9871
|
+
tokens.push(tok2);
|
|
9872
|
+
prev = tok2;
|
|
9873
9873
|
};
|
|
9874
9874
|
const extglobOpen = (type, value2) => {
|
|
9875
9875
|
const token = { ...EXTGLOB_CHARS[value2], conditions: 1, inner: "" };
|
|
@@ -10473,8 +10473,8 @@ var require_parse2 = __commonJS({
|
|
|
10473
10473
|
if (opts2.noglobstar === true) return star;
|
|
10474
10474
|
return `(${capture}(?:(?!${START_ANCHOR}${opts2.dot ? DOTS_SLASH : DOT_LITERAL}).)*?)`;
|
|
10475
10475
|
};
|
|
10476
|
-
const create = (
|
|
10477
|
-
switch (
|
|
10476
|
+
const create = (str3) => {
|
|
10477
|
+
switch (str3) {
|
|
10478
10478
|
case "*":
|
|
10479
10479
|
return `${nodot}${ONE_CHAR}${star}`;
|
|
10480
10480
|
case ".*":
|
|
@@ -10492,7 +10492,7 @@ var require_parse2 = __commonJS({
|
|
|
10492
10492
|
case "**/.*":
|
|
10493
10493
|
return `(?:${nodot}${globstar(opts)}${SLASH_LITERAL})?${DOT_LITERAL}${ONE_CHAR}${star}`;
|
|
10494
10494
|
default: {
|
|
10495
|
-
const match = /^(.*?)\.(\w+)$/.exec(
|
|
10495
|
+
const match = /^(.*?)\.(\w+)$/.exec(str3);
|
|
10496
10496
|
if (!match) return;
|
|
10497
10497
|
const source2 = create(match[1]);
|
|
10498
10498
|
if (!source2) return;
|
|
@@ -10525,9 +10525,9 @@ var require_picomatch = __commonJS({
|
|
|
10525
10525
|
var picomatch = (glob2, options, returnState = false) => {
|
|
10526
10526
|
if (Array.isArray(glob2)) {
|
|
10527
10527
|
const fns = glob2.map((input2) => picomatch(input2, options, returnState));
|
|
10528
|
-
const arrayMatcher = (
|
|
10528
|
+
const arrayMatcher = (str3) => {
|
|
10529
10529
|
for (const isMatch of fns) {
|
|
10530
|
-
const state2 = isMatch(
|
|
10530
|
+
const state2 = isMatch(str3);
|
|
10531
10531
|
if (state2) return state2;
|
|
10532
10532
|
}
|
|
10533
10533
|
return false;
|
|
@@ -10603,7 +10603,7 @@ var require_picomatch = __commonJS({
|
|
|
10603
10603
|
const regex = glob2 instanceof RegExp ? glob2 : picomatch.makeRe(glob2, options);
|
|
10604
10604
|
return regex.test(path3.basename(input2));
|
|
10605
10605
|
};
|
|
10606
|
-
picomatch.isMatch = (
|
|
10606
|
+
picomatch.isMatch = (str3, patterns, options) => picomatch(patterns, options)(str3);
|
|
10607
10607
|
picomatch.parse = (pattern2, options) => {
|
|
10608
10608
|
if (Array.isArray(pattern2)) return pattern2.map((p) => picomatch.parse(p, options));
|
|
10609
10609
|
return parse3(pattern2, { ...options, fastpaths: false });
|
|
@@ -10719,7 +10719,7 @@ var require_micromatch = __commonJS({
|
|
|
10719
10719
|
};
|
|
10720
10720
|
micromatch.match = micromatch;
|
|
10721
10721
|
micromatch.matcher = (pattern2, options) => picomatch(pattern2, options);
|
|
10722
|
-
micromatch.isMatch = (
|
|
10722
|
+
micromatch.isMatch = (str3, patterns, options) => picomatch(patterns, options)(str3);
|
|
10723
10723
|
micromatch.any = micromatch.isMatch;
|
|
10724
10724
|
micromatch.not = (list2, patterns, options = {}) => {
|
|
10725
10725
|
patterns = [].concat(patterns).map(String);
|
|
@@ -10737,22 +10737,22 @@ var require_micromatch = __commonJS({
|
|
|
10737
10737
|
}
|
|
10738
10738
|
return [...result];
|
|
10739
10739
|
};
|
|
10740
|
-
micromatch.contains = (
|
|
10741
|
-
if (typeof
|
|
10742
|
-
throw new TypeError(`Expected a string: "${util2.inspect(
|
|
10740
|
+
micromatch.contains = (str3, pattern2, options) => {
|
|
10741
|
+
if (typeof str3 !== "string") {
|
|
10742
|
+
throw new TypeError(`Expected a string: "${util2.inspect(str3)}"`);
|
|
10743
10743
|
}
|
|
10744
10744
|
if (Array.isArray(pattern2)) {
|
|
10745
|
-
return pattern2.some((p) => micromatch.contains(
|
|
10745
|
+
return pattern2.some((p) => micromatch.contains(str3, p, options));
|
|
10746
10746
|
}
|
|
10747
10747
|
if (typeof pattern2 === "string") {
|
|
10748
|
-
if (isEmptyString(
|
|
10748
|
+
if (isEmptyString(str3) || isEmptyString(pattern2)) {
|
|
10749
10749
|
return false;
|
|
10750
10750
|
}
|
|
10751
|
-
if (
|
|
10751
|
+
if (str3.includes(pattern2) || str3.startsWith("./") && str3.slice(2).includes(pattern2)) {
|
|
10752
10752
|
return true;
|
|
10753
10753
|
}
|
|
10754
10754
|
}
|
|
10755
|
-
return micromatch.isMatch(
|
|
10755
|
+
return micromatch.isMatch(str3, pattern2, { ...options, contains: true });
|
|
10756
10756
|
};
|
|
10757
10757
|
micromatch.matchKeys = (obj, patterns, options) => {
|
|
10758
10758
|
if (!utils.isObject(obj)) {
|
|
@@ -10783,11 +10783,11 @@ var require_micromatch = __commonJS({
|
|
|
10783
10783
|
}
|
|
10784
10784
|
return true;
|
|
10785
10785
|
};
|
|
10786
|
-
micromatch.all = (
|
|
10787
|
-
if (typeof
|
|
10788
|
-
throw new TypeError(`Expected a string: "${util2.inspect(
|
|
10786
|
+
micromatch.all = (str3, patterns, options) => {
|
|
10787
|
+
if (typeof str3 !== "string") {
|
|
10788
|
+
throw new TypeError(`Expected a string: "${util2.inspect(str3)}"`);
|
|
10789
10789
|
}
|
|
10790
|
-
return [].concat(patterns).every((p) => picomatch(p, options)(
|
|
10790
|
+
return [].concat(patterns).every((p) => picomatch(p, options)(str3));
|
|
10791
10791
|
};
|
|
10792
10792
|
micromatch.capture = (glob2, input2, options) => {
|
|
10793
10793
|
let posix2 = utils.isWindows(options);
|
|
@@ -10802,8 +10802,8 @@ var require_micromatch = __commonJS({
|
|
|
10802
10802
|
micromatch.parse = (patterns, options) => {
|
|
10803
10803
|
let res = [];
|
|
10804
10804
|
for (let pattern2 of [].concat(patterns || [])) {
|
|
10805
|
-
for (let
|
|
10806
|
-
res.push(picomatch.parse(
|
|
10805
|
+
for (let str3 of braces(String(pattern2), options)) {
|
|
10806
|
+
res.push(picomatch.parse(str3, options));
|
|
10807
10807
|
}
|
|
10808
10808
|
}
|
|
10809
10809
|
return res;
|
|
@@ -10926,8 +10926,8 @@ var require_pattern2 = __commonJS({
|
|
|
10926
10926
|
}
|
|
10927
10927
|
exports.endsWithSlashGlobStar = endsWithSlashGlobStar;
|
|
10928
10928
|
function isAffectDepthOfReadingPattern(pattern2) {
|
|
10929
|
-
const
|
|
10930
|
-
return endsWithSlashGlobStar(pattern2) || isStaticPattern(
|
|
10929
|
+
const basename3 = path3.basename(pattern2);
|
|
10930
|
+
return endsWithSlashGlobStar(pattern2) || isStaticPattern(basename3);
|
|
10931
10931
|
}
|
|
10932
10932
|
exports.isAffectDepthOfReadingPattern = isAffectDepthOfReadingPattern;
|
|
10933
10933
|
function expandPatternsWithBraceExpansion(patterns) {
|
|
@@ -11411,11 +11411,11 @@ var require_out = __commonJS({
|
|
|
11411
11411
|
async.read(path3, getSettings(optionsOrSettingsOrCallback), callback);
|
|
11412
11412
|
}
|
|
11413
11413
|
exports.stat = stat;
|
|
11414
|
-
function
|
|
11414
|
+
function statSync12(path3, optionsOrSettings) {
|
|
11415
11415
|
const settings = getSettings(optionsOrSettings);
|
|
11416
11416
|
return sync.read(path3, settings);
|
|
11417
11417
|
}
|
|
11418
|
-
exports.statSync =
|
|
11418
|
+
exports.statSync = statSync12;
|
|
11419
11419
|
function getSettings(settingsOrOptions = {}) {
|
|
11420
11420
|
if (settingsOrOptions instanceof settings_1.default) {
|
|
11421
11421
|
return settingsOrOptions;
|
|
@@ -12096,41 +12096,41 @@ var require_queue = __commonJS({
|
|
|
12096
12096
|
queue.drained = drained;
|
|
12097
12097
|
return queue;
|
|
12098
12098
|
function push(value) {
|
|
12099
|
-
var p = new Promise(function(
|
|
12099
|
+
var p = new Promise(function(resolve9, reject) {
|
|
12100
12100
|
pushCb(value, function(err, result) {
|
|
12101
12101
|
if (err) {
|
|
12102
12102
|
reject(err);
|
|
12103
12103
|
return;
|
|
12104
12104
|
}
|
|
12105
|
-
|
|
12105
|
+
resolve9(result);
|
|
12106
12106
|
});
|
|
12107
12107
|
});
|
|
12108
12108
|
p.catch(noop);
|
|
12109
12109
|
return p;
|
|
12110
12110
|
}
|
|
12111
12111
|
function unshift(value) {
|
|
12112
|
-
var p = new Promise(function(
|
|
12112
|
+
var p = new Promise(function(resolve9, reject) {
|
|
12113
12113
|
unshiftCb(value, function(err, result) {
|
|
12114
12114
|
if (err) {
|
|
12115
12115
|
reject(err);
|
|
12116
12116
|
return;
|
|
12117
12117
|
}
|
|
12118
|
-
|
|
12118
|
+
resolve9(result);
|
|
12119
12119
|
});
|
|
12120
12120
|
});
|
|
12121
12121
|
p.catch(noop);
|
|
12122
12122
|
return p;
|
|
12123
12123
|
}
|
|
12124
12124
|
function drained() {
|
|
12125
|
-
var p = new Promise(function(
|
|
12125
|
+
var p = new Promise(function(resolve9) {
|
|
12126
12126
|
process.nextTick(function() {
|
|
12127
12127
|
if (queue.idle()) {
|
|
12128
|
-
|
|
12128
|
+
resolve9();
|
|
12129
12129
|
} else {
|
|
12130
12130
|
var previousDrain = queue.drain;
|
|
12131
12131
|
queue.drain = function() {
|
|
12132
12132
|
if (typeof previousDrain === "function") previousDrain();
|
|
12133
|
-
|
|
12133
|
+
resolve9();
|
|
12134
12134
|
queue.drain = previousDrain;
|
|
12135
12135
|
};
|
|
12136
12136
|
}
|
|
@@ -12627,9 +12627,9 @@ var require_stream3 = __commonJS({
|
|
|
12627
12627
|
});
|
|
12628
12628
|
}
|
|
12629
12629
|
_getStat(filepath) {
|
|
12630
|
-
return new Promise((
|
|
12630
|
+
return new Promise((resolve9, reject) => {
|
|
12631
12631
|
this._stat(filepath, this._fsStatSettings, (error62, stats) => {
|
|
12632
|
-
return error62 === null ?
|
|
12632
|
+
return error62 === null ? resolve9(stats) : reject(error62);
|
|
12633
12633
|
});
|
|
12634
12634
|
});
|
|
12635
12635
|
}
|
|
@@ -12654,10 +12654,10 @@ var require_async5 = __commonJS({
|
|
|
12654
12654
|
this._readerStream = new stream_1.default(this._settings);
|
|
12655
12655
|
}
|
|
12656
12656
|
dynamic(root, options) {
|
|
12657
|
-
return new Promise((
|
|
12657
|
+
return new Promise((resolve9, reject) => {
|
|
12658
12658
|
this._walkAsync(root, options, (error62, entries) => {
|
|
12659
12659
|
if (error62 === null) {
|
|
12660
|
-
|
|
12660
|
+
resolve9(entries);
|
|
12661
12661
|
} else {
|
|
12662
12662
|
reject(error62);
|
|
12663
12663
|
}
|
|
@@ -12667,10 +12667,10 @@ var require_async5 = __commonJS({
|
|
|
12667
12667
|
async static(patterns, options) {
|
|
12668
12668
|
const entries = [];
|
|
12669
12669
|
const stream = this._readerStream.static(patterns, options);
|
|
12670
|
-
return new Promise((
|
|
12670
|
+
return new Promise((resolve9, reject) => {
|
|
12671
12671
|
stream.once("error", reject);
|
|
12672
12672
|
stream.on("data", (entry) => entries.push(entry));
|
|
12673
|
-
stream.once("end", () =>
|
|
12673
|
+
stream.once("end", () => resolve9(entries));
|
|
12674
12674
|
});
|
|
12675
12675
|
}
|
|
12676
12676
|
};
|
|
@@ -18982,14 +18982,14 @@ function buildHistory(ctx, db, opts = {}) {
|
|
|
18982
18982
|
};
|
|
18983
18983
|
});
|
|
18984
18984
|
const bySha = new Map(commits.map((c) => [c.sha, c]));
|
|
18985
|
-
const
|
|
18985
|
+
const resolve9 = (prefix) => {
|
|
18986
18986
|
if (bySha.has(prefix)) return bySha.get(prefix);
|
|
18987
18987
|
for (const c of commits) if (c.sha.startsWith(prefix)) return c;
|
|
18988
18988
|
return void 0;
|
|
18989
18989
|
};
|
|
18990
18990
|
for (const c of commits) {
|
|
18991
18991
|
if (c.kind !== "revert" || !c.revertOf) continue;
|
|
18992
|
-
const target =
|
|
18992
|
+
const target = resolve9(c.revertOf);
|
|
18993
18993
|
if (target) {
|
|
18994
18994
|
target.revertedBy = c.sha;
|
|
18995
18995
|
c.origins.push(target.sha);
|
|
@@ -24006,14 +24006,14 @@ function promiseAllObject(promisesObj) {
|
|
|
24006
24006
|
}
|
|
24007
24007
|
function randomString(length = 10) {
|
|
24008
24008
|
const chars = "abcdefghijklmnopqrstuvwxyz";
|
|
24009
|
-
let
|
|
24009
|
+
let str3 = "";
|
|
24010
24010
|
for (let i = 0; i < length; i++) {
|
|
24011
|
-
|
|
24011
|
+
str3 += chars[Math.floor(Math.random() * chars.length)];
|
|
24012
24012
|
}
|
|
24013
|
-
return
|
|
24013
|
+
return str3;
|
|
24014
24014
|
}
|
|
24015
|
-
function esc(
|
|
24016
|
-
return JSON.stringify(
|
|
24015
|
+
function esc(str3) {
|
|
24016
|
+
return JSON.stringify(str3);
|
|
24017
24017
|
}
|
|
24018
24018
|
function slugify(input2) {
|
|
24019
24019
|
return input2.toLowerCase().trim().replace(/[^\w\s-]/g, "").replace(/[\s_-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
@@ -24127,8 +24127,8 @@ var primitiveTypes = /* @__PURE__ */ new Set([
|
|
|
24127
24127
|
"symbol",
|
|
24128
24128
|
"undefined"
|
|
24129
24129
|
]);
|
|
24130
|
-
function escapeRegex(
|
|
24131
|
-
return
|
|
24130
|
+
function escapeRegex(str3) {
|
|
24131
|
+
return str3.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
24132
24132
|
}
|
|
24133
24133
|
function clone(inst, def, params) {
|
|
24134
24134
|
const cl = new inst._zod.constr(def ?? inst._zod.def);
|
|
@@ -24394,13 +24394,13 @@ function getSizableOrigin(input2) {
|
|
|
24394
24394
|
return "unknown";
|
|
24395
24395
|
}
|
|
24396
24396
|
var highSurrogate = /[\uD800-\uDBFF]/;
|
|
24397
|
-
function codePointLength(
|
|
24398
|
-
const units =
|
|
24399
|
-
if (!highSurrogate.test(
|
|
24397
|
+
function codePointLength(str3) {
|
|
24398
|
+
const units = str3.length;
|
|
24399
|
+
if (!highSurrogate.test(str3))
|
|
24400
24400
|
return units;
|
|
24401
24401
|
let count = units;
|
|
24402
24402
|
for (let i = 0; i < units - 1; i++) {
|
|
24403
|
-
if ((
|
|
24403
|
+
if ((str3.charCodeAt(i) & 64512) === 55296 && (str3.charCodeAt(i + 1) & 64512) === 56320) {
|
|
24404
24404
|
count--;
|
|
24405
24405
|
i++;
|
|
24406
24406
|
}
|
|
@@ -25213,8 +25213,8 @@ function anchor(source) {
|
|
|
25213
25213
|
}
|
|
25214
25214
|
var date = /* @__PURE__ */ anchor(dateSource);
|
|
25215
25215
|
function timeSource(args) {
|
|
25216
|
-
const
|
|
25217
|
-
const regex = typeof args.precision === "number" ? args.precision === -1 ? `${
|
|
25216
|
+
const hhmm2 = `(?:[01]\\d|2[0-3]):[0-5]\\d`;
|
|
25217
|
+
const regex = typeof args.precision === "number" ? args.precision === -1 ? `${hhmm2}` : args.precision === 0 ? `${hhmm2}:[0-5]\\d` : `${hhmm2}:[0-5]\\d\\.\\d{${args.precision}}` : args.seconds ? `${hhmm2}:[0-5]\\d(?:\\.\\d+)?` : `${hhmm2}(?::[0-5]\\d(?:\\.\\d+)?)?`;
|
|
25218
25218
|
return regex;
|
|
25219
25219
|
}
|
|
25220
25220
|
function time(args) {
|
|
@@ -28252,7 +28252,7 @@ var recursive = /* @__PURE__ */ new WeakMap();
|
|
|
28252
28252
|
var NONE = 0;
|
|
28253
28253
|
var ASSUMED = 1;
|
|
28254
28254
|
var PROVEN = 2;
|
|
28255
|
-
function isRecursive(inst, stack,
|
|
28255
|
+
function isRecursive(inst, stack, resolve9) {
|
|
28256
28256
|
const cached5 = recursive.get(inst);
|
|
28257
28257
|
if (cached5 !== void 0)
|
|
28258
28258
|
return cached5 ? PROVEN : NONE;
|
|
@@ -28262,7 +28262,7 @@ function isRecursive(inst, stack, resolve8) {
|
|
|
28262
28262
|
let result = NONE;
|
|
28263
28263
|
const check3 = (child) => {
|
|
28264
28264
|
if (result !== PROVEN && child?._zod) {
|
|
28265
|
-
const answer = isRecursive(child, stack,
|
|
28265
|
+
const answer = isRecursive(child, stack, resolve9);
|
|
28266
28266
|
if (answer > result)
|
|
28267
28267
|
result = answer;
|
|
28268
28268
|
}
|
|
@@ -28273,7 +28273,7 @@ function isRecursive(inst, stack, resolve8) {
|
|
|
28273
28273
|
const desc = Object.getOwnPropertyDescriptor(sh, key);
|
|
28274
28274
|
if (spread && !desc.enumerable)
|
|
28275
28275
|
continue;
|
|
28276
|
-
const child = desc.get ? ASSUMED : desc.value?._zod ? isRecursive(desc.value, stack,
|
|
28276
|
+
const child = desc.get ? ASSUMED : desc.value?._zod ? isRecursive(desc.value, stack, resolve9) : NONE;
|
|
28277
28277
|
if (child > answer)
|
|
28278
28278
|
answer = child;
|
|
28279
28279
|
}
|
|
@@ -28337,7 +28337,7 @@ function isRecursive(inst, stack, resolve8) {
|
|
|
28337
28337
|
break;
|
|
28338
28338
|
// `$ZodLazy` caches its inner on the def, so a resolved edge is followed exactly
|
|
28339
28339
|
case "lazy": {
|
|
28340
|
-
const inner = def._cachedInner ?? (
|
|
28340
|
+
const inner = def._cachedInner ?? (resolve9 ? inst._zod.innerType : void 0);
|
|
28341
28341
|
merge2(inner ? isRecursive(inner, stack, false) : ASSUMED);
|
|
28342
28342
|
break;
|
|
28343
28343
|
}
|
|
@@ -46930,7 +46930,7 @@ var Protocol = class {
|
|
|
46930
46930
|
return;
|
|
46931
46931
|
}
|
|
46932
46932
|
const pollInterval = task2.pollInterval ?? this._options?.defaultTaskPollInterval ?? 1e3;
|
|
46933
|
-
await new Promise((
|
|
46933
|
+
await new Promise((resolve9) => setTimeout(resolve9, pollInterval));
|
|
46934
46934
|
options?.signal?.throwIfAborted();
|
|
46935
46935
|
}
|
|
46936
46936
|
} catch (error62) {
|
|
@@ -46947,7 +46947,7 @@ var Protocol = class {
|
|
|
46947
46947
|
*/
|
|
46948
46948
|
request(request, resultSchema, options) {
|
|
46949
46949
|
const { relatedRequestId, resumptionToken, onresumptiontoken, task, relatedTask } = options ?? {};
|
|
46950
|
-
return new Promise((
|
|
46950
|
+
return new Promise((resolve9, reject) => {
|
|
46951
46951
|
const earlyReject = (error62) => {
|
|
46952
46952
|
reject(error62);
|
|
46953
46953
|
};
|
|
@@ -47025,7 +47025,7 @@ var Protocol = class {
|
|
|
47025
47025
|
if (!parseResult.success) {
|
|
47026
47026
|
reject(parseResult.error);
|
|
47027
47027
|
} else {
|
|
47028
|
-
|
|
47028
|
+
resolve9(parseResult.data);
|
|
47029
47029
|
}
|
|
47030
47030
|
} catch (error62) {
|
|
47031
47031
|
reject(error62);
|
|
@@ -47287,12 +47287,12 @@ var Protocol = class {
|
|
|
47287
47287
|
}
|
|
47288
47288
|
} catch {
|
|
47289
47289
|
}
|
|
47290
|
-
return new Promise((
|
|
47290
|
+
return new Promise((resolve9, reject) => {
|
|
47291
47291
|
if (signal.aborted) {
|
|
47292
47292
|
reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
|
|
47293
47293
|
return;
|
|
47294
47294
|
}
|
|
47295
|
-
const timeoutId = setTimeout(
|
|
47295
|
+
const timeoutId = setTimeout(resolve9, interval);
|
|
47296
47296
|
signal.addEventListener("abort", () => {
|
|
47297
47297
|
clearTimeout(timeoutId);
|
|
47298
47298
|
reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
|
|
@@ -48435,7 +48435,7 @@ var McpServer = class {
|
|
|
48435
48435
|
let task = createTaskResult.task;
|
|
48436
48436
|
const pollInterval = task.pollInterval ?? 5e3;
|
|
48437
48437
|
while (task.status !== "completed" && task.status !== "failed" && task.status !== "cancelled") {
|
|
48438
|
-
await new Promise((
|
|
48438
|
+
await new Promise((resolve9) => setTimeout(resolve9, pollInterval));
|
|
48439
48439
|
const updatedTask = await extra.taskStore.getTask(taskId);
|
|
48440
48440
|
if (!updatedTask) {
|
|
48441
48441
|
throw new McpError(ErrorCode.InternalError, `Task ${taskId} not found during polling`);
|
|
@@ -49101,12 +49101,12 @@ var StdioServerTransport = class {
|
|
|
49101
49101
|
this.onclose?.();
|
|
49102
49102
|
}
|
|
49103
49103
|
send(message) {
|
|
49104
|
-
return new Promise((
|
|
49104
|
+
return new Promise((resolve9) => {
|
|
49105
49105
|
const json2 = serializeMessage(message);
|
|
49106
49106
|
if (this._stdout.write(json2)) {
|
|
49107
|
-
|
|
49107
|
+
resolve9();
|
|
49108
49108
|
} else {
|
|
49109
|
-
this._stdout.once("drain",
|
|
49109
|
+
this._stdout.once("drain", resolve9);
|
|
49110
49110
|
}
|
|
49111
49111
|
});
|
|
49112
49112
|
}
|
|
@@ -49271,6 +49271,33 @@ var ConfigSchema = external_exports.object({
|
|
|
49271
49271
|
}).optional()
|
|
49272
49272
|
})
|
|
49273
49273
|
).default([])
|
|
49274
|
+
}).prefault({}),
|
|
49275
|
+
usage: external_exports.object({
|
|
49276
|
+
/** Ingest Claude Code transcripts (tokens, cache, cost per model call) and record statusline snapshots. */
|
|
49277
|
+
enabled: external_exports.boolean().default(true),
|
|
49278
|
+
/** Soft budgets; crossing one adds a one-line warning to the next prompt (never blocks). */
|
|
49279
|
+
budgets: external_exports.object({
|
|
49280
|
+
sessionUsd: external_exports.number().positive().optional(),
|
|
49281
|
+
dailyUsd: external_exports.number().positive().optional(),
|
|
49282
|
+
/** A single API call above this is flagged as an expensive turn. */
|
|
49283
|
+
turnUsd: external_exports.number().positive().optional()
|
|
49284
|
+
}).prefault({}),
|
|
49285
|
+
/**
|
|
49286
|
+
* Price overrides per model, USD per million tokens, matched by substring of the model id
|
|
49287
|
+
* (first match wins). Missing fields fall back to the bundled table.
|
|
49288
|
+
*/
|
|
49289
|
+
pricing: external_exports.record(
|
|
49290
|
+
external_exports.string(),
|
|
49291
|
+
external_exports.object({
|
|
49292
|
+
input: external_exports.number().nonnegative().optional(),
|
|
49293
|
+
output: external_exports.number().nonnegative().optional(),
|
|
49294
|
+
cacheWrite5m: external_exports.number().nonnegative().optional(),
|
|
49295
|
+
cacheWrite1h: external_exports.number().nonnegative().optional(),
|
|
49296
|
+
cacheRead: external_exports.number().nonnegative().optional()
|
|
49297
|
+
})
|
|
49298
|
+
).default({}),
|
|
49299
|
+
/** Show cost, context and plan usage segments in `eminiar statusline`. */
|
|
49300
|
+
statusline: external_exports.boolean().default(true)
|
|
49274
49301
|
}).prefault({})
|
|
49275
49302
|
});
|
|
49276
49303
|
function readJson(file3) {
|
|
@@ -49780,6 +49807,107 @@ CREATE TABLE IF NOT EXISTS index_runs (
|
|
|
49780
49807
|
head_sha TEXT,
|
|
49781
49808
|
detail TEXT
|
|
49782
49809
|
);
|
|
49810
|
+
`
|
|
49811
|
+
},
|
|
49812
|
+
{
|
|
49813
|
+
// Usage insights: api_calls mirrors one API request per Claude Code transcript entry
|
|
49814
|
+
// (deduped on request_id + message_id; cost cached at ingest), tool_uses pairs each
|
|
49815
|
+
// tool_use block with its tool_result size, context_events records compactions,
|
|
49816
|
+
// transcript_files keeps the byte offset so ingest only reads new bytes, and
|
|
49817
|
+
// statusline_snapshots stores what `eminiar statusline` saw. Column names = row fields.
|
|
49818
|
+
version: 5,
|
|
49819
|
+
sql: `
|
|
49820
|
+
CREATE TABLE IF NOT EXISTS api_calls (
|
|
49821
|
+
request_id TEXT NOT NULL,
|
|
49822
|
+
message_id TEXT NOT NULL,
|
|
49823
|
+
session_id TEXT NOT NULL,
|
|
49824
|
+
agent_id TEXT,
|
|
49825
|
+
sidechain INTEGER NOT NULL DEFAULT 0,
|
|
49826
|
+
ts INTEGER NOT NULL,
|
|
49827
|
+
model TEXT NOT NULL,
|
|
49828
|
+
input_tokens INTEGER NOT NULL DEFAULT 0,
|
|
49829
|
+
output_tokens INTEGER NOT NULL DEFAULT 0,
|
|
49830
|
+
cache_create_5m INTEGER NOT NULL DEFAULT 0,
|
|
49831
|
+
cache_create_1h INTEGER NOT NULL DEFAULT 0,
|
|
49832
|
+
cache_read INTEGER NOT NULL DEFAULT 0,
|
|
49833
|
+
thinking_tokens INTEGER NOT NULL DEFAULT 0,
|
|
49834
|
+
web_searches INTEGER NOT NULL DEFAULT 0,
|
|
49835
|
+
web_fetches INTEGER NOT NULL DEFAULT 0,
|
|
49836
|
+
service_tier TEXT,
|
|
49837
|
+
speed TEXT,
|
|
49838
|
+
stop_reason TEXT,
|
|
49839
|
+
prompt_id TEXT,
|
|
49840
|
+
git_branch TEXT,
|
|
49841
|
+
cwd TEXT,
|
|
49842
|
+
effort TEXT,
|
|
49843
|
+
version TEXT,
|
|
49844
|
+
entrypoint TEXT,
|
|
49845
|
+
cost_usd REAL,
|
|
49846
|
+
priced INTEGER NOT NULL DEFAULT 0,
|
|
49847
|
+
PRIMARY KEY (request_id, message_id)
|
|
49848
|
+
);
|
|
49849
|
+
CREATE INDEX IF NOT EXISTS api_calls_session_ts ON api_calls(session_id, ts);
|
|
49850
|
+
CREATE INDEX IF NOT EXISTS api_calls_ts ON api_calls(ts);
|
|
49851
|
+
|
|
49852
|
+
CREATE TABLE IF NOT EXISTS tool_uses (
|
|
49853
|
+
tool_use_id TEXT PRIMARY KEY,
|
|
49854
|
+
session_id TEXT NOT NULL,
|
|
49855
|
+
agent_id TEXT,
|
|
49856
|
+
ts INTEGER NOT NULL,
|
|
49857
|
+
tool TEXT NOT NULL,
|
|
49858
|
+
input_chars INTEGER NOT NULL DEFAULT 0,
|
|
49859
|
+
result_chars INTEGER,
|
|
49860
|
+
is_error INTEGER NOT NULL DEFAULT 0,
|
|
49861
|
+
request_id TEXT
|
|
49862
|
+
);
|
|
49863
|
+
CREATE INDEX IF NOT EXISTS tool_uses_session_ts ON tool_uses(session_id, ts);
|
|
49864
|
+
CREATE INDEX IF NOT EXISTS tool_uses_ts ON tool_uses(ts);
|
|
49865
|
+
|
|
49866
|
+
CREATE TABLE IF NOT EXISTS context_events (
|
|
49867
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
49868
|
+
session_id TEXT NOT NULL,
|
|
49869
|
+
ts INTEGER NOT NULL,
|
|
49870
|
+
kind TEXT NOT NULL,
|
|
49871
|
+
trigger TEXT,
|
|
49872
|
+
pre_tokens INTEGER,
|
|
49873
|
+
post_tokens INTEGER
|
|
49874
|
+
);
|
|
49875
|
+
CREATE INDEX IF NOT EXISTS context_events_session_ts ON context_events(session_id, ts);
|
|
49876
|
+
CREATE INDEX IF NOT EXISTS context_events_ts ON context_events(ts);
|
|
49877
|
+
|
|
49878
|
+
CREATE TABLE IF NOT EXISTS transcript_files (
|
|
49879
|
+
path TEXT PRIMARY KEY,
|
|
49880
|
+
session_id TEXT,
|
|
49881
|
+
offset INTEGER NOT NULL DEFAULT 0,
|
|
49882
|
+
size INTEGER NOT NULL DEFAULT 0,
|
|
49883
|
+
mtime INTEGER NOT NULL DEFAULT 0,
|
|
49884
|
+
head_hash TEXT,
|
|
49885
|
+
ingested_at INTEGER NOT NULL
|
|
49886
|
+
);
|
|
49887
|
+
|
|
49888
|
+
CREATE TABLE IF NOT EXISTS statusline_snapshots (
|
|
49889
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
49890
|
+
session_id TEXT NOT NULL,
|
|
49891
|
+
ts INTEGER NOT NULL,
|
|
49892
|
+
model_id TEXT,
|
|
49893
|
+
cost_usd REAL,
|
|
49894
|
+
api_ms INTEGER,
|
|
49895
|
+
duration_ms INTEGER,
|
|
49896
|
+
ctx_input_tokens INTEGER,
|
|
49897
|
+
ctx_output_tokens INTEGER,
|
|
49898
|
+
ctx_size INTEGER,
|
|
49899
|
+
ctx_used_pct REAL,
|
|
49900
|
+
cache_warm INTEGER,
|
|
49901
|
+
cache_ttl TEXT,
|
|
49902
|
+
five_hour_pct REAL,
|
|
49903
|
+
five_hour_resets_at INTEGER,
|
|
49904
|
+
seven_day_pct REAL,
|
|
49905
|
+
seven_day_resets_at INTEGER,
|
|
49906
|
+
lines_added INTEGER,
|
|
49907
|
+
lines_removed INTEGER
|
|
49908
|
+
);
|
|
49909
|
+
CREATE INDEX IF NOT EXISTS statusline_snapshots_session_ts ON statusline_snapshots(session_id, ts);
|
|
49910
|
+
CREATE INDEX IF NOT EXISTS statusline_snapshots_ts ON statusline_snapshots(ts);
|
|
49783
49911
|
`
|
|
49784
49912
|
}
|
|
49785
49913
|
];
|
|
@@ -49819,6 +49947,27 @@ function filterSql(f) {
|
|
|
49819
49947
|
}
|
|
49820
49948
|
return { where: clauses.length ? ` AND ${clauses.join(" AND ")}` : "", params };
|
|
49821
49949
|
}
|
|
49950
|
+
function usageWhere(f, opts = {}) {
|
|
49951
|
+
const clauses = ["1=1"];
|
|
49952
|
+
const params = [];
|
|
49953
|
+
if (f.sessionId) {
|
|
49954
|
+
clauses.push("session_id = ?");
|
|
49955
|
+
params.push(f.sessionId);
|
|
49956
|
+
}
|
|
49957
|
+
if (f.since !== void 0) {
|
|
49958
|
+
clauses.push("ts >= ?");
|
|
49959
|
+
params.push(f.since);
|
|
49960
|
+
}
|
|
49961
|
+
if (f.until !== void 0) {
|
|
49962
|
+
clauses.push("ts < ?");
|
|
49963
|
+
params.push(f.until);
|
|
49964
|
+
}
|
|
49965
|
+
if (f.sidechain !== void 0 && opts.sidechain !== null) {
|
|
49966
|
+
const expr = opts.sidechain ?? "sidechain = 1";
|
|
49967
|
+
clauses.push(f.sidechain ? expr : `NOT (${expr})`);
|
|
49968
|
+
}
|
|
49969
|
+
return { where: clauses.join(" AND "), params };
|
|
49970
|
+
}
|
|
49822
49971
|
var SqliteAdapter = class _SqliteAdapter {
|
|
49823
49972
|
constructor(path3, opts = {}) {
|
|
49824
49973
|
this.path = path3;
|
|
@@ -50694,6 +50843,233 @@ var SqliteAdapter = class _SqliteAdapter {
|
|
|
50694
50843
|
memoryCount() {
|
|
50695
50844
|
return this.db.prepare("SELECT COUNT(*) n FROM memories").get().n;
|
|
50696
50845
|
}
|
|
50846
|
+
// --- usage: model calls, tool results, context events, statusline ------------
|
|
50847
|
+
apiCallsUpsert(rows) {
|
|
50848
|
+
if (!rows.length) return 0;
|
|
50849
|
+
const ins = this.prep(
|
|
50850
|
+
`INSERT INTO api_calls (request_id, message_id, session_id, agent_id, sidechain, ts, model,
|
|
50851
|
+
input_tokens, output_tokens, cache_create_5m, cache_create_1h, cache_read, thinking_tokens,
|
|
50852
|
+
web_searches, web_fetches, service_tier, speed, stop_reason, prompt_id, git_branch, cwd, effort,
|
|
50853
|
+
version, entrypoint, cost_usd, priced)
|
|
50854
|
+
VALUES (@request_id, @message_id, @session_id, @agent_id, @sidechain, @ts, @model,
|
|
50855
|
+
@input_tokens, @output_tokens, @cache_create_5m, @cache_create_1h, @cache_read, @thinking_tokens,
|
|
50856
|
+
@web_searches, @web_fetches, @service_tier, @speed, @stop_reason, @prompt_id, @git_branch, @cwd, @effort,
|
|
50857
|
+
@version, @entrypoint, @cost_usd, @priced)
|
|
50858
|
+
ON CONFLICT(request_id, message_id) DO UPDATE SET
|
|
50859
|
+
cost_usd = CASE WHEN excluded.output_tokens >= api_calls.output_tokens THEN excluded.cost_usd ELSE api_calls.cost_usd END,
|
|
50860
|
+
priced = CASE WHEN excluded.output_tokens >= api_calls.output_tokens THEN excluded.priced ELSE api_calls.priced END,
|
|
50861
|
+
input_tokens = MAX(api_calls.input_tokens, excluded.input_tokens),
|
|
50862
|
+
output_tokens = MAX(api_calls.output_tokens, excluded.output_tokens),
|
|
50863
|
+
cache_create_5m = MAX(api_calls.cache_create_5m, excluded.cache_create_5m),
|
|
50864
|
+
cache_create_1h = MAX(api_calls.cache_create_1h, excluded.cache_create_1h),
|
|
50865
|
+
cache_read = MAX(api_calls.cache_read, excluded.cache_read),
|
|
50866
|
+
thinking_tokens = MAX(api_calls.thinking_tokens, excluded.thinking_tokens),
|
|
50867
|
+
web_searches = MAX(api_calls.web_searches, excluded.web_searches),
|
|
50868
|
+
web_fetches = MAX(api_calls.web_fetches, excluded.web_fetches),
|
|
50869
|
+
stop_reason = COALESCE(excluded.stop_reason, api_calls.stop_reason),
|
|
50870
|
+
service_tier = COALESCE(excluded.service_tier, api_calls.service_tier),
|
|
50871
|
+
speed = COALESCE(excluded.speed, api_calls.speed),
|
|
50872
|
+
prompt_id = COALESCE(api_calls.prompt_id, excluded.prompt_id),
|
|
50873
|
+
effort = COALESCE(api_calls.effort, excluded.effort)`
|
|
50874
|
+
);
|
|
50875
|
+
const count = this.prep("SELECT COUNT(*) AS n FROM api_calls");
|
|
50876
|
+
return this.transaction(() => {
|
|
50877
|
+
const before = count.get().n;
|
|
50878
|
+
for (const r of rows) {
|
|
50879
|
+
ins.run({
|
|
50880
|
+
request_id: r.request_id,
|
|
50881
|
+
message_id: r.message_id,
|
|
50882
|
+
session_id: r.session_id,
|
|
50883
|
+
agent_id: r.agent_id ?? null,
|
|
50884
|
+
sidechain: r.sidechain ? 1 : 0,
|
|
50885
|
+
ts: r.ts,
|
|
50886
|
+
model: r.model,
|
|
50887
|
+
input_tokens: r.input_tokens,
|
|
50888
|
+
output_tokens: r.output_tokens,
|
|
50889
|
+
cache_create_5m: r.cache_create_5m,
|
|
50890
|
+
cache_create_1h: r.cache_create_1h,
|
|
50891
|
+
cache_read: r.cache_read,
|
|
50892
|
+
thinking_tokens: r.thinking_tokens,
|
|
50893
|
+
web_searches: r.web_searches,
|
|
50894
|
+
web_fetches: r.web_fetches,
|
|
50895
|
+
service_tier: r.service_tier ?? null,
|
|
50896
|
+
speed: r.speed ?? null,
|
|
50897
|
+
stop_reason: r.stop_reason ?? null,
|
|
50898
|
+
prompt_id: r.prompt_id ?? null,
|
|
50899
|
+
git_branch: r.git_branch ?? null,
|
|
50900
|
+
cwd: r.cwd ?? null,
|
|
50901
|
+
effort: r.effort ?? null,
|
|
50902
|
+
version: r.version ?? null,
|
|
50903
|
+
entrypoint: r.entrypoint ?? null,
|
|
50904
|
+
cost_usd: r.cost_usd ?? null,
|
|
50905
|
+
priced: r.priced ?? 0
|
|
50906
|
+
});
|
|
50907
|
+
}
|
|
50908
|
+
return count.get().n - before;
|
|
50909
|
+
});
|
|
50910
|
+
}
|
|
50911
|
+
apiCalls(filter = {}) {
|
|
50912
|
+
const { where, params } = usageWhere(filter);
|
|
50913
|
+
params.push(filter.limit ?? 1e5);
|
|
50914
|
+
return this.prep(
|
|
50915
|
+
`SELECT * FROM api_calls WHERE ${where} ORDER BY ts ASC, rowid ASC LIMIT ?`
|
|
50916
|
+
).all(...params);
|
|
50917
|
+
}
|
|
50918
|
+
apiCallsLatestTs() {
|
|
50919
|
+
const row = this.prep("SELECT MAX(ts) AS ts FROM api_calls").get();
|
|
50920
|
+
return row?.ts ?? void 0;
|
|
50921
|
+
}
|
|
50922
|
+
toolUsesUpsert(rows) {
|
|
50923
|
+
if (!rows.length) return 0;
|
|
50924
|
+
const ins = this.prep(
|
|
50925
|
+
`INSERT INTO tool_uses (tool_use_id, session_id, agent_id, ts, tool, input_chars, result_chars, is_error, request_id)
|
|
50926
|
+
VALUES (@tool_use_id, @session_id, @agent_id, @ts, @tool, @input_chars, @result_chars, @is_error, @request_id)
|
|
50927
|
+
ON CONFLICT(tool_use_id) DO UPDATE SET
|
|
50928
|
+
tool = CASE WHEN tool_uses.tool = '' THEN excluded.tool ELSE tool_uses.tool END,
|
|
50929
|
+
input_chars = MAX(tool_uses.input_chars, excluded.input_chars),
|
|
50930
|
+
result_chars = COALESCE(excluded.result_chars, tool_uses.result_chars),
|
|
50931
|
+
is_error = MAX(tool_uses.is_error, excluded.is_error),
|
|
50932
|
+
request_id = COALESCE(tool_uses.request_id, excluded.request_id),
|
|
50933
|
+
agent_id = COALESCE(tool_uses.agent_id, excluded.agent_id),
|
|
50934
|
+
ts = MIN(tool_uses.ts, excluded.ts)`
|
|
50935
|
+
);
|
|
50936
|
+
const count = this.prep("SELECT COUNT(*) AS n FROM tool_uses");
|
|
50937
|
+
return this.transaction(() => {
|
|
50938
|
+
const before = count.get().n;
|
|
50939
|
+
for (const r of rows) {
|
|
50940
|
+
ins.run({
|
|
50941
|
+
tool_use_id: r.tool_use_id,
|
|
50942
|
+
session_id: r.session_id,
|
|
50943
|
+
agent_id: r.agent_id ?? null,
|
|
50944
|
+
ts: r.ts,
|
|
50945
|
+
tool: r.tool,
|
|
50946
|
+
input_chars: r.input_chars,
|
|
50947
|
+
result_chars: r.result_chars ?? null,
|
|
50948
|
+
is_error: r.is_error ? 1 : 0,
|
|
50949
|
+
request_id: r.request_id ?? null
|
|
50950
|
+
});
|
|
50951
|
+
}
|
|
50952
|
+
return count.get().n - before;
|
|
50953
|
+
});
|
|
50954
|
+
}
|
|
50955
|
+
toolUses(filter = {}) {
|
|
50956
|
+
const { where, params } = usageWhere(filter, { sidechain: "agent_id IS NOT NULL" });
|
|
50957
|
+
params.push(filter.limit ?? 1e5);
|
|
50958
|
+
return this.prep(
|
|
50959
|
+
`SELECT * FROM tool_uses WHERE ${where} ORDER BY ts ASC, rowid ASC LIMIT ?`
|
|
50960
|
+
).all(...params);
|
|
50961
|
+
}
|
|
50962
|
+
contextEventInsert(e) {
|
|
50963
|
+
const info = this.prep(
|
|
50964
|
+
`INSERT INTO context_events (session_id, ts, kind, trigger, pre_tokens, post_tokens)
|
|
50965
|
+
VALUES (?, ?, ?, ?, ?, ?)`
|
|
50966
|
+
).run(
|
|
50967
|
+
e.session_id,
|
|
50968
|
+
e.ts,
|
|
50969
|
+
e.kind,
|
|
50970
|
+
e.trigger ?? null,
|
|
50971
|
+
e.pre_tokens ?? null,
|
|
50972
|
+
e.post_tokens ?? null
|
|
50973
|
+
);
|
|
50974
|
+
return Number(info.lastInsertRowid);
|
|
50975
|
+
}
|
|
50976
|
+
contextEvents(filter = {}) {
|
|
50977
|
+
const { where, params } = usageWhere(filter, { sidechain: null });
|
|
50978
|
+
params.push(filter.limit ?? 1e5);
|
|
50979
|
+
return this.prep(
|
|
50980
|
+
`SELECT * FROM context_events WHERE ${where} ORDER BY ts ASC, id ASC LIMIT ?`
|
|
50981
|
+
).all(...params);
|
|
50982
|
+
}
|
|
50983
|
+
transcriptFileGet(path3) {
|
|
50984
|
+
return this.prep("SELECT * FROM transcript_files WHERE path = ?").get(path3);
|
|
50985
|
+
}
|
|
50986
|
+
transcriptFileSet(row) {
|
|
50987
|
+
this.prep(
|
|
50988
|
+
`INSERT INTO transcript_files (path, session_id, offset, size, mtime, head_hash, ingested_at)
|
|
50989
|
+
VALUES (@path, @session_id, @offset, @size, @mtime, @head_hash, @ingested_at)
|
|
50990
|
+
ON CONFLICT(path) DO UPDATE SET
|
|
50991
|
+
session_id = COALESCE(excluded.session_id, transcript_files.session_id),
|
|
50992
|
+
offset = excluded.offset, size = excluded.size, mtime = excluded.mtime,
|
|
50993
|
+
head_hash = excluded.head_hash, ingested_at = excluded.ingested_at`
|
|
50994
|
+
).run({
|
|
50995
|
+
path: row.path,
|
|
50996
|
+
session_id: row.session_id ?? null,
|
|
50997
|
+
offset: row.offset,
|
|
50998
|
+
size: row.size,
|
|
50999
|
+
mtime: row.mtime,
|
|
51000
|
+
head_hash: row.head_hash ?? null,
|
|
51001
|
+
ingested_at: row.ingested_at
|
|
51002
|
+
});
|
|
51003
|
+
}
|
|
51004
|
+
transcriptFiles() {
|
|
51005
|
+
return this.prep("SELECT * FROM transcript_files ORDER BY path").all();
|
|
51006
|
+
}
|
|
51007
|
+
statuslineSnapshotInsert(s) {
|
|
51008
|
+
const info = this.prep(
|
|
51009
|
+
`INSERT INTO statusline_snapshots (session_id, ts, model_id, cost_usd, api_ms, duration_ms,
|
|
51010
|
+
ctx_input_tokens, ctx_output_tokens, ctx_size, ctx_used_pct, cache_warm, cache_ttl,
|
|
51011
|
+
five_hour_pct, five_hour_resets_at, seven_day_pct, seven_day_resets_at, lines_added, lines_removed)
|
|
51012
|
+
VALUES (@session_id, @ts, @model_id, @cost_usd, @api_ms, @duration_ms,
|
|
51013
|
+
@ctx_input_tokens, @ctx_output_tokens, @ctx_size, @ctx_used_pct, @cache_warm, @cache_ttl,
|
|
51014
|
+
@five_hour_pct, @five_hour_resets_at, @seven_day_pct, @seven_day_resets_at, @lines_added, @lines_removed)`
|
|
51015
|
+
).run({
|
|
51016
|
+
session_id: s.session_id,
|
|
51017
|
+
ts: s.ts,
|
|
51018
|
+
model_id: s.model_id ?? null,
|
|
51019
|
+
cost_usd: s.cost_usd ?? null,
|
|
51020
|
+
api_ms: s.api_ms ?? null,
|
|
51021
|
+
duration_ms: s.duration_ms ?? null,
|
|
51022
|
+
ctx_input_tokens: s.ctx_input_tokens ?? null,
|
|
51023
|
+
ctx_output_tokens: s.ctx_output_tokens ?? null,
|
|
51024
|
+
ctx_size: s.ctx_size ?? null,
|
|
51025
|
+
ctx_used_pct: s.ctx_used_pct ?? null,
|
|
51026
|
+
cache_warm: s.cache_warm ?? null,
|
|
51027
|
+
cache_ttl: s.cache_ttl ?? null,
|
|
51028
|
+
five_hour_pct: s.five_hour_pct ?? null,
|
|
51029
|
+
five_hour_resets_at: s.five_hour_resets_at ?? null,
|
|
51030
|
+
seven_day_pct: s.seven_day_pct ?? null,
|
|
51031
|
+
seven_day_resets_at: s.seven_day_resets_at ?? null,
|
|
51032
|
+
lines_added: s.lines_added ?? null,
|
|
51033
|
+
lines_removed: s.lines_removed ?? null
|
|
51034
|
+
});
|
|
51035
|
+
return Number(info.lastInsertRowid);
|
|
51036
|
+
}
|
|
51037
|
+
statuslineSnapshots(filter = {}) {
|
|
51038
|
+
const { where, params } = usageWhere(filter, { sidechain: null });
|
|
51039
|
+
params.push(filter.limit ?? 1e5);
|
|
51040
|
+
return this.prep(
|
|
51041
|
+
`SELECT * FROM statusline_snapshots WHERE ${where} ORDER BY ts ASC, id ASC LIMIT ?`
|
|
51042
|
+
).all(...params);
|
|
51043
|
+
}
|
|
51044
|
+
statuslineLatest(sessionId) {
|
|
51045
|
+
if (sessionId) {
|
|
51046
|
+
return this.prep(
|
|
51047
|
+
"SELECT * FROM statusline_snapshots WHERE session_id = ? ORDER BY ts DESC, id DESC LIMIT 1"
|
|
51048
|
+
).get(sessionId);
|
|
51049
|
+
}
|
|
51050
|
+
return this.prep(
|
|
51051
|
+
"SELECT * FROM statusline_snapshots ORDER BY ts DESC, id DESC LIMIT 1"
|
|
51052
|
+
).get();
|
|
51053
|
+
}
|
|
51054
|
+
statuslineSnapshotsPrune(opts) {
|
|
51055
|
+
const keep = Math.max(0, Math.floor(opts.keepPerSession));
|
|
51056
|
+
return this.transaction(() => {
|
|
51057
|
+
let n = this.prep("DELETE FROM statusline_snapshots WHERE ts < ?").run(
|
|
51058
|
+
opts.olderThan
|
|
51059
|
+
).changes;
|
|
51060
|
+
n += this.prep(
|
|
51061
|
+
`DELETE FROM statusline_snapshots WHERE id IN (
|
|
51062
|
+
SELECT s.id FROM statusline_snapshots s
|
|
51063
|
+
WHERE s.id NOT IN (
|
|
51064
|
+
SELECT k.id FROM statusline_snapshots k
|
|
51065
|
+
WHERE k.session_id = s.session_id
|
|
51066
|
+
ORDER BY k.ts DESC, k.id DESC LIMIT ?
|
|
51067
|
+
)
|
|
51068
|
+
)`
|
|
51069
|
+
).run(keep).changes;
|
|
51070
|
+
return n;
|
|
51071
|
+
});
|
|
51072
|
+
}
|
|
50697
51073
|
// --- misc --------------------------------------------------------------
|
|
50698
51074
|
metaGet(key) {
|
|
50699
51075
|
const row = this.db.prepare("SELECT value FROM meta WHERE key = ?").get(key);
|
|
@@ -50724,6 +51100,11 @@ var SqliteAdapter = class _SqliteAdapter {
|
|
|
50724
51100
|
"DELETE FROM graph_edges; DELETE FROM graph_nodes; DELETE FROM graph_metrics;"
|
|
50725
51101
|
);
|
|
50726
51102
|
}
|
|
51103
|
+
if (scope === "usage" || scope === "all") {
|
|
51104
|
+
this.db.exec(
|
|
51105
|
+
"DELETE FROM api_calls; DELETE FROM tool_uses; DELETE FROM context_events; DELETE FROM transcript_files; DELETE FROM statusline_snapshots;"
|
|
51106
|
+
);
|
|
51107
|
+
}
|
|
50727
51108
|
if (scope === "memories" || scope === "all") {
|
|
50728
51109
|
this.db.exec("DELETE FROM memories;");
|
|
50729
51110
|
if (scope === "memories") {
|
|
@@ -50813,7 +51194,7 @@ function impact(db, ref, opts = {}) {
|
|
|
50813
51194
|
const depth = opts.depth ?? 2;
|
|
50814
51195
|
const kinds = opts.edgeKinds ?? STRUCTURAL;
|
|
50815
51196
|
const hits = /* @__PURE__ */ new Map();
|
|
50816
|
-
const
|
|
51197
|
+
const add2 = (id, score, hops, reason) => {
|
|
50817
51198
|
const key = toFile(id);
|
|
50818
51199
|
if (key === toFile(root.id)) return;
|
|
50819
51200
|
const h = hits.get(key) ?? { id: key, score: 0, hops, reasons: [] };
|
|
@@ -50830,7 +51211,7 @@ function impact(db, ref, opts = {}) {
|
|
|
50830
51211
|
const next = [];
|
|
50831
51212
|
for (const e of incoming) {
|
|
50832
51213
|
const via = e.dst === root.id ? root.name : e.dst.replace(/^sym:[^#]*#/, "");
|
|
50833
|
-
|
|
51214
|
+
add2(
|
|
50834
51215
|
e.src,
|
|
50835
51216
|
e.confidence * e.weight / hop,
|
|
50836
51217
|
hop,
|
|
@@ -50847,14 +51228,14 @@ function impact(db, ref, opts = {}) {
|
|
|
50847
51228
|
for (const e of db.graphEdgesTo(start, ["mentions", "documents"])) {
|
|
50848
51229
|
const via = e.dst === root.id ? root.name : e.dst.replace(/^sym:[^#]*#/, "");
|
|
50849
51230
|
const who = e.kind === "documents" ? "doc documents" : "memory mentions";
|
|
50850
|
-
|
|
51231
|
+
add2(e.src, 0.5 * e.confidence, 1, `${who} ${via}`);
|
|
50851
51232
|
}
|
|
50852
51233
|
if (opts.includeGit !== false) {
|
|
50853
51234
|
const file3 = toFile(root.id);
|
|
50854
51235
|
for (const e of db.graphEdgesFrom([file3], ["co_changes"])) {
|
|
50855
51236
|
if (e.weight < 0.3) continue;
|
|
50856
51237
|
const meta4 = e.meta ? JSON.parse(e.meta) : {};
|
|
50857
|
-
|
|
51238
|
+
add2(e.dst, e.weight, 1, `co-changed ${meta4.count ?? "?"}\xD7 (${Math.round(e.weight * 100)}%)`);
|
|
50858
51239
|
}
|
|
50859
51240
|
}
|
|
50860
51241
|
const list2 = [...hits.values()].sort((a, b) => b.score - a.score);
|
|
@@ -51198,7 +51579,7 @@ function pkgJson(root) {
|
|
|
51198
51579
|
}
|
|
51199
51580
|
function folderRows(files, languages2) {
|
|
51200
51581
|
const acc = /* @__PURE__ */ new Map();
|
|
51201
|
-
const
|
|
51582
|
+
const add2 = (key, r) => {
|
|
51202
51583
|
const a = acc.get(key) ?? { files: 0, loc: 0, score: 0, langs: {} };
|
|
51203
51584
|
a.files++;
|
|
51204
51585
|
a.loc += r.loc;
|
|
@@ -51209,8 +51590,8 @@ function folderRows(files, languages2) {
|
|
|
51209
51590
|
};
|
|
51210
51591
|
for (const r of files) {
|
|
51211
51592
|
const parts = (r.path ?? "").split("/");
|
|
51212
|
-
|
|
51213
|
-
if (parts.length > 2)
|
|
51593
|
+
add2(parts.length > 1 ? `${parts[0]}/` : "(root)", r);
|
|
51594
|
+
if (parts.length > 2) add2(`${parts[0]}/${parts[1]}/`, r);
|
|
51214
51595
|
}
|
|
51215
51596
|
const rows = [...acc.entries()].map(([path3, a]) => ({
|
|
51216
51597
|
path: path3,
|
|
@@ -52425,7 +52806,7 @@ init_esm_shims();
|
|
|
52425
52806
|
import { spawn } from "child_process";
|
|
52426
52807
|
function runProcess(argv, opts) {
|
|
52427
52808
|
const started = Date.now();
|
|
52428
|
-
return new Promise((
|
|
52809
|
+
return new Promise((resolve9) => {
|
|
52429
52810
|
const [cmd, ...args] = argv;
|
|
52430
52811
|
const child = spawn(cmd, args, {
|
|
52431
52812
|
cwd: opts.cwd,
|
|
@@ -52468,7 +52849,7 @@ function runProcess(argv, opts) {
|
|
|
52468
52849
|
if (done) return;
|
|
52469
52850
|
done = true;
|
|
52470
52851
|
clearTimeout(timer);
|
|
52471
|
-
|
|
52852
|
+
resolve9({
|
|
52472
52853
|
stdout: Buffer.concat(out).toString("utf8"),
|
|
52473
52854
|
stderr: errTail,
|
|
52474
52855
|
exitCode,
|
|
@@ -55026,7 +55407,7 @@ function discoverDocs(ctx, opts = {}) {
|
|
|
55026
55407
|
}
|
|
55027
55408
|
}
|
|
55028
55409
|
const byName = /* @__PURE__ */ new Map();
|
|
55029
|
-
const
|
|
55410
|
+
const add2 = (c) => {
|
|
55030
55411
|
const cur = byName.get(c.name);
|
|
55031
55412
|
if (cur) {
|
|
55032
55413
|
if (!cur.usedBy.includes(c.usedBy)) cur.usedBy.push(c.usedBy);
|
|
@@ -55052,7 +55433,7 @@ function discoverDocs(ctx, opts = {}) {
|
|
|
55052
55433
|
const entry = catalogLookup(s.kind, s.key, catalog);
|
|
55053
55434
|
const evidence = `${s.kind}:${s.key}`;
|
|
55054
55435
|
if (entry) {
|
|
55055
|
-
|
|
55436
|
+
add2({
|
|
55056
55437
|
name: entry.id,
|
|
55057
55438
|
provider: `${s.kind}:${s.key}`,
|
|
55058
55439
|
title: entry.title,
|
|
@@ -55069,7 +55450,7 @@ function discoverDocs(ctx, opts = {}) {
|
|
|
55069
55450
|
continue;
|
|
55070
55451
|
}
|
|
55071
55452
|
if (s.kind === "npm" || s.kind === "pypi" || s.kind === "go") {
|
|
55072
|
-
|
|
55453
|
+
add2({
|
|
55073
55454
|
name: slugify3(s.key.replace(/^github\.com\//, "")),
|
|
55074
55455
|
provider: `${s.kind}:${s.key}`,
|
|
55075
55456
|
title: s.key,
|
|
@@ -55079,7 +55460,7 @@ function discoverDocs(ctx, opts = {}) {
|
|
|
55079
55460
|
evidence
|
|
55080
55461
|
});
|
|
55081
55462
|
} else if (s.kind === "image" && !s.key.includes(".") && !s.key.includes("/")) {
|
|
55082
|
-
|
|
55463
|
+
add2({
|
|
55083
55464
|
name: slugify3(s.key.split(":")[0]),
|
|
55084
55465
|
provider: `image:${s.key.split(":")[0]}`,
|
|
55085
55466
|
title: s.key.split(":")[0],
|
|
@@ -55094,7 +55475,7 @@ function discoverDocs(ctx, opts = {}) {
|
|
|
55094
55475
|
if (info.vars.length < 2) continue;
|
|
55095
55476
|
const provider = `env:${token}`;
|
|
55096
55477
|
for (const p of info.projects)
|
|
55097
|
-
|
|
55478
|
+
add2({
|
|
55098
55479
|
name: slugify3(token),
|
|
55099
55480
|
provider,
|
|
55100
55481
|
title: token,
|
|
@@ -55113,7 +55494,7 @@ function discoverDocs(ctx, opts = {}) {
|
|
|
55113
55494
|
if (!h.calls) continue;
|
|
55114
55495
|
const evidence = `api:${host} (${h.samples.slice(0, 2).join(", ")})${specUrls.length ? ` \xB7 spec ${specUrls[0]}` : ""}`;
|
|
55115
55496
|
if (entry) {
|
|
55116
|
-
|
|
55497
|
+
add2({
|
|
55117
55498
|
name: entry.id,
|
|
55118
55499
|
provider: `api:${host}`,
|
|
55119
55500
|
title: entry.title,
|
|
@@ -55129,7 +55510,7 @@ function discoverDocs(ctx, opts = {}) {
|
|
|
55129
55510
|
for (const u of specUrls) if (!c.urls.includes(u)) c.urls.push(u);
|
|
55130
55511
|
} else {
|
|
55131
55512
|
const domain2 = rootDomain(host);
|
|
55132
|
-
|
|
55513
|
+
add2({
|
|
55133
55514
|
name: slugify3(domain2.replace(/\.[a-z.]+$/, "")),
|
|
55134
55515
|
provider: `api:${host}`,
|
|
55135
55516
|
title: `${domain2} API`,
|
|
@@ -55148,7 +55529,7 @@ function discoverDocs(ctx, opts = {}) {
|
|
|
55148
55529
|
for (const l of list2) {
|
|
55149
55530
|
const evidence = `link:${host} (${l.samples.slice(0, 2).join(", ")})`;
|
|
55150
55531
|
if (entry) {
|
|
55151
|
-
|
|
55532
|
+
add2({
|
|
55152
55533
|
name: entry.id,
|
|
55153
55534
|
provider: `link:${host}`,
|
|
55154
55535
|
title: entry.title,
|
|
@@ -55172,7 +55553,7 @@ function discoverDocs(ctx, opts = {}) {
|
|
|
55172
55553
|
if (!existing.usedBy.includes(l.project || ".")) existing.usedBy.push(l.project || ".");
|
|
55173
55554
|
continue;
|
|
55174
55555
|
}
|
|
55175
|
-
|
|
55556
|
+
add2({
|
|
55176
55557
|
name,
|
|
55177
55558
|
provider: `link:${host}`,
|
|
55178
55559
|
title: `${host} (documentation linked from code)`,
|
|
@@ -56675,8 +57056,8 @@ function formatDiscovery(d, max = 40) {
|
|
|
56675
57056
|
for (const p of list2.slice(0, max)) {
|
|
56676
57057
|
const used = p.usedBy.length > 1 ? ` \xB7 used by ${p.usedBy.length} projects` : p.usedBy[0] && p.usedBy[0] !== "." ? ` \xB7 ${p.usedBy[0]}` : "";
|
|
56677
57058
|
const where = p.urls[0] ? ` \xB7 ${p.urls[0]}` : "";
|
|
56678
|
-
const
|
|
56679
|
-
lines.push(`- ${p.name} \u2190 ${p.provider}${used}${where}${
|
|
57059
|
+
const ev2 = conf === "probe" || conf === "unknown" ? ` \xB7 ${p.evidence[0]}` : "";
|
|
57060
|
+
lines.push(`- ${p.name} \u2190 ${p.provider}${used}${where}${ev2}${reg(p.registered)}`);
|
|
56680
57061
|
}
|
|
56681
57062
|
if (list2.length > max) lines.push(` \u2026 ${list2.length - max} more`);
|
|
56682
57063
|
}
|
|
@@ -58341,9 +58722,9 @@ ${body}`);
|
|
|
58341
58722
|
init_esm_shims();
|
|
58342
58723
|
var emPurge = defineTool({
|
|
58343
58724
|
name: "em_purge",
|
|
58344
|
-
description: "Permanently delete eminiar data for this project. scope: index (documents/chunks/vectors/caches), sessions (events, snapshots, stats), graph, memories (the database mirror only; the markdown files under .eminiar/memories stay and are re-imported on the next memory call \u2014 delete files with em_memory), or all. Requires confirm: true.",
|
|
58725
|
+
description: "Permanently delete eminiar data for this project. scope: index (documents/chunks/vectors/caches), sessions (events, snapshots, stats), graph, memories (the database mirror only; the markdown files under .eminiar/memories stay and are re-imported on the next memory call \u2014 delete files with em_memory), usage (API calls, tool uses, context events, transcript offsets, statusline snapshots), or all. Requires confirm: true.",
|
|
58345
58726
|
schema: {
|
|
58346
|
-
scope: external_exports.enum(["index", "sessions", "graph", "memories", "all"]),
|
|
58727
|
+
scope: external_exports.enum(["index", "sessions", "graph", "memories", "usage", "all"]),
|
|
58347
58728
|
confirm: external_exports.boolean().describe("Must be true; this cannot be undone")
|
|
58348
58729
|
},
|
|
58349
58730
|
handler: async (args, ctx) => {
|
|
@@ -58632,8 +59013,8 @@ var top = (m, n = 4) => Object.entries(m).sort((a, b) => b[1] - a[1]).slice(0, n
|
|
|
58632
59013
|
function formatRoutingStats(title, s) {
|
|
58633
59014
|
if (!s.decisions) return `${title}: no routing decisions recorded yet`;
|
|
58634
59015
|
const tools2 = Object.entries(s.byTool).sort((a, b) => b[1].calls - a[1].calls).map(([tool, r]) => {
|
|
58635
|
-
const
|
|
58636
|
-
const parts = [`${tool} ${r.denied}/${r.calls} denied (${
|
|
59016
|
+
const pct3 = Math.round(100 * r.denied / r.calls);
|
|
59017
|
+
const parts = [`${tool} ${r.denied}/${r.calls} denied (${pct3}%)`];
|
|
58637
59018
|
if (r.annotated) parts.push(`${r.annotated} annotated`);
|
|
58638
59019
|
return parts.join(", ");
|
|
58639
59020
|
});
|
|
@@ -58649,6 +59030,1787 @@ function formatRoutingStats(title, s) {
|
|
|
58649
59030
|
return lines.join("\n");
|
|
58650
59031
|
}
|
|
58651
59032
|
|
|
59033
|
+
// src/usage/brief.ts
|
|
59034
|
+
init_esm_shims();
|
|
59035
|
+
|
|
59036
|
+
// src/usage/aggregate.ts
|
|
59037
|
+
init_esm_shims();
|
|
59038
|
+
|
|
59039
|
+
// src/usage/blocks.ts
|
|
59040
|
+
init_esm_shims();
|
|
59041
|
+
var HOUR = 36e5;
|
|
59042
|
+
function floorToHour(ts) {
|
|
59043
|
+
return Math.floor(ts / HOUR) * HOUR;
|
|
59044
|
+
}
|
|
59045
|
+
function newBlock(call, hours) {
|
|
59046
|
+
const start = floorToHour(call.ts);
|
|
59047
|
+
return {
|
|
59048
|
+
id: new Date(start).toISOString(),
|
|
59049
|
+
start,
|
|
59050
|
+
end: start + hours * HOUR,
|
|
59051
|
+
firstCall: call.ts,
|
|
59052
|
+
lastCall: call.ts,
|
|
59053
|
+
calls: 0,
|
|
59054
|
+
tokens: { input: 0, output: 0, cacheCreate: 0, cacheRead: 0 },
|
|
59055
|
+
costUsd: 0,
|
|
59056
|
+
pricedCalls: 0,
|
|
59057
|
+
unpricedCalls: 0,
|
|
59058
|
+
models: [],
|
|
59059
|
+
active: false
|
|
59060
|
+
};
|
|
59061
|
+
}
|
|
59062
|
+
function addCall(block, call) {
|
|
59063
|
+
block.calls += 1;
|
|
59064
|
+
block.lastCall = Math.max(block.lastCall, call.ts);
|
|
59065
|
+
block.tokens.input += call.input_tokens || 0;
|
|
59066
|
+
block.tokens.output += call.output_tokens || 0;
|
|
59067
|
+
block.tokens.cacheCreate += (call.cache_create_5m || 0) + (call.cache_create_1h || 0);
|
|
59068
|
+
block.tokens.cacheRead += call.cache_read || 0;
|
|
59069
|
+
if (call.priced && call.cost_usd != null && Number.isFinite(call.cost_usd)) {
|
|
59070
|
+
block.costUsd += call.cost_usd;
|
|
59071
|
+
block.pricedCalls += 1;
|
|
59072
|
+
} else {
|
|
59073
|
+
block.unpricedCalls += 1;
|
|
59074
|
+
}
|
|
59075
|
+
if (call.model && !block.models.includes(call.model)) block.models.push(call.model);
|
|
59076
|
+
}
|
|
59077
|
+
function billingBlocks(calls, now = Date.now(), hours = 5) {
|
|
59078
|
+
const sorted = calls.filter((c) => Number.isFinite(c.ts) && c.model !== "<synthetic>").slice().sort((a, b) => a.ts - b.ts);
|
|
59079
|
+
const blocks = [];
|
|
59080
|
+
let current;
|
|
59081
|
+
for (const call of sorted) {
|
|
59082
|
+
if (!current || call.ts >= current.end) {
|
|
59083
|
+
const next = newBlock(call, hours);
|
|
59084
|
+
if (current) next.gapBefore = Math.max(0, call.ts - current.lastCall);
|
|
59085
|
+
blocks.push(next);
|
|
59086
|
+
current = next;
|
|
59087
|
+
}
|
|
59088
|
+
addCall(current, call);
|
|
59089
|
+
}
|
|
59090
|
+
for (const b of blocks) {
|
|
59091
|
+
b.models.sort();
|
|
59092
|
+
b.active = now >= b.start && now < b.end;
|
|
59093
|
+
}
|
|
59094
|
+
return blocks;
|
|
59095
|
+
}
|
|
59096
|
+
function activeBlock(blocks, now = Date.now()) {
|
|
59097
|
+
return blocks.find((b) => now >= b.start && now < b.end);
|
|
59098
|
+
}
|
|
59099
|
+
|
|
59100
|
+
// src/usage/pricing.ts
|
|
59101
|
+
init_esm_shims();
|
|
59102
|
+
var WEB_SEARCH_PER_1K = 10;
|
|
59103
|
+
function price(input2, output2, cacheRead, extra = {}) {
|
|
59104
|
+
return {
|
|
59105
|
+
input: input2,
|
|
59106
|
+
output: output2,
|
|
59107
|
+
cacheWrite5m: input2 * 1.25,
|
|
59108
|
+
cacheWrite1h: input2 * 2,
|
|
59109
|
+
cacheRead,
|
|
59110
|
+
webSearch: WEB_SEARCH_PER_1K,
|
|
59111
|
+
...extra
|
|
59112
|
+
};
|
|
59113
|
+
}
|
|
59114
|
+
var FABLE_5_1 = price(10, 50, 0.25);
|
|
59115
|
+
var FABLE_5 = price(10, 50, 1);
|
|
59116
|
+
var OPUS_5_5 = price(4, 20, 0.2);
|
|
59117
|
+
var OPUS_5 = price(5, 25, 0.5);
|
|
59118
|
+
var OPUS_4_LATE = price(5, 25, 0.5);
|
|
59119
|
+
var OPUS_4_EARLY = price(15, 75, 1.5);
|
|
59120
|
+
var SONNET_5_5 = price(2, 10, 0.1);
|
|
59121
|
+
var SONNET_5 = price(2, 10, 0.2);
|
|
59122
|
+
var SONNET_4 = price(3, 15, 0.3);
|
|
59123
|
+
var HAIKU_5_5 = price(0.1, 0.5, 0.01, {
|
|
59124
|
+
longContext: { threshold: 1e5, input: 0.5, output: 2.5 }
|
|
59125
|
+
});
|
|
59126
|
+
var HAIKU_4_5 = price(1, 5, 0.1);
|
|
59127
|
+
var HAIKU_3_5 = price(0.8, 4, 0.08);
|
|
59128
|
+
var FAMILIES = [
|
|
59129
|
+
["fable-5-1", FABLE_5_1],
|
|
59130
|
+
["mythos-5-1", FABLE_5_1],
|
|
59131
|
+
["fable-5", FABLE_5],
|
|
59132
|
+
["mythos-5", FABLE_5],
|
|
59133
|
+
["opus-5-5", OPUS_5_5],
|
|
59134
|
+
["opus-5", OPUS_5],
|
|
59135
|
+
["opus-4-8", OPUS_4_LATE],
|
|
59136
|
+
["opus-4-7", OPUS_4_LATE],
|
|
59137
|
+
["opus-4-6", OPUS_4_LATE],
|
|
59138
|
+
["opus-4-5", OPUS_4_LATE],
|
|
59139
|
+
["opus-4-1", OPUS_4_EARLY],
|
|
59140
|
+
["opus-4", OPUS_4_EARLY],
|
|
59141
|
+
["sonnet-5-5", SONNET_5_5],
|
|
59142
|
+
["sonnet-5", SONNET_5],
|
|
59143
|
+
["sonnet-4-6", SONNET_4],
|
|
59144
|
+
["sonnet-4-5", SONNET_4],
|
|
59145
|
+
["sonnet-4", SONNET_4],
|
|
59146
|
+
["haiku-5-5", HAIKU_5_5],
|
|
59147
|
+
["haiku-4-5", HAIKU_4_5],
|
|
59148
|
+
["3-5-haiku", HAIKU_3_5]
|
|
59149
|
+
];
|
|
59150
|
+
var ALIASES = [
|
|
59151
|
+
["fable", FABLE_5_1],
|
|
59152
|
+
["mythos", FABLE_5_1],
|
|
59153
|
+
["opus", OPUS_5_5],
|
|
59154
|
+
["sonnet", SONNET_5_5],
|
|
59155
|
+
["haiku", HAIKU_4_5]
|
|
59156
|
+
];
|
|
59157
|
+
function bundledPrice(id) {
|
|
59158
|
+
for (const [pattern2, p] of FAMILIES) if (id.includes(pattern2)) return p;
|
|
59159
|
+
if (/\d/.test(id)) return void 0;
|
|
59160
|
+
for (const [pattern2, p] of ALIASES) if (id.includes(pattern2)) return p;
|
|
59161
|
+
return void 0;
|
|
59162
|
+
}
|
|
59163
|
+
function finite(n) {
|
|
59164
|
+
return typeof n === "number" && Number.isFinite(n) && n >= 0;
|
|
59165
|
+
}
|
|
59166
|
+
function resolvePrice(model, overrides) {
|
|
59167
|
+
const id = String(model ?? "").toLowerCase();
|
|
59168
|
+
if (!id) return void 0;
|
|
59169
|
+
const base = bundledPrice(id);
|
|
59170
|
+
if (!overrides) return base;
|
|
59171
|
+
for (const [pattern2, o] of Object.entries(overrides)) {
|
|
59172
|
+
if (!pattern2 || !id.includes(pattern2.toLowerCase())) continue;
|
|
59173
|
+
const input2 = finite(o.input) ? o.input : base?.input;
|
|
59174
|
+
const output2 = finite(o.output) ? o.output : base?.output;
|
|
59175
|
+
if (input2 === void 0 || output2 === void 0) return base;
|
|
59176
|
+
const merged = {
|
|
59177
|
+
input: input2,
|
|
59178
|
+
output: output2,
|
|
59179
|
+
cacheWrite5m: finite(o.cacheWrite5m) ? o.cacheWrite5m : base && !finite(o.input) ? base.cacheWrite5m : input2 * 1.25,
|
|
59180
|
+
cacheWrite1h: finite(o.cacheWrite1h) ? o.cacheWrite1h : base && !finite(o.input) ? base.cacheWrite1h : input2 * 2,
|
|
59181
|
+
cacheRead: finite(o.cacheRead) ? o.cacheRead : base && !finite(o.input) ? base.cacheRead : input2 * 0.1,
|
|
59182
|
+
webSearch: finite(o.webSearch) ? o.webSearch : base?.webSearch ?? WEB_SEARCH_PER_1K
|
|
59183
|
+
};
|
|
59184
|
+
if (o.longContext) merged.longContext = o.longContext;
|
|
59185
|
+
else if (base?.longContext) merged.longContext = base.longContext;
|
|
59186
|
+
return merged;
|
|
59187
|
+
}
|
|
59188
|
+
return base;
|
|
59189
|
+
}
|
|
59190
|
+
var tok = (n) => finite(n) ? n : 0;
|
|
59191
|
+
function costOfCall(call, price2) {
|
|
59192
|
+
const input2 = tok(call.input_tokens);
|
|
59193
|
+
const output2 = tok(call.output_tokens);
|
|
59194
|
+
const c5m = tok(call.cache_create_5m);
|
|
59195
|
+
const c1h = tok(call.cache_create_1h);
|
|
59196
|
+
const read2 = tok(call.cache_read);
|
|
59197
|
+
const searches = tok(call.web_searches);
|
|
59198
|
+
let p = price2;
|
|
59199
|
+
const lc = price2.longContext;
|
|
59200
|
+
if (lc && input2 + c5m + c1h + read2 > lc.threshold) {
|
|
59201
|
+
const ratio = price2.input > 0 ? lc.input / price2.input : 1;
|
|
59202
|
+
p = {
|
|
59203
|
+
...price2,
|
|
59204
|
+
input: lc.input,
|
|
59205
|
+
output: lc.output,
|
|
59206
|
+
cacheWrite5m: price2.cacheWrite5m * ratio,
|
|
59207
|
+
cacheWrite1h: price2.cacheWrite1h * ratio,
|
|
59208
|
+
cacheRead: price2.cacheRead * ratio
|
|
59209
|
+
};
|
|
59210
|
+
}
|
|
59211
|
+
const M = 1e6;
|
|
59212
|
+
const breakdown = {
|
|
59213
|
+
input: input2 * p.input / M,
|
|
59214
|
+
output: output2 * p.output / M,
|
|
59215
|
+
cacheWrite: (c5m * p.cacheWrite5m + c1h * p.cacheWrite1h) / M,
|
|
59216
|
+
cacheRead: read2 * p.cacheRead / M,
|
|
59217
|
+
tools: searches / 1e3 * (p.webSearch ?? 0)
|
|
59218
|
+
};
|
|
59219
|
+
return {
|
|
59220
|
+
...breakdown,
|
|
59221
|
+
total: breakdown.input + breakdown.output + breakdown.cacheWrite + breakdown.cacheRead + breakdown.tools
|
|
59222
|
+
};
|
|
59223
|
+
}
|
|
59224
|
+
function formatUsd(n) {
|
|
59225
|
+
if (!Number.isFinite(n) || n === 0) return "$0.00";
|
|
59226
|
+
const sign = n < 0 ? "-" : "";
|
|
59227
|
+
const v = Math.abs(n);
|
|
59228
|
+
if (v < 1e-4) return sign ? "-$0.0001" : "<$0.0001";
|
|
59229
|
+
if (v < 1) {
|
|
59230
|
+
const fixed = v.toFixed(4);
|
|
59231
|
+
if (fixed.startsWith("1")) return `${sign}$1.00`;
|
|
59232
|
+
return `${sign}$${fixed.replace(/0+$/, "").replace(/\.(\d)$/, ".$10")}`;
|
|
59233
|
+
}
|
|
59234
|
+
const two = v.toFixed(2);
|
|
59235
|
+
if (Number(two) >= 1e3) return `${sign}$${Math.round(v).toLocaleString("en-US")}`;
|
|
59236
|
+
return `${sign}$${two}`;
|
|
59237
|
+
}
|
|
59238
|
+
function estTokensFromChars(chars) {
|
|
59239
|
+
return finite(chars) ? Math.ceil(chars / 4) : 0;
|
|
59240
|
+
}
|
|
59241
|
+
|
|
59242
|
+
// src/usage/aggregate.ts
|
|
59243
|
+
var num2 = (n) => typeof n === "number" && Number.isFinite(n) ? n : 0;
|
|
59244
|
+
var isRealCall = (c) => c.model !== "<synthetic>";
|
|
59245
|
+
function totals(calls) {
|
|
59246
|
+
const t = {
|
|
59247
|
+
calls: 0,
|
|
59248
|
+
input: 0,
|
|
59249
|
+
output: 0,
|
|
59250
|
+
cacheCreate5m: 0,
|
|
59251
|
+
cacheCreate1h: 0,
|
|
59252
|
+
cacheRead: 0,
|
|
59253
|
+
thinking: 0,
|
|
59254
|
+
costUsd: 0,
|
|
59255
|
+
unpricedCalls: 0,
|
|
59256
|
+
cacheHitRate: 0,
|
|
59257
|
+
models: {}
|
|
59258
|
+
};
|
|
59259
|
+
for (const c of calls) {
|
|
59260
|
+
if (!isRealCall(c)) continue;
|
|
59261
|
+
t.calls += 1;
|
|
59262
|
+
t.input += num2(c.input_tokens);
|
|
59263
|
+
t.output += num2(c.output_tokens);
|
|
59264
|
+
t.cacheCreate5m += num2(c.cache_create_5m);
|
|
59265
|
+
t.cacheCreate1h += num2(c.cache_create_1h);
|
|
59266
|
+
t.cacheRead += num2(c.cache_read);
|
|
59267
|
+
t.thinking += num2(c.thinking_tokens);
|
|
59268
|
+
if (c.priced && c.cost_usd != null && Number.isFinite(c.cost_usd)) t.costUsd += c.cost_usd;
|
|
59269
|
+
else t.unpricedCalls += 1;
|
|
59270
|
+
const m = c.model || "unknown";
|
|
59271
|
+
t.models[m] = (t.models[m] ?? 0) + 1;
|
|
59272
|
+
}
|
|
59273
|
+
const denom = t.input + t.cacheCreate5m + t.cacheCreate1h + t.cacheRead;
|
|
59274
|
+
t.cacheHitRate = denom > 0 ? t.cacheRead / denom : 0;
|
|
59275
|
+
return t;
|
|
59276
|
+
}
|
|
59277
|
+
function groupBy(calls, keyFn) {
|
|
59278
|
+
const buckets = /* @__PURE__ */ new Map();
|
|
59279
|
+
for (const c of calls) {
|
|
59280
|
+
if (!isRealCall(c)) continue;
|
|
59281
|
+
const k = keyFn(c);
|
|
59282
|
+
if (k == null) continue;
|
|
59283
|
+
const list2 = buckets.get(k);
|
|
59284
|
+
if (list2) list2.push(c);
|
|
59285
|
+
else buckets.set(k, [c]);
|
|
59286
|
+
}
|
|
59287
|
+
return [...buckets.entries()].sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([key, rows]) => ({ key, totals: totals(rows) }));
|
|
59288
|
+
}
|
|
59289
|
+
var fmtCache = /* @__PURE__ */ new Map();
|
|
59290
|
+
function formatter(tz) {
|
|
59291
|
+
const key = tz ?? "";
|
|
59292
|
+
let f = fmtCache.get(key);
|
|
59293
|
+
if (!f) {
|
|
59294
|
+
f = new Intl.DateTimeFormat("en-CA", {
|
|
59295
|
+
year: "numeric",
|
|
59296
|
+
month: "2-digit",
|
|
59297
|
+
day: "2-digit",
|
|
59298
|
+
...tz ? { timeZone: tz } : {}
|
|
59299
|
+
});
|
|
59300
|
+
fmtCache.set(key, f);
|
|
59301
|
+
}
|
|
59302
|
+
return f;
|
|
59303
|
+
}
|
|
59304
|
+
function localYmd(ts, tz) {
|
|
59305
|
+
const parts = formatter(tz).formatToParts(new Date(ts));
|
|
59306
|
+
const get = (type) => Number(parts.find((p) => p.type === type)?.value ?? 0);
|
|
59307
|
+
return { y: get("year"), m: get("month"), d: get("day") };
|
|
59308
|
+
}
|
|
59309
|
+
var pad2 = (n) => String(n).padStart(2, "0");
|
|
59310
|
+
var ymdKey = ({ y, m, d }) => `${y}-${pad2(m)}-${pad2(d)}`;
|
|
59311
|
+
function dayKey(ts, tz) {
|
|
59312
|
+
return ymdKey(localYmd(ts, tz));
|
|
59313
|
+
}
|
|
59314
|
+
function weekKey(ts, tz) {
|
|
59315
|
+
const { y, m, d } = localYmd(ts, tz);
|
|
59316
|
+
const date5 = new Date(Date.UTC(y, m - 1, d));
|
|
59317
|
+
const back = (date5.getUTCDay() + 6) % 7;
|
|
59318
|
+
date5.setUTCDate(date5.getUTCDate() - back);
|
|
59319
|
+
return ymdKey({ y: date5.getUTCFullYear(), m: date5.getUTCMonth() + 1, d: date5.getUTCDate() });
|
|
59320
|
+
}
|
|
59321
|
+
function monthKey(ts, tz) {
|
|
59322
|
+
const { y, m } = localYmd(ts, tz);
|
|
59323
|
+
return `${y}-${pad2(m)}`;
|
|
59324
|
+
}
|
|
59325
|
+
var byModel = (calls) => groupBy(calls, (c) => c.model || "unknown");
|
|
59326
|
+
var bySession = (calls) => groupBy(calls, (c) => c.session_id);
|
|
59327
|
+
var byEffort = (calls) => groupBy(calls, (c) => c.effort || "default");
|
|
59328
|
+
var bySpeed = (calls) => groupBy(calls, (c) => c.speed === "fast" ? "fast" : "standard");
|
|
59329
|
+
function byAgent(calls) {
|
|
59330
|
+
const mainRows = calls.filter((c) => !c.sidechain);
|
|
59331
|
+
const side = calls.filter((c) => !!c.sidechain);
|
|
59332
|
+
const main = totals(mainRows);
|
|
59333
|
+
const subagents = groupBy(side, (c) => c.agent_id || "unknown").map((g) => ({ agentId: g.key, totals: g.totals })).sort((a, b) => b.totals.costUsd - a.totals.costUsd || b.totals.calls - a.totals.calls);
|
|
59334
|
+
const subCost = subagents.reduce((s, a) => s + a.totals.costUsd, 0);
|
|
59335
|
+
const total = main.costUsd + subCost;
|
|
59336
|
+
return { main, subagents, delegationShare: total > 0 ? subCost / total : 0 };
|
|
59337
|
+
}
|
|
59338
|
+
function cacheStats(calls) {
|
|
59339
|
+
const t = totals(calls);
|
|
59340
|
+
const writes = t.cacheCreate5m + t.cacheCreate1h;
|
|
59341
|
+
const first = /* @__PURE__ */ new Map();
|
|
59342
|
+
for (const c of calls) {
|
|
59343
|
+
if (!isRealCall(c)) continue;
|
|
59344
|
+
const prev = first.get(c.session_id);
|
|
59345
|
+
if (!prev || c.ts < prev.ts || c.ts === prev.ts && c.request_id < prev.request_id)
|
|
59346
|
+
first.set(c.session_id, c);
|
|
59347
|
+
}
|
|
59348
|
+
let coldCalls = 0;
|
|
59349
|
+
for (const c of calls) {
|
|
59350
|
+
if (!isRealCall(c) || first.get(c.session_id) === c) continue;
|
|
59351
|
+
if (num2(c.cache_read) === 0) coldCalls += 1;
|
|
59352
|
+
}
|
|
59353
|
+
return {
|
|
59354
|
+
hitRate: t.cacheHitRate,
|
|
59355
|
+
cacheWrites: writes,
|
|
59356
|
+
cacheReads: t.cacheRead,
|
|
59357
|
+
writeReadRatio: t.cacheRead > 0 ? writes / t.cacheRead : 0,
|
|
59358
|
+
share1h: writes > 0 ? t.cacheCreate1h / writes : 0,
|
|
59359
|
+
coldCalls,
|
|
59360
|
+
calls: t.calls
|
|
59361
|
+
};
|
|
59362
|
+
}
|
|
59363
|
+
function contextTimeline(sessionCalls, contextEvents = []) {
|
|
59364
|
+
const points = sessionCalls.filter(isRealCall).sort((a, b) => a.ts - b.ts).map((c) => ({
|
|
59365
|
+
ts: c.ts,
|
|
59366
|
+
requestId: c.request_id,
|
|
59367
|
+
model: c.model,
|
|
59368
|
+
contextTokens: num2(c.input_tokens) + num2(c.cache_create_5m) + num2(c.cache_create_1h) + num2(c.cache_read),
|
|
59369
|
+
output: num2(c.output_tokens),
|
|
59370
|
+
costUsd: c.cost_usd != null && Number.isFinite(c.cost_usd) ? c.cost_usd : 0
|
|
59371
|
+
}));
|
|
59372
|
+
const events = contextEvents.slice().sort((a, b) => a.ts - b.ts);
|
|
59373
|
+
let i = 0;
|
|
59374
|
+
for (const ev2 of events) {
|
|
59375
|
+
while (i < points.length && points[i].ts < ev2.ts) i += 1;
|
|
59376
|
+
const p = points[i];
|
|
59377
|
+
if (p) p.compactBefore = true;
|
|
59378
|
+
}
|
|
59379
|
+
return points;
|
|
59380
|
+
}
|
|
59381
|
+
var BURN_MIN_MINUTES = 5;
|
|
59382
|
+
var BURN_MIN_CALLS = 2;
|
|
59383
|
+
function burnRate(calls, now = Date.now()) {
|
|
59384
|
+
const none = {
|
|
59385
|
+
tokensPerMin: 0,
|
|
59386
|
+
usdPerHour: 0,
|
|
59387
|
+
projectedBlockUsd: null,
|
|
59388
|
+
windowMinutes: 0,
|
|
59389
|
+
reliable: false
|
|
59390
|
+
};
|
|
59391
|
+
const block = activeBlock(billingBlocks(calls, now), now);
|
|
59392
|
+
if (!block) return none;
|
|
59393
|
+
const windowMinutes = Math.max(0, (Math.min(now, block.end) - block.firstCall) / 6e4);
|
|
59394
|
+
if (windowMinutes < BURN_MIN_MINUTES || block.calls < BURN_MIN_CALLS)
|
|
59395
|
+
return { ...none, windowMinutes };
|
|
59396
|
+
const tokens = block.tokens.input + block.tokens.output + block.tokens.cacheCreate + block.tokens.cacheRead;
|
|
59397
|
+
const usdPerHour = block.costUsd / windowMinutes * 60;
|
|
59398
|
+
const remainingHours = Math.max(0, block.end - now) / 36e5;
|
|
59399
|
+
return {
|
|
59400
|
+
tokensPerMin: tokens / windowMinutes,
|
|
59401
|
+
usdPerHour,
|
|
59402
|
+
projectedBlockUsd: block.costUsd + usdPerHour * remainingHours,
|
|
59403
|
+
windowMinutes,
|
|
59404
|
+
reliable: true
|
|
59405
|
+
};
|
|
59406
|
+
}
|
|
59407
|
+
function topToolConsumers(toolUses, limit = 10) {
|
|
59408
|
+
const byTool = /* @__PURE__ */ new Map();
|
|
59409
|
+
for (const u of toolUses) {
|
|
59410
|
+
const tool = u.tool || "unknown";
|
|
59411
|
+
let row = byTool.get(tool);
|
|
59412
|
+
if (!row) {
|
|
59413
|
+
row = { tool, calls: 0, chars: 0, estTokens: 0, avgChars: 0, errors: 0 };
|
|
59414
|
+
byTool.set(tool, row);
|
|
59415
|
+
}
|
|
59416
|
+
row.calls += 1;
|
|
59417
|
+
row.chars += num2(u.result_chars);
|
|
59418
|
+
if (u.is_error) row.errors += 1;
|
|
59419
|
+
}
|
|
59420
|
+
return [...byTool.values()].map((r) => ({
|
|
59421
|
+
...r,
|
|
59422
|
+
estTokens: estTokensFromChars(r.chars),
|
|
59423
|
+
avgChars: r.calls > 0 ? Math.round(r.chars / r.calls) : 0
|
|
59424
|
+
})).sort((a, b) => b.chars - a.chars || b.calls - a.calls || a.tool.localeCompare(b.tool)).slice(0, Math.max(0, limit));
|
|
59425
|
+
}
|
|
59426
|
+
|
|
59427
|
+
// src/usage/insights.ts
|
|
59428
|
+
init_esm_shims();
|
|
59429
|
+
|
|
59430
|
+
// src/usage/savings.ts
|
|
59431
|
+
init_esm_shims();
|
|
59432
|
+
var FALLBACK_MODEL = "claude-sonnet-5-5";
|
|
59433
|
+
function dominantModel(calls) {
|
|
59434
|
+
const counts = /* @__PURE__ */ new Map();
|
|
59435
|
+
for (const c of calls) {
|
|
59436
|
+
if (!c.model) continue;
|
|
59437
|
+
counts.set(c.model, (counts.get(c.model) ?? 0) + 1);
|
|
59438
|
+
}
|
|
59439
|
+
let best;
|
|
59440
|
+
let bestN = 0;
|
|
59441
|
+
for (const [m, n] of counts) {
|
|
59442
|
+
if (n > bestN) {
|
|
59443
|
+
best = m;
|
|
59444
|
+
bestN = n;
|
|
59445
|
+
}
|
|
59446
|
+
}
|
|
59447
|
+
return best;
|
|
59448
|
+
}
|
|
59449
|
+
function pricedSavings(stats, calls, overrides, opts = {}) {
|
|
59450
|
+
const assumptions = [
|
|
59451
|
+
"Saved tokens = ceil(raw_chars / 4) - ceil(returned_chars / 4) per stat row, floored at 0 (same estimate as em_stats).",
|
|
59452
|
+
"Direct savings price saved tokens at the 5-minute cache-write rate: a tool result enters context once and is written to cache on that turn.",
|
|
59453
|
+
"Compounding savings price saved tokens at the cache-read rate once per later API call in the same session; stat rows carry no timestamps, so half of the session's calls are taken as the expected number of later calls.",
|
|
59454
|
+
"Sessions with no recorded API calls earn no compounding savings."
|
|
59455
|
+
];
|
|
59456
|
+
const global2 = dominantModel(calls);
|
|
59457
|
+
let fallbackUsed = false;
|
|
59458
|
+
const priceFor = (model2) => {
|
|
59459
|
+
const m = model2 ?? global2;
|
|
59460
|
+
const p = m ? resolvePrice(m, overrides) : void 0;
|
|
59461
|
+
if (m && p) return { model: m, price: p };
|
|
59462
|
+
fallbackUsed = true;
|
|
59463
|
+
const fb = resolvePrice(FALLBACK_MODEL, overrides) ?? resolvePrice(FALLBACK_MODEL);
|
|
59464
|
+
return { model: FALLBACK_MODEL, price: fb };
|
|
59465
|
+
};
|
|
59466
|
+
const bySession2 = /* @__PURE__ */ new Map();
|
|
59467
|
+
for (const c of calls) {
|
|
59468
|
+
const list2 = bySession2.get(c.session_id);
|
|
59469
|
+
if (list2) list2.push(c);
|
|
59470
|
+
else bySession2.set(c.session_id, [c]);
|
|
59471
|
+
}
|
|
59472
|
+
const sessionModel = /* @__PURE__ */ new Map();
|
|
59473
|
+
for (const [sid, rows] of bySession2) sessionModel.set(sid, dominantModel(rows));
|
|
59474
|
+
const windowed = opts.since !== void 0;
|
|
59475
|
+
assumptions.push(
|
|
59476
|
+
windowed ? `Savings cover sessions active in the window (since ${new Date(opts.since).toISOString()}): stat rows carry no timestamps, so rows of sessions without an API call in the window are left out.` : "All-time: every stat row of the project is counted."
|
|
59477
|
+
);
|
|
59478
|
+
let savedTokens = 0;
|
|
59479
|
+
let directUsd = 0;
|
|
59480
|
+
let compoundingUsd = 0;
|
|
59481
|
+
const laterBySession = /* @__PURE__ */ new Map();
|
|
59482
|
+
for (const row of stats) {
|
|
59483
|
+
if (windowed && !bySession2.has(row.session_id)) continue;
|
|
59484
|
+
const saved = Math.max(
|
|
59485
|
+
0,
|
|
59486
|
+
estTokensFromChars(row.raw_chars) - estTokensFromChars(row.returned_chars)
|
|
59487
|
+
);
|
|
59488
|
+
if (saved === 0) continue;
|
|
59489
|
+
savedTokens += saved;
|
|
59490
|
+
const { price: price2 } = priceFor(sessionModel.get(row.session_id));
|
|
59491
|
+
directUsd += saved * price2.cacheWrite5m / 1e6;
|
|
59492
|
+
const later = (bySession2.get(row.session_id)?.length ?? 0) / 2;
|
|
59493
|
+
laterBySession.set(row.session_id, later);
|
|
59494
|
+
compoundingUsd += saved * price2.cacheRead * later / 1e6;
|
|
59495
|
+
}
|
|
59496
|
+
const laterValues = [...laterBySession.values()];
|
|
59497
|
+
const laterCallsPerSession = laterValues.length > 0 ? laterValues.reduce((a, b) => a + b, 0) / laterValues.length : 0;
|
|
59498
|
+
const model = global2 ?? FALLBACK_MODEL;
|
|
59499
|
+
if (!global2) assumptions.push(`No API calls recorded; priced at ${FALLBACK_MODEL} list prices.`);
|
|
59500
|
+
else
|
|
59501
|
+
assumptions.push(
|
|
59502
|
+
`Each session is priced at its own dominant model (by calls); ${model} overall.`
|
|
59503
|
+
);
|
|
59504
|
+
if (fallbackUsed && global2)
|
|
59505
|
+
assumptions.push(`Some sessions had no priceable model and were priced at ${FALLBACK_MODEL}.`);
|
|
59506
|
+
return { savedTokens, directUsd, compoundingUsd, laterCallsPerSession, model, assumptions };
|
|
59507
|
+
}
|
|
59508
|
+
|
|
59509
|
+
// src/usage/insights.ts
|
|
59510
|
+
var THRASH_WRITE_SHARE = 0.2;
|
|
59511
|
+
var THRASH_RUN = 3;
|
|
59512
|
+
var TTL_5M_MS = 5 * 6e4;
|
|
59513
|
+
var TTL_1H_MS = 60 * 6e4;
|
|
59514
|
+
var COLD_WRITE_SHARE = 0.5;
|
|
59515
|
+
var BLOAT_TOKENS = 8e3;
|
|
59516
|
+
var BLOAT_TOOLS = /* @__PURE__ */ new Set(["Read", "Bash", "Grep", "WebFetch", "Glob"]);
|
|
59517
|
+
var CHURN_COMPACTIONS = 3;
|
|
59518
|
+
var CHURN_EARLY_SHARE = 0.5;
|
|
59519
|
+
var DEFAULT_WINDOW = 2e5;
|
|
59520
|
+
var TRIVIAL_OUTPUT = 100;
|
|
59521
|
+
var TRIVIAL_SHARE = 0.3;
|
|
59522
|
+
var MIN_PREMIUM_CALLS = 5;
|
|
59523
|
+
var FAST_COST_SHARE = 0.25;
|
|
59524
|
+
var THINKING_SHARE = 0.4;
|
|
59525
|
+
var MIN_OUTPUT_FOR_THINKING = 1e3;
|
|
59526
|
+
var DELEGATION_SHARE = 0.6;
|
|
59527
|
+
var DEFAULT_TURN_USD = 1;
|
|
59528
|
+
var ERROR_RUN = 3;
|
|
59529
|
+
var NEAR_LIMIT_PCT = 85;
|
|
59530
|
+
var NEAR_LIMIT_HIGH = 95;
|
|
59531
|
+
var num3 = (n) => typeof n === "number" && Number.isFinite(n) ? n : 0;
|
|
59532
|
+
var pct = (x) => `${Math.round(x * 100)}%`;
|
|
59533
|
+
var usd = (n) => n >= 1 ? `$${n.toFixed(2)}` : `$${n.toFixed(3)}`;
|
|
59534
|
+
var round4 = (n) => Math.round(n * 1e4) / 1e4;
|
|
59535
|
+
var clamp = (n) => Math.max(0, Math.min(100, Math.round(n)));
|
|
59536
|
+
var fmtK = (n) => n >= 1e6 ? `${(n / 1e6).toFixed(1)}M` : n >= 1e3 ? `${Math.round(n / 1e3)}k` : `${n}`;
|
|
59537
|
+
var contextOf = (c) => num3(c.input_tokens) + num3(c.cache_create_5m) + num3(c.cache_create_1h) + num3(c.cache_read);
|
|
59538
|
+
var writesOf = (c) => num3(c.cache_create_5m) + num3(c.cache_create_1h);
|
|
59539
|
+
var costOf = (c) => c.priced && c.cost_usd != null && Number.isFinite(c.cost_usd) ? c.cost_usd : 0;
|
|
59540
|
+
var isPremium = (model) => /opus|fable|mythos/i.test(model);
|
|
59541
|
+
function threads(calls) {
|
|
59542
|
+
const out = /* @__PURE__ */ new Map();
|
|
59543
|
+
for (const c of calls) {
|
|
59544
|
+
const key = `${c.session_id}\0${c.sidechain ? c.agent_id ?? "sidechain" : ""}`;
|
|
59545
|
+
const list2 = out.get(key);
|
|
59546
|
+
if (list2) list2.push(c);
|
|
59547
|
+
else out.set(key, [c]);
|
|
59548
|
+
}
|
|
59549
|
+
for (const list2 of out.values()) list2.sort((a, b) => a.ts - b.ts);
|
|
59550
|
+
return out;
|
|
59551
|
+
}
|
|
59552
|
+
function groupBySession(rows) {
|
|
59553
|
+
const out = /* @__PURE__ */ new Map();
|
|
59554
|
+
for (const r of rows) {
|
|
59555
|
+
const list2 = out.get(r.session_id);
|
|
59556
|
+
if (list2) list2.push(r);
|
|
59557
|
+
else out.set(r.session_id, [r]);
|
|
59558
|
+
}
|
|
59559
|
+
return out;
|
|
59560
|
+
}
|
|
59561
|
+
function dominantModel2(calls) {
|
|
59562
|
+
const counts = /* @__PURE__ */ new Map();
|
|
59563
|
+
for (const c of calls) counts.set(c.model, (counts.get(c.model) ?? 0) + 1);
|
|
59564
|
+
let best;
|
|
59565
|
+
let n = 0;
|
|
59566
|
+
for (const [m, k] of counts)
|
|
59567
|
+
if (k > n) {
|
|
59568
|
+
best = m;
|
|
59569
|
+
n = k;
|
|
59570
|
+
}
|
|
59571
|
+
return best;
|
|
59572
|
+
}
|
|
59573
|
+
function writePrice(c, p) {
|
|
59574
|
+
return num3(c.cache_create_1h) > num3(c.cache_create_5m) ? p.cacheWrite1h : p.cacheWrite5m;
|
|
59575
|
+
}
|
|
59576
|
+
function scopeFor(sessionIds) {
|
|
59577
|
+
const ids = [...new Set(sessionIds)];
|
|
59578
|
+
return ids.length === 1 ? { scope: "session", sessionId: ids[0] } : { scope: "project" };
|
|
59579
|
+
}
|
|
59580
|
+
function withScope(base, sessionIds) {
|
|
59581
|
+
const s = scopeFor(sessionIds);
|
|
59582
|
+
const out = { ...base, scope: s.scope };
|
|
59583
|
+
if (s.sessionId) out.sessionId = s.sessionId;
|
|
59584
|
+
return out;
|
|
59585
|
+
}
|
|
59586
|
+
var SEVERITY_RANK = { high: 0, warn: 1, info: 2 };
|
|
59587
|
+
function hasCompactionBetween(ctx, sessionId, from, to) {
|
|
59588
|
+
const list2 = ctx.compactionsBySession.get(sessionId);
|
|
59589
|
+
if (!list2) return false;
|
|
59590
|
+
return list2.some((t) => t > from && t <= to);
|
|
59591
|
+
}
|
|
59592
|
+
function detectCacheThrash(ctx) {
|
|
59593
|
+
let runs = 0;
|
|
59594
|
+
let longest = 0;
|
|
59595
|
+
let rewritten = 0;
|
|
59596
|
+
let savings = 0;
|
|
59597
|
+
const sessions = /* @__PURE__ */ new Set();
|
|
59598
|
+
for (const list2 of ctx.threads.values()) {
|
|
59599
|
+
let run = [];
|
|
59600
|
+
const flush = () => {
|
|
59601
|
+
if (run.length >= THRASH_RUN) {
|
|
59602
|
+
runs += 1;
|
|
59603
|
+
longest = Math.max(longest, run.length);
|
|
59604
|
+
for (const c of run) {
|
|
59605
|
+
const w = writesOf(c);
|
|
59606
|
+
rewritten += w;
|
|
59607
|
+
sessions.add(c.session_id);
|
|
59608
|
+
const p = ctx.priceOf(c.model);
|
|
59609
|
+
if (p) savings += w * (writePrice(c, p) - p.cacheRead) / 1e6;
|
|
59610
|
+
}
|
|
59611
|
+
}
|
|
59612
|
+
run = [];
|
|
59613
|
+
};
|
|
59614
|
+
for (let i = 1; i < list2.length; i++) {
|
|
59615
|
+
const c = list2[i];
|
|
59616
|
+
const prev = list2[i - 1];
|
|
59617
|
+
const ctxTokens = contextOf(c);
|
|
59618
|
+
const heavy = ctxTokens > 0 && writesOf(c) / ctxTokens >= THRASH_WRITE_SHARE;
|
|
59619
|
+
const broken = hasCompactionBetween(ctx, c.session_id, prev.ts, c.ts) || c.ts - prev.ts > ttlFor(list2);
|
|
59620
|
+
if (!heavy || broken) {
|
|
59621
|
+
flush();
|
|
59622
|
+
continue;
|
|
59623
|
+
}
|
|
59624
|
+
run.push(c);
|
|
59625
|
+
}
|
|
59626
|
+
flush();
|
|
59627
|
+
}
|
|
59628
|
+
if (runs === 0) return void 0;
|
|
59629
|
+
return withScope(
|
|
59630
|
+
{
|
|
59631
|
+
id: "cache-thrash",
|
|
59632
|
+
severity: runs >= 3 || rewritten >= 5e5 ? "high" : "warn",
|
|
59633
|
+
title: "Prompt cache keeps being rewritten",
|
|
59634
|
+
reason: `${runs} run${runs === 1 ? "" : "s"} of ${THRASH_RUN}+ consecutive calls rewrote \u2265 ${pct(THRASH_WRITE_SHARE)} of their context (longest run ${longest} calls, ${fmtK(rewritten)} tokens written again).`,
|
|
59635
|
+
evidence: { runs, longestRun: longest, tokensRewritten: rewritten },
|
|
59636
|
+
estimatedSavingsUsd: round4(savings),
|
|
59637
|
+
recommendation: "Something early in the prompt changes every turn (system prompt, tool list, a file re-read with a timestamp); keep the stable prefix stable so later calls hit the cache."
|
|
59638
|
+
},
|
|
59639
|
+
sessions
|
|
59640
|
+
);
|
|
59641
|
+
}
|
|
59642
|
+
function ttlFor(list2) {
|
|
59643
|
+
let w5 = 0;
|
|
59644
|
+
let w1 = 0;
|
|
59645
|
+
for (const c of list2) {
|
|
59646
|
+
w5 += num3(c.cache_create_5m);
|
|
59647
|
+
w1 += num3(c.cache_create_1h);
|
|
59648
|
+
}
|
|
59649
|
+
return w1 > w5 ? TTL_1H_MS : TTL_5M_MS;
|
|
59650
|
+
}
|
|
59651
|
+
function detectCacheCold(ctx) {
|
|
59652
|
+
let restarts = 0;
|
|
59653
|
+
let rewritten = 0;
|
|
59654
|
+
let savings = 0;
|
|
59655
|
+
let longestGap = 0;
|
|
59656
|
+
const sessions = /* @__PURE__ */ new Set();
|
|
59657
|
+
for (const list2 of ctx.threads.values()) {
|
|
59658
|
+
const ttl = ttlFor(list2);
|
|
59659
|
+
for (let i = 1; i < list2.length; i++) {
|
|
59660
|
+
const c = list2[i];
|
|
59661
|
+
const prev = list2[i - 1];
|
|
59662
|
+
const gap = c.ts - prev.ts;
|
|
59663
|
+
if (gap <= ttl) continue;
|
|
59664
|
+
if (hasCompactionBetween(ctx, c.session_id, prev.ts, c.ts)) continue;
|
|
59665
|
+
const ctxTokens = contextOf(c);
|
|
59666
|
+
const w = writesOf(c);
|
|
59667
|
+
if (ctxTokens === 0 || w / ctxTokens < COLD_WRITE_SHARE) continue;
|
|
59668
|
+
restarts += 1;
|
|
59669
|
+
rewritten += w;
|
|
59670
|
+
longestGap = Math.max(longestGap, gap);
|
|
59671
|
+
sessions.add(c.session_id);
|
|
59672
|
+
const p = ctx.priceOf(c.model);
|
|
59673
|
+
if (p) savings += w * (writePrice(c, p) - p.cacheRead) / 1e6;
|
|
59674
|
+
}
|
|
59675
|
+
}
|
|
59676
|
+
if (restarts === 0) return void 0;
|
|
59677
|
+
const minutes = Math.round(longestGap / 6e4);
|
|
59678
|
+
return withScope(
|
|
59679
|
+
{
|
|
59680
|
+
id: "cache-cold",
|
|
59681
|
+
severity: restarts >= 2 ? "warn" : "info",
|
|
59682
|
+
title: `Cache went cold ${restarts === 1 ? "once" : `${restarts} times`}`,
|
|
59683
|
+
reason: `${restarts} pause${restarts === 1 ? "" : "s"} longer than the cache TTL (longest ${minutes} min) forced ${fmtK(rewritten)} tokens to be written to cache again (~${usd(savings)}).`,
|
|
59684
|
+
evidence: { restarts, tokensRewritten: rewritten, longestGapMinutes: minutes },
|
|
59685
|
+
estimatedSavingsUsd: round4(savings),
|
|
59686
|
+
recommendation: "Long pauses mid-session re-pay the cache write; for walk-away breaks start a new session, or keep the turn cadence inside the TTL."
|
|
59687
|
+
},
|
|
59688
|
+
sessions
|
|
59689
|
+
);
|
|
59690
|
+
}
|
|
59691
|
+
function detectContextBloat(ctx) {
|
|
59692
|
+
const bySession2 = groupBySession(ctx.calls);
|
|
59693
|
+
const modelFor = /* @__PURE__ */ new Map();
|
|
59694
|
+
for (const [sid, rows] of bySession2) modelFor.set(sid, dominantModel2(rows));
|
|
59695
|
+
let count = 0;
|
|
59696
|
+
let tokens = 0;
|
|
59697
|
+
let savings = 0;
|
|
59698
|
+
const byTool = /* @__PURE__ */ new Map();
|
|
59699
|
+
const sessions = /* @__PURE__ */ new Set();
|
|
59700
|
+
for (const u of ctx.toolUses) {
|
|
59701
|
+
if (!BLOAT_TOOLS.has(u.tool)) continue;
|
|
59702
|
+
const est = estTokensFromChars(num3(u.result_chars));
|
|
59703
|
+
if (est <= BLOAT_TOKENS) continue;
|
|
59704
|
+
count += 1;
|
|
59705
|
+
tokens += est;
|
|
59706
|
+
byTool.set(u.tool, (byTool.get(u.tool) ?? 0) + 1);
|
|
59707
|
+
sessions.add(u.session_id);
|
|
59708
|
+
const p = ctx.priceOf(modelFor.get(u.session_id) ?? FALLBACK_MODEL) ?? ctx.priceOf(FALLBACK_MODEL);
|
|
59709
|
+
if (p) {
|
|
59710
|
+
const later = ctx.laterCalls(u.session_id, u.ts);
|
|
59711
|
+
savings += (est * p.cacheWrite5m + est * p.cacheRead * later) / 1e6;
|
|
59712
|
+
}
|
|
59713
|
+
}
|
|
59714
|
+
if (count === 0) return void 0;
|
|
59715
|
+
const top2 = [...byTool.entries()].sort((a, b) => b[1] - a[1])[0];
|
|
59716
|
+
return withScope(
|
|
59717
|
+
{
|
|
59718
|
+
id: "context-bloat",
|
|
59719
|
+
severity: count >= 5 || tokens >= 1e5 ? "high" : "warn",
|
|
59720
|
+
title: "Oversized tool results in context",
|
|
59721
|
+
reason: `${count} ${[...byTool.keys()].join("/")} result${count === 1 ? "" : "s"} above ${fmtK(BLOAT_TOKENS)} tokens put ~${fmtK(tokens)} tokens into context (mostly ${top2[0]}, ${top2[1]}\xD7), re-read on every later call.`,
|
|
59722
|
+
evidence: { results: count, estTokens: tokens, topTool: top2[0], topToolCount: top2[1] },
|
|
59723
|
+
estimatedSavingsUsd: round4(savings),
|
|
59724
|
+
recommendation: "Route large outputs through em_execute / em_execute_file / em_search so only the matching sections come back."
|
|
59725
|
+
},
|
|
59726
|
+
sessions
|
|
59727
|
+
);
|
|
59728
|
+
}
|
|
59729
|
+
function detectCompactionChurn(ctx) {
|
|
59730
|
+
const windowFor = /* @__PURE__ */ new Map();
|
|
59731
|
+
for (const s of ctx.snapshots) {
|
|
59732
|
+
if (num3(s.ctx_size) > 0) {
|
|
59733
|
+
const prev = windowFor.get(s.session_id);
|
|
59734
|
+
if (prev === void 0) windowFor.set(s.session_id, num3(s.ctx_size));
|
|
59735
|
+
}
|
|
59736
|
+
}
|
|
59737
|
+
let worstSession;
|
|
59738
|
+
let worstCount = 0;
|
|
59739
|
+
let early = 0;
|
|
59740
|
+
const sessions = /* @__PURE__ */ new Set();
|
|
59741
|
+
for (const [sid, evs] of groupBySession(ctx.contextEvents)) {
|
|
59742
|
+
const compactions = evs.filter((e) => e.kind === "compact");
|
|
59743
|
+
const window2 = windowFor.get(sid) ?? DEFAULT_WINDOW;
|
|
59744
|
+
const earlyHere = compactions.filter(
|
|
59745
|
+
(e) => e.pre_tokens != null && e.pre_tokens > 0 && e.pre_tokens < window2 * CHURN_EARLY_SHARE
|
|
59746
|
+
).length;
|
|
59747
|
+
if (compactions.length >= CHURN_COMPACTIONS || earlyHere > 0) {
|
|
59748
|
+
sessions.add(sid);
|
|
59749
|
+
early += earlyHere;
|
|
59750
|
+
if (compactions.length > worstCount) {
|
|
59751
|
+
worstCount = compactions.length;
|
|
59752
|
+
worstSession = sid;
|
|
59753
|
+
}
|
|
59754
|
+
}
|
|
59755
|
+
}
|
|
59756
|
+
if (sessions.size === 0) return void 0;
|
|
59757
|
+
return withScope(
|
|
59758
|
+
{
|
|
59759
|
+
id: "compaction-churn",
|
|
59760
|
+
severity: worstCount >= CHURN_COMPACTIONS * 2 ? "high" : "warn",
|
|
59761
|
+
title: "Frequent context compactions",
|
|
59762
|
+
reason: `${sessions.size} session${sessions.size === 1 ? "" : "s"} compacted repeatedly (up to ${worstCount} times in ${(worstSession ?? "").slice(0, 8)}; ${early} below ${pct(CHURN_EARLY_SHARE)} of the window).`,
|
|
59763
|
+
evidence: { sessions: sessions.size, maxCompactions: worstCount, earlyCompactions: early },
|
|
59764
|
+
recommendation: "Each compaction rewrites the whole cache and loses detail; keep raw output out of context (em_* tools) and split long tasks into sessions."
|
|
59765
|
+
},
|
|
59766
|
+
sessions
|
|
59767
|
+
);
|
|
59768
|
+
}
|
|
59769
|
+
function detectOpusForTrivial(ctx) {
|
|
59770
|
+
const toolRequests = /* @__PURE__ */ new Set();
|
|
59771
|
+
for (const u of ctx.toolUses) if (u.request_id) toolRequests.add(u.request_id);
|
|
59772
|
+
const premium = ctx.calls.filter((c) => isPremium(c.model));
|
|
59773
|
+
if (premium.length < MIN_PREMIUM_CALLS) return void 0;
|
|
59774
|
+
const trivial = premium.filter(
|
|
59775
|
+
(c) => num3(c.output_tokens) < TRIVIAL_OUTPUT && c.stop_reason !== "tool_use" && !toolRequests.has(c.request_id)
|
|
59776
|
+
);
|
|
59777
|
+
const share2 = trivial.length / premium.length;
|
|
59778
|
+
if (share2 <= TRIVIAL_SHARE) return void 0;
|
|
59779
|
+
const sonnet = resolvePrice(FALLBACK_MODEL, ctx.overrides);
|
|
59780
|
+
let spent = 0;
|
|
59781
|
+
let repriced = 0;
|
|
59782
|
+
for (const c of trivial) {
|
|
59783
|
+
spent += costOf(c);
|
|
59784
|
+
if (sonnet) repriced += costOfCall(c, sonnet).total;
|
|
59785
|
+
}
|
|
59786
|
+
const savings = Math.max(0, spent - repriced);
|
|
59787
|
+
return withScope(
|
|
59788
|
+
{
|
|
59789
|
+
id: "opus-for-trivial",
|
|
59790
|
+
severity: share2 >= 0.6 ? "warn" : "info",
|
|
59791
|
+
title: "Premium model on trivial turns",
|
|
59792
|
+
reason: `${trivial.length} of ${premium.length} premium-model calls (${pct(share2)}) produced under ${TRIVIAL_OUTPUT} output tokens with no tool use, costing ${usd(spent)} (~${usd(repriced)} at Sonnet).`,
|
|
59793
|
+
evidence: {
|
|
59794
|
+
trivialCalls: trivial.length,
|
|
59795
|
+
premiumCalls: premium.length,
|
|
59796
|
+
share: Math.round(share2 * 100),
|
|
59797
|
+
spentUsd: round4(spent)
|
|
59798
|
+
},
|
|
59799
|
+
estimatedSavingsUsd: round4(savings),
|
|
59800
|
+
recommendation: "Short acknowledgements and lookups do not need the premium model; delegate routine turns to a Sonnet/Haiku subagent or switch model for simple work."
|
|
59801
|
+
},
|
|
59802
|
+
trivial.map((c) => c.session_id)
|
|
59803
|
+
);
|
|
59804
|
+
}
|
|
59805
|
+
function detectFastModeShare(ctx) {
|
|
59806
|
+
let fast = 0;
|
|
59807
|
+
let fastCalls = 0;
|
|
59808
|
+
let total = 0;
|
|
59809
|
+
const sessions = /* @__PURE__ */ new Set();
|
|
59810
|
+
for (const c of ctx.calls) {
|
|
59811
|
+
const cost = costOf(c);
|
|
59812
|
+
total += cost;
|
|
59813
|
+
if (c.speed === "fast") {
|
|
59814
|
+
fast += cost;
|
|
59815
|
+
fastCalls += 1;
|
|
59816
|
+
sessions.add(c.session_id);
|
|
59817
|
+
}
|
|
59818
|
+
}
|
|
59819
|
+
if (total <= 0) return void 0;
|
|
59820
|
+
const share2 = fast / total;
|
|
59821
|
+
if (share2 <= FAST_COST_SHARE) return void 0;
|
|
59822
|
+
return withScope(
|
|
59823
|
+
{
|
|
59824
|
+
id: "fast-mode-share",
|
|
59825
|
+
severity: "info",
|
|
59826
|
+
title: "Fast mode is a large share of spend",
|
|
59827
|
+
reason: `${fastCalls} fast-mode calls account for ${usd(fast)} of ${usd(total)} (${pct(share2)}) of priced cost.`,
|
|
59828
|
+
evidence: {
|
|
59829
|
+
fastCalls,
|
|
59830
|
+
fastUsd: round4(fast),
|
|
59831
|
+
totalUsd: round4(total),
|
|
59832
|
+
share: Math.round(share2 * 100)
|
|
59833
|
+
},
|
|
59834
|
+
recommendation: "Fast mode is billed at a premium; reserve it for interactive turns where latency matters."
|
|
59835
|
+
},
|
|
59836
|
+
sessions
|
|
59837
|
+
);
|
|
59838
|
+
}
|
|
59839
|
+
function detectThinkingShare(ctx) {
|
|
59840
|
+
const t = totals(ctx.calls);
|
|
59841
|
+
if (t.output < MIN_OUTPUT_FOR_THINKING || t.thinking <= 0) return void 0;
|
|
59842
|
+
const share2 = t.thinking / t.output;
|
|
59843
|
+
if (share2 <= THINKING_SHARE) return void 0;
|
|
59844
|
+
return withScope(
|
|
59845
|
+
{
|
|
59846
|
+
id: "thinking-share",
|
|
59847
|
+
severity: "info",
|
|
59848
|
+
title: "Most output tokens are thinking",
|
|
59849
|
+
reason: `${fmtK(t.thinking)} of ${fmtK(t.output)} output tokens (${pct(share2)}) were thinking, billed at the output rate.`,
|
|
59850
|
+
evidence: {
|
|
59851
|
+
thinkingTokens: t.thinking,
|
|
59852
|
+
outputTokens: t.output,
|
|
59853
|
+
share: Math.round(share2 * 100)
|
|
59854
|
+
},
|
|
59855
|
+
recommendation: "Lower the effort level for routine edits and lookups; keep high effort for design and debugging."
|
|
59856
|
+
},
|
|
59857
|
+
ctx.calls.map((c) => c.session_id)
|
|
59858
|
+
);
|
|
59859
|
+
}
|
|
59860
|
+
function detectSubagentCost(ctx) {
|
|
59861
|
+
const a = byAgent(ctx.calls);
|
|
59862
|
+
if (a.delegationShare <= DELEGATION_SHARE) return void 0;
|
|
59863
|
+
const subCost = a.subagents.reduce((s, x) => s + x.totals.costUsd, 0);
|
|
59864
|
+
const subCalls = a.subagents.reduce((s, x) => s + x.totals.calls, 0);
|
|
59865
|
+
return withScope(
|
|
59866
|
+
{
|
|
59867
|
+
id: "subagent-cost",
|
|
59868
|
+
severity: "info",
|
|
59869
|
+
title: "Subagents dominate spend",
|
|
59870
|
+
reason: `${a.subagents.length} subagent${a.subagents.length === 1 ? "" : "s"} made ${subCalls} calls for ${usd(subCost)}, ${pct(a.delegationShare)} of priced cost (main thread ${usd(a.main.costUsd)}).`,
|
|
59871
|
+
evidence: {
|
|
59872
|
+
subagents: a.subagents.length,
|
|
59873
|
+
subagentCalls: subCalls,
|
|
59874
|
+
subagentUsd: round4(subCost),
|
|
59875
|
+
share: Math.round(a.delegationShare * 100)
|
|
59876
|
+
},
|
|
59877
|
+
recommendation: "Check that subagents run on the cheapest model that fits (model: sonnet/haiku) and return summaries, not transcripts."
|
|
59878
|
+
},
|
|
59879
|
+
ctx.calls.filter((c) => c.sidechain).map((c) => c.session_id)
|
|
59880
|
+
);
|
|
59881
|
+
}
|
|
59882
|
+
var DRIVER_ADVICE = {
|
|
59883
|
+
"cache-write": {
|
|
59884
|
+
what: "cache writes",
|
|
59885
|
+
recommendation: "Cache writes dominate: a long idle gap or a cache-busting change (edited system prompt, reordered tools) re-wrote the prefix. Keep the prefix stable, or use the 1h cache deliberately for long pauses."
|
|
59886
|
+
},
|
|
59887
|
+
output: {
|
|
59888
|
+
what: "output",
|
|
59889
|
+
recommendation: "Output dominates: a long answer or heavy thinking. Lower the effort level or ask for a shorter answer; keep high effort for design and debugging."
|
|
59890
|
+
},
|
|
59891
|
+
context: {
|
|
59892
|
+
what: "context",
|
|
59893
|
+
recommendation: "Context dominates: the call re-read a large conversation. Compact, start a fresh session, or move the bulk into a subagent."
|
|
59894
|
+
}
|
|
59895
|
+
};
|
|
59896
|
+
function detectExpensiveTurn(ctx) {
|
|
59897
|
+
const limit = ctx.budgets.turnUsd ?? DEFAULT_TURN_USD;
|
|
59898
|
+
const hits = ctx.calls.filter((c) => costOf(c) > limit).sort((a, b2) => costOf(b2) - costOf(a));
|
|
59899
|
+
if (hits.length === 0) return void 0;
|
|
59900
|
+
const top2 = hits[0];
|
|
59901
|
+
const price2 = ctx.priceOf(top2.model);
|
|
59902
|
+
const b = price2 ? costOfCall(top2, price2) : { input: 0, output: 0, cacheWrite: 0, cacheRead: 0, tools: 0, total: costOf(top2) };
|
|
59903
|
+
const contextUsd = b.input + b.cacheRead;
|
|
59904
|
+
const driver = b.cacheWrite > b.output && b.cacheWrite > contextUsd ? "cache-write" : b.output > contextUsd ? "output" : "context";
|
|
59905
|
+
const advice = DRIVER_ADVICE[driver];
|
|
59906
|
+
const n = hits.length;
|
|
59907
|
+
return withScope(
|
|
59908
|
+
{
|
|
59909
|
+
id: "expensive-turn",
|
|
59910
|
+
severity: costOf(top2) > limit * 3 ? "high" : "warn",
|
|
59911
|
+
title: `${n} call${n === 1 ? "" : "s"} over ${usd(limit)}`,
|
|
59912
|
+
reason: `${n} call${n === 1 ? "" : "s"} cost more than ${usd(limit)} each; the largest was ${usd(costOf(top2))} on ${top2.model} with ${fmtK(contextOf(top2))} context tokens, driven by ${advice.what} (context ${usd(contextUsd)}, output ${usd(b.output)}, cache writes ${usd(b.cacheWrite)}).`,
|
|
59913
|
+
evidence: {
|
|
59914
|
+
calls: n,
|
|
59915
|
+
maxUsd: round4(costOf(top2)),
|
|
59916
|
+
model: top2.model,
|
|
59917
|
+
contextTokens: contextOf(top2),
|
|
59918
|
+
limitUsd: limit,
|
|
59919
|
+
driver,
|
|
59920
|
+
contextUsd: round4(contextUsd),
|
|
59921
|
+
outputUsd: round4(b.output),
|
|
59922
|
+
cacheWriteUsd: round4(b.cacheWrite)
|
|
59923
|
+
},
|
|
59924
|
+
recommendation: advice.recommendation
|
|
59925
|
+
},
|
|
59926
|
+
hits.map((c) => c.session_id)
|
|
59927
|
+
);
|
|
59928
|
+
}
|
|
59929
|
+
function detectErrorLoop(ctx) {
|
|
59930
|
+
const loops = ctx.events.filter((e) => e.category === "iteration_loop");
|
|
59931
|
+
const sessions = new Set(loops.map((e) => e.session_id));
|
|
59932
|
+
let worstTool = "";
|
|
59933
|
+
let worstRun = 0;
|
|
59934
|
+
const byThread = /* @__PURE__ */ new Map();
|
|
59935
|
+
for (const u of ctx.toolUses) {
|
|
59936
|
+
const key = `${u.session_id}\0${u.agent_id ?? ""}`;
|
|
59937
|
+
const list2 = byThread.get(key);
|
|
59938
|
+
if (list2) list2.push(u);
|
|
59939
|
+
else byThread.set(key, [u]);
|
|
59940
|
+
}
|
|
59941
|
+
for (const list2 of byThread.values()) {
|
|
59942
|
+
list2.sort((a, b) => a.ts - b.ts);
|
|
59943
|
+
let run = 0;
|
|
59944
|
+
let tool = "";
|
|
59945
|
+
for (const u of list2) {
|
|
59946
|
+
if (u.is_error && u.tool === tool) run += 1;
|
|
59947
|
+
else {
|
|
59948
|
+
run = u.is_error ? 1 : 0;
|
|
59949
|
+
tool = u.is_error ? u.tool : "";
|
|
59950
|
+
}
|
|
59951
|
+
if (run >= ERROR_RUN) {
|
|
59952
|
+
sessions.add(u.session_id);
|
|
59953
|
+
if (run > worstRun) {
|
|
59954
|
+
worstRun = run;
|
|
59955
|
+
worstTool = tool;
|
|
59956
|
+
}
|
|
59957
|
+
}
|
|
59958
|
+
}
|
|
59959
|
+
}
|
|
59960
|
+
if (loops.length === 0 && worstRun === 0) return void 0;
|
|
59961
|
+
const parts = [];
|
|
59962
|
+
if (loops.length)
|
|
59963
|
+
parts.push(`${loops.length} repeated-call loop${loops.length === 1 ? "" : "s"} recorded`);
|
|
59964
|
+
if (worstRun) parts.push(`${worstRun} consecutive ${worstTool} failures`);
|
|
59965
|
+
return withScope(
|
|
59966
|
+
{
|
|
59967
|
+
id: "error-loop",
|
|
59968
|
+
severity: "warn",
|
|
59969
|
+
title: "Repeated failures or loops",
|
|
59970
|
+
reason: `${parts.join(" and ")}; every retry re-reads the whole context.`,
|
|
59971
|
+
evidence: { loops: loops.length, longestErrorRun: worstRun, tool: worstTool },
|
|
59972
|
+
recommendation: "After two identical failures stop and change approach (em_recall for an earlier fix, read the error, try a different tool)."
|
|
59973
|
+
},
|
|
59974
|
+
sessions
|
|
59975
|
+
);
|
|
59976
|
+
}
|
|
59977
|
+
function detectBudget(ctx) {
|
|
59978
|
+
const out = [];
|
|
59979
|
+
const { sessionUsd, dailyUsd } = ctx.budgets;
|
|
59980
|
+
if (sessionUsd) {
|
|
59981
|
+
for (const [sid, rows] of groupBySession(ctx.calls)) {
|
|
59982
|
+
const cost = totals(rows).costUsd;
|
|
59983
|
+
if (cost <= sessionUsd) continue;
|
|
59984
|
+
out.push({
|
|
59985
|
+
id: "budget",
|
|
59986
|
+
severity: "high",
|
|
59987
|
+
title: "Session budget exceeded",
|
|
59988
|
+
reason: `Session ${sid.slice(0, 8)} has cost ${usd(cost)}, over the ${usd(sessionUsd)} session budget (${pct(cost / sessionUsd)}).`,
|
|
59989
|
+
evidence: { sessionUsd: round4(cost), budgetUsd: sessionUsd },
|
|
59990
|
+
scope: "session",
|
|
59991
|
+
sessionId: sid
|
|
59992
|
+
});
|
|
59993
|
+
}
|
|
59994
|
+
}
|
|
59995
|
+
if (dailyUsd) {
|
|
59996
|
+
const today = dayKey(ctx.now);
|
|
59997
|
+
const cost = totals(ctx.calls.filter((c) => dayKey(c.ts) === today)).costUsd;
|
|
59998
|
+
if (cost > dailyUsd)
|
|
59999
|
+
out.push({
|
|
60000
|
+
id: "budget",
|
|
60001
|
+
severity: "high",
|
|
60002
|
+
title: "Daily budget exceeded",
|
|
60003
|
+
reason: `Today (${today}) has cost ${usd(cost)}, over the ${usd(dailyUsd)} daily budget (${pct(cost / dailyUsd)}).`,
|
|
60004
|
+
evidence: { day: today, todayUsd: round4(cost), budgetUsd: dailyUsd },
|
|
60005
|
+
scope: "project"
|
|
60006
|
+
});
|
|
60007
|
+
}
|
|
60008
|
+
return out;
|
|
60009
|
+
}
|
|
60010
|
+
function detectUnpricedModel(ctx) {
|
|
60011
|
+
const unpriced = ctx.calls.filter((c) => !c.priced);
|
|
60012
|
+
if (unpriced.length === 0) return void 0;
|
|
60013
|
+
const models = [...new Set(unpriced.map((c) => c.model || "unknown"))];
|
|
60014
|
+
return withScope(
|
|
60015
|
+
{
|
|
60016
|
+
id: "unpriced-model",
|
|
60017
|
+
severity: "info",
|
|
60018
|
+
title: "Some calls are unpriced",
|
|
60019
|
+
reason: `${unpriced.length} of ${ctx.calls.length} calls use ${models.length} model${models.length === 1 ? "" : "s"} without a price (${models.slice(0, 3).join(", ")}), so cost totals undercount.`,
|
|
60020
|
+
evidence: { calls: unpriced.length, models: models.join(", ") },
|
|
60021
|
+
recommendation: 'Add the model to config.usage.pricing, e.g. {"pricing": {"<model substring>": {"input": 3, "output": 15}}} (USD per MTok).'
|
|
60022
|
+
},
|
|
60023
|
+
unpriced.map((c) => c.session_id)
|
|
60024
|
+
);
|
|
60025
|
+
}
|
|
60026
|
+
function detectContextNearLimit(ctx) {
|
|
60027
|
+
let latest;
|
|
60028
|
+
for (const s of ctx.snapshots) if (!latest || s.ts > latest.ts) latest = s;
|
|
60029
|
+
if (!latest || latest.ctx_used_pct == null || latest.ctx_used_pct < NEAR_LIMIT_PCT)
|
|
60030
|
+
return void 0;
|
|
60031
|
+
const p = latest.ctx_used_pct;
|
|
60032
|
+
return {
|
|
60033
|
+
id: "context-near-limit",
|
|
60034
|
+
severity: p >= NEAR_LIMIT_HIGH ? "high" : "warn",
|
|
60035
|
+
title: "Context window nearly full",
|
|
60036
|
+
reason: `The latest statusline reports ${Math.round(p)}% of the ${fmtK(num3(latest.ctx_size) || DEFAULT_WINDOW)}-token window in use; the next compaction is close.`,
|
|
60037
|
+
evidence: { ctxUsedPct: Math.round(p), ctxSize: num3(latest.ctx_size) },
|
|
60038
|
+
recommendation: "Save durable findings with em_remember now, then compact or start a fresh session.",
|
|
60039
|
+
scope: "session",
|
|
60040
|
+
sessionId: latest.session_id
|
|
60041
|
+
};
|
|
60042
|
+
}
|
|
60043
|
+
function findInsight(insights, id) {
|
|
60044
|
+
return insights.find((i) => i.id === id);
|
|
60045
|
+
}
|
|
60046
|
+
var ev = (i, key) => {
|
|
60047
|
+
const v = i?.evidence?.[key];
|
|
60048
|
+
return typeof v === "number" ? v : 0;
|
|
60049
|
+
};
|
|
60050
|
+
function scoreContextHealth(ctx, insights) {
|
|
60051
|
+
const reasons = [];
|
|
60052
|
+
let v = 100;
|
|
60053
|
+
const bloat = ev(findInsight(insights, "context-bloat"), "results");
|
|
60054
|
+
if (bloat) {
|
|
60055
|
+
const d = Math.min(30, bloat * 5);
|
|
60056
|
+
v -= d;
|
|
60057
|
+
reasons.push(`${bloat} oversized tool result${bloat === 1 ? "" : "s"} in context (\u2212${d})`);
|
|
60058
|
+
}
|
|
60059
|
+
let extra = 0;
|
|
60060
|
+
for (const evs of groupBySession(ctx.contextEvents).values())
|
|
60061
|
+
extra += Math.max(0, evs.filter((e) => e.kind === "compact").length - 1);
|
|
60062
|
+
if (extra) {
|
|
60063
|
+
const d = Math.min(30, extra * 10);
|
|
60064
|
+
v -= d;
|
|
60065
|
+
reasons.push(
|
|
60066
|
+
`${extra} compaction${extra === 1 ? "" : "s"} beyond the first per session (\u2212${d})`
|
|
60067
|
+
);
|
|
60068
|
+
}
|
|
60069
|
+
const near = findInsight(insights, "context-near-limit");
|
|
60070
|
+
if (near) {
|
|
60071
|
+
v -= 20;
|
|
60072
|
+
reasons.push(`context ${ev(near, "ctxUsedPct")}% full (\u221220)`);
|
|
60073
|
+
}
|
|
60074
|
+
const loop = findInsight(insights, "error-loop");
|
|
60075
|
+
if (loop) {
|
|
60076
|
+
const n = ev(loop, "loops") + (ev(loop, "longestErrorRun") ? 1 : 0);
|
|
60077
|
+
const d = Math.min(20, n * 10);
|
|
60078
|
+
v -= d;
|
|
60079
|
+
reasons.push(`${n} error loop${n === 1 ? "" : "s"} (\u2212${d})`);
|
|
60080
|
+
}
|
|
60081
|
+
if (!reasons.length)
|
|
60082
|
+
reasons.push(
|
|
60083
|
+
ctx.calls.length ? "no oversized results, compaction churn or loops" : "no usage recorded yet"
|
|
60084
|
+
);
|
|
60085
|
+
return { id: "context-health", value: clamp(v), reasons };
|
|
60086
|
+
}
|
|
60087
|
+
function scoreCostEfficiency(ctx, insights) {
|
|
60088
|
+
const reasons = [];
|
|
60089
|
+
let v = 100;
|
|
60090
|
+
const trivial = findInsight(insights, "opus-for-trivial");
|
|
60091
|
+
if (trivial) {
|
|
60092
|
+
const d = Math.round(ev(trivial, "share") / 100 * 50);
|
|
60093
|
+
v -= d;
|
|
60094
|
+
reasons.push(`${ev(trivial, "share")}% of premium-model calls were trivial (\u2212${d})`);
|
|
60095
|
+
}
|
|
60096
|
+
const fast = findInsight(insights, "fast-mode-share");
|
|
60097
|
+
if (fast) {
|
|
60098
|
+
const d = Math.round(ev(fast, "share") / 100 * 40);
|
|
60099
|
+
v -= d;
|
|
60100
|
+
reasons.push(`fast mode is ${ev(fast, "share")}% of spend (\u2212${d})`);
|
|
60101
|
+
}
|
|
60102
|
+
const exp = findInsight(insights, "expensive-turn");
|
|
60103
|
+
if (exp) {
|
|
60104
|
+
const n = ev(exp, "calls");
|
|
60105
|
+
const d = Math.min(30, n * 10);
|
|
60106
|
+
v -= d;
|
|
60107
|
+
reasons.push(`${n} call${n === 1 ? "" : "s"} over ${usd(ev(exp, "limitUsd"))} (\u2212${d})`);
|
|
60108
|
+
}
|
|
60109
|
+
const unpriced = findInsight(insights, "unpriced-model");
|
|
60110
|
+
if (unpriced && ctx.calls.length) {
|
|
60111
|
+
const share2 = ev(unpriced, "calls") / ctx.calls.length;
|
|
60112
|
+
const d = Math.round(share2 * 20);
|
|
60113
|
+
v -= d;
|
|
60114
|
+
reasons.push(`${pct(share2)} of calls unpriced (\u2212${d})`);
|
|
60115
|
+
}
|
|
60116
|
+
const budgets = insights.filter((i) => i.id === "budget").length;
|
|
60117
|
+
if (budgets) {
|
|
60118
|
+
const d = Math.min(40, budgets * 20);
|
|
60119
|
+
v -= d;
|
|
60120
|
+
reasons.push(`${budgets} budget${budgets === 1 ? "" : "s"} exceeded (\u2212${d})`);
|
|
60121
|
+
}
|
|
60122
|
+
if (!reasons.length)
|
|
60123
|
+
reasons.push(
|
|
60124
|
+
ctx.calls.length ? "no premium waste, fast-mode or budget issues" : "no usage recorded yet"
|
|
60125
|
+
);
|
|
60126
|
+
return { id: "cost-efficiency", value: clamp(v), reasons };
|
|
60127
|
+
}
|
|
60128
|
+
function scoreCacheEfficiency(ctx, insights) {
|
|
60129
|
+
const reasons = [];
|
|
60130
|
+
const t = totals(ctx.calls);
|
|
60131
|
+
if (t.calls === 0)
|
|
60132
|
+
return { id: "cache-efficiency", value: 0, reasons: ["no usage recorded yet"] };
|
|
60133
|
+
let v = t.cacheHitRate * 100;
|
|
60134
|
+
reasons.push(`cache hit rate ${pct(t.cacheHitRate)} over ${t.calls} calls`);
|
|
60135
|
+
const cold = ev(findInsight(insights, "cache-cold"), "restarts");
|
|
60136
|
+
if (cold) {
|
|
60137
|
+
const d = Math.min(25, cold * 5);
|
|
60138
|
+
v -= d;
|
|
60139
|
+
reasons.push(`${cold} cold restart${cold === 1 ? "" : "s"} (\u2212${d})`);
|
|
60140
|
+
}
|
|
60141
|
+
const thrash = ev(findInsight(insights, "cache-thrash"), "runs");
|
|
60142
|
+
if (thrash) {
|
|
60143
|
+
const d = Math.min(30, thrash * 10);
|
|
60144
|
+
v -= d;
|
|
60145
|
+
reasons.push(`${thrash} cache-rewrite run${thrash === 1 ? "" : "s"} (\u2212${d})`);
|
|
60146
|
+
}
|
|
60147
|
+
return { id: "cache-efficiency", value: clamp(v), reasons };
|
|
60148
|
+
}
|
|
60149
|
+
function scoreDelegation(ctx) {
|
|
60150
|
+
const reasons = [];
|
|
60151
|
+
const a = byAgent(ctx.calls);
|
|
60152
|
+
if (ctx.calls.length === 0)
|
|
60153
|
+
return { id: "delegation", value: 0, reasons: ["no usage recorded yet"] };
|
|
60154
|
+
if (a.subagents.length === 0)
|
|
60155
|
+
return {
|
|
60156
|
+
id: "delegation",
|
|
60157
|
+
value: 60,
|
|
60158
|
+
reasons: ["no subagent calls: the main thread carries every tool result itself"]
|
|
60159
|
+
};
|
|
60160
|
+
let v;
|
|
60161
|
+
if (a.delegationShare <= DELEGATION_SHARE) {
|
|
60162
|
+
v = 100;
|
|
60163
|
+
reasons.push(
|
|
60164
|
+
`subagents are ${pct(a.delegationShare)} of cost across ${a.subagents.length} agent${a.subagents.length === 1 ? "" : "s"}`
|
|
60165
|
+
);
|
|
60166
|
+
} else {
|
|
60167
|
+
v = Math.max(20, 100 - 150 * (a.delegationShare - DELEGATION_SHARE));
|
|
60168
|
+
reasons.push(
|
|
60169
|
+
`subagents are ${pct(a.delegationShare)} of cost, above ${pct(DELEGATION_SHARE)} (\u2212${100 - Math.round(v)})`
|
|
60170
|
+
);
|
|
60171
|
+
}
|
|
60172
|
+
const sub = totals(ctx.calls.filter((c) => c.sidechain));
|
|
60173
|
+
if (sub.calls && sub.cacheHitRate < 0.5) {
|
|
60174
|
+
v -= 15;
|
|
60175
|
+
reasons.push(`subagent cache hit rate ${pct(sub.cacheHitRate)} (\u221215)`);
|
|
60176
|
+
}
|
|
60177
|
+
return { id: "delegation", value: clamp(v), reasons };
|
|
60178
|
+
}
|
|
60179
|
+
function detectInsights(input2) {
|
|
60180
|
+
const calls = input2.calls.filter(isRealCall);
|
|
60181
|
+
const compactionsBySession = /* @__PURE__ */ new Map();
|
|
60182
|
+
for (const e of input2.contextEvents) {
|
|
60183
|
+
const list2 = compactionsBySession.get(e.session_id);
|
|
60184
|
+
if (list2) list2.push(e.ts);
|
|
60185
|
+
else compactionsBySession.set(e.session_id, [e.ts]);
|
|
60186
|
+
}
|
|
60187
|
+
for (const list2 of compactionsBySession.values()) list2.sort((a, b) => a - b);
|
|
60188
|
+
const callTs = /* @__PURE__ */ new Map();
|
|
60189
|
+
for (const [sid, rows] of groupBySession(calls))
|
|
60190
|
+
callTs.set(
|
|
60191
|
+
sid,
|
|
60192
|
+
rows.map((c) => c.ts).sort((a, b) => a - b)
|
|
60193
|
+
);
|
|
60194
|
+
const priceCache = /* @__PURE__ */ new Map();
|
|
60195
|
+
const overrides = input2.overrides;
|
|
60196
|
+
const ctx = {
|
|
60197
|
+
calls,
|
|
60198
|
+
threads: threads(calls),
|
|
60199
|
+
toolUses: input2.toolUses,
|
|
60200
|
+
contextEvents: input2.contextEvents,
|
|
60201
|
+
stats: input2.stats,
|
|
60202
|
+
events: input2.events,
|
|
60203
|
+
snapshots: input2.snapshots,
|
|
60204
|
+
now: input2.now ?? Date.now(),
|
|
60205
|
+
budgets: input2.budgets ?? {},
|
|
60206
|
+
overrides,
|
|
60207
|
+
priceOf: (model) => {
|
|
60208
|
+
if (!priceCache.has(model)) priceCache.set(model, resolvePrice(model, overrides));
|
|
60209
|
+
return priceCache.get(model);
|
|
60210
|
+
},
|
|
60211
|
+
compactionsBySession,
|
|
60212
|
+
laterCalls: (sid, ts) => (callTs.get(sid) ?? []).filter((t) => t > ts).length
|
|
60213
|
+
};
|
|
60214
|
+
const insights = [];
|
|
60215
|
+
const push = (i) => {
|
|
60216
|
+
if (i) insights.push(i);
|
|
60217
|
+
};
|
|
60218
|
+
push(detectCacheThrash(ctx));
|
|
60219
|
+
push(detectCacheCold(ctx));
|
|
60220
|
+
push(detectContextBloat(ctx));
|
|
60221
|
+
push(detectCompactionChurn(ctx));
|
|
60222
|
+
push(detectOpusForTrivial(ctx));
|
|
60223
|
+
push(detectFastModeShare(ctx));
|
|
60224
|
+
push(detectThinkingShare(ctx));
|
|
60225
|
+
push(detectSubagentCost(ctx));
|
|
60226
|
+
push(detectExpensiveTurn(ctx));
|
|
60227
|
+
push(detectErrorLoop(ctx));
|
|
60228
|
+
for (const b of detectBudget(ctx)) push(b);
|
|
60229
|
+
push(detectUnpricedModel(ctx));
|
|
60230
|
+
push(detectContextNearLimit(ctx));
|
|
60231
|
+
insights.sort(
|
|
60232
|
+
(a, b) => SEVERITY_RANK[a.severity] - SEVERITY_RANK[b.severity] || (b.estimatedSavingsUsd ?? 0) - (a.estimatedSavingsUsd ?? 0) || a.id.localeCompare(b.id)
|
|
60233
|
+
);
|
|
60234
|
+
const scores = [
|
|
60235
|
+
scoreContextHealth(ctx, insights),
|
|
60236
|
+
scoreCostEfficiency(ctx, insights),
|
|
60237
|
+
scoreCacheEfficiency(ctx, insights),
|
|
60238
|
+
scoreDelegation(ctx)
|
|
60239
|
+
];
|
|
60240
|
+
return { insights, scores };
|
|
60241
|
+
}
|
|
60242
|
+
var INSIGHT_EVENT_CATEGORIES = ["iteration_loop", "error", "tool_call"];
|
|
60243
|
+
function loadInsightInput(db, ctx, opts = {}) {
|
|
60244
|
+
const filter = { sessionId: opts.sessionId, since: opts.since };
|
|
60245
|
+
const usage = ctx.config.usage;
|
|
60246
|
+
return {
|
|
60247
|
+
calls: db.apiCalls(filter),
|
|
60248
|
+
toolUses: db.toolUses(filter),
|
|
60249
|
+
contextEvents: db.contextEvents(filter),
|
|
60250
|
+
snapshots: db.statuslineSnapshots(filter),
|
|
60251
|
+
stats: opts.sessionId ? db.statsBySession(opts.sessionId) : db.statsAll(),
|
|
60252
|
+
events: opts.sessionId ? db.eventsBySession(opts.sessionId, { categories: INSIGHT_EVENT_CATEGORIES }) : db.eventsRecent(500, INSIGHT_EVENT_CATEGORIES).filter((e) => !opts.since || e.ts >= opts.since),
|
|
60253
|
+
now: opts.now ?? Date.now(),
|
|
60254
|
+
budgets: usage.budgets,
|
|
60255
|
+
overrides: usage.pricing
|
|
60256
|
+
};
|
|
60257
|
+
}
|
|
60258
|
+
|
|
60259
|
+
// src/usage/brief.ts
|
|
60260
|
+
var DAY_MS = 864e5;
|
|
60261
|
+
function todaysCalls(db, now) {
|
|
60262
|
+
const today = dayKey(now);
|
|
60263
|
+
return db.apiCalls({ since: now - 1.5 * DAY_MS }).filter((c) => dayKey(c.ts) === today);
|
|
60264
|
+
}
|
|
60265
|
+
function usageBrief(db, ctx, opts = {}) {
|
|
60266
|
+
const now = opts.now ?? Date.now();
|
|
60267
|
+
const today = totals(todaysCalls(db, now));
|
|
60268
|
+
const session = totals(opts.sessionId ? db.apiCalls({ sessionId: opts.sessionId }) : []);
|
|
60269
|
+
const cacheHitRate = today.calls ? today.cacheHitRate : session.cacheHitRate;
|
|
60270
|
+
const out = {
|
|
60271
|
+
line: "",
|
|
60272
|
+
sessionUsd: session.costUsd,
|
|
60273
|
+
todayUsd: today.costUsd,
|
|
60274
|
+
cacheHitRate
|
|
60275
|
+
};
|
|
60276
|
+
if (today.calls === 0 && session.calls === 0) return out;
|
|
60277
|
+
const parts = [
|
|
60278
|
+
`today ${formatUsd(today.costUsd)} (${today.calls} call${today.calls === 1 ? "" : "s"}, ${Math.round(cacheHitRate * 100)}% cache hits)`
|
|
60279
|
+
];
|
|
60280
|
+
if (session.calls > 0) parts.push(`session ${formatUsd(session.costUsd)}`);
|
|
60281
|
+
try {
|
|
60282
|
+
const scope = session.calls > 0 ? { sessionId: opts.sessionId } : { since: now - 1.5 * DAY_MS };
|
|
60283
|
+
const { insights } = detectInsights(loadInsightInput(db, ctx, { ...scope, now }));
|
|
60284
|
+
const top2 = insights[0];
|
|
60285
|
+
if (top2) {
|
|
60286
|
+
out.topInsight = top2;
|
|
60287
|
+
const saving = top2.estimatedSavingsUsd && top2.estimatedSavingsUsd >= 5e-3 ? ` (~${formatUsd(top2.estimatedSavingsUsd)})` : "";
|
|
60288
|
+
parts.push(`top insight: ${top2.title.charAt(0).toLowerCase()}${top2.title.slice(1)}${saving}`);
|
|
60289
|
+
}
|
|
60290
|
+
} catch {
|
|
60291
|
+
}
|
|
60292
|
+
out.line = `usage: ${parts.join(" \xB7 ")}`;
|
|
60293
|
+
return out;
|
|
60294
|
+
}
|
|
60295
|
+
|
|
60296
|
+
// src/usage/ingest.ts
|
|
60297
|
+
init_esm_shims();
|
|
60298
|
+
import { createHash as createHash3 } from "crypto";
|
|
60299
|
+
import { closeSync, existsSync as existsSync18, openSync, readdirSync as readdirSync6, readSync, statSync as statSync11 } from "fs";
|
|
60300
|
+
import { homedir as homedir4 } from "os";
|
|
60301
|
+
import { basename as basename2, dirname as dirname11, join as join24, resolve as resolve8, sep as sep3 } from "path";
|
|
60302
|
+
|
|
60303
|
+
// src/usage/transcript.ts
|
|
60304
|
+
init_esm_shims();
|
|
60305
|
+
var NON_TEXT_PART_CHARS = 6400;
|
|
60306
|
+
var DENIAL_RE = /^\s*PreToolUse:|hook error|\bdenied\b/i;
|
|
60307
|
+
function isObj2(v) {
|
|
60308
|
+
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
60309
|
+
}
|
|
60310
|
+
function str2(v) {
|
|
60311
|
+
return typeof v === "string" && v.length ? v : null;
|
|
60312
|
+
}
|
|
60313
|
+
function num4(v) {
|
|
60314
|
+
return typeof v === "number" && Number.isFinite(v) ? v : 0;
|
|
60315
|
+
}
|
|
60316
|
+
function numOrNull(v) {
|
|
60317
|
+
return typeof v === "number" && Number.isFinite(v) ? v : null;
|
|
60318
|
+
}
|
|
60319
|
+
function tsOf(e) {
|
|
60320
|
+
const t = e.timestamp;
|
|
60321
|
+
if (typeof t === "string") {
|
|
60322
|
+
const ms = Date.parse(t);
|
|
60323
|
+
if (Number.isFinite(ms) && ms > 0) return ms;
|
|
60324
|
+
return void 0;
|
|
60325
|
+
}
|
|
60326
|
+
if (typeof t === "number" && Number.isFinite(t) && t > 0) return t < 1e12 ? t * 1e3 : t;
|
|
60327
|
+
return void 0;
|
|
60328
|
+
}
|
|
60329
|
+
function resultChars(content) {
|
|
60330
|
+
if (typeof content === "string") return content.length;
|
|
60331
|
+
if (Array.isArray(content)) {
|
|
60332
|
+
let n = 0;
|
|
60333
|
+
for (const part of content) {
|
|
60334
|
+
if (!isObj2(part)) {
|
|
60335
|
+
n += safeJsonLength(part);
|
|
60336
|
+
continue;
|
|
60337
|
+
}
|
|
60338
|
+
const kind = str2(part.type);
|
|
60339
|
+
if (kind === "text" || typeof part.text === "string") n += str2(part.text)?.length ?? 0;
|
|
60340
|
+
else if (kind === "image" || kind === "document" || kind === "tool_reference")
|
|
60341
|
+
n += NON_TEXT_PART_CHARS;
|
|
60342
|
+
else n += safeJsonLength(part);
|
|
60343
|
+
}
|
|
60344
|
+
return n;
|
|
60345
|
+
}
|
|
60346
|
+
if (content === void 0 || content === null) return 0;
|
|
60347
|
+
return safeJsonLength(content);
|
|
60348
|
+
}
|
|
60349
|
+
function resultText(content) {
|
|
60350
|
+
if (typeof content === "string") return content;
|
|
60351
|
+
if (Array.isArray(content)) {
|
|
60352
|
+
const parts = [];
|
|
60353
|
+
for (const part of content)
|
|
60354
|
+
if (isObj2(part) && typeof part.text === "string") parts.push(part.text);
|
|
60355
|
+
return parts.join("\n");
|
|
60356
|
+
}
|
|
60357
|
+
return "";
|
|
60358
|
+
}
|
|
60359
|
+
function safeJsonLength(v) {
|
|
60360
|
+
try {
|
|
60361
|
+
return JSON.stringify(v)?.length ?? 0;
|
|
60362
|
+
} catch {
|
|
60363
|
+
return 0;
|
|
60364
|
+
}
|
|
60365
|
+
}
|
|
60366
|
+
function parseTranscriptLines(lines, ctx) {
|
|
60367
|
+
const calls = /* @__PURE__ */ new Map();
|
|
60368
|
+
const toolUses = /* @__PURE__ */ new Map();
|
|
60369
|
+
const contextEvents = [];
|
|
60370
|
+
let lastTs;
|
|
60371
|
+
let prevTs;
|
|
60372
|
+
const fallbackTs = typeof ctx.fallbackTs === "number" && Number.isFinite(ctx.fallbackTs) && ctx.fallbackTs > 0 ? ctx.fallbackTs : Date.now();
|
|
60373
|
+
let denied = 0;
|
|
60374
|
+
for (const raw of lines) {
|
|
60375
|
+
const line = raw.trim();
|
|
60376
|
+
if (!line) continue;
|
|
60377
|
+
let e;
|
|
60378
|
+
try {
|
|
60379
|
+
e = JSON.parse(line);
|
|
60380
|
+
} catch {
|
|
60381
|
+
continue;
|
|
60382
|
+
}
|
|
60383
|
+
if (!isObj2(e)) continue;
|
|
60384
|
+
const type = str2(e.type);
|
|
60385
|
+
if (!type) continue;
|
|
60386
|
+
const sessionId = str2(e.sessionId) ?? ctx.sessionId;
|
|
60387
|
+
const agentId = str2(e.agentId);
|
|
60388
|
+
const sidechain = e.isSidechain === true ? 1 : 0;
|
|
60389
|
+
const real = tsOf(e);
|
|
60390
|
+
const ts = real ?? prevTs ?? fallbackTs;
|
|
60391
|
+
prevTs = ts;
|
|
60392
|
+
if (real !== void 0) lastTs = lastTs === void 0 ? real : Math.max(lastTs, real);
|
|
60393
|
+
if (type === "assistant") {
|
|
60394
|
+
const msg = isObj2(e.message) ? e.message : void 0;
|
|
60395
|
+
if (!msg) continue;
|
|
60396
|
+
const messageId = str2(msg.id);
|
|
60397
|
+
const requestId = str2(e.requestId) ?? messageId;
|
|
60398
|
+
if (Array.isArray(msg.content)) {
|
|
60399
|
+
for (const block of msg.content) {
|
|
60400
|
+
if (!isObj2(block) || block.type !== "tool_use") continue;
|
|
60401
|
+
const id = str2(block.id);
|
|
60402
|
+
if (!id) continue;
|
|
60403
|
+
const prev2 = toolUses.get(id);
|
|
60404
|
+
toolUses.set(id, {
|
|
60405
|
+
tool_use_id: id,
|
|
60406
|
+
session_id: prev2?.session_id ?? sessionId,
|
|
60407
|
+
agent_id: prev2?.agent_id ?? agentId,
|
|
60408
|
+
ts: prev2?.ts ?? ts,
|
|
60409
|
+
tool: str2(block.name) ?? prev2?.tool ?? "",
|
|
60410
|
+
input_chars: block.input === void 0 ? 0 : safeJsonLength(block.input),
|
|
60411
|
+
result_chars: prev2?.result_chars ?? null,
|
|
60412
|
+
is_error: prev2?.is_error ?? 0,
|
|
60413
|
+
request_id: requestId ?? prev2?.request_id ?? null
|
|
60414
|
+
});
|
|
60415
|
+
}
|
|
60416
|
+
}
|
|
60417
|
+
const usage = isObj2(msg.usage) ? msg.usage : void 0;
|
|
60418
|
+
if (!usage || !messageId || !requestId) continue;
|
|
60419
|
+
const model = str2(msg.model) ?? "unknown";
|
|
60420
|
+
const cc = isObj2(usage.cache_creation) ? usage.cache_creation : void 0;
|
|
60421
|
+
const create5m = cc ? num4(cc.ephemeral_5m_input_tokens) : num4(usage.cache_creation_input_tokens);
|
|
60422
|
+
const create1h = cc ? num4(cc.ephemeral_1h_input_tokens) : 0;
|
|
60423
|
+
const details = isObj2(usage.output_tokens_details) ? usage.output_tokens_details : void 0;
|
|
60424
|
+
const server2 = isObj2(usage.server_tool_use) ? usage.server_tool_use : void 0;
|
|
60425
|
+
const key = `${requestId}\0${messageId}`;
|
|
60426
|
+
const prev = calls.get(key);
|
|
60427
|
+
calls.set(key, {
|
|
60428
|
+
request_id: requestId,
|
|
60429
|
+
message_id: messageId,
|
|
60430
|
+
session_id: prev?.session_id ?? sessionId,
|
|
60431
|
+
agent_id: agentId ?? prev?.agent_id ?? null,
|
|
60432
|
+
sidechain: prev?.sidechain || sidechain,
|
|
60433
|
+
ts: prev?.ts ?? ts,
|
|
60434
|
+
model,
|
|
60435
|
+
input_tokens: num4(usage.input_tokens),
|
|
60436
|
+
output_tokens: num4(usage.output_tokens),
|
|
60437
|
+
cache_create_5m: create5m,
|
|
60438
|
+
cache_create_1h: create1h,
|
|
60439
|
+
cache_read: num4(usage.cache_read_input_tokens),
|
|
60440
|
+
thinking_tokens: details ? num4(details.thinking_tokens) : 0,
|
|
60441
|
+
web_searches: server2 ? num4(server2.web_search_requests) : 0,
|
|
60442
|
+
web_fetches: server2 ? num4(server2.web_fetch_requests) : 0,
|
|
60443
|
+
service_tier: str2(usage.service_tier),
|
|
60444
|
+
speed: str2(usage.speed),
|
|
60445
|
+
stop_reason: str2(msg.stop_reason) ?? prev?.stop_reason ?? null,
|
|
60446
|
+
prompt_id: str2(e.promptId) ?? prev?.prompt_id ?? null,
|
|
60447
|
+
git_branch: str2(e.gitBranch) ?? prev?.git_branch ?? null,
|
|
60448
|
+
cwd: str2(e.cwd) ?? prev?.cwd ?? null,
|
|
60449
|
+
effort: str2(e.effort) ?? prev?.effort ?? null,
|
|
60450
|
+
version: str2(e.version) ?? prev?.version ?? null,
|
|
60451
|
+
entrypoint: str2(e.entrypoint) ?? prev?.entrypoint ?? null
|
|
60452
|
+
});
|
|
60453
|
+
continue;
|
|
60454
|
+
}
|
|
60455
|
+
if (type === "user") {
|
|
60456
|
+
const msg = isObj2(e.message) ? e.message : void 0;
|
|
60457
|
+
if (!msg || !Array.isArray(msg.content)) continue;
|
|
60458
|
+
for (const block of msg.content) {
|
|
60459
|
+
if (!isObj2(block) || block.type !== "tool_result") continue;
|
|
60460
|
+
const id = str2(block.tool_use_id);
|
|
60461
|
+
if (!id) continue;
|
|
60462
|
+
const prev = toolUses.get(id);
|
|
60463
|
+
let isError = prev?.is_error ?? 0;
|
|
60464
|
+
if (block.is_error === true) {
|
|
60465
|
+
if (DENIAL_RE.test(resultText(block.content))) denied += 1;
|
|
60466
|
+
else isError = 1;
|
|
60467
|
+
}
|
|
60468
|
+
toolUses.set(id, {
|
|
60469
|
+
tool_use_id: id,
|
|
60470
|
+
session_id: prev?.session_id ?? sessionId,
|
|
60471
|
+
agent_id: prev?.agent_id ?? agentId,
|
|
60472
|
+
ts: prev?.ts ?? ts,
|
|
60473
|
+
tool: prev?.tool ?? "",
|
|
60474
|
+
input_chars: prev?.input_chars ?? 0,
|
|
60475
|
+
result_chars: resultChars(block.content),
|
|
60476
|
+
is_error: isError,
|
|
60477
|
+
request_id: prev?.request_id ?? null
|
|
60478
|
+
});
|
|
60479
|
+
}
|
|
60480
|
+
continue;
|
|
60481
|
+
}
|
|
60482
|
+
if (type === "system") {
|
|
60483
|
+
const subtype = str2(e.subtype);
|
|
60484
|
+
if (subtype === "compact_boundary") {
|
|
60485
|
+
const meta4 = isObj2(e.compactMetadata) ? e.compactMetadata : void 0;
|
|
60486
|
+
contextEvents.push({
|
|
60487
|
+
session_id: sessionId,
|
|
60488
|
+
ts,
|
|
60489
|
+
kind: "compact",
|
|
60490
|
+
trigger: meta4 ? str2(meta4.trigger) : null,
|
|
60491
|
+
pre_tokens: meta4 ? numOrNull(meta4.preTokens) : null,
|
|
60492
|
+
post_tokens: meta4 ? numOrNull(meta4.postTokens) : null
|
|
60493
|
+
});
|
|
60494
|
+
} else if (subtype === "local_command") {
|
|
60495
|
+
const run = isObj2(e.commandRun) ? e.commandRun : void 0;
|
|
60496
|
+
if (run && str2(run.command) === "clear") {
|
|
60497
|
+
contextEvents.push({
|
|
60498
|
+
session_id: sessionId,
|
|
60499
|
+
ts,
|
|
60500
|
+
kind: "clear",
|
|
60501
|
+
trigger: "manual",
|
|
60502
|
+
pre_tokens: null,
|
|
60503
|
+
post_tokens: null
|
|
60504
|
+
});
|
|
60505
|
+
}
|
|
60506
|
+
}
|
|
60507
|
+
}
|
|
60508
|
+
}
|
|
60509
|
+
const out = {
|
|
60510
|
+
calls: [...calls.values()],
|
|
60511
|
+
toolUses: [...toolUses.values()],
|
|
60512
|
+
contextEvents,
|
|
60513
|
+
denied
|
|
60514
|
+
};
|
|
60515
|
+
if (lastTs !== void 0) out.lastTs = lastTs;
|
|
60516
|
+
return out;
|
|
60517
|
+
}
|
|
60518
|
+
|
|
60519
|
+
// src/usage/ingest.ts
|
|
60520
|
+
var ZERO = { calls: 0, toolUses: 0, contextEvents: 0 };
|
|
60521
|
+
var HEAD_BYTES = 512;
|
|
60522
|
+
var SNAPSHOT_MAX_AGE_MS = 30 * 24 * 60 * 6e4;
|
|
60523
|
+
var SNAPSHOT_KEEP_PER_SESSION = 500;
|
|
60524
|
+
var SESSION_ID_RE = /^[A-Za-z0-9_-]{1,80}$/;
|
|
60525
|
+
function isValidSessionId(id) {
|
|
60526
|
+
return SESSION_ID_RE.test(id);
|
|
60527
|
+
}
|
|
60528
|
+
function claudeDir2() {
|
|
60529
|
+
return process.env.EMINIAR_CLAUDE_DIR || join24(homedir4(), ".claude");
|
|
60530
|
+
}
|
|
60531
|
+
function insideDir(path3, dir) {
|
|
60532
|
+
const d = resolve8(dir);
|
|
60533
|
+
const p = resolve8(path3);
|
|
60534
|
+
return p === d || p.startsWith(d.endsWith(sep3) ? d : d + sep3);
|
|
60535
|
+
}
|
|
60536
|
+
function isTranscriptFile(path3) {
|
|
60537
|
+
if (!path3.endsWith(".jsonl")) return false;
|
|
60538
|
+
try {
|
|
60539
|
+
return statSync11(path3).isFile();
|
|
60540
|
+
} catch {
|
|
60541
|
+
return false;
|
|
60542
|
+
}
|
|
60543
|
+
}
|
|
60544
|
+
function transcriptSlug(root) {
|
|
60545
|
+
return root.replace(/[^A-Za-z0-9]/g, "-");
|
|
60546
|
+
}
|
|
60547
|
+
function transcriptDirFor(root) {
|
|
60548
|
+
return join24(claudeDir2(), "projects", transcriptSlug(root));
|
|
60549
|
+
}
|
|
60550
|
+
function usageEnabled(ctx) {
|
|
60551
|
+
return ctx.config.usage.enabled !== false;
|
|
60552
|
+
}
|
|
60553
|
+
function priceCalls(calls, overrides) {
|
|
60554
|
+
return calls.map((c) => {
|
|
60555
|
+
const price2 = resolvePrice(c.model, overrides);
|
|
60556
|
+
if (!price2) return { ...c, cost_usd: null, priced: 0 };
|
|
60557
|
+
return { ...c, cost_usd: costOfCall(c, price2).total, priced: 1 };
|
|
60558
|
+
});
|
|
60559
|
+
}
|
|
60560
|
+
function readCompleteLines(path3, offset, size) {
|
|
60561
|
+
const length = size - offset;
|
|
60562
|
+
if (length <= 0) return { lines: [], nextOffset: offset };
|
|
60563
|
+
const buf = Buffer.alloc(length);
|
|
60564
|
+
const fd = openSync(path3, "r");
|
|
60565
|
+
let read2 = 0;
|
|
60566
|
+
try {
|
|
60567
|
+
while (read2 < length) {
|
|
60568
|
+
const n = readSync(fd, buf, read2, length - read2, offset + read2);
|
|
60569
|
+
if (n <= 0) break;
|
|
60570
|
+
read2 += n;
|
|
60571
|
+
}
|
|
60572
|
+
} finally {
|
|
60573
|
+
closeSync(fd);
|
|
60574
|
+
}
|
|
60575
|
+
const end = buf.lastIndexOf(10, read2 - 1);
|
|
60576
|
+
if (end < 0) return { lines: [], nextOffset: offset };
|
|
60577
|
+
const text2 = buf.toString("utf8", 0, end + 1);
|
|
60578
|
+
return { lines: text2.split("\n"), nextOffset: offset + end + 1 };
|
|
60579
|
+
}
|
|
60580
|
+
function headHash(path3, length) {
|
|
60581
|
+
if (length <= 0) return null;
|
|
60582
|
+
const buf = Buffer.alloc(length);
|
|
60583
|
+
let read2 = 0;
|
|
60584
|
+
const fd = openSync(path3, "r");
|
|
60585
|
+
try {
|
|
60586
|
+
while (read2 < length) {
|
|
60587
|
+
const n = readSync(fd, buf, read2, length - read2, read2);
|
|
60588
|
+
if (n <= 0) break;
|
|
60589
|
+
read2 += n;
|
|
60590
|
+
}
|
|
60591
|
+
} finally {
|
|
60592
|
+
closeSync(fd);
|
|
60593
|
+
}
|
|
60594
|
+
return createHash3("sha1").update(buf.subarray(0, read2)).digest("hex");
|
|
60595
|
+
}
|
|
60596
|
+
function byteAt(path3, pos) {
|
|
60597
|
+
if (pos < 0) return void 0;
|
|
60598
|
+
const buf = Buffer.alloc(1);
|
|
60599
|
+
const fd = openSync(path3, "r");
|
|
60600
|
+
try {
|
|
60601
|
+
return readSync(fd, buf, 0, 1, pos) === 1 ? buf[0] : void 0;
|
|
60602
|
+
} finally {
|
|
60603
|
+
closeSync(fd);
|
|
60604
|
+
}
|
|
60605
|
+
}
|
|
60606
|
+
function sessionIdFromName(path3) {
|
|
60607
|
+
const dir = dirname11(path3);
|
|
60608
|
+
if (basename2(dir) === "subagents") return basename2(dirname11(dir));
|
|
60609
|
+
const name = basename2(path3, ".jsonl");
|
|
60610
|
+
return name.startsWith("agent-") ? void 0 : name;
|
|
60611
|
+
}
|
|
60612
|
+
function contextEventExists(db, e) {
|
|
60613
|
+
return db.contextEvents({ sessionId: e.session_id, since: e.ts, until: e.ts + 1 }).some((x) => x.kind === e.kind);
|
|
60614
|
+
}
|
|
60615
|
+
function resumeOffset(path3, prev, size, mtime) {
|
|
60616
|
+
if (!prev || prev.offset <= 0) return 0;
|
|
60617
|
+
if (size < prev.offset) return 0;
|
|
60618
|
+
if (prev.head_hash) {
|
|
60619
|
+
const want = Math.min(HEAD_BYTES, prev.size);
|
|
60620
|
+
try {
|
|
60621
|
+
if (headHash(path3, want) !== prev.head_hash) return 0;
|
|
60622
|
+
} catch {
|
|
60623
|
+
return 0;
|
|
60624
|
+
}
|
|
60625
|
+
}
|
|
60626
|
+
if (prev.mtime !== mtime) {
|
|
60627
|
+
try {
|
|
60628
|
+
if (byteAt(path3, prev.offset - 1) !== 10) return 0;
|
|
60629
|
+
} catch {
|
|
60630
|
+
return 0;
|
|
60631
|
+
}
|
|
60632
|
+
}
|
|
60633
|
+
return prev.offset;
|
|
60634
|
+
}
|
|
60635
|
+
function ingestTranscript(db, path3, opts = {}) {
|
|
60636
|
+
if (!path3.endsWith(".jsonl")) return ZERO;
|
|
60637
|
+
let size;
|
|
60638
|
+
let mtime;
|
|
60639
|
+
try {
|
|
60640
|
+
const st = statSync11(path3);
|
|
60641
|
+
if (!st.isFile()) return ZERO;
|
|
60642
|
+
size = st.size;
|
|
60643
|
+
mtime = Math.floor(st.mtimeMs);
|
|
60644
|
+
} catch {
|
|
60645
|
+
return ZERO;
|
|
60646
|
+
}
|
|
60647
|
+
const prev = db.transcriptFileGet(path3);
|
|
60648
|
+
const offset = resumeOffset(path3, prev, size, mtime);
|
|
60649
|
+
const sessionHint = opts.sessionId ?? prev?.session_id ?? sessionIdFromName(path3);
|
|
60650
|
+
if (offset === size) {
|
|
60651
|
+
if (!prev || prev.size !== size || prev.mtime !== mtime || sessionHint && !prev.session_id)
|
|
60652
|
+
db.transcriptFileSet({
|
|
60653
|
+
path: path3,
|
|
60654
|
+
session_id: sessionHint ?? null,
|
|
60655
|
+
offset,
|
|
60656
|
+
size,
|
|
60657
|
+
mtime,
|
|
60658
|
+
head_hash: prev?.size === size ? prev.head_hash ?? null : fingerprint(path3, size),
|
|
60659
|
+
ingested_at: Date.now()
|
|
60660
|
+
});
|
|
60661
|
+
return ZERO;
|
|
60662
|
+
}
|
|
60663
|
+
const { lines, nextOffset } = readCompleteLines(path3, offset, size);
|
|
60664
|
+
const parsed = parseTranscriptLines(lines, {
|
|
60665
|
+
sessionId: sessionHint ?? "unknown",
|
|
60666
|
+
path: path3,
|
|
60667
|
+
fallbackTs: mtime
|
|
60668
|
+
});
|
|
60669
|
+
const calls = priceCalls(parsed.calls, opts.overrides);
|
|
60670
|
+
const counts = { calls: 0, toolUses: 0, contextEvents: 0 };
|
|
60671
|
+
const sessionSeen = parsed.calls[0]?.session_id ?? parsed.toolUses[0]?.session_id;
|
|
60672
|
+
const head = fingerprint(path3, size);
|
|
60673
|
+
db.transaction(() => {
|
|
60674
|
+
counts.calls = db.apiCallsUpsert(calls);
|
|
60675
|
+
counts.toolUses = db.toolUsesUpsert(parsed.toolUses);
|
|
60676
|
+
for (const e of parsed.contextEvents) {
|
|
60677
|
+
if (contextEventExists(db, e)) continue;
|
|
60678
|
+
db.contextEventInsert(e);
|
|
60679
|
+
counts.contextEvents++;
|
|
60680
|
+
}
|
|
60681
|
+
db.transcriptFileSet({
|
|
60682
|
+
path: path3,
|
|
60683
|
+
session_id: sessionHint ?? sessionSeen ?? null,
|
|
60684
|
+
offset: nextOffset,
|
|
60685
|
+
size,
|
|
60686
|
+
mtime,
|
|
60687
|
+
head_hash: head,
|
|
60688
|
+
ingested_at: Date.now()
|
|
60689
|
+
});
|
|
60690
|
+
});
|
|
60691
|
+
return counts;
|
|
60692
|
+
}
|
|
60693
|
+
function fingerprint(path3, size) {
|
|
60694
|
+
try {
|
|
60695
|
+
return headHash(path3, Math.min(HEAD_BYTES, size));
|
|
60696
|
+
} catch {
|
|
60697
|
+
return null;
|
|
60698
|
+
}
|
|
60699
|
+
}
|
|
60700
|
+
function pruneSnapshots(db) {
|
|
60701
|
+
try {
|
|
60702
|
+
db.statuslineSnapshotsPrune({
|
|
60703
|
+
olderThan: Date.now() - SNAPSHOT_MAX_AGE_MS,
|
|
60704
|
+
keepPerSession: SNAPSHOT_KEEP_PER_SESSION
|
|
60705
|
+
});
|
|
60706
|
+
} catch {
|
|
60707
|
+
}
|
|
60708
|
+
}
|
|
60709
|
+
function add(a, b) {
|
|
60710
|
+
return {
|
|
60711
|
+
calls: a.calls + b.calls,
|
|
60712
|
+
toolUses: a.toolUses + b.toolUses,
|
|
60713
|
+
contextEvents: a.contextEvents + b.contextEvents
|
|
60714
|
+
};
|
|
60715
|
+
}
|
|
60716
|
+
function listJsonl(dir) {
|
|
60717
|
+
try {
|
|
60718
|
+
return readdirSync6(dir).filter((f) => f.endsWith(".jsonl")).map((f) => join24(dir, f));
|
|
60719
|
+
} catch {
|
|
60720
|
+
return [];
|
|
60721
|
+
}
|
|
60722
|
+
}
|
|
60723
|
+
function firstEntrySessionId(path3) {
|
|
60724
|
+
try {
|
|
60725
|
+
const { lines } = readCompleteLines(path3, 0, Math.min(statSync11(path3).size, 64 * 1024));
|
|
60726
|
+
for (const line of lines) {
|
|
60727
|
+
if (!line.trim()) continue;
|
|
60728
|
+
const e = JSON.parse(line);
|
|
60729
|
+
return typeof e.sessionId === "string" ? e.sessionId : void 0;
|
|
60730
|
+
}
|
|
60731
|
+
} catch {
|
|
60732
|
+
}
|
|
60733
|
+
return void 0;
|
|
60734
|
+
}
|
|
60735
|
+
function sidechainFilesFor(db, dir, sessionId) {
|
|
60736
|
+
if (!isValidSessionId(sessionId)) return [];
|
|
60737
|
+
const inside = (p) => insideDir(p, dir) && p.endsWith(".jsonl");
|
|
60738
|
+
const out = listJsonl(join24(dir, sessionId, "subagents")).filter(inside);
|
|
60739
|
+
for (const path3 of listJsonl(dir)) {
|
|
60740
|
+
if (!inside(path3) || !basename2(path3).startsWith("agent-")) continue;
|
|
60741
|
+
const known = db.transcriptFileGet(path3)?.session_id;
|
|
60742
|
+
let owner = known ?? void 0;
|
|
60743
|
+
if (!owner) {
|
|
60744
|
+
owner = firstEntrySessionId(path3);
|
|
60745
|
+
if (owner) {
|
|
60746
|
+
db.transcriptFileSet({
|
|
60747
|
+
path: path3,
|
|
60748
|
+
session_id: owner,
|
|
60749
|
+
offset: 0,
|
|
60750
|
+
size: 0,
|
|
60751
|
+
mtime: 0,
|
|
60752
|
+
ingested_at: Date.now()
|
|
60753
|
+
});
|
|
60754
|
+
}
|
|
60755
|
+
}
|
|
60756
|
+
if (owner === sessionId) out.push(path3);
|
|
60757
|
+
}
|
|
60758
|
+
return out;
|
|
60759
|
+
}
|
|
60760
|
+
function ingestSession(db, root, sessionId, transcriptPath, opts = {}) {
|
|
60761
|
+
if (!isValidSessionId(sessionId)) return ZERO;
|
|
60762
|
+
let main;
|
|
60763
|
+
const trusted = transcriptPath ?? db.sessionGet(sessionId)?.transcript_path ?? void 0;
|
|
60764
|
+
if (trusted) {
|
|
60765
|
+
if (!isTranscriptFile(trusted)) return ZERO;
|
|
60766
|
+
main = resolve8(trusted);
|
|
60767
|
+
} else {
|
|
60768
|
+
const built = resolve8(transcriptDirFor(root), `${sessionId}.jsonl`);
|
|
60769
|
+
if (!insideDir(built, claudeDir2())) return ZERO;
|
|
60770
|
+
main = built;
|
|
60771
|
+
}
|
|
60772
|
+
let total = ingestTranscript(db, main, { sessionId, overrides: opts.overrides });
|
|
60773
|
+
for (const path3 of sidechainFilesFor(db, dirname11(main), sessionId)) {
|
|
60774
|
+
total = add(total, ingestTranscript(db, path3, { sessionId, overrides: opts.overrides }));
|
|
60775
|
+
}
|
|
60776
|
+
pruneSnapshots(db);
|
|
60777
|
+
return total;
|
|
60778
|
+
}
|
|
60779
|
+
function ingestProject(db, root, opts = {}) {
|
|
60780
|
+
const dir = transcriptDirFor(root);
|
|
60781
|
+
const files = new Set(listJsonl(dir));
|
|
60782
|
+
if (existsSync18(dir)) {
|
|
60783
|
+
try {
|
|
60784
|
+
for (const entry of readdirSync6(dir, { withFileTypes: true })) {
|
|
60785
|
+
if (entry.isDirectory())
|
|
60786
|
+
for (const sub of listJsonl(join24(dir, entry.name, "subagents"))) files.add(sub);
|
|
60787
|
+
}
|
|
60788
|
+
} catch {
|
|
60789
|
+
}
|
|
60790
|
+
}
|
|
60791
|
+
for (const s of db.sessionsRecent(1e3)) if (s.transcript_path) files.add(s.transcript_path);
|
|
60792
|
+
let total = ZERO;
|
|
60793
|
+
let touched = 0;
|
|
60794
|
+
for (const path3 of files) {
|
|
60795
|
+
let st;
|
|
60796
|
+
try {
|
|
60797
|
+
const s = statSync11(path3);
|
|
60798
|
+
if (!s.isFile()) continue;
|
|
60799
|
+
st = { size: s.size, mtime: Math.floor(s.mtimeMs) };
|
|
60800
|
+
} catch {
|
|
60801
|
+
continue;
|
|
60802
|
+
}
|
|
60803
|
+
const prev = db.transcriptFileGet(path3);
|
|
60804
|
+
if (prev && prev.size === st.size && prev.mtime === st.mtime && prev.offset === st.size)
|
|
60805
|
+
continue;
|
|
60806
|
+
touched++;
|
|
60807
|
+
const sessionId = prev?.session_id ?? sessionIdFromName(path3);
|
|
60808
|
+
total = add(total, ingestTranscript(db, path3, { sessionId, overrides: opts.overrides }));
|
|
60809
|
+
}
|
|
60810
|
+
pruneSnapshots(db);
|
|
60811
|
+
return { ...total, files: touched };
|
|
60812
|
+
}
|
|
60813
|
+
|
|
58652
60814
|
// src/tools/em_stats.ts
|
|
58653
60815
|
var emStats = defineTool({
|
|
58654
60816
|
name: "em_stats",
|
|
@@ -58704,9 +60866,23 @@ var emStats = defineTool({
|
|
|
58704
60866
|
parts.push(
|
|
58705
60867
|
`index: ${db.countDocuments()} documents (${db.countDocuments("exec")} exec, ${db.countDocuments("batch")} batch, ${db.countDocuments("file")} file, ${db.countDocuments("url")} url, ${db.countDocuments("code")} code), ${db.vecCount()} vectors`
|
|
58706
60868
|
);
|
|
60869
|
+
const cost = usageLine(ctx, sessionId);
|
|
60870
|
+
if (cost) parts.push(cost);
|
|
58707
60871
|
return text(parts.join("\n\n"));
|
|
58708
60872
|
}
|
|
58709
60873
|
});
|
|
60874
|
+
function usageLine(ctx, sessionId) {
|
|
60875
|
+
if (!usageEnabled(ctx)) return void 0;
|
|
60876
|
+
try {
|
|
60877
|
+
const db = getStorage(ctx);
|
|
60878
|
+
ingestSession(db, ctx.root, sessionId, void 0, { overrides: ctx.config.usage.pricing });
|
|
60879
|
+
const b = usageBrief(db, ctx, { sessionId });
|
|
60880
|
+
if (!b.todayUsd && !b.sessionUsd) return void 0;
|
|
60881
|
+
return `usage: today ${formatUsd(b.todayUsd)} \xB7 session ${formatUsd(b.sessionUsd)} \xB7 see em_usage`;
|
|
60882
|
+
} catch {
|
|
60883
|
+
return void 0;
|
|
60884
|
+
}
|
|
60885
|
+
}
|
|
58710
60886
|
function mergeByTool(rows) {
|
|
58711
60887
|
const m = /* @__PURE__ */ new Map();
|
|
58712
60888
|
for (const r of rows) {
|
|
@@ -58860,6 +61036,536 @@ function groupBySource(edges) {
|
|
|
58860
61036
|
return out;
|
|
58861
61037
|
}
|
|
58862
61038
|
|
|
61039
|
+
// src/tools/em_usage.ts
|
|
61040
|
+
init_esm_shims();
|
|
61041
|
+
var USAGE_VIEWS = [
|
|
61042
|
+
"summary",
|
|
61043
|
+
"daily",
|
|
61044
|
+
"weekly",
|
|
61045
|
+
"monthly",
|
|
61046
|
+
"models",
|
|
61047
|
+
"sessions",
|
|
61048
|
+
"blocks",
|
|
61049
|
+
"turns",
|
|
61050
|
+
"tools",
|
|
61051
|
+
"insights",
|
|
61052
|
+
"savings"
|
|
61053
|
+
];
|
|
61054
|
+
var DAY4 = 864e5;
|
|
61055
|
+
var UNITS = { s: 1e3, m: 6e4, h: 36e5, d: DAY4, w: 7 * DAY4 };
|
|
61056
|
+
var SINCE_MIN = Date.UTC(2e3, 0, 1);
|
|
61057
|
+
function parseSince(value, now = Date.now()) {
|
|
61058
|
+
const s = value?.trim();
|
|
61059
|
+
if (!s) return void 0;
|
|
61060
|
+
const bad = () => new Error(
|
|
61061
|
+
`invalid since "${value}": use an ISO date (2026-10-01) or a relative value like 30d, 12h, 2w`
|
|
61062
|
+
);
|
|
61063
|
+
let ms;
|
|
61064
|
+
const rel = /^(\d{1,6})([smhdw])$/i.exec(s);
|
|
61065
|
+
if (rel) ms = now - Number(rel[1]) * (UNITS[rel[2].toLowerCase()] ?? DAY4);
|
|
61066
|
+
else if (/^\d{4}-\d{2}-\d{2}(T.*)?$/.test(s)) ms = Date.parse(s);
|
|
61067
|
+
else throw bad();
|
|
61068
|
+
if (!Number.isFinite(ms) || ms < SINCE_MIN || ms > now + DAY4) throw bad();
|
|
61069
|
+
return ms;
|
|
61070
|
+
}
|
|
61071
|
+
var LIMIT_MAX = 1e3;
|
|
61072
|
+
var LIMIT_DEFAULT = 20;
|
|
61073
|
+
function parseLimit(value) {
|
|
61074
|
+
if (value === void 0) return void 0;
|
|
61075
|
+
const n = typeof value === "number" ? value : /^\d{1,6}$/.test(value.trim()) ? Number(value.trim()) : Number.NaN;
|
|
61076
|
+
if (!Number.isInteger(n) || n < 1 || n > LIMIT_MAX)
|
|
61077
|
+
return `limit must be a whole number between 1 and ${LIMIT_MAX}`;
|
|
61078
|
+
return n;
|
|
61079
|
+
}
|
|
61080
|
+
var clampLimit = (n) => n === void 0 || !Number.isFinite(n) ? LIMIT_DEFAULT : Math.min(LIMIT_MAX, Math.max(1, Math.floor(n)));
|
|
61081
|
+
var INGEST_GUARD_MS = 1e4;
|
|
61082
|
+
var lastIngest = /* @__PURE__ */ new Map();
|
|
61083
|
+
var sessionIngestKey = (root, sessionId) => `${root}\0${sessionId}`;
|
|
61084
|
+
function recentlyIngested(root, sessionId, now = Date.now()) {
|
|
61085
|
+
const within = (key) => {
|
|
61086
|
+
const t = lastIngest.get(key);
|
|
61087
|
+
return t !== void 0 && now - t < INGEST_GUARD_MS;
|
|
61088
|
+
};
|
|
61089
|
+
return within(root) || sessionId !== void 0 && within(sessionIngestKey(root, sessionId));
|
|
61090
|
+
}
|
|
61091
|
+
function ingestFor(db, ctx, scope, sessionId, now) {
|
|
61092
|
+
const overrides = ctx.config.usage.pricing;
|
|
61093
|
+
const project = scope === "project";
|
|
61094
|
+
if (recentlyIngested(ctx.root, project ? void 0 : sessionId, now)) return;
|
|
61095
|
+
try {
|
|
61096
|
+
if (project) ingestProject(db, ctx.root, { overrides });
|
|
61097
|
+
else ingestSession(db, ctx.root, sessionId, void 0, { overrides });
|
|
61098
|
+
} catch {
|
|
61099
|
+
} finally {
|
|
61100
|
+
lastIngest.set(project ? ctx.root : sessionIngestKey(ctx.root, sessionId), now);
|
|
61101
|
+
}
|
|
61102
|
+
}
|
|
61103
|
+
function loadUsage(ctx, opts = {}) {
|
|
61104
|
+
const db = getStorage(ctx);
|
|
61105
|
+
const scope = opts.scope ?? "project";
|
|
61106
|
+
const sessionId = opts.sessionId ?? getCurrentSession(ctx);
|
|
61107
|
+
const now = opts.now ?? Date.now();
|
|
61108
|
+
if (opts.ingest !== false) ingestFor(db, ctx, scope, sessionId, now);
|
|
61109
|
+
const filter = scope === "session" ? { sessionId, since: opts.since } : { since: opts.since, sessionId: void 0 };
|
|
61110
|
+
return {
|
|
61111
|
+
scope,
|
|
61112
|
+
sessionId,
|
|
61113
|
+
since: opts.since,
|
|
61114
|
+
now,
|
|
61115
|
+
calls: db.apiCalls(filter).filter(isRealCall),
|
|
61116
|
+
toolUses: db.toolUses(filter),
|
|
61117
|
+
contextEvents: db.contextEvents(scope === "session" ? { sessionId } : {}),
|
|
61118
|
+
stats: scope === "session" ? db.statsBySession(sessionId) : db.statsAll(),
|
|
61119
|
+
snapshot: db.statuslineLatest(scope === "session" ? sessionId : void 0)
|
|
61120
|
+
};
|
|
61121
|
+
}
|
|
61122
|
+
function modelSlices(rows) {
|
|
61123
|
+
return byModel(rows).map((g) => ({
|
|
61124
|
+
model: g.key,
|
|
61125
|
+
calls: g.totals.calls,
|
|
61126
|
+
costUsd: g.totals.costUsd,
|
|
61127
|
+
unpricedCalls: g.totals.unpricedCalls
|
|
61128
|
+
})).sort((a, b) => b.costUsd - a.costUsd || b.calls - a.calls);
|
|
61129
|
+
}
|
|
61130
|
+
function periods(calls, keyFn) {
|
|
61131
|
+
const buckets = /* @__PURE__ */ new Map();
|
|
61132
|
+
for (const c of calls) {
|
|
61133
|
+
const k = keyFn(c.ts);
|
|
61134
|
+
const list2 = buckets.get(k);
|
|
61135
|
+
if (list2) list2.push(c);
|
|
61136
|
+
else buckets.set(k, [c]);
|
|
61137
|
+
}
|
|
61138
|
+
return [...buckets.entries()].sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([key, rows]) => ({ key, totals: totals(rows), models: modelSlices(rows) }));
|
|
61139
|
+
}
|
|
61140
|
+
function modelRows(calls) {
|
|
61141
|
+
const all = totals(calls);
|
|
61142
|
+
return byModel(calls).map((g) => ({
|
|
61143
|
+
model: g.key,
|
|
61144
|
+
calls: g.totals.calls,
|
|
61145
|
+
costUsd: g.totals.costUsd,
|
|
61146
|
+
unpricedCalls: g.totals.unpricedCalls,
|
|
61147
|
+
input: g.totals.input,
|
|
61148
|
+
output: g.totals.output,
|
|
61149
|
+
cacheRead: g.totals.cacheRead,
|
|
61150
|
+
cacheWrite: g.totals.cacheCreate5m + g.totals.cacheCreate1h,
|
|
61151
|
+
thinking: g.totals.thinking,
|
|
61152
|
+
share: all.costUsd > 0 ? g.totals.costUsd / all.costUsd : 0
|
|
61153
|
+
})).sort((a, b) => b.costUsd - a.costUsd || b.calls - a.calls);
|
|
61154
|
+
}
|
|
61155
|
+
function insightsFor(ctx, data) {
|
|
61156
|
+
const db = getStorage(ctx);
|
|
61157
|
+
const input2 = loadInsightInput(db, ctx, {
|
|
61158
|
+
...data.scope === "session" ? { sessionId: data.sessionId } : {},
|
|
61159
|
+
...data.since !== void 0 ? { since: data.since } : {},
|
|
61160
|
+
now: data.now
|
|
61161
|
+
});
|
|
61162
|
+
const r = detectInsights(input2);
|
|
61163
|
+
return { insights: r.insights, scores: r.scores };
|
|
61164
|
+
}
|
|
61165
|
+
var SEVERITY_RANK2 = { high: 0, warn: 1, info: 2 };
|
|
61166
|
+
function sortInsights(list2) {
|
|
61167
|
+
return list2.slice().sort(
|
|
61168
|
+
(a, b) => SEVERITY_RANK2[a.severity] - SEVERITY_RANK2[b.severity] || (b.estimatedSavingsUsd ?? 0) - (a.estimatedSavingsUsd ?? 0)
|
|
61169
|
+
);
|
|
61170
|
+
}
|
|
61171
|
+
function summaryPayload(ctx, data) {
|
|
61172
|
+
const { calls, now } = data;
|
|
61173
|
+
const today = dayKey(now);
|
|
61174
|
+
const blocks = billingBlocks(calls, now);
|
|
61175
|
+
const ins = insightsFor(ctx, data);
|
|
61176
|
+
return {
|
|
61177
|
+
scope: data.scope,
|
|
61178
|
+
sessionId: data.sessionId,
|
|
61179
|
+
since: data.since,
|
|
61180
|
+
now,
|
|
61181
|
+
totals: totals(calls),
|
|
61182
|
+
today: totals(calls.filter((c) => dayKey(c.ts) === today)),
|
|
61183
|
+
last7d: totals(calls.filter((c) => c.ts >= now - 7 * DAY4)),
|
|
61184
|
+
last30d: totals(calls.filter((c) => c.ts >= now - 30 * DAY4)),
|
|
61185
|
+
cache: cacheStats(calls),
|
|
61186
|
+
models: modelRows(calls),
|
|
61187
|
+
burn: burnRate(calls, now),
|
|
61188
|
+
block: activeBlock(blocks, now) ?? null,
|
|
61189
|
+
insights: sortInsights(ins.insights).slice(0, 3),
|
|
61190
|
+
scores: ins.scores,
|
|
61191
|
+
savings: savingsFor(ctx, data),
|
|
61192
|
+
snapshot: data.snapshot ?? null
|
|
61193
|
+
};
|
|
61194
|
+
}
|
|
61195
|
+
function savingsFor(ctx, data) {
|
|
61196
|
+
return pricedSavings(data.stats, data.calls, ctx.config.usage.pricing, { since: data.since });
|
|
61197
|
+
}
|
|
61198
|
+
function sessionsPayload(ctx, data, limit) {
|
|
61199
|
+
const db = getStorage(ctx);
|
|
61200
|
+
const span = /* @__PURE__ */ new Map();
|
|
61201
|
+
for (const c of data.calls) {
|
|
61202
|
+
const s = span.get(c.session_id);
|
|
61203
|
+
if (!s) span.set(c.session_id, { first: c.ts, last: c.ts, branch: c.git_branch });
|
|
61204
|
+
else {
|
|
61205
|
+
s.first = Math.min(s.first, c.ts);
|
|
61206
|
+
if (c.ts >= s.last) {
|
|
61207
|
+
s.last = c.ts;
|
|
61208
|
+
s.branch = c.git_branch ?? s.branch;
|
|
61209
|
+
}
|
|
61210
|
+
}
|
|
61211
|
+
}
|
|
61212
|
+
const rows = bySession(data.calls).map((g) => {
|
|
61213
|
+
const s = span.get(g.key);
|
|
61214
|
+
const row = db.sessionGet(g.key);
|
|
61215
|
+
return {
|
|
61216
|
+
session: g.key,
|
|
61217
|
+
startedAt: row?.started_at ?? null,
|
|
61218
|
+
firstCall: s?.first ?? 0,
|
|
61219
|
+
lastCall: s?.last ?? 0,
|
|
61220
|
+
branch: s?.branch ?? null,
|
|
61221
|
+
lastPrompt: row?.last_prompt ?? null,
|
|
61222
|
+
totals: g.totals
|
|
61223
|
+
};
|
|
61224
|
+
}).sort((a, b) => b.lastCall - a.lastCall);
|
|
61225
|
+
return { rows: rows.slice(0, limit) };
|
|
61226
|
+
}
|
|
61227
|
+
function turnsPayload(ctx, data, limit) {
|
|
61228
|
+
const db = getStorage(ctx);
|
|
61229
|
+
const sessionId = data.sessionId;
|
|
61230
|
+
const calls = data.scope === "session" ? data.calls : data.calls.filter((c) => c.session_id === sessionId);
|
|
61231
|
+
const events = data.scope === "session" ? data.contextEvents : db.contextEvents({ sessionId });
|
|
61232
|
+
const timeline = contextTimeline(calls, events);
|
|
61233
|
+
return {
|
|
61234
|
+
sessionId,
|
|
61235
|
+
timeline: timeline.length > limit ? timeline.slice(-limit) : timeline,
|
|
61236
|
+
compactions: events.length,
|
|
61237
|
+
totals: totals(calls)
|
|
61238
|
+
};
|
|
61239
|
+
}
|
|
61240
|
+
function usageView(ctx, view, data, limitArg = LIMIT_DEFAULT) {
|
|
61241
|
+
const { calls, now } = data;
|
|
61242
|
+
const limit = clampLimit(limitArg);
|
|
61243
|
+
switch (view) {
|
|
61244
|
+
case "summary":
|
|
61245
|
+
return summaryPayload(ctx, data);
|
|
61246
|
+
case "daily":
|
|
61247
|
+
return { rows: periods(calls, (ts) => dayKey(ts)), totals: totals(calls) };
|
|
61248
|
+
case "weekly":
|
|
61249
|
+
return { rows: periods(calls, (ts) => weekKey(ts)), totals: totals(calls) };
|
|
61250
|
+
case "monthly":
|
|
61251
|
+
return { rows: periods(calls, (ts) => monthKey(ts)), totals: totals(calls) };
|
|
61252
|
+
case "models":
|
|
61253
|
+
return {
|
|
61254
|
+
rows: modelRows(calls),
|
|
61255
|
+
byEffort: byEffort(calls),
|
|
61256
|
+
bySpeed: bySpeed(calls),
|
|
61257
|
+
agents: byAgent(calls)
|
|
61258
|
+
};
|
|
61259
|
+
case "sessions":
|
|
61260
|
+
return sessionsPayload(ctx, data, limit);
|
|
61261
|
+
case "blocks": {
|
|
61262
|
+
const blocks = billingBlocks(calls, now).reverse();
|
|
61263
|
+
return { rows: blocks.slice(0, limit), burn: burnRate(calls, now) };
|
|
61264
|
+
}
|
|
61265
|
+
case "turns":
|
|
61266
|
+
return turnsPayload(ctx, data, limit);
|
|
61267
|
+
case "tools": {
|
|
61268
|
+
const rows = topToolConsumers(data.toolUses, limit);
|
|
61269
|
+
return {
|
|
61270
|
+
rows,
|
|
61271
|
+
totalChars: data.toolUses.reduce((n, u) => n + (u.result_chars ?? 0), 0)
|
|
61272
|
+
};
|
|
61273
|
+
}
|
|
61274
|
+
case "insights": {
|
|
61275
|
+
const r = insightsFor(ctx, data);
|
|
61276
|
+
return { insights: sortInsights(r.insights), scores: r.scores };
|
|
61277
|
+
}
|
|
61278
|
+
case "savings":
|
|
61279
|
+
return savingsFor(ctx, data);
|
|
61280
|
+
}
|
|
61281
|
+
}
|
|
61282
|
+
function textTable(headers, rows, aligns) {
|
|
61283
|
+
const widths = headers.map((h, i) => Math.max(h.length, ...rows.map((r) => (r[i] ?? "").length)));
|
|
61284
|
+
const line = (cells) => ` ${cells.map((c, i) => {
|
|
61285
|
+
const w = widths[i] ?? c.length;
|
|
61286
|
+
return (aligns?.[i] ?? (i === 0 ? "l" : "r")) === "l" ? c.padEnd(w) : c.padStart(w);
|
|
61287
|
+
}).join(" ").trimEnd()}`;
|
|
61288
|
+
return [line(headers), ...rows.map(line)].join("\n");
|
|
61289
|
+
}
|
|
61290
|
+
var usd2 = (n) => formatUsd(n);
|
|
61291
|
+
var pct2 = (x) => `${Math.round(x * 100)}%`;
|
|
61292
|
+
var shortId = (id) => id.slice(0, 8);
|
|
61293
|
+
var hhmm = (ts) => new Date(ts).toISOString().slice(11, 19);
|
|
61294
|
+
var dateTime = (ts) => new Date(ts).toISOString().slice(0, 16).replace("T", " ");
|
|
61295
|
+
var shortModel = (m) => m.replace(/^claude-/, "").replace(/-\d{8}(\[1m\])?$/, "$1");
|
|
61296
|
+
function fmtGap(ms) {
|
|
61297
|
+
if (!(ms >= 36e5)) return "<1h";
|
|
61298
|
+
const h = Math.floor(ms / 36e5);
|
|
61299
|
+
const m = Math.floor(ms % 36e5 / 6e4);
|
|
61300
|
+
return `${h}:${String(m).padStart(2, "0")}`;
|
|
61301
|
+
}
|
|
61302
|
+
var burnLine = (b) => b.reliable ? `${usd2(b.usdPerHour)}/h \xB7 ${fmtTokens(Math.round(b.tokensPerMin))} tok/min \xB7 projected ${usd2(b.projectedBlockUsd ?? 0)}` : `burn n/a (needs ${BURN_MIN_CALLS}+ calls over ${BURN_MIN_MINUTES}+ min)`;
|
|
61303
|
+
var EST_NOTE = "est. at list price";
|
|
61304
|
+
function totalsLine(t) {
|
|
61305
|
+
const unpriced = t.unpricedCalls ? ` \xB7 ${t.unpricedCalls} unpriced` : "";
|
|
61306
|
+
return `${t.calls} calls \xB7 ${usd2(t.costUsd)}${unpriced} \xB7 in ${fmtTokens(t.input)} \xB7 out ${fmtTokens(t.output)} \xB7 cache read ${fmtTokens(t.cacheRead)} \xB7 cache write ${fmtTokens(t.cacheCreate5m + t.cacheCreate1h)} \xB7 ${pct2(t.cacheHitRate)} cache hits`;
|
|
61307
|
+
}
|
|
61308
|
+
function scopeTitle(p) {
|
|
61309
|
+
const s = p.scope === "session" ? `session ${shortId(p.sessionId)}` : "project";
|
|
61310
|
+
return p.since ? `${s} since ${dateTime(p.since)} UTC` : s;
|
|
61311
|
+
}
|
|
61312
|
+
function renderSummary(p) {
|
|
61313
|
+
const out = [`usage (${scopeTitle(p)}, ${EST_NOTE}): ${totalsLine(p.totals)}`];
|
|
61314
|
+
out.push(
|
|
61315
|
+
` cost: today ${usd2(p.today.costUsd)} (${p.today.calls} calls) \xB7 7d ${usd2(p.last7d.costUsd)} \xB7 30d ${usd2(p.last30d.costUsd)}`
|
|
61316
|
+
);
|
|
61317
|
+
out.push(
|
|
61318
|
+
` cache: ${pct2(p.cache.hitRate)} hit rate \xB7 ${fmtTokens(p.cache.cacheReads)} read \xB7 ${fmtTokens(p.cache.cacheWrites)} written \xB7 ${p.cache.coldCalls} cold call(s)${p.cache.share1h > 0 ? ` \xB7 ${pct2(p.cache.share1h)} 1h writes` : ""}`
|
|
61319
|
+
);
|
|
61320
|
+
if (p.models.length)
|
|
61321
|
+
out.push(
|
|
61322
|
+
` models: ${p.models.map(
|
|
61323
|
+
(m) => `${shortModel(m.model)} ${m.calls} calls ${usd2(m.costUsd)}${m.unpricedCalls ? ` (${m.unpricedCalls} unpriced)` : ""}`
|
|
61324
|
+
).join(" \xB7 ")}`
|
|
61325
|
+
);
|
|
61326
|
+
if (p.block)
|
|
61327
|
+
out.push(
|
|
61328
|
+
` active block: started ${hhmm(p.block.start)} UTC \xB7 ${p.block.calls} calls \xB7 ${usd2(p.block.costUsd)} \xB7 ${burnLine(p.burn)}`
|
|
61329
|
+
);
|
|
61330
|
+
else out.push(" active block: none (no calls in the current 5-hour window)");
|
|
61331
|
+
if (p.snapshot && (p.snapshot.five_hour_pct != null || p.snapshot.seven_day_pct != null)) {
|
|
61332
|
+
const plan = [
|
|
61333
|
+
p.snapshot.five_hour_pct != null ? `5h ${p.snapshot.five_hour_pct}%` : "",
|
|
61334
|
+
p.snapshot.seven_day_pct != null ? `7d ${p.snapshot.seven_day_pct}%` : "",
|
|
61335
|
+
p.snapshot.ctx_used_pct != null ? `ctx ${p.snapshot.ctx_used_pct}%` : ""
|
|
61336
|
+
].filter(Boolean);
|
|
61337
|
+
out.push(` plan: ${plan.join(" \xB7 ")}`);
|
|
61338
|
+
}
|
|
61339
|
+
if (p.insights.length) {
|
|
61340
|
+
out.push(" insights:");
|
|
61341
|
+
for (const i of p.insights) out.push(` [${i.severity}] ${i.title}: ${i.reason}`);
|
|
61342
|
+
} else out.push(" insights: none");
|
|
61343
|
+
out.push(
|
|
61344
|
+
` eminiar savings: ${fmtTokens(p.savings.savedTokens)} tokens kept out of context \u2248 ${usd2(p.savings.directUsd)} direct + ${usd2(p.savings.compoundingUsd)} compounding (see em_usage view:"savings")`
|
|
61345
|
+
);
|
|
61346
|
+
if (p.totals.unpricedCalls)
|
|
61347
|
+
out.push(
|
|
61348
|
+
` note: ${p.totals.unpricedCalls} call(s) use a model without a bundled price; add config.usage.pricing to price them`
|
|
61349
|
+
);
|
|
61350
|
+
return out.join("\n");
|
|
61351
|
+
}
|
|
61352
|
+
function renderPeriods(label, p) {
|
|
61353
|
+
if (!p.rows.length) return `${label}: no API calls recorded`;
|
|
61354
|
+
const rows = p.rows.map((r) => [
|
|
61355
|
+
r.key,
|
|
61356
|
+
String(r.totals.calls),
|
|
61357
|
+
usd2(r.totals.costUsd),
|
|
61358
|
+
fmtTokens(r.totals.input),
|
|
61359
|
+
fmtTokens(r.totals.output),
|
|
61360
|
+
fmtTokens(r.totals.cacheRead),
|
|
61361
|
+
fmtTokens(r.totals.cacheCreate5m + r.totals.cacheCreate1h),
|
|
61362
|
+
pct2(r.totals.cacheHitRate),
|
|
61363
|
+
r.models.map((m) => `${shortModel(m.model)} ${usd2(m.costUsd)}`).join(", ") + (r.totals.unpricedCalls ? ` (${r.totals.unpricedCalls} unpriced)` : "")
|
|
61364
|
+
]);
|
|
61365
|
+
return `${label}: ${totalsLine(p.totals)}
|
|
61366
|
+
${textTable(
|
|
61367
|
+
["period", "calls", "cost", "in", "out", "cache rd", "cache wr", "hits", "models"],
|
|
61368
|
+
rows,
|
|
61369
|
+
["l", "r", "r", "r", "r", "r", "r", "r", "l"]
|
|
61370
|
+
)}
|
|
61371
|
+
periods are local calendar days; cost ${EST_NOTE}`;
|
|
61372
|
+
}
|
|
61373
|
+
function renderModels(p) {
|
|
61374
|
+
if (!p.rows.length) return "models: no API calls recorded";
|
|
61375
|
+
const out = [
|
|
61376
|
+
"models:",
|
|
61377
|
+
textTable(
|
|
61378
|
+
["model", "calls", "cost", "share", "in", "out", "cache rd", "cache wr", "thinking"],
|
|
61379
|
+
p.rows.map((m) => [
|
|
61380
|
+
shortModel(m.model) + (m.unpricedCalls ? " (unpriced)" : ""),
|
|
61381
|
+
String(m.calls),
|
|
61382
|
+
usd2(m.costUsd),
|
|
61383
|
+
pct2(m.share),
|
|
61384
|
+
fmtTokens(m.input),
|
|
61385
|
+
fmtTokens(m.output),
|
|
61386
|
+
fmtTokens(m.cacheRead),
|
|
61387
|
+
fmtTokens(m.cacheWrite),
|
|
61388
|
+
fmtTokens(m.thinking)
|
|
61389
|
+
])
|
|
61390
|
+
)
|
|
61391
|
+
];
|
|
61392
|
+
const line = (title, groups) => ` ${title}: ${groups.map((g) => `${g.key} ${g.totals.calls} calls ${usd2(g.totals.costUsd)}`).join(" \xB7 ")}`;
|
|
61393
|
+
if (p.byEffort.length > 1) out.push(line("effort", p.byEffort));
|
|
61394
|
+
if (p.bySpeed.some((g) => g.key === "fast")) out.push(line("speed", p.bySpeed));
|
|
61395
|
+
if (p.agents.subagents.length)
|
|
61396
|
+
out.push(
|
|
61397
|
+
` subagents: ${p.agents.subagents.length} \xB7 ${usd2(p.agents.subagents.reduce((s, a) => s + a.totals.costUsd, 0))} (${pct2(p.agents.delegationShare)} of cost)`
|
|
61398
|
+
);
|
|
61399
|
+
out.push(` cost ${EST_NOTE}`);
|
|
61400
|
+
return out.join("\n");
|
|
61401
|
+
}
|
|
61402
|
+
function renderSessions(p) {
|
|
61403
|
+
if (!p.rows.length) return "sessions: no API calls recorded";
|
|
61404
|
+
return `sessions:
|
|
61405
|
+
${textTable(
|
|
61406
|
+
["session", "last call (UTC)", "branch", "calls", "cost", "in", "out", "hits", "prompt"],
|
|
61407
|
+
p.rows.map((s) => [
|
|
61408
|
+
shortId(s.session),
|
|
61409
|
+
dateTime(s.lastCall),
|
|
61410
|
+
s.branch ?? "",
|
|
61411
|
+
String(s.totals.calls),
|
|
61412
|
+
usd2(s.totals.costUsd),
|
|
61413
|
+
fmtTokens(s.totals.input),
|
|
61414
|
+
fmtTokens(s.totals.output),
|
|
61415
|
+
pct2(s.totals.cacheHitRate),
|
|
61416
|
+
(s.lastPrompt ?? "").replace(/\s+/g, " ").slice(0, 50)
|
|
61417
|
+
]),
|
|
61418
|
+
["l", "l", "l", "r", "r", "r", "r", "r", "l"]
|
|
61419
|
+
)}`;
|
|
61420
|
+
}
|
|
61421
|
+
function renderBlocks(p) {
|
|
61422
|
+
if (!p.rows.length) return "blocks: no API calls recorded";
|
|
61423
|
+
const rows = p.rows.map((b) => [
|
|
61424
|
+
`${dateTime(b.start)}${b.active ? " *" : ""}`,
|
|
61425
|
+
`${hhmm(b.firstCall)}\u2013${hhmm(b.lastCall)}`,
|
|
61426
|
+
String(b.calls),
|
|
61427
|
+
usd2(b.costUsd),
|
|
61428
|
+
fmtTokens(b.tokens.input + b.tokens.output + b.tokens.cacheCreate + b.tokens.cacheRead),
|
|
61429
|
+
b.gapBefore != null ? fmtGap(b.gapBefore) : "",
|
|
61430
|
+
b.models.map(shortModel).join(", ") + (b.unpricedCalls ? ` (${b.unpricedCalls} unpriced)` : "")
|
|
61431
|
+
]);
|
|
61432
|
+
const out = [
|
|
61433
|
+
"5-hour billing blocks (times in UTC, * = active):",
|
|
61434
|
+
textTable(["block (UTC)", "calls at (UTC)", "calls", "cost", "tokens", "gap", "models"], rows, [
|
|
61435
|
+
"l",
|
|
61436
|
+
"l",
|
|
61437
|
+
"r",
|
|
61438
|
+
"r",
|
|
61439
|
+
"r",
|
|
61440
|
+
"r",
|
|
61441
|
+
"l"
|
|
61442
|
+
])
|
|
61443
|
+
];
|
|
61444
|
+
if (p.rows.some((b) => b.active))
|
|
61445
|
+
out.push(
|
|
61446
|
+
p.burn.reliable ? ` burn: ${usd2(p.burn.usdPerHour)}/h \xB7 ${fmtTokens(Math.round(p.burn.tokensPerMin))} tok/min over ${Math.round(p.burn.windowMinutes)} min \xB7 projected ${usd2(p.burn.projectedBlockUsd ?? 0)} for the block` : ` ${burnLine(p.burn)}`
|
|
61447
|
+
);
|
|
61448
|
+
return out.join("\n");
|
|
61449
|
+
}
|
|
61450
|
+
function renderTurns(p) {
|
|
61451
|
+
if (!p.timeline.length) return `turns (session ${shortId(p.sessionId)}): no API calls recorded`;
|
|
61452
|
+
const rows = p.timeline.map((t) => [
|
|
61453
|
+
hhmm(t.ts),
|
|
61454
|
+
shortModel(t.model),
|
|
61455
|
+
fmtTokens(t.contextTokens),
|
|
61456
|
+
fmtTokens(t.output),
|
|
61457
|
+
usd2(t.costUsd),
|
|
61458
|
+
t.compactBefore ? "compacted before this call" : ""
|
|
61459
|
+
]);
|
|
61460
|
+
const shown = p.timeline.length;
|
|
61461
|
+
const count = shown < p.totals.calls ? `last ${shown} of ${p.totals.calls} calls` : `${shown} calls`;
|
|
61462
|
+
return `turns (session ${shortId(p.sessionId)}): ${count} \xB7 ${p.compactions} compaction(s) \xB7 ${usd2(p.totals.costUsd)}
|
|
61463
|
+
${textTable(
|
|
61464
|
+
["time (UTC)", "model", "context", "output", "cost", ""],
|
|
61465
|
+
rows,
|
|
61466
|
+
["l", "l", "r", "r", "r", "l"]
|
|
61467
|
+
)}`;
|
|
61468
|
+
}
|
|
61469
|
+
function renderTools(p) {
|
|
61470
|
+
if (!p.rows.length) return "tools: no tool results recorded";
|
|
61471
|
+
return `tool results in context (${fmtTokens(Math.ceil(p.totalChars / 4))} est. tokens total):
|
|
61472
|
+
${textTable(
|
|
61473
|
+
["tool", "calls", "chars", "est tokens", "avg chars", "errors"],
|
|
61474
|
+
p.rows.map((r) => [
|
|
61475
|
+
r.tool,
|
|
61476
|
+
String(r.calls),
|
|
61477
|
+
fmtTokens(r.chars),
|
|
61478
|
+
fmtTokens(r.estTokens),
|
|
61479
|
+
fmtTokens(r.avgChars),
|
|
61480
|
+
String(r.errors)
|
|
61481
|
+
])
|
|
61482
|
+
)}`;
|
|
61483
|
+
}
|
|
61484
|
+
function renderInsights(p) {
|
|
61485
|
+
const out = [];
|
|
61486
|
+
if (p.insights.length) {
|
|
61487
|
+
out.push(`insights (${p.insights.length}):`);
|
|
61488
|
+
for (const i of p.insights) {
|
|
61489
|
+
out.push(` [${i.severity}] ${i.title} (${i.id}, ${i.scope})`);
|
|
61490
|
+
out.push(` ${i.reason}`);
|
|
61491
|
+
if (i.recommendation) out.push(` \u2192 ${i.recommendation}`);
|
|
61492
|
+
if (i.estimatedSavingsUsd) out.push(` est. savings ${usd2(i.estimatedSavingsUsd)}`);
|
|
61493
|
+
}
|
|
61494
|
+
} else out.push("insights: nothing to flag");
|
|
61495
|
+
if (p.scores.length) {
|
|
61496
|
+
out.push("scores (0\u2013100, higher is better):");
|
|
61497
|
+
for (const s of p.scores) {
|
|
61498
|
+
out.push(` ${s.id.padEnd(16)} ${String(Math.round(s.value)).padStart(3)}`);
|
|
61499
|
+
for (const r of s.reasons) out.push(` - ${r}`);
|
|
61500
|
+
}
|
|
61501
|
+
}
|
|
61502
|
+
return out.join("\n");
|
|
61503
|
+
}
|
|
61504
|
+
function renderSavings(p) {
|
|
61505
|
+
const out = [
|
|
61506
|
+
`eminiar savings: ${fmtTokens(p.savedTokens)} tokens kept out of context`,
|
|
61507
|
+
` direct ${usd2(p.directUsd)} (never written to cache: cache-write price of ${shortModel(p.model)})`,
|
|
61508
|
+
` compounding ${usd2(p.compoundingUsd)} (never re-read on later turns: cache-read price \xD7 ${p.laterCallsPerSession.toFixed(1)} later calls per session)`,
|
|
61509
|
+
" assumptions:",
|
|
61510
|
+
...p.assumptions.map((a) => ` - ${a}`)
|
|
61511
|
+
];
|
|
61512
|
+
return out.join("\n");
|
|
61513
|
+
}
|
|
61514
|
+
function renderUsage(view, payload) {
|
|
61515
|
+
switch (view) {
|
|
61516
|
+
case "summary":
|
|
61517
|
+
return renderSummary(payload);
|
|
61518
|
+
case "daily":
|
|
61519
|
+
return renderPeriods("daily", payload);
|
|
61520
|
+
case "weekly":
|
|
61521
|
+
return renderPeriods("weekly (Monday)", payload);
|
|
61522
|
+
case "monthly":
|
|
61523
|
+
return renderPeriods("monthly", payload);
|
|
61524
|
+
case "models":
|
|
61525
|
+
return renderModels(payload);
|
|
61526
|
+
case "sessions":
|
|
61527
|
+
return renderSessions(payload);
|
|
61528
|
+
case "blocks":
|
|
61529
|
+
return renderBlocks(payload);
|
|
61530
|
+
case "turns":
|
|
61531
|
+
return renderTurns(payload);
|
|
61532
|
+
case "tools":
|
|
61533
|
+
return renderTools(payload);
|
|
61534
|
+
case "insights":
|
|
61535
|
+
return renderInsights(payload);
|
|
61536
|
+
case "savings":
|
|
61537
|
+
return renderSavings(payload);
|
|
61538
|
+
}
|
|
61539
|
+
}
|
|
61540
|
+
var emUsage = defineTool({
|
|
61541
|
+
name: "em_usage",
|
|
61542
|
+
description: "Token, cache and cost insights for Claude Code sessions, from the transcripts on this machine: cost today / 7d / 30d, cache hit rate, per-model and per-session breakdowns, 5-hour billing blocks with burn rate, the context timeline of a session with compactions, which tools put the most into context, detected insights with scores, and what eminiar's own tools saved. Cost is an estimate at list prices; everything stays local.",
|
|
61543
|
+
schema: {
|
|
61544
|
+
scope: external_exports.enum(["session", "project"]).optional().describe("default project"),
|
|
61545
|
+
view: external_exports.enum(USAGE_VIEWS).optional().describe("default summary"),
|
|
61546
|
+
since: external_exports.string().optional().describe("ISO date/time or relative: 30d, 12h, 2w (default: everything)"),
|
|
61547
|
+
limit: external_exports.number().int().positive().max(LIMIT_MAX).optional().describe(`rows for list views (${LIMIT_DEFAULT})`),
|
|
61548
|
+
json: external_exports.boolean().optional().describe("return the aggregate object instead of text")
|
|
61549
|
+
},
|
|
61550
|
+
handler: async (args, ctx) => {
|
|
61551
|
+
if (!usageEnabled(ctx))
|
|
61552
|
+
return text("usage insights are off (config.usage.enabled = false); nothing is ingested");
|
|
61553
|
+
const view = args.view ?? "summary";
|
|
61554
|
+
let since;
|
|
61555
|
+
try {
|
|
61556
|
+
since = parseSince(args.since);
|
|
61557
|
+
} catch (err) {
|
|
61558
|
+
return errorResult(err.message);
|
|
61559
|
+
}
|
|
61560
|
+
const limit = parseLimit(args.limit);
|
|
61561
|
+
if (typeof limit === "string") return errorResult(limit);
|
|
61562
|
+
const data = loadUsage(ctx, { scope: args.scope ?? "project", since });
|
|
61563
|
+
const payload = usageView(ctx, view, data, limit ?? LIMIT_DEFAULT);
|
|
61564
|
+
if (args.json) return text(JSON.stringify({ view, scope: data.scope, ...payload }));
|
|
61565
|
+
return text(renderUsage(view, payload));
|
|
61566
|
+
}
|
|
61567
|
+
});
|
|
61568
|
+
|
|
58863
61569
|
// src/tools/index.ts
|
|
58864
61570
|
var tools = [
|
|
58865
61571
|
emExecute,
|
|
@@ -58882,6 +61588,7 @@ var tools = [
|
|
|
58882
61588
|
emReindex,
|
|
58883
61589
|
emIndexStatus,
|
|
58884
61590
|
emStats,
|
|
61591
|
+
emUsage,
|
|
58885
61592
|
emPurge,
|
|
58886
61593
|
emDoctor
|
|
58887
61594
|
];
|