@botconnector/bccli 0.5.0-rc.4 → 0.5.1
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/LICENSE +21 -203
- package/README.en.md +253 -0
- package/README.md +250 -0
- package/dist/abort.d.ts +5 -0
- package/dist/acp-HWHF25KU.js +221 -0
- package/dist/acp.d.ts +44 -0
- package/dist/agent.d.ts +158 -0
- package/dist/agents.d.ts +4 -0
- package/dist/args-XA77XUMD.js +13 -0
- package/dist/args.d.ts +59 -0
- package/dist/budget.d.ts +26 -0
- package/dist/checkpoints.d.ts +35 -0
- package/dist/chunk-2IP5F3EK.js +56 -0
- package/dist/chunk-5CVHTYBQ.js +270 -0
- package/dist/chunk-6LO6YRDY.js +216 -0
- package/dist/chunk-6PJRWKVI.js +50 -0
- package/dist/chunk-6YRTPXGY.js +8 -0
- package/dist/chunk-7OTFZK4B.js +159 -0
- package/dist/chunk-7U7CMKKI.js +861 -0
- package/dist/chunk-A3NBWIEQ.js +2706 -0
- package/dist/chunk-FY543EQW.js +50 -0
- package/dist/chunk-IDZLHEVL.js +524 -0
- package/dist/chunk-IF5RNXBI.js +341 -0
- package/dist/chunk-M5T4AM4I.js +256 -0
- package/dist/chunk-NGFWBVHD.js +71 -0
- package/dist/chunk-OCEQ2TRJ.js +1075 -0
- package/dist/chunk-OK2USAXX.js +88 -0
- package/dist/chunk-PSO7R5AD.js +10 -0
- package/dist/chunk-RLSQAZBT.js +78 -0
- package/dist/chunk-VPFCIUC6.js +91 -0
- package/dist/chunk-YGBTNDPK.js +202 -0
- package/dist/chunk-YYTFJUO2.js +148 -0
- package/dist/chunk-ZXWRLN7E.js +378 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +251 -0
- package/dist/clipboardImage.d.ts +17 -0
- package/dist/commands.d.ts +16 -0
- package/dist/completion-IRVEVW6H.js +125 -0
- package/dist/completion.d.ts +14 -0
- package/dist/config-OVWCUVY7.js +25 -0
- package/dist/config-S3CZUIBB.js +34 -0
- package/dist/config.d.ts +99 -0
- package/dist/context.d.ts +13 -0
- package/dist/contextWindow.d.ts +3 -0
- package/dist/diff.d.ts +7 -0
- package/dist/exit.d.ts +9 -0
- package/dist/extensions.d.ts +34 -0
- package/dist/ghBotCli-FYKJDJOD.js +680 -0
- package/dist/ghBotCli.d.ts +9 -0
- package/dist/ghBotDispatch.d.ts +68 -0
- package/dist/ghBotRunner.d.ts +82 -0
- package/dist/ghBotServe.d.ts +21 -0
- package/dist/ghBotWebhook.d.ts +45 -0
- package/dist/hooks.d.ts +27 -0
- package/dist/i18n/id.d.ts +1 -0
- package/dist/i18n.d.ts +16 -0
- package/dist/integrationCli-GY73S2K7.js +876 -0
- package/dist/integrationCli.d.ts +9 -0
- package/dist/keybinds.d.ts +22 -0
- package/dist/login-KSN63EM4.js +18 -0
- package/dist/login.d.ts +21 -0
- package/dist/lsp.d.ts +73 -0
- package/dist/mcp/catalog.d.ts +14 -0
- package/dist/mcp/config.d.ts +36 -0
- package/dist/mcp/manager.d.ts +52 -0
- package/dist/mcp/oauth.d.ts +82 -0
- package/dist/mcpCli-VWSCCDWT.js +113 -0
- package/dist/mcpCli.d.ts +3 -0
- package/dist/mcpServer/serve.d.ts +8 -0
- package/dist/mcpServer/server.d.ts +19 -0
- package/dist/models.d.ts +13 -0
- package/dist/modelsCli-4Q6KFWV2.js +36 -0
- package/dist/modelsCli.d.ts +15 -0
- package/dist/npmRunner-LU43GM3W.js +46 -0
- package/dist/npmRunner.d.ts +21 -0
- package/dist/oauthLogin-JWXQZTMZ.js +210 -0
- package/dist/oauthLogin.d.ts +47 -0
- package/dist/onboarding.d.ts +6 -0
- package/dist/permissions.d.ts +30 -0
- package/dist/pluginSpec.d.ts +25 -0
- package/dist/plugins.d.ts +45 -0
- package/dist/pluginsCli-7XWDLJEL.js +288 -0
- package/dist/pluginsCli.d.ts +16 -0
- package/dist/prCli-FH7N26D4.js +94 -0
- package/dist/prCli.d.ts +46 -0
- package/dist/presets.d.ts +8 -0
- package/dist/print.d.ts +17 -0
- package/dist/provider.d.ts +145 -0
- package/dist/providerCli-YMG4G56R.js +15 -0
- package/dist/providerCli.d.ts +17 -0
- package/dist/providers.d.ts +6 -0
- package/dist/reasoning.d.ts +12 -0
- package/dist/repomap.d.ts +10 -0
- package/dist/sdk.d.ts +61 -0
- package/dist/sdk.js +6532 -0
- package/dist/serve-HSFF6MGB.js +178 -0
- package/dist/serve-R2IHP26A.js +532 -0
- package/dist/serve.d.ts +37 -0
- package/dist/serveHeartbeat.d.ts +14 -0
- package/dist/serveProtocol.d.ts +47 -0
- package/dist/session-YM36TGU7.js +12 -0
- package/dist/session.d.ts +33 -0
- package/dist/setup-3YB6EKYX.js +24 -0
- package/dist/setup.d.ts +79 -0
- package/dist/slash/copy.d.ts +1 -0
- package/dist/slash/diff.d.ts +1 -0
- package/dist/slash/doctor.d.ts +18 -0
- package/dist/slash/export.d.ts +2 -0
- package/dist/slash/info.d.ts +46 -0
- package/dist/slash/memory.d.ts +10 -0
- package/dist/statsCli-I4TSDU3X.js +353 -0
- package/dist/statsCli.d.ts +62 -0
- package/dist/textToolCalls.d.ts +6 -0
- package/dist/thinking.d.ts +17 -0
- package/dist/tokenBudget.d.ts +22 -0
- package/dist/tools/bash.d.ts +54 -0
- package/dist/tools/diagnostics.d.ts +13 -0
- package/dist/tools/edit.d.ts +12 -0
- package/dist/tools/fetch.d.ts +41 -0
- package/dist/tools/glob.d.ts +5 -0
- package/dist/tools/globSafety.d.ts +7 -0
- package/dist/tools/grep.d.ts +25 -0
- package/dist/tools/ignore.d.ts +2 -0
- package/dist/tools/index.d.ts +12 -0
- package/dist/tools/memory.d.ts +13 -0
- package/dist/tools/paths.d.ts +4 -0
- package/dist/tools/plan.d.ts +10 -0
- package/dist/tools/read.d.ts +16 -0
- package/dist/tools/repomap.d.ts +2 -0
- package/dist/tools/sandbox.d.ts +15 -0
- package/dist/tools/shell.d.ts +18 -0
- package/dist/tools/skill.d.ts +3 -0
- package/dist/tools/task.d.ts +28 -0
- package/dist/tools/todo.d.ts +13 -0
- package/dist/tools/types.d.ts +52 -0
- package/dist/tools/websearch.d.ts +9 -0
- package/dist/tools/write.d.ts +5 -0
- package/dist/transferCli-3B4LMOYI.js +73 -0
- package/dist/transferCli.d.ts +23 -0
- package/dist/ui/App.d.ts +7 -0
- package/dist/ui/DiffView.d.ts +3 -0
- package/dist/ui/EditableText.d.ts +7 -0
- package/dist/ui/LinePrompt.d.ts +6 -0
- package/dist/ui/ListPicker.d.ts +11 -0
- package/dist/ui/Markdown.d.ts +8 -0
- package/dist/ui/McpMenu.d.ts +11 -0
- package/dist/ui/ModelPicker.d.ts +6 -0
- package/dist/ui/PermissionPrompt.d.ts +5 -0
- package/dist/ui/PlanApproval.d.ts +5 -0
- package/dist/ui/PromptInput.d.ts +20 -0
- package/dist/ui/ProviderMenu.d.ts +10 -0
- package/dist/ui/Spinner.d.ts +4 -0
- package/dist/ui/StatusBar.d.ts +14 -0
- package/dist/ui/TodoList.d.ts +4 -0
- package/dist/ui/ToolBlock.d.ts +14 -0
- package/dist/ui/complete.d.ts +8 -0
- package/dist/ui/index.d.ts +7 -0
- package/dist/ui/lineEdit.d.ts +42 -0
- package/dist/ui/liveCap.d.ts +29 -0
- package/dist/ui/markdownLayout.d.ts +23 -0
- package/dist/ui/replay.d.ts +4 -0
- package/dist/ui/stdinFilter.d.ts +18 -0
- package/dist/ui/table.d.ts +20 -0
- package/dist/ui/theme.d.ts +5 -0
- package/dist/ui/thinkingMouse.d.ts +38 -0
- package/dist/ui/transcript.d.ts +55 -0
- package/dist/ui/tty.d.ts +11 -0
- package/dist/ui/vim.d.ts +26 -0
- package/dist/ui-TAZMRXRI.js +3044 -0
- package/dist/update-XC3B6SJC.js +145 -0
- package/dist/update.d.ts +30 -0
- package/dist/verify.d.ts +16 -0
- package/dist/version.d.ts +1 -0
- package/dist/webBudget.d.ts +25 -0
- package/dist/worktree.d.ts +12 -0
- package/package.json +59 -35
- package/THIRD_PARTY_NOTICES.md +0 -9
- package/branding.mjs +0 -347
- package/cli.mjs +0 -120
- package/core.mjs +0 -260
- package/credentials.mjs +0 -52
- package/gateway.mjs +0 -2
- package/model-command.mjs +0 -61
- package/provider-cli.mjs +0 -77
- package/providers.mjs +0 -166
- package/proxy.mjs +0 -242
- package/startup-menu.mjs +0 -120
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
createRuntime,
|
|
4
|
+
lunaPtcAutoEnabled
|
|
5
|
+
} from "./chunk-A3NBWIEQ.js";
|
|
6
|
+
import "./chunk-OK2USAXX.js";
|
|
7
|
+
import "./chunk-7U7CMKKI.js";
|
|
8
|
+
import "./chunk-IF5RNXBI.js";
|
|
9
|
+
import "./chunk-PSO7R5AD.js";
|
|
10
|
+
import "./chunk-YYTFJUO2.js";
|
|
11
|
+
import "./chunk-RLSQAZBT.js";
|
|
12
|
+
import "./chunk-2IP5F3EK.js";
|
|
13
|
+
import "./chunk-FY543EQW.js";
|
|
14
|
+
import "./chunk-YGBTNDPK.js";
|
|
15
|
+
import "./chunk-6LO6YRDY.js";
|
|
16
|
+
import "./chunk-OCEQ2TRJ.js";
|
|
17
|
+
import "./chunk-NGFWBVHD.js";
|
|
18
|
+
import "./chunk-VPFCIUC6.js";
|
|
19
|
+
import "./chunk-ZXWRLN7E.js";
|
|
20
|
+
import "./chunk-IDZLHEVL.js";
|
|
21
|
+
export {
|
|
22
|
+
createRuntime,
|
|
23
|
+
lunaPtcAutoEnabled
|
|
24
|
+
};
|
package/dist/setup.d.ts
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { Agent } from './agent';
|
|
2
|
+
import { CheckpointStore } from './checkpoints';
|
|
3
|
+
import type { McpServerSpec } from './mcp/config';
|
|
4
|
+
import { McpManager } from './mcp/manager';
|
|
5
|
+
import type { CliArgs } from './args';
|
|
6
|
+
import { type Config } from './config';
|
|
7
|
+
import { type AgentDef, type CommandDef, type SkillDef } from './extensions';
|
|
8
|
+
import { type ModelGroup } from './models';
|
|
9
|
+
import { type ChatMessage, type Provider } from './provider';
|
|
10
|
+
import type { ReasoningLevel } from './reasoning';
|
|
11
|
+
import { addProviderKey } from './providerCli';
|
|
12
|
+
import { Session } from './session';
|
|
13
|
+
import { type Interaction } from './tools/plan';
|
|
14
|
+
import { TodoStore } from './tools/todo';
|
|
15
|
+
import { type PluginPayload } from './plugins';
|
|
16
|
+
import type { HookEvent } from './hooks';
|
|
17
|
+
export interface Runtime {
|
|
18
|
+
cwd: string;
|
|
19
|
+
home: string;
|
|
20
|
+
config: Config;
|
|
21
|
+
modelRef: string;
|
|
22
|
+
reasoning: ReasoningLevel;
|
|
23
|
+
agent: Agent;
|
|
24
|
+
session: Session;
|
|
25
|
+
setModel(ref: string): void;
|
|
26
|
+
setReasoning(level: ReasoningLevel): void;
|
|
27
|
+
env: NodeJS.ProcessEnv;
|
|
28
|
+
skills: SkillDef[];
|
|
29
|
+
commands: CommandDef[];
|
|
30
|
+
agentDefs: AgentDef[];
|
|
31
|
+
todos: TodoStore;
|
|
32
|
+
interaction: Interaction;
|
|
33
|
+
mcp: McpManager;
|
|
34
|
+
startMcp(specs: McpServerSpec[]): Promise<void>;
|
|
35
|
+
reloadConfig(): void;
|
|
36
|
+
providerLabel(): string;
|
|
37
|
+
listModels: (only?: string) => Promise<ModelGroup[]>;
|
|
38
|
+
addProviderKey(id: string, key: string): ReturnType<typeof addProviderKey>;
|
|
39
|
+
/** Continue an earlier session: load its history and append new messages to its file. */
|
|
40
|
+
resume(session: Session): void;
|
|
41
|
+
/** Starts a fresh session file; the previous one stays on disk for /resume. */
|
|
42
|
+
newSession(): void;
|
|
43
|
+
/** Re-reads AGENTS.md/BCCLI.md into the system prompt (after /memory). */
|
|
44
|
+
rebuildSystemPrompt(): void;
|
|
45
|
+
/** Fires a plugin observer event (SessionStart/Stop are emitted here; tool events come from the agent). */
|
|
46
|
+
emitPlugins(event: HookEvent, payload?: PluginPayload): Promise<void>;
|
|
47
|
+
checkpoints: CheckpointStore;
|
|
48
|
+
/**
|
|
49
|
+
* Steps back n conversation turns: truncates the history (and the session log) to before
|
|
50
|
+
* them and restores the file snapshots they took. Returns undefined when nothing is left.
|
|
51
|
+
*/
|
|
52
|
+
rewindTurns(n: number): Promise<RewindResult | undefined>;
|
|
53
|
+
startedAt: Date;
|
|
54
|
+
}
|
|
55
|
+
export interface RewindResult {
|
|
56
|
+
turns: number;
|
|
57
|
+
restored: string[];
|
|
58
|
+
deleted: string[];
|
|
59
|
+
skipped: string[];
|
|
60
|
+
failed: string[];
|
|
61
|
+
}
|
|
62
|
+
/** PTC is automatic for Luna. This env var is only an emergency/internal kill switch. */
|
|
63
|
+
export declare function lunaPtcAutoEnabled(env: NodeJS.ProcessEnv): boolean;
|
|
64
|
+
export declare function createRuntime(opts: {
|
|
65
|
+
cwd: string;
|
|
66
|
+
args: CliArgs;
|
|
67
|
+
env?: NodeJS.ProcessEnv;
|
|
68
|
+
provider?: Provider;
|
|
69
|
+
history?: ChatMessage[];
|
|
70
|
+
fetch?: typeof fetch;
|
|
71
|
+
userHome?: string;
|
|
72
|
+
/** Override langkah agent (mis. githubBot.stepLimit) — menang atas config.maxSteps. */
|
|
73
|
+
maxSteps?: number | null;
|
|
74
|
+
/**
|
|
75
|
+
* Write the conversation to ~/.bccli/sessions (default true). One-shot hosts such as
|
|
76
|
+
* `gh-bot` turn it off: a session log per webhook comment is never resumed.
|
|
77
|
+
*/
|
|
78
|
+
persistSession?: boolean;
|
|
79
|
+
}): Runtime;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function osc52(text: string, inTmux: boolean): string;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function gitDiff(cwd: string, maxLines?: number, bin?: string): Promise<string>;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export interface DoctorDeps {
|
|
2
|
+
which: (cmd: string) => boolean | Promise<boolean>;
|
|
3
|
+
nodeVersion: string;
|
|
4
|
+
listModels: () => Promise<unknown[]>;
|
|
5
|
+
}
|
|
6
|
+
export declare function doctorText(input: {
|
|
7
|
+
providers: {
|
|
8
|
+
id: string;
|
|
9
|
+
name: string;
|
|
10
|
+
ready: boolean;
|
|
11
|
+
}[];
|
|
12
|
+
mcp: {
|
|
13
|
+
name: string;
|
|
14
|
+
status: string;
|
|
15
|
+
error?: string;
|
|
16
|
+
}[];
|
|
17
|
+
activeProvider: string;
|
|
18
|
+
}, deps: DoctorDeps): Promise<string>;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
export interface StatusInput {
|
|
2
|
+
version: string;
|
|
3
|
+
modelRef: string;
|
|
4
|
+
providerLabel: string;
|
|
5
|
+
mode: string;
|
|
6
|
+
cwd: string;
|
|
7
|
+
mcp: {
|
|
8
|
+
name: string;
|
|
9
|
+
status: string;
|
|
10
|
+
error?: string;
|
|
11
|
+
tools: number;
|
|
12
|
+
}[];
|
|
13
|
+
usage: {
|
|
14
|
+
inputTokens: number;
|
|
15
|
+
outputTokens: number;
|
|
16
|
+
cachedInputTokens?: number;
|
|
17
|
+
cacheWriteTokens?: number;
|
|
18
|
+
};
|
|
19
|
+
lastInputTokens: number;
|
|
20
|
+
contextWindow?: number;
|
|
21
|
+
}
|
|
22
|
+
export declare function statusText(s: StatusInput): string;
|
|
23
|
+
export declare function agentsText(defs: {
|
|
24
|
+
name: string;
|
|
25
|
+
description: string;
|
|
26
|
+
tools?: string[];
|
|
27
|
+
model?: string;
|
|
28
|
+
}[]): string;
|
|
29
|
+
export declare function skillsText(skills: {
|
|
30
|
+
name: string;
|
|
31
|
+
description: string;
|
|
32
|
+
dir: string;
|
|
33
|
+
}[], commands: {
|
|
34
|
+
name: string;
|
|
35
|
+
description?: string;
|
|
36
|
+
}[]): string;
|
|
37
|
+
export declare function sessionText(s: {
|
|
38
|
+
file: string;
|
|
39
|
+
started: Date;
|
|
40
|
+
messages: number;
|
|
41
|
+
usage: {
|
|
42
|
+
inputTokens: number;
|
|
43
|
+
outputTokens: number;
|
|
44
|
+
};
|
|
45
|
+
modelRef: string;
|
|
46
|
+
}): string;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare function instructionFiles(cwd: string, home: string): {
|
|
2
|
+
path: string;
|
|
3
|
+
lines: number;
|
|
4
|
+
}[];
|
|
5
|
+
export declare function appendMemory(file: string, text: string): void;
|
|
6
|
+
/** `/memory [global] <teks>` → target file and text; text is empty when nothing follows. */
|
|
7
|
+
export declare function parseMemoryArgs(args: string): {
|
|
8
|
+
global: boolean;
|
|
9
|
+
text: string;
|
|
10
|
+
};
|
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
estimateTokens
|
|
4
|
+
} from "./chunk-OK2USAXX.js";
|
|
5
|
+
import {
|
|
6
|
+
t
|
|
7
|
+
} from "./chunk-IDZLHEVL.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);
|
|
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
|
+
};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
export interface StatsRow {
|
|
2
|
+
project: string;
|
|
3
|
+
sessions: number;
|
|
4
|
+
messages: number;
|
|
5
|
+
/** Estimated tokens (messages without recorded usage only). */
|
|
6
|
+
inputTokens: number;
|
|
7
|
+
outputTokens: number;
|
|
8
|
+
/** Tokens from persisted `t:usage` records. */
|
|
9
|
+
usageInputTokens: number;
|
|
10
|
+
usageOutputTokens: number;
|
|
11
|
+
cachedInputTokens: number;
|
|
12
|
+
cacheWriteTokens: number;
|
|
13
|
+
toolCalls: Record<string, number>;
|
|
14
|
+
/** Total tokens per session (recorded basis when present, estimate otherwise). */
|
|
15
|
+
sessionTotals: number[];
|
|
16
|
+
/** Per-model token totals from `t:usage` records that carry a model ref. */
|
|
17
|
+
modelUsage: Record<string, ModelTotals>;
|
|
18
|
+
}
|
|
19
|
+
export interface ModelTotals {
|
|
20
|
+
turns: number;
|
|
21
|
+
inputTokens: number;
|
|
22
|
+
outputTokens: number;
|
|
23
|
+
cachedInputTokens: number;
|
|
24
|
+
cacheWriteTokens: number;
|
|
25
|
+
}
|
|
26
|
+
export interface AggregateOptions {
|
|
27
|
+
days: number;
|
|
28
|
+
now?: number;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Walk home/sessions/<project>/*.jsonl, keep files with mtime within `days`,
|
|
32
|
+
* and aggregate per project. Token basis switches per session: sessions with
|
|
33
|
+
* recorded usage records use them (incl. cache counters); older sessions fall
|
|
34
|
+
* back to ~4 chars/token estimates from message content.
|
|
35
|
+
*/
|
|
36
|
+
export declare function aggregateSessions(home: string, opts: AggregateOptions): StatsRow[];
|
|
37
|
+
/** Compact token formatting like the opencode stats boxes: 291.6K / 41.0M / 627.4M. */
|
|
38
|
+
export declare function fmtCompact(n: number): string;
|
|
39
|
+
export interface StatsCliArgs {
|
|
40
|
+
subArgs: string[];
|
|
41
|
+
home: string;
|
|
42
|
+
cwd?: string;
|
|
43
|
+
now?: number;
|
|
44
|
+
/** Already-parsed `--days` from parseCliArgs (subArgs never contains it). */
|
|
45
|
+
days?: number;
|
|
46
|
+
/** Already-parsed `--tools` top-N cap. */
|
|
47
|
+
tools?: number;
|
|
48
|
+
/** Already-parsed `--models` ("all" or a number as string). */
|
|
49
|
+
models?: string;
|
|
50
|
+
/** Already-parsed `--project` filter (empty string = current project). */
|
|
51
|
+
project?: string;
|
|
52
|
+
}
|
|
53
|
+
export interface StatsDeps {
|
|
54
|
+
out(line: string): void;
|
|
55
|
+
err?(line: string): void;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* `bccli stats [--days N] [--tools N] [--models [N|all]] [--project slug]` —
|
|
59
|
+
* token usage and tool-call stats from local session files, rendered as
|
|
60
|
+
* opencode-style overview/tokens/model/tool boxes.
|
|
61
|
+
*/
|
|
62
|
+
export declare function runStatsCommand(args: StatsCliArgs, deps: StatsDeps): number;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export declare class ThinkSplitter {
|
|
2
|
+
private inside;
|
|
3
|
+
private held;
|
|
4
|
+
push(delta: string): {
|
|
5
|
+
text: string;
|
|
6
|
+
thinking: string;
|
|
7
|
+
};
|
|
8
|
+
/** Whatever was held back as a possible tag start is plain text after all. */
|
|
9
|
+
flush(): {
|
|
10
|
+
text: string;
|
|
11
|
+
thinking: string;
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
export declare function splitThinking(text: string): {
|
|
15
|
+
text: string;
|
|
16
|
+
thinking: string;
|
|
17
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { ChatMessage } from './provider';
|
|
2
|
+
/** ~4 chars per token, the same conservative estimate the usage fallback uses. */
|
|
3
|
+
export declare const estimateTokens: (text: string) => number;
|
|
4
|
+
export declare function estimateMessages(messages: ChatMessage[]): number;
|
|
5
|
+
/**
|
|
6
|
+
* Drops whole oldest turns (everything up to the next user message) until the history fits the
|
|
7
|
+
* budget. Cutting only at turn boundaries keeps assistant tool_calls paired with their tool
|
|
8
|
+
* replies, so the trimmed history is always valid for a provider request.
|
|
9
|
+
*/
|
|
10
|
+
export declare function trimToFit(messages: ChatMessage[], maxTokens: number): ChatMessage[];
|
|
11
|
+
/**
|
|
12
|
+
* Splits history into [old, keep]: the newest whole turns that fit `keepTokens` (at least the
|
|
13
|
+
* last user turn) stay verbatim; everything older goes to `old` for summarization. Cutting at
|
|
14
|
+
* user boundaries keeps tool_calls paired with their replies.
|
|
15
|
+
*/
|
|
16
|
+
export declare function splitKeepRecent(messages: ChatMessage[], keepTokens: number): [ChatMessage[], ChatMessage[]];
|
|
17
|
+
export declare const isToolStub: (content: string) => boolean;
|
|
18
|
+
/**
|
|
19
|
+
* Replaces old tool results with a one-line stub once they add up past PRUNE_MIN_TOKENS, so the
|
|
20
|
+
* prompt prefix (and its cache) changes rarely. The newest KEEP_TOKENS worth stay intact.
|
|
21
|
+
*/
|
|
22
|
+
export declare function pruneOldToolOutputs(messages: ChatMessage[]): boolean;
|