@botconnector/bccli 0.4.0-beta.44 → 0.4.0-beta.46
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{acp-U25MNDND.js → acp-YXBMSWYP.js} +2 -2
- package/dist/{args-MFTMBNRN.js → args-6MWRODGH.js} +4 -4
- package/dist/{chunk-ZGH4B7OU.js → chunk-2QNYWBDG.js} +21 -12
- package/dist/{chunk-TW5HZOTB.js → chunk-33OBF43H.js} +3 -3
- package/dist/{chunk-VO6MF6JJ.js → chunk-65OGHOGS.js} +1 -1
- package/dist/{chunk-Y3QKYORR.js → chunk-6RML6SN7.js} +6 -6
- package/dist/{chunk-PSRNKKGA.js → chunk-7PRH5NDO.js} +1 -1
- package/dist/{chunk-RMDW3OQ6.js → chunk-ASJRO2WF.js} +68 -12
- package/dist/{chunk-ZKMSTD6G.js → chunk-FIY424O5.js} +1 -1
- package/dist/{chunk-NVRNFR4S.js → chunk-GF6CRNT3.js} +1 -1
- package/dist/{chunk-QHNWI3OT.js → chunk-GI5GVU2L.js} +8 -2
- package/dist/{chunk-BQHKN7JI.js → chunk-IA5PRX4Z.js} +4 -1
- package/dist/{chunk-LTAQXU5Z.js → chunk-IHYNME3G.js} +1 -1
- package/dist/{chunk-I7GXIJCY.js → chunk-K3SWFB5S.js} +2 -2
- package/dist/{chunk-2ON3WIDP.js → chunk-KDVKD3QA.js} +4 -4
- package/dist/{chunk-GIGWZFCY.js → chunk-NGHOZRIM.js} +2 -1
- package/dist/{chunk-CYTRCZXI.js → chunk-OZPSNMUD.js} +26 -5
- package/dist/{chunk-25DSN3Y3.js → chunk-PBBD37E7.js} +4 -4
- package/dist/{chunk-PZC2Q6MJ.js → chunk-PQB4CGSL.js} +2 -2
- package/dist/chunk-QZKLZELZ.js +64 -0
- package/dist/cli.js +42 -39
- package/dist/{completion-6K4AXKK2.js → completion-D3HXSAQY.js} +1 -1
- package/dist/{config-Z4HSA435.js → config-BAAESXVB.js} +3 -3
- package/dist/{config-LCY27K3L.js → config-CJGPM5MJ.js} +2 -2
- package/dist/{ghBotCli-TJMKJU3E.js → ghBotCli-DCCFACAA.js} +14 -14
- package/dist/{integrationCli-33KU6GIK.js → integrationCli-EYAHT6VL.js} +2 -2
- package/dist/{login-LNF7ZV2S.js → login-FXTMDCCC.js} +3 -3
- package/dist/{mcpCli-TXFS3AOJ.js → mcpCli-57JTECJY.js} +5 -5
- package/dist/{modelsCli-IZSMIYVB.js → modelsCli-NH7W6MAZ.js} +6 -6
- package/dist/{oauthLogin-QGDYTOW7.js → oauthLogin-FTOPZW7X.js} +1 -1
- package/dist/{pluginsCli-RD4QW3DX.js → pluginsCli-UUPFG2CF.js} +2 -2
- package/dist/{prCli-PE7KIPMK.js → prCli-6OQ5ZUBO.js} +1 -1
- package/dist/providerCli-L6SQHIRK.js +15 -0
- package/dist/sdk.js +149 -14
- package/dist/{serve-HW5XZ5LF.js → serve-HPQ2K7UE.js} +15 -15
- package/dist/{serve-YNKE332Z.js → serve-HZZYEI3J.js} +3 -3
- package/dist/session-QIZKAZK3.js +12 -0
- package/dist/setup-4I5FXW37.js +24 -0
- package/dist/statsCli-G7GYLCPM.js +353 -0
- package/dist/{transferCli-RQBB6RVY.js → transferCli-7FW2SJY4.js} +4 -4
- package/dist/{ui-4ZTUMTYM.js → ui-KHNDA2RT.js} +120 -32
- package/dist/{update-34FDI6H3.js → update-UG7GWBXT.js} +2 -2
- package/package.json +1 -1
- package/dist/chunk-U6PCNS4B.js +0 -20
- package/dist/providerCli-AA3CHCGV.js +0 -15
- package/dist/session-KRV2UP5C.js +0 -12
- package/dist/setup-L4OPF3XW.js +0 -24
- package/dist/statsCli-4KYPVYCN.js +0 -109
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
estimateTokens
|
|
4
|
+
} from "./chunk-QZKLZELZ.js";
|
|
5
|
+
import {
|
|
6
|
+
t
|
|
7
|
+
} from "./chunk-GI5GVU2L.js";
|
|
8
|
+
|
|
9
|
+
// src/statsCli.ts
|
|
10
|
+
import { existsSync, readdirSync, readFileSync, statSync } from "fs";
|
|
11
|
+
import { join } from "path";
|
|
12
|
+
function isUsageRecord(value) {
|
|
13
|
+
if (!value || typeof value !== "object") return false;
|
|
14
|
+
const v = value;
|
|
15
|
+
return v.t === "usage" && typeof v.inputTokens === "number" && typeof v.outputTokens === "number";
|
|
16
|
+
}
|
|
17
|
+
function isMsgRecord(value) {
|
|
18
|
+
if (!value || typeof value !== "object") return false;
|
|
19
|
+
const v = value;
|
|
20
|
+
return v.t === "msg" && !!v.m;
|
|
21
|
+
}
|
|
22
|
+
function emptyRow(project) {
|
|
23
|
+
return {
|
|
24
|
+
project,
|
|
25
|
+
sessions: 0,
|
|
26
|
+
messages: 0,
|
|
27
|
+
inputTokens: 0,
|
|
28
|
+
outputTokens: 0,
|
|
29
|
+
usageInputTokens: 0,
|
|
30
|
+
usageOutputTokens: 0,
|
|
31
|
+
cachedInputTokens: 0,
|
|
32
|
+
cacheWriteTokens: 0,
|
|
33
|
+
toolCalls: {},
|
|
34
|
+
sessionTotals: [],
|
|
35
|
+
modelUsage: {}
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
function aggregateSessions(home, opts) {
|
|
39
|
+
const now = opts.now ?? Date.now();
|
|
40
|
+
const cutoff = now - opts.days * 864e5;
|
|
41
|
+
const root = join(home, "sessions");
|
|
42
|
+
if (!existsSync(root)) return [];
|
|
43
|
+
const byProject = /* @__PURE__ */ new Map();
|
|
44
|
+
for (const project of readdirSync(root)) {
|
|
45
|
+
const dir = join(root, project);
|
|
46
|
+
let st;
|
|
47
|
+
try {
|
|
48
|
+
st = statSync(dir);
|
|
49
|
+
} catch {
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
if (!st.isDirectory()) continue;
|
|
53
|
+
for (const file of readdirSync(dir)) {
|
|
54
|
+
if (!file.endsWith(".jsonl")) continue;
|
|
55
|
+
const path = join(dir, file);
|
|
56
|
+
let fst;
|
|
57
|
+
try {
|
|
58
|
+
fst = statSync(path);
|
|
59
|
+
} catch {
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
if (fst.mtimeMs < cutoff) continue;
|
|
63
|
+
let row = byProject.get(project);
|
|
64
|
+
if (!row) {
|
|
65
|
+
row = emptyRow(project);
|
|
66
|
+
byProject.set(project, row);
|
|
67
|
+
}
|
|
68
|
+
row.sessions++;
|
|
69
|
+
let usageIn = 0;
|
|
70
|
+
let usageOut = 0;
|
|
71
|
+
let cachedIn = 0;
|
|
72
|
+
let cacheWrite = 0;
|
|
73
|
+
let usageCount = 0;
|
|
74
|
+
let estIn = 0;
|
|
75
|
+
let estOut = 0;
|
|
76
|
+
let messages = 0;
|
|
77
|
+
const tools = {};
|
|
78
|
+
for (const line of readFileSync(path, "utf8").split("\n")) {
|
|
79
|
+
if (!line.trim()) continue;
|
|
80
|
+
let record;
|
|
81
|
+
try {
|
|
82
|
+
record = JSON.parse(line);
|
|
83
|
+
} catch {
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
if (isUsageRecord(record)) {
|
|
87
|
+
usageIn += record.inputTokens;
|
|
88
|
+
usageOut += record.outputTokens;
|
|
89
|
+
cachedIn += record.cachedInputTokens ?? 0;
|
|
90
|
+
cacheWrite += record.cacheWriteTokens ?? 0;
|
|
91
|
+
usageCount++;
|
|
92
|
+
if (record.model) {
|
|
93
|
+
let mt = row.modelUsage[record.model];
|
|
94
|
+
if (!mt) {
|
|
95
|
+
mt = { turns: 0, inputTokens: 0, outputTokens: 0, cachedInputTokens: 0, cacheWriteTokens: 0 };
|
|
96
|
+
row.modelUsage[record.model] = mt;
|
|
97
|
+
}
|
|
98
|
+
mt.turns++;
|
|
99
|
+
mt.inputTokens += record.inputTokens;
|
|
100
|
+
mt.outputTokens += record.outputTokens;
|
|
101
|
+
mt.cachedInputTokens += record.cachedInputTokens ?? 0;
|
|
102
|
+
mt.cacheWriteTokens += record.cacheWriteTokens ?? 0;
|
|
103
|
+
}
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
if (!isMsgRecord(record)) continue;
|
|
107
|
+
messages++;
|
|
108
|
+
if (record.m.role === "user" || record.m.role === "assistant") {
|
|
109
|
+
const text = typeof record.m.content === "string" ? record.m.content : JSON.stringify(record.m.content ?? "");
|
|
110
|
+
const tokens = estimateTokens(text);
|
|
111
|
+
if (record.m.role === "user") estIn += tokens;
|
|
112
|
+
else estOut += tokens;
|
|
113
|
+
}
|
|
114
|
+
if (record.m.role === "assistant") {
|
|
115
|
+
for (const call of record.m.tool_calls ?? []) {
|
|
116
|
+
const name = call?.function?.name;
|
|
117
|
+
if (typeof name === "string" && name) tools[name] = (tools[name] ?? 0) + 1;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
row.messages += messages;
|
|
122
|
+
if (usageCount > 0) {
|
|
123
|
+
row.usageInputTokens += usageIn;
|
|
124
|
+
row.usageOutputTokens += usageOut;
|
|
125
|
+
row.cachedInputTokens += cachedIn;
|
|
126
|
+
row.cacheWriteTokens += cacheWrite;
|
|
127
|
+
row.sessionTotals.push(usageIn + usageOut + cachedIn + cacheWrite);
|
|
128
|
+
} else {
|
|
129
|
+
row.inputTokens += estIn;
|
|
130
|
+
row.outputTokens += estOut;
|
|
131
|
+
row.sessionTotals.push(estIn + estOut);
|
|
132
|
+
}
|
|
133
|
+
for (const [name, count] of Object.entries(tools)) row.toolCalls[name] = (row.toolCalls[name] ?? 0) + count;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return [...byProject.values()].sort((a, b) => b.messages - a.messages || a.project.localeCompare(b.project));
|
|
137
|
+
}
|
|
138
|
+
function fmtCompact(n) {
|
|
139
|
+
if (n < 1e3) return String(Math.round(n));
|
|
140
|
+
const units = [
|
|
141
|
+
[1e9, "B"],
|
|
142
|
+
[1e6, "M"],
|
|
143
|
+
[1e3, "K"]
|
|
144
|
+
];
|
|
145
|
+
for (const [size, suffix] of units) {
|
|
146
|
+
if (n >= size) return `${(n / size).toFixed(1)}${suffix}`;
|
|
147
|
+
}
|
|
148
|
+
return String(Math.round(n));
|
|
149
|
+
}
|
|
150
|
+
function fmtCount(n) {
|
|
151
|
+
return n.toLocaleString("en-US");
|
|
152
|
+
}
|
|
153
|
+
function median(values) {
|
|
154
|
+
if (!values.length) return 0;
|
|
155
|
+
const sorted = [...values].sort((a, b) => a - b);
|
|
156
|
+
const mid = sorted.length >> 1;
|
|
157
|
+
return sorted.length % 2 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2;
|
|
158
|
+
}
|
|
159
|
+
var BOX_INNER = 46;
|
|
160
|
+
function border(left, right) {
|
|
161
|
+
return `${left}${"\u2500".repeat(BOX_INNER + 2)}${right}`;
|
|
162
|
+
}
|
|
163
|
+
function center(text) {
|
|
164
|
+
const pad = Math.max(0, BOX_INNER - text.length);
|
|
165
|
+
const left = Math.floor(pad / 2);
|
|
166
|
+
return `\u2502${" ".repeat(left)}${text}${" ".repeat(pad - left)}\u2502`;
|
|
167
|
+
}
|
|
168
|
+
function contentLine(content) {
|
|
169
|
+
return `\u2502${content}${" ".repeat(Math.max(1, BOX_INNER + 1 - content.length))}\u2502`;
|
|
170
|
+
}
|
|
171
|
+
function valueLine(label, value) {
|
|
172
|
+
return contentLine(` ${label}${" ".repeat(Math.max(1, BOX_INNER - 1 - label.length - value.length))}${value}`);
|
|
173
|
+
}
|
|
174
|
+
function box(title, rows) {
|
|
175
|
+
return [border("\u250C", "\u2510"), center(title), border("\u251C", "\u2524"), ...rows.map(([l, v]) => valueLine(l, v)), border("\u2514", "\u2518")];
|
|
176
|
+
}
|
|
177
|
+
function truncateName(name) {
|
|
178
|
+
return name.length > 18 ? `${name.slice(0, 16)}..` : name;
|
|
179
|
+
}
|
|
180
|
+
function toolBox(tools) {
|
|
181
|
+
const total = tools.reduce((sum, [, n]) => sum + n, 0);
|
|
182
|
+
const max = tools[0]?.[1] ?? 1;
|
|
183
|
+
const lines = tools.map(([name, count]) => {
|
|
184
|
+
const blocks = Math.max(1, Math.round(count / max * 20));
|
|
185
|
+
const pct = (count / total * 100).toFixed(1).padStart(5);
|
|
186
|
+
return contentLine(` ${truncateName(name).padEnd(16)} ${"\u2588".repeat(blocks).padEnd(20)} ${count} (${pct}%)`);
|
|
187
|
+
});
|
|
188
|
+
return [border("\u250C", "\u2510"), center("TOOL USAGE"), border("\u251C", "\u2524"), ...lines, border("\u2514", "\u2518")];
|
|
189
|
+
}
|
|
190
|
+
function modelBox(models) {
|
|
191
|
+
const lines = [];
|
|
192
|
+
for (let i = 0; i < models.length; i++) {
|
|
193
|
+
const [name, m] = models[i];
|
|
194
|
+
if (i > 0) lines.push(border("\u251C", "\u2524"));
|
|
195
|
+
lines.push(contentLine(` ${truncateName(name)}`));
|
|
196
|
+
lines.push(valueLine(" Turns", fmtCount(m.turns)));
|
|
197
|
+
lines.push(valueLine(" Input Tokens", fmtCompact(m.inputTokens)));
|
|
198
|
+
lines.push(valueLine(" Output Tokens", fmtCompact(m.outputTokens)));
|
|
199
|
+
lines.push(valueLine(" Cache Read", fmtCompact(m.cachedInputTokens)));
|
|
200
|
+
lines.push(valueLine(" Cache Write", fmtCompact(m.cacheWriteTokens)));
|
|
201
|
+
}
|
|
202
|
+
return [border("\u250C", "\u2510"), center("MODEL USAGE"), border("\u251C", "\u2524"), ...lines, border("\u2514", "\u2518")];
|
|
203
|
+
}
|
|
204
|
+
function projectSlug(cwd) {
|
|
205
|
+
return cwd.replace(/[^a-zA-Z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(-100) || "root";
|
|
206
|
+
}
|
|
207
|
+
function runStatsCommand(args, deps) {
|
|
208
|
+
let days = args.days ?? 7;
|
|
209
|
+
let toolsCap = args.tools;
|
|
210
|
+
let modelsFlag = args.models;
|
|
211
|
+
let projectFilter = args.project;
|
|
212
|
+
const rest = args.subArgs;
|
|
213
|
+
for (let i = 0; i < rest.length; i++) {
|
|
214
|
+
const a = rest[i];
|
|
215
|
+
if (a === "--days" || a === "--tools" || a === "--models" || a === "--project") {
|
|
216
|
+
const raw = rest[++i] ?? "";
|
|
217
|
+
if (a === "--days") {
|
|
218
|
+
const n = Number(raw);
|
|
219
|
+
if (!Number.isInteger(n) || n < 1) {
|
|
220
|
+
deps.err?.(t("--days must be a positive integer"));
|
|
221
|
+
return 1;
|
|
222
|
+
}
|
|
223
|
+
days = n;
|
|
224
|
+
} else if (a === "--tools") {
|
|
225
|
+
const n = Number(raw);
|
|
226
|
+
if (!Number.isInteger(n) || n < 1) {
|
|
227
|
+
deps.err?.(t("--tools must be a positive integer"));
|
|
228
|
+
return 1;
|
|
229
|
+
}
|
|
230
|
+
toolsCap = n;
|
|
231
|
+
} else if (a === "--models") {
|
|
232
|
+
if (raw === "" || raw === "all" || raw.startsWith("-")) {
|
|
233
|
+
modelsFlag = "all";
|
|
234
|
+
if (raw.startsWith("-")) i--;
|
|
235
|
+
} else {
|
|
236
|
+
const n = Number(raw);
|
|
237
|
+
if (!Number.isInteger(n) || n < 1) {
|
|
238
|
+
deps.err?.(t('--models must be a positive integer or "all"'));
|
|
239
|
+
return 1;
|
|
240
|
+
}
|
|
241
|
+
modelsFlag = raw;
|
|
242
|
+
}
|
|
243
|
+
} else {
|
|
244
|
+
projectFilter = raw;
|
|
245
|
+
}
|
|
246
|
+
} else if (a.startsWith("--days=")) {
|
|
247
|
+
const n = Number(a.slice("--days=".length));
|
|
248
|
+
if (!Number.isInteger(n) || n < 1) {
|
|
249
|
+
deps.err?.(t("--days must be a positive integer"));
|
|
250
|
+
return 1;
|
|
251
|
+
}
|
|
252
|
+
days = n;
|
|
253
|
+
} else if (a.startsWith("--tools=")) {
|
|
254
|
+
const n = Number(a.slice("--tools=".length));
|
|
255
|
+
if (!Number.isInteger(n) || n < 1) {
|
|
256
|
+
deps.err?.(t("--tools must be a positive integer"));
|
|
257
|
+
return 1;
|
|
258
|
+
}
|
|
259
|
+
toolsCap = n;
|
|
260
|
+
} else if (a.startsWith("--models=")) {
|
|
261
|
+
const raw = a.slice("--models=".length);
|
|
262
|
+
if (raw === "" || raw === "all") {
|
|
263
|
+
modelsFlag = "all";
|
|
264
|
+
} else {
|
|
265
|
+
const n = Number(raw);
|
|
266
|
+
if (!Number.isInteger(n) || n < 1) {
|
|
267
|
+
deps.err?.(t('--models must be a positive integer or "all"'));
|
|
268
|
+
return 1;
|
|
269
|
+
}
|
|
270
|
+
modelsFlag = raw;
|
|
271
|
+
}
|
|
272
|
+
} else if (a.startsWith("--project=")) {
|
|
273
|
+
projectFilter = a.slice("--project=".length);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
let rows = aggregateSessions(args.home, { days, now: args.now });
|
|
277
|
+
if (projectFilter !== void 0) {
|
|
278
|
+
const needle = projectFilter === "" ? projectSlug(args.cwd ?? "") : projectFilter;
|
|
279
|
+
rows = rows.filter((r) => r.project.includes(needle));
|
|
280
|
+
}
|
|
281
|
+
if (!rows.length) {
|
|
282
|
+
deps.out(t("no sessions in the last {days} day(s)", { days: String(days) }));
|
|
283
|
+
return 0;
|
|
284
|
+
}
|
|
285
|
+
const sessions = rows.reduce((s, r) => s + r.sessions, 0);
|
|
286
|
+
const messages = rows.reduce((s, r) => s + r.messages, 0);
|
|
287
|
+
const input = rows.reduce((s, r) => s + r.inputTokens + r.usageInputTokens, 0);
|
|
288
|
+
const output = rows.reduce((s, r) => s + r.outputTokens + r.usageOutputTokens, 0);
|
|
289
|
+
const cachedIn = rows.reduce((s, r) => s + r.cachedInputTokens, 0);
|
|
290
|
+
const cacheWrite = rows.reduce((s, r) => s + r.cacheWriteTokens, 0);
|
|
291
|
+
const totals = rows.flatMap((r) => r.sessionTotals);
|
|
292
|
+
const avg = totals.length ? totals.reduce((s, n) => s + n, 0) / totals.length : 0;
|
|
293
|
+
const tools = {};
|
|
294
|
+
for (const r of rows) for (const [name, count] of Object.entries(r.toolCalls)) tools[name] = (tools[name] ?? 0) + count;
|
|
295
|
+
const modelUsage = {};
|
|
296
|
+
for (const r of rows) {
|
|
297
|
+
for (const [name, m] of Object.entries(r.modelUsage)) {
|
|
298
|
+
let mt = modelUsage[name];
|
|
299
|
+
if (!mt) {
|
|
300
|
+
mt = { turns: 0, inputTokens: 0, outputTokens: 0, cachedInputTokens: 0, cacheWriteTokens: 0 };
|
|
301
|
+
modelUsage[name] = mt;
|
|
302
|
+
}
|
|
303
|
+
mt.turns += m.turns;
|
|
304
|
+
mt.inputTokens += m.inputTokens;
|
|
305
|
+
mt.outputTokens += m.outputTokens;
|
|
306
|
+
mt.cachedInputTokens += m.cachedInputTokens;
|
|
307
|
+
mt.cacheWriteTokens += m.cacheWriteTokens;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
for (const line of box("OVERVIEW", [
|
|
311
|
+
["Sessions", fmtCount(sessions)],
|
|
312
|
+
["Messages", fmtCount(messages)],
|
|
313
|
+
["Days", String(days)]
|
|
314
|
+
])) deps.out(line);
|
|
315
|
+
deps.out("");
|
|
316
|
+
for (const line of box("TOKENS", [
|
|
317
|
+
["Input", fmtCompact(input)],
|
|
318
|
+
["Output", fmtCompact(output)],
|
|
319
|
+
["Cache Read", fmtCompact(cachedIn)],
|
|
320
|
+
["Cache Write", fmtCompact(cacheWrite)],
|
|
321
|
+
["Avg Tokens/Session", fmtCompact(avg)],
|
|
322
|
+
["Median Tokens/Session", fmtCompact(median(totals))]
|
|
323
|
+
])) deps.out(line);
|
|
324
|
+
if (modelsFlag !== void 0) {
|
|
325
|
+
let modelEntries = Object.entries(modelUsage).sort(
|
|
326
|
+
(a, b) => b[1].inputTokens + b[1].outputTokens - (a[1].inputTokens + a[1].outputTokens) || a[0].localeCompare(b[0])
|
|
327
|
+
);
|
|
328
|
+
if (modelsFlag !== "all") modelEntries = modelEntries.slice(0, Number(modelsFlag));
|
|
329
|
+
if (modelEntries.length) {
|
|
330
|
+
deps.out("");
|
|
331
|
+
for (const line of modelBox(modelEntries)) deps.out(line);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
let toolEntries = Object.entries(tools).sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0]));
|
|
335
|
+
if (toolsCap !== void 0) toolEntries = toolEntries.slice(0, toolsCap);
|
|
336
|
+
if (toolEntries.length) {
|
|
337
|
+
deps.out("");
|
|
338
|
+
for (const line of toolBox(toolEntries)) deps.out(line);
|
|
339
|
+
}
|
|
340
|
+
deps.out("");
|
|
341
|
+
deps.out(t("sessions (last {days} day(s)):", { days: String(days) }));
|
|
342
|
+
for (const r of rows) {
|
|
343
|
+
const inputTotal = r.inputTokens + r.usageInputTokens;
|
|
344
|
+
const outputTotal = r.outputTokens + r.usageOutputTokens;
|
|
345
|
+
deps.out(`${r.project} ${r.sessions} ${t("session(s)")} ${r.messages} ${t("message(s)")} ${fmtCompact(inputTotal)}in/${fmtCompact(outputTotal)}out`);
|
|
346
|
+
}
|
|
347
|
+
return 0;
|
|
348
|
+
}
|
|
349
|
+
export {
|
|
350
|
+
aggregateSessions,
|
|
351
|
+
fmtCompact,
|
|
352
|
+
runStatsCommand
|
|
353
|
+
};
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
Session
|
|
4
|
-
} from "./chunk-
|
|
5
|
-
import "./chunk-
|
|
6
|
-
import "./chunk-
|
|
4
|
+
} from "./chunk-IA5PRX4Z.js";
|
|
5
|
+
import "./chunk-ASJRO2WF.js";
|
|
6
|
+
import "./chunk-FIY424O5.js";
|
|
7
7
|
import {
|
|
8
8
|
t
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-GI5GVU2L.js";
|
|
10
10
|
|
|
11
11
|
// src/transferCli.ts
|
|
12
12
|
import { copyFileSync, existsSync, mkdirSync, readdirSync, readFileSync, statSync, writeFileSync } from "fs";
|
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
import {
|
|
3
3
|
CATALOG,
|
|
4
4
|
fillTemplate
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-K3SWFB5S.js";
|
|
6
6
|
import {
|
|
7
7
|
MAX_VERIFY_ROUNDS,
|
|
8
8
|
runVerify,
|
|
9
9
|
toolLabel,
|
|
10
10
|
verifyFollowup,
|
|
11
11
|
worktreeListText
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-33OBF43H.js";
|
|
13
13
|
import {
|
|
14
14
|
BUILTIN_AGENTS,
|
|
15
15
|
appendMemory,
|
|
@@ -21,7 +21,7 @@ import {
|
|
|
21
21
|
renderBackgroundTasks,
|
|
22
22
|
runHooks,
|
|
23
23
|
validateGlobPattern
|
|
24
|
-
} from "./chunk-
|
|
24
|
+
} from "./chunk-7PRH5NDO.js";
|
|
25
25
|
import "./chunk-PSO7R5AD.js";
|
|
26
26
|
import {
|
|
27
27
|
nextMode
|
|
@@ -30,39 +30,39 @@ import {
|
|
|
30
30
|
hasKey,
|
|
31
31
|
providerName,
|
|
32
32
|
writeGlobalConfig
|
|
33
|
-
} from "./chunk-
|
|
33
|
+
} from "./chunk-65OGHOGS.js";
|
|
34
34
|
import {
|
|
35
35
|
Session
|
|
36
|
-
} from "./chunk-
|
|
36
|
+
} from "./chunk-IA5PRX4Z.js";
|
|
37
37
|
import {
|
|
38
38
|
contentText
|
|
39
|
-
} from "./chunk-
|
|
39
|
+
} from "./chunk-ASJRO2WF.js";
|
|
40
40
|
import {
|
|
41
41
|
REASONING_LEVELS,
|
|
42
42
|
assertReasoningSupported,
|
|
43
43
|
parseReasoningLevel,
|
|
44
44
|
supportedReasoningLevels
|
|
45
|
-
} from "./chunk-
|
|
45
|
+
} from "./chunk-FIY424O5.js";
|
|
46
46
|
import {
|
|
47
47
|
addGlobalServer,
|
|
48
48
|
globalMcpPath,
|
|
49
49
|
readMcpFile,
|
|
50
50
|
removeGlobalServer
|
|
51
|
-
} from "./chunk-
|
|
51
|
+
} from "./chunk-PQB4CGSL.js";
|
|
52
52
|
import {
|
|
53
53
|
ACCENT,
|
|
54
54
|
PRESETS,
|
|
55
55
|
color,
|
|
56
56
|
matchKeybind,
|
|
57
57
|
removeCredential
|
|
58
|
-
} from "./chunk-
|
|
58
|
+
} from "./chunk-NGHOZRIM.js";
|
|
59
59
|
import {
|
|
60
60
|
getLanguage,
|
|
61
61
|
locale,
|
|
62
62
|
parseLang,
|
|
63
63
|
setLanguage,
|
|
64
64
|
t
|
|
65
|
-
} from "./chunk-
|
|
65
|
+
} from "./chunk-GI5GVU2L.js";
|
|
66
66
|
|
|
67
67
|
// src/ui/index.tsx
|
|
68
68
|
import { render } from "ink";
|
|
@@ -72,7 +72,7 @@ import { execFile as execFile3 } from "child_process";
|
|
|
72
72
|
import { existsSync as existsSync2, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
73
73
|
import { homedir, tmpdir } from "os";
|
|
74
74
|
import { dirname, join as join3, relative, resolve } from "path";
|
|
75
|
-
import { Box as Box13, Static, Text as Text16, useApp, useInput as useInput9 } from "ink";
|
|
75
|
+
import { Box as Box13, Static, Text as Text16, measureElement, useApp, useInput as useInput9, useStdin, useStdout as useStdout2 } from "ink";
|
|
76
76
|
import { useCallback, useEffect as useEffect3, useRef, useState as useState8 } from "react";
|
|
77
77
|
|
|
78
78
|
// src/commands.ts
|
|
@@ -782,11 +782,46 @@ function EditableText({ state, mask = false, showCursor = true }) {
|
|
|
782
782
|
// src/ui/focusReport.ts
|
|
783
783
|
var isFocusReport = (input) => input === "[I" || input === "[O";
|
|
784
784
|
|
|
785
|
+
// src/ui/thinkingMouse.ts
|
|
786
|
+
function parseMousePress(value) {
|
|
787
|
+
const start = value.indexOf("\x1B[<");
|
|
788
|
+
if (start < 0) return void 0;
|
|
789
|
+
const match = /^<(\d+);(\d+);(\d+)M/.exec(value.slice(start + 2));
|
|
790
|
+
if (!match) return void 0;
|
|
791
|
+
const button = Number(match[1]);
|
|
792
|
+
if (button !== 0) return void 0;
|
|
793
|
+
const x = Number(match[2]);
|
|
794
|
+
const y = Number(match[3]);
|
|
795
|
+
if (x < 1 || y < 1) return void 0;
|
|
796
|
+
return { x, y };
|
|
797
|
+
}
|
|
798
|
+
function parseCursorPosition(value) {
|
|
799
|
+
const start = value.indexOf("\x1B[");
|
|
800
|
+
if (start < 0) return void 0;
|
|
801
|
+
const match = /^(\d+);(\d+)R/.exec(value.slice(start + 2));
|
|
802
|
+
if (!match) return void 0;
|
|
803
|
+
const row = Number(match[1]);
|
|
804
|
+
const col = Number(match[2]);
|
|
805
|
+
if (row < 1 || col < 1) return void 0;
|
|
806
|
+
return { row, col };
|
|
807
|
+
}
|
|
808
|
+
function clickedThinking(click, cursorRow, rootHeight, target) {
|
|
809
|
+
if (!rootHeight || !target.width || !target.height) return false;
|
|
810
|
+
const origin = cursorRow - rootHeight + 1;
|
|
811
|
+
const row = origin + target.y;
|
|
812
|
+
const col = target.x + 1;
|
|
813
|
+
return click.y >= row && click.y < row + target.height && click.x >= col && click.x < col + Math.min(target.width, 46);
|
|
814
|
+
}
|
|
815
|
+
function isMouseControlInput(input) {
|
|
816
|
+
return /^\[<(?:\d+;){2}\d+[mM]$/.test(input) || /^\[(?:\d+;)\d+R$/.test(input);
|
|
817
|
+
}
|
|
818
|
+
|
|
785
819
|
// src/ui/LinePrompt.tsx
|
|
786
820
|
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
787
821
|
function LinePrompt({ label, mask, onSubmit, onCancel }) {
|
|
788
822
|
const [line, setLine] = useState({ value: "", cursor: 0 });
|
|
789
823
|
useInput((input, key) => {
|
|
824
|
+
if (isMouseControlInput(input)) return;
|
|
790
825
|
if (key.return) onSubmit(line.value.trim());
|
|
791
826
|
else if (key.escape) onCancel();
|
|
792
827
|
else if (applyEditKey(line, input, key)) setLine((current) => applyEditKey(current, input, key) ?? current);
|
|
@@ -1080,6 +1115,7 @@ function PromptInput({ disabled, history, cwd, onSubmit, extraCommands, injected
|
|
|
1080
1115
|
};
|
|
1081
1116
|
useInput6(
|
|
1082
1117
|
(input, key) => {
|
|
1118
|
+
if (isMouseControlInput(input)) return;
|
|
1083
1119
|
if (suggestions.length) {
|
|
1084
1120
|
if (key.upArrow) {
|
|
1085
1121
|
setSelected(Math.max(0, highlighted - 1));
|
|
@@ -1494,7 +1530,11 @@ function App({ runtime, initialPrompt, version }) {
|
|
|
1494
1530
|
const [listPicker, setListPicker] = useState8(null);
|
|
1495
1531
|
const [planAsk, setPlanAsk] = useState8(null);
|
|
1496
1532
|
const [queued, setQueued] = useState8([]);
|
|
1497
|
-
const [showThinking, setShowThinking] = useState8(
|
|
1533
|
+
const [showThinking, setShowThinking] = useState8(runtime.config.thinkingVisible === true);
|
|
1534
|
+
const { stdin } = useStdin();
|
|
1535
|
+
const { stdout } = useStdout2();
|
|
1536
|
+
const liveRootRef = useRef(null);
|
|
1537
|
+
const thinkingTargetRef = useRef(null);
|
|
1498
1538
|
const [injected, setInjected] = useState8();
|
|
1499
1539
|
useEffect3(() => runtime.todos.subscribe(setTodos), [runtime]);
|
|
1500
1540
|
const extraCommands = [
|
|
@@ -2043,6 +2083,55 @@ ARGUMENTS: ${slash.args}`);
|
|
|
2043
2083
|
submit(initialPrompt);
|
|
2044
2084
|
}
|
|
2045
2085
|
}, [initialPrompt, submit]);
|
|
2086
|
+
const toggleThinking = useCallback(() => {
|
|
2087
|
+
const bind = runtime.config.keybinds.thinking.spec;
|
|
2088
|
+
const next = !showThinking;
|
|
2089
|
+
const toggled = next ? t("Thinking shown ({bind} to hide).", { bind }) : t("Thinking hidden ({bind} to show).", { bind });
|
|
2090
|
+
setShowThinking(next);
|
|
2091
|
+
try {
|
|
2092
|
+
writeGlobalConfig({ thinkingVisible: next }, runtime.env);
|
|
2093
|
+
} catch (error) {
|
|
2094
|
+
notice(t("Could not save thinking visibility: {error}", { error: error.message }), "warn");
|
|
2095
|
+
}
|
|
2096
|
+
setTranscript((t2) => {
|
|
2097
|
+
const last = [...t2.done, ...t2.live].findLast((e) => e.kind === "thinking");
|
|
2098
|
+
const reprint = next && last?.kind === "thinking" && t2.done.includes(last);
|
|
2099
|
+
return {
|
|
2100
|
+
...t2,
|
|
2101
|
+
done: [
|
|
2102
|
+
...t2.done,
|
|
2103
|
+
...reprint ? [entry({ kind: "thinking", text: last.text, open: true })] : [],
|
|
2104
|
+
entry({ kind: "notice", text: toggled, tone: "info" })
|
|
2105
|
+
]
|
|
2106
|
+
};
|
|
2107
|
+
});
|
|
2108
|
+
}, [runtime, showThinking, notice]);
|
|
2109
|
+
useEffect3(() => {
|
|
2110
|
+
if (!busy || pending || !stdin.isTTY || !stdout.isTTY || !thinkingTargetRef.current) return;
|
|
2111
|
+
let click;
|
|
2112
|
+
const onData = (data) => {
|
|
2113
|
+
const value = String(data);
|
|
2114
|
+
const press = parseMousePress(value);
|
|
2115
|
+
if (press) {
|
|
2116
|
+
click = press;
|
|
2117
|
+
stdout.write("\x1B[6n");
|
|
2118
|
+
return;
|
|
2119
|
+
}
|
|
2120
|
+
const cursor = parseCursorPosition(value);
|
|
2121
|
+
if (!cursor || !click || !liveRootRef.current || !thinkingTargetRef.current) return;
|
|
2122
|
+
const current = click;
|
|
2123
|
+
click = void 0;
|
|
2124
|
+
const root = measureElement(liveRootRef.current);
|
|
2125
|
+
const target = measureElement(thinkingTargetRef.current);
|
|
2126
|
+
if (clickedThinking(current, cursor.row, root.height, target)) toggleThinking();
|
|
2127
|
+
};
|
|
2128
|
+
stdin.on("data", onData);
|
|
2129
|
+
stdout.write("\x1B[?1000h\x1B[?1006h");
|
|
2130
|
+
return () => {
|
|
2131
|
+
stdin.off("data", onData);
|
|
2132
|
+
stdout.write("\x1B[?1000l\x1B[?1006l");
|
|
2133
|
+
};
|
|
2134
|
+
}, [busy, pending, stdin, stdout, toggleThinking]);
|
|
2046
2135
|
useInput9((input, key) => {
|
|
2047
2136
|
if (key.tab && key.shift) {
|
|
2048
2137
|
const next = nextMode(runtime.agent.permissions.mode);
|
|
@@ -2066,22 +2155,7 @@ ARGUMENTS: ${slash.args}`);
|
|
|
2066
2155
|
}
|
|
2067
2156
|
})();
|
|
2068
2157
|
} else if (matchKeybind(runtime.config.keybinds.thinking, input, key)) {
|
|
2069
|
-
|
|
2070
|
-
const next = !showThinking;
|
|
2071
|
-
const toggled = next ? t("Thinking shown ({bind} to hide).", { bind }) : t("Thinking hidden ({bind} to show).", { bind });
|
|
2072
|
-
setShowThinking(next);
|
|
2073
|
-
setTranscript((t2) => {
|
|
2074
|
-
const last = [...t2.done, ...t2.live].findLast((e) => e.kind === "thinking");
|
|
2075
|
-
const reprint = next && last?.kind === "thinking" && t2.done.includes(last);
|
|
2076
|
-
return {
|
|
2077
|
-
...t2,
|
|
2078
|
-
done: [
|
|
2079
|
-
...t2.done,
|
|
2080
|
-
...reprint ? [entry({ kind: "thinking", text: last.text, open: true })] : [],
|
|
2081
|
-
entry({ kind: "notice", text: toggled, tone: "info" })
|
|
2082
|
-
]
|
|
2083
|
-
};
|
|
2084
|
-
});
|
|
2158
|
+
toggleThinking();
|
|
2085
2159
|
} else if (matchKeybind(runtime.config.keybinds.toolOutput, input, key) && lastTool.current) {
|
|
2086
2160
|
const t2 = lastTool.current;
|
|
2087
2161
|
notice(`\u23BF ${t2.tool} ${t2.target}
|
|
@@ -2183,10 +2257,24 @@ ${t2.output}`);
|
|
|
2183
2257
|
return /* @__PURE__ */ jsx16(Box13, { marginTop: 1, children: /* @__PURE__ */ jsx16(Text16, { color: e.tone === "error" ? color("red") : e.tone === "warn" ? color("yellow") : void 0, dimColor: e.tone === "info", children: e.text }) }, e.id);
|
|
2184
2258
|
}
|
|
2185
2259
|
};
|
|
2186
|
-
return /* @__PURE__ */ jsxs12(Box13, { flexDirection: "column", children: [
|
|
2260
|
+
return /* @__PURE__ */ jsxs12(Box13, { ref: liveRootRef, flexDirection: "column", children: [
|
|
2187
2261
|
/* @__PURE__ */ jsx16(Static, { items: transcript.done, children: renderEntry }),
|
|
2188
2262
|
transcript.live.map(renderEntry),
|
|
2189
|
-
busy && !pending && !planAsk ? /* @__PURE__ */
|
|
2263
|
+
busy && !pending && !planAsk ? /* @__PURE__ */ jsxs12(Box13, { ref: thinkingTargetRef, marginTop: 1, flexDirection: "column", children: [
|
|
2264
|
+
/* @__PURE__ */ jsxs12(Box13, { flexDirection: "row", children: [
|
|
2265
|
+
/* @__PURE__ */ jsx16(Spinner, { label: cancelling ? t("Cancelling") : t("Thinking"), startedAt }),
|
|
2266
|
+
/* @__PURE__ */ jsxs12(Text16, { dimColor: true, children: [
|
|
2267
|
+
" \xB7 ",
|
|
2268
|
+
runtime.config.keybinds.thinking.spec,
|
|
2269
|
+
" \xB7 ",
|
|
2270
|
+
t("click to toggle")
|
|
2271
|
+
] })
|
|
2272
|
+
] }),
|
|
2273
|
+
showThinking ? /* @__PURE__ */ jsxs12(Box13, { flexDirection: "column", paddingLeft: 2, children: [
|
|
2274
|
+
/* @__PURE__ */ jsx16(Text16, { dimColor: true, children: t("Model: {model} \xB7 reasoning: {level}", { model: runtime.modelRef, level: reasoning }) }),
|
|
2275
|
+
![...transcript.done, ...transcript.live].some((e) => e.kind === "thinking" && !!e.text.trim()) ? /* @__PURE__ */ jsx16(Text16, { dimColor: true, children: t("No reasoning summary received yet; tool activity remains visible above.") }) : null
|
|
2276
|
+
] }) : null
|
|
2277
|
+
] }) : null,
|
|
2190
2278
|
pending ? /* @__PURE__ */ jsx16(PermissionPrompt, { request: pending.request, onAnswer: answer }, pending.id) : null,
|
|
2191
2279
|
picker ? /* @__PURE__ */ jsx16(
|
|
2192
2280
|
ModelPicker,
|
|
@@ -2239,7 +2327,7 @@ ${t2.output}`);
|
|
|
2239
2327
|
import { jsx as jsx17 } from "react/jsx-runtime";
|
|
2240
2328
|
async function startInteractive(rt, opts) {
|
|
2241
2329
|
if (opts.resume) {
|
|
2242
|
-
const { Session: Session2 } = await import("./session-
|
|
2330
|
+
const { Session: Session2 } = await import("./session-QIZKAZK3.js");
|
|
2243
2331
|
const list = Session2.list(rt.home, rt.cwd).slice(0, 20);
|
|
2244
2332
|
if (!list.length) console.log(t("No sessions in this folder yet; starting a new one."));
|
|
2245
2333
|
else {
|
|
@@ -2254,7 +2342,7 @@ async function startInteractive(rt, opts) {
|
|
|
2254
2342
|
if (chosen) rt.resume(chosen.session);
|
|
2255
2343
|
}
|
|
2256
2344
|
}
|
|
2257
|
-
const { serversToStart, setProjectTrust } = await import("./config-
|
|
2345
|
+
const { serversToStart, setProjectTrust } = await import("./config-BAAESXVB.js");
|
|
2258
2346
|
const plan = serversToStart(rt.home, rt.cwd);
|
|
2259
2347
|
if (plan.needTrust.length) {
|
|
2260
2348
|
const { createInterface } = await import("readline/promises");
|
package/package.json
CHANGED
package/dist/chunk-U6PCNS4B.js
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
// src/tokenBudget.ts
|
|
4
|
-
var estimateTokens = (text) => Math.ceil(text.length / 4);
|
|
5
|
-
var estimateMessages = (messages) => estimateTokens(JSON.stringify(messages));
|
|
6
|
-
function trimToFit(messages, maxTokens) {
|
|
7
|
-
let out = messages;
|
|
8
|
-
while (estimateMessages(out) > maxTokens) {
|
|
9
|
-
const boundary = out.findIndex((m, i) => i > 0 && m.role === "user");
|
|
10
|
-
if (boundary === -1) return out;
|
|
11
|
-
out = out.slice(boundary);
|
|
12
|
-
}
|
|
13
|
-
return out;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export {
|
|
17
|
-
estimateTokens,
|
|
18
|
-
estimateMessages,
|
|
19
|
-
trimToFit
|
|
20
|
-
};
|