codebot-ai 1.5.0 → 1.7.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.
@@ -0,0 +1,214 @@
1
+ "use strict";
2
+ /**
3
+ * Docker Sandbox Execution for CodeBot v1.7.0
4
+ *
5
+ * Runs shell commands inside disposable Docker containers for isolation.
6
+ * Features:
7
+ * - Read-only root filesystem
8
+ * - Project directory mounted read-write
9
+ * - No network by default (configurable)
10
+ * - CPU, memory, PID limits
11
+ * - Automatic container cleanup
12
+ * - Graceful fallback to host execution when Docker unavailable
13
+ */
14
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
15
+ if (k2 === undefined) k2 = k;
16
+ var desc = Object.getOwnPropertyDescriptor(m, k);
17
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
18
+ desc = { enumerable: true, get: function() { return m[k]; } };
19
+ }
20
+ Object.defineProperty(o, k2, desc);
21
+ }) : (function(o, m, k, k2) {
22
+ if (k2 === undefined) k2 = k;
23
+ o[k2] = m[k];
24
+ }));
25
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
26
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
27
+ }) : function(o, v) {
28
+ o["default"] = v;
29
+ });
30
+ var __importStar = (this && this.__importStar) || (function () {
31
+ var ownKeys = function(o) {
32
+ ownKeys = Object.getOwnPropertyNames || function (o) {
33
+ var ar = [];
34
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
35
+ return ar;
36
+ };
37
+ return ownKeys(o);
38
+ };
39
+ return function (mod) {
40
+ if (mod && mod.__esModule) return mod;
41
+ var result = {};
42
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
43
+ __setModuleDefault(result, mod);
44
+ return result;
45
+ };
46
+ })();
47
+ Object.defineProperty(exports, "__esModule", { value: true });
48
+ exports.isDockerAvailable = isDockerAvailable;
49
+ exports.resetDockerCheck = resetDockerCheck;
50
+ exports.sandboxExec = sandboxExec;
51
+ exports.ensureSandboxImage = ensureSandboxImage;
52
+ exports.getSandboxInfo = getSandboxInfo;
53
+ const child_process_1 = require("child_process");
54
+ const path = __importStar(require("path"));
55
+ // ── Docker Detection ──
56
+ let _dockerAvailable = null;
57
+ /** Check if Docker is installed and the daemon is running */
58
+ function isDockerAvailable() {
59
+ if (_dockerAvailable !== null)
60
+ return _dockerAvailable;
61
+ try {
62
+ (0, child_process_1.execSync)('docker info', {
63
+ timeout: 5000,
64
+ stdio: ['pipe', 'pipe', 'pipe'],
65
+ encoding: 'utf-8',
66
+ });
67
+ _dockerAvailable = true;
68
+ }
69
+ catch {
70
+ _dockerAvailable = false;
71
+ }
72
+ return _dockerAvailable;
73
+ }
74
+ /** Reset the cached Docker availability check (for testing) */
75
+ function resetDockerCheck() {
76
+ _dockerAvailable = null;
77
+ }
78
+ // ── Sandbox Execution ──
79
+ const DEFAULT_CONFIG = {
80
+ cpus: 2,
81
+ memoryMb: 512,
82
+ pidsLimit: 100,
83
+ network: false,
84
+ timeoutMs: 120_000,
85
+ workDir: '/workspace',
86
+ image: 'node:20-slim',
87
+ };
88
+ /**
89
+ * Execute a command inside a Docker sandbox.
90
+ *
91
+ * The project directory is mounted at /workspace (read-write).
92
+ * Root filesystem is read-only. /tmp is tmpfs (100MB).
93
+ * Network is disabled by default.
94
+ *
95
+ * Falls back to host execution if Docker is unavailable.
96
+ */
97
+ function sandboxExec(command, projectDir, config) {
98
+ const cfg = { ...DEFAULT_CONFIG, ...config };
99
+ const resolvedProjectDir = path.resolve(projectDir);
100
+ if (!isDockerAvailable()) {
101
+ return hostFallback(command, resolvedProjectDir, cfg.timeoutMs);
102
+ }
103
+ // Build docker run command
104
+ const dockerArgs = [
105
+ 'docker', 'run',
106
+ '--rm', // Cleanup on exit
107
+ '--read-only', // Read-only root filesystem
108
+ '--tmpfs', '/tmp:size=100m', // Writable /tmp
109
+ `--cpus="${cfg.cpus}"`, // CPU limit
110
+ `--memory="${cfg.memoryMb}m"`, // Memory limit
111
+ `--pids-limit`, String(cfg.pidsLimit), // PID limit
112
+ '--security-opt', 'no-new-privileges', // No privilege escalation
113
+ '--cap-drop=ALL', // Drop all capabilities
114
+ ];
115
+ // Network
116
+ if (!cfg.network) {
117
+ dockerArgs.push('--network', 'none');
118
+ }
119
+ // Mount project directory
120
+ dockerArgs.push('-v', `${resolvedProjectDir}:${cfg.workDir}:rw`);
121
+ // Working directory
122
+ dockerArgs.push('-w', cfg.workDir);
123
+ // Image
124
+ dockerArgs.push(cfg.image);
125
+ // Command (via sh -c for shell features)
126
+ dockerArgs.push('sh', '-c', command);
127
+ const dockerCmd = dockerArgs.join(' ');
128
+ try {
129
+ const stdout = (0, child_process_1.execSync)(dockerCmd, {
130
+ timeout: cfg.timeoutMs,
131
+ maxBuffer: 1024 * 1024, // 1MB
132
+ encoding: 'utf-8',
133
+ stdio: ['pipe', 'pipe', 'pipe'],
134
+ });
135
+ return { stdout: stdout || '', stderr: '', exitCode: 0, sandboxed: true };
136
+ }
137
+ catch (err) {
138
+ const e = err;
139
+ return {
140
+ stdout: e.stdout || '',
141
+ stderr: e.stderr || '',
142
+ exitCode: e.status || 1,
143
+ sandboxed: true,
144
+ };
145
+ }
146
+ }
147
+ /**
148
+ * Fallback: execute on host with existing security measures.
149
+ * Used when Docker is not available.
150
+ */
151
+ function hostFallback(command, cwd, timeoutMs) {
152
+ try {
153
+ const stdout = (0, child_process_1.execSync)(command, {
154
+ cwd,
155
+ timeout: timeoutMs,
156
+ maxBuffer: 1024 * 1024,
157
+ encoding: 'utf-8',
158
+ stdio: ['pipe', 'pipe', 'pipe'],
159
+ });
160
+ return { stdout: stdout || '', stderr: '', exitCode: 0, sandboxed: false };
161
+ }
162
+ catch (err) {
163
+ const e = err;
164
+ return {
165
+ stdout: e.stdout || '',
166
+ stderr: e.stderr || '',
167
+ exitCode: e.status || 1,
168
+ sandboxed: false,
169
+ };
170
+ }
171
+ }
172
+ /**
173
+ * Build or pull the sandbox Docker image.
174
+ * Call this during `codebot --setup` or first sandbox use.
175
+ */
176
+ function ensureSandboxImage(image) {
177
+ const img = image || DEFAULT_CONFIG.image;
178
+ if (!isDockerAvailable()) {
179
+ return { ready: false, error: 'Docker is not available' };
180
+ }
181
+ try {
182
+ // Check if image exists locally
183
+ (0, child_process_1.execSync)(`docker image inspect ${img}`, {
184
+ timeout: 10_000,
185
+ stdio: ['pipe', 'pipe', 'pipe'],
186
+ });
187
+ return { ready: true };
188
+ }
189
+ catch {
190
+ // Image not found, try to pull
191
+ try {
192
+ (0, child_process_1.execSync)(`docker pull ${img}`, {
193
+ timeout: 120_000,
194
+ stdio: ['pipe', 'pipe', 'pipe'],
195
+ });
196
+ return { ready: true };
197
+ }
198
+ catch (pullErr) {
199
+ const msg = pullErr instanceof Error ? pullErr.message : String(pullErr);
200
+ return { ready: false, error: `Failed to pull ${img}: ${msg}` };
201
+ }
202
+ }
203
+ }
204
+ /**
205
+ * Get a summary of sandbox configuration for display.
206
+ */
207
+ function getSandboxInfo() {
208
+ return {
209
+ available: isDockerAvailable(),
210
+ image: DEFAULT_CONFIG.image,
211
+ defaults: { ...DEFAULT_CONFIG },
212
+ };
213
+ }
214
+ //# sourceMappingURL=sandbox.js.map
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Secret detection module for CodeBot.
3
+ *
4
+ * Scans content for common credential patterns (API keys, tokens, passwords).
5
+ * Returns matches with line numbers and masked excerpts.
6
+ * Used to warn before writing secrets to files — does NOT block writes.
7
+ */
8
+ export interface SecretMatch {
9
+ type: string;
10
+ line: number;
11
+ snippet: string;
12
+ }
13
+ /**
14
+ * Scan content for secrets. Returns array of matches with line numbers and masked snippets.
15
+ */
16
+ export declare function scanForSecrets(content: string): SecretMatch[];
17
+ /**
18
+ * Quick check: does the content contain any secrets?
19
+ */
20
+ export declare function hasSecrets(content: string): boolean;
21
+ /**
22
+ * Mask secrets in an arbitrary string (e.g., for audit logging).
23
+ * Replaces all detected secret matches with masked versions.
24
+ */
25
+ export declare function maskSecretsInString(text: string): string;
26
+ //# sourceMappingURL=secrets.d.ts.map
@@ -0,0 +1,86 @@
1
+ "use strict";
2
+ /**
3
+ * Secret detection module for CodeBot.
4
+ *
5
+ * Scans content for common credential patterns (API keys, tokens, passwords).
6
+ * Returns matches with line numbers and masked excerpts.
7
+ * Used to warn before writing secrets to files — does NOT block writes.
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.scanForSecrets = scanForSecrets;
11
+ exports.hasSecrets = hasSecrets;
12
+ exports.maskSecretsInString = maskSecretsInString;
13
+ const SECRET_PATTERNS = [
14
+ { name: 'aws_access_key', pattern: /AKIA[0-9A-Z]{16}/ },
15
+ { name: 'aws_secret_key', pattern: /(?:aws_secret_access_key|aws_secret)\s*[:=]\s*['"]?[A-Za-z0-9/+=]{40}/ },
16
+ { name: 'private_key', pattern: /-----BEGIN (RSA |EC |DSA |OPENSSH )?PRIVATE KEY-----/ },
17
+ { name: 'github_token', pattern: /gh[ps]_[A-Za-z0-9_]{36,}/ },
18
+ { name: 'github_oauth', pattern: /gho_[A-Za-z0-9_]{36,}/ },
19
+ { name: 'generic_api_key', pattern: /(?:api[_-]?key|apikey|secret[_-]?key)\s*[:=]\s*['"]?[A-Za-z0-9_\-]{20,}/i },
20
+ { name: 'jwt', pattern: /eyJ[A-Za-z0-9_-]{10,}\.eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_\-]+/ },
21
+ { name: 'password_assign', pattern: /(?:password|passwd|pwd)\s*[:=]\s*['"][^'"]{8,}['"]/i },
22
+ { name: 'connection_string', pattern: /(?:mongodb|postgres|postgresql|mysql|redis|amqp):\/\/[^\s]{10,}/ },
23
+ { name: 'slack_token', pattern: /xox[bprs]-[0-9]{10,}-[A-Za-z0-9\-]+/ },
24
+ { name: 'slack_webhook', pattern: /hooks\.slack\.com\/services\/T[A-Z0-9]+\/B[A-Z0-9]+\/[A-Za-z0-9]+/ },
25
+ { name: 'generic_secret', pattern: /(?:secret|token|credential|auth_token)\s*[:=]\s*['"][^'"]{16,}['"]/i },
26
+ { name: 'npm_token', pattern: /\/\/registry\.npmjs\.org\/:_authToken=[^\s]+/ },
27
+ { name: 'sendgrid_key', pattern: /SG\.[A-Za-z0-9_\-]{22}\.[A-Za-z0-9_\-]{43}/ },
28
+ { name: 'stripe_key', pattern: /sk_(live|test)_[A-Za-z0-9]{24,}/ },
29
+ ];
30
+ /**
31
+ * Mask a matched secret for safe display.
32
+ * Shows first 4 chars + **** + last 4 chars for strings >= 12 chars.
33
+ * For shorter strings, shows first 2 + **** + last 2.
34
+ */
35
+ function maskSecret(match) {
36
+ if (match.length >= 12) {
37
+ return match.substring(0, 4) + '****' + match.substring(match.length - 4);
38
+ }
39
+ if (match.length >= 6) {
40
+ return match.substring(0, 2) + '****' + match.substring(match.length - 2);
41
+ }
42
+ return '****';
43
+ }
44
+ /**
45
+ * Scan content for secrets. Returns array of matches with line numbers and masked snippets.
46
+ */
47
+ function scanForSecrets(content) {
48
+ const matches = [];
49
+ const lines = content.split('\n');
50
+ for (let i = 0; i < lines.length; i++) {
51
+ const line = lines[i];
52
+ for (const { name, pattern } of SECRET_PATTERNS) {
53
+ const match = line.match(pattern);
54
+ if (match) {
55
+ matches.push({
56
+ type: name,
57
+ line: i + 1,
58
+ snippet: maskSecret(match[0]),
59
+ });
60
+ }
61
+ }
62
+ }
63
+ return matches;
64
+ }
65
+ /**
66
+ * Quick check: does the content contain any secrets?
67
+ */
68
+ function hasSecrets(content) {
69
+ for (const { pattern } of SECRET_PATTERNS) {
70
+ if (pattern.test(content))
71
+ return true;
72
+ }
73
+ return false;
74
+ }
75
+ /**
76
+ * Mask secrets in an arbitrary string (e.g., for audit logging).
77
+ * Replaces all detected secret matches with masked versions.
78
+ */
79
+ function maskSecretsInString(text) {
80
+ let masked = text;
81
+ for (const { pattern } of SECRET_PATTERNS) {
82
+ masked = masked.replace(new RegExp(pattern.source, pattern.flags + (pattern.flags.includes('g') ? '' : 'g')), match => maskSecret(match));
83
+ }
84
+ return masked;
85
+ }
86
+ //# sourceMappingURL=secrets.js.map
@@ -0,0 +1,18 @@
1
+ export interface PathSafetyResult {
2
+ safe: boolean;
3
+ reason?: string;
4
+ }
5
+ /**
6
+ * Check if a file path is safe for write/edit operations.
7
+ *
8
+ * Resolves symlinks, checks against blocked system paths,
9
+ * and verifies the path is within the project or user home.
10
+ */
11
+ export declare function isPathSafe(targetPath: string, projectRoot: string): PathSafetyResult;
12
+ /**
13
+ * Check if a working directory is safe for command execution.
14
+ *
15
+ * Ensures the CWD exists, is a directory, and is under the project root.
16
+ */
17
+ export declare function isCwdSafe(cwd: string, projectRoot: string): PathSafetyResult;
18
+ //# sourceMappingURL=security.d.ts.map
@@ -0,0 +1,167 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.isPathSafe = isPathSafe;
37
+ exports.isCwdSafe = isCwdSafe;
38
+ const fs = __importStar(require("fs"));
39
+ const path = __importStar(require("path"));
40
+ const os = __importStar(require("os"));
41
+ /**
42
+ * Path safety module for CodeBot.
43
+ *
44
+ * Prevents tools from reading/writing system-critical files and directories.
45
+ * Resolves symlinks to prevent bypass attacks.
46
+ */
47
+ /** System-critical absolute paths that should NEVER be written to */
48
+ const BLOCKED_ABSOLUTE_PATHS = [
49
+ '/etc', '/usr', '/bin', '/sbin', '/boot', '/dev', '/proc', '/sys',
50
+ '/var/log', '/var/run', '/lib', '/lib64',
51
+ // macOS system directories
52
+ '/System', '/Library',
53
+ // Windows system directories
54
+ 'C:\\Windows', 'C:\\Program Files', 'C:\\Program Files (x86)',
55
+ ];
56
+ /** Home-relative sensitive directories/files that should NEVER be written to */
57
+ const BLOCKED_HOME_RELATIVE = [
58
+ '.ssh',
59
+ '.gnupg',
60
+ '.aws/credentials',
61
+ '.config/gcloud',
62
+ '.bashrc',
63
+ '.bash_profile',
64
+ '.zshrc',
65
+ '.profile',
66
+ '.gitconfig',
67
+ '.npmrc',
68
+ ];
69
+ /**
70
+ * Check if a file path is safe for write/edit operations.
71
+ *
72
+ * Resolves symlinks, checks against blocked system paths,
73
+ * and verifies the path is within the project or user home.
74
+ */
75
+ function isPathSafe(targetPath, projectRoot) {
76
+ try {
77
+ const resolved = path.resolve(targetPath);
78
+ // Resolve symlinks — for new files, resolve the parent directory
79
+ let realPath;
80
+ try {
81
+ realPath = fs.realpathSync(resolved);
82
+ }
83
+ catch {
84
+ // File doesn't exist yet — resolve the parent
85
+ const parentDir = path.dirname(resolved);
86
+ try {
87
+ const realParent = fs.realpathSync(parentDir);
88
+ realPath = path.join(realParent, path.basename(resolved));
89
+ }
90
+ catch {
91
+ // Parent doesn't exist either — use the resolved path as-is
92
+ realPath = resolved;
93
+ }
94
+ }
95
+ // Check against blocked absolute paths
96
+ const normalizedPath = realPath.replace(/\\/g, '/').toLowerCase();
97
+ for (const blocked of BLOCKED_ABSOLUTE_PATHS) {
98
+ const normalizedBlocked = blocked.replace(/\\/g, '/').toLowerCase();
99
+ if (normalizedPath === normalizedBlocked || normalizedPath.startsWith(normalizedBlocked + '/')) {
100
+ return { safe: false, reason: `Blocked: "${realPath}" is inside system directory "${blocked}"` };
101
+ }
102
+ }
103
+ // Check against home-relative sensitive paths
104
+ const home = os.homedir();
105
+ for (const relative of BLOCKED_HOME_RELATIVE) {
106
+ const blockedPath = path.join(home, relative);
107
+ const normalizedBlockedHome = blockedPath.replace(/\\/g, '/').toLowerCase();
108
+ if (normalizedPath === normalizedBlockedHome || normalizedPath.startsWith(normalizedBlockedHome + '/')) {
109
+ return { safe: false, reason: `Blocked: "${realPath}" is a sensitive file/directory (~/${relative})` };
110
+ }
111
+ }
112
+ // Verify path is under project root or user home
113
+ const normalizedProject = path.resolve(projectRoot).replace(/\\/g, '/').toLowerCase();
114
+ const normalizedHome = home.replace(/\\/g, '/').toLowerCase();
115
+ const isUnderProject = normalizedPath.startsWith(normalizedProject + '/') || normalizedPath === normalizedProject;
116
+ const isUnderHome = normalizedPath.startsWith(normalizedHome + '/') || normalizedPath === normalizedHome;
117
+ if (!isUnderProject && !isUnderHome) {
118
+ return { safe: false, reason: `Blocked: "${realPath}" is outside both project root and user home directory` };
119
+ }
120
+ return { safe: true };
121
+ }
122
+ catch (err) {
123
+ return { safe: false, reason: `Path validation error: ${err instanceof Error ? err.message : String(err)}` };
124
+ }
125
+ }
126
+ /**
127
+ * Check if a working directory is safe for command execution.
128
+ *
129
+ * Ensures the CWD exists, is a directory, and is under the project root.
130
+ */
131
+ function isCwdSafe(cwd, projectRoot) {
132
+ try {
133
+ const resolved = path.resolve(cwd);
134
+ // Check it exists and is a directory
135
+ try {
136
+ const stat = fs.statSync(resolved);
137
+ if (!stat.isDirectory()) {
138
+ return { safe: false, reason: `"${resolved}" is not a directory` };
139
+ }
140
+ }
141
+ catch {
142
+ return { safe: false, reason: `Directory does not exist: "${resolved}"` };
143
+ }
144
+ // Resolve symlinks
145
+ let realPath;
146
+ try {
147
+ realPath = fs.realpathSync(resolved);
148
+ }
149
+ catch {
150
+ realPath = resolved;
151
+ }
152
+ // Verify it's under project root or user home
153
+ const normalizedPath = realPath.replace(/\\/g, '/').toLowerCase();
154
+ const normalizedProject = path.resolve(projectRoot).replace(/\\/g, '/').toLowerCase();
155
+ const normalizedHome = os.homedir().replace(/\\/g, '/').toLowerCase();
156
+ const isUnderProject = normalizedPath.startsWith(normalizedProject + '/') || normalizedPath === normalizedProject;
157
+ const isUnderHome = normalizedPath.startsWith(normalizedHome + '/') || normalizedPath === normalizedHome;
158
+ if (!isUnderProject && !isUnderHome) {
159
+ return { safe: false, reason: `CWD "${realPath}" is outside project root and user home directory` };
160
+ }
161
+ return { safe: true };
162
+ }
163
+ catch (err) {
164
+ return { safe: false, reason: `CWD validation error: ${err instanceof Error ? err.message : String(err)}` };
165
+ }
166
+ }
167
+ //# sourceMappingURL=security.js.map
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Token & Cost Tracking for CodeBot v1.7.0
3
+ *
4
+ * Tracks per-request and per-session token usage and estimated costs.
5
+ * Supports cost limits and historical usage queries.
6
+ */
7
+ export interface UsageRecord {
8
+ timestamp: string;
9
+ model: string;
10
+ provider: string;
11
+ inputTokens: number;
12
+ outputTokens: number;
13
+ costUsd: number;
14
+ }
15
+ export interface SessionSummary {
16
+ sessionId: string;
17
+ model: string;
18
+ provider: string;
19
+ startTime: string;
20
+ endTime: string;
21
+ totalInputTokens: number;
22
+ totalOutputTokens: number;
23
+ totalCostUsd: number;
24
+ requestCount: number;
25
+ toolCalls: number;
26
+ filesModified: number;
27
+ }
28
+ export declare class TokenTracker {
29
+ private model;
30
+ private provider;
31
+ private sessionId;
32
+ private records;
33
+ private toolCallCount;
34
+ private filesModifiedSet;
35
+ private startTime;
36
+ private costLimitUsd;
37
+ constructor(model: string, provider: string, sessionId?: string);
38
+ /** Set cost limit in USD. 0 = no limit. */
39
+ setCostLimit(usd: number): void;
40
+ /** Record token usage from an LLM request */
41
+ recordUsage(inputTokens: number, outputTokens: number): UsageRecord;
42
+ /** Record a tool call (for summary) */
43
+ recordToolCall(): void;
44
+ /** Record a file modification (for summary) */
45
+ recordFileModified(filePath: string): void;
46
+ /** Check if cost limit has been exceeded */
47
+ isOverBudget(): boolean;
48
+ /** Get remaining budget in USD (Infinity if no limit) */
49
+ getRemainingBudget(): number;
50
+ getTotalInputTokens(): number;
51
+ getTotalOutputTokens(): number;
52
+ getTotalCost(): number;
53
+ getRequestCount(): number;
54
+ /** Generate a session summary */
55
+ getSummary(): SessionSummary;
56
+ /** Format cost for display */
57
+ formatCost(): string;
58
+ /** Format a compact status line for CLI */
59
+ formatStatusLine(): string;
60
+ /** Save session usage to ~/.codebot/usage/ for historical tracking */
61
+ saveUsage(): void;
62
+ /**
63
+ * Load historical usage from ~/.codebot/usage/
64
+ */
65
+ static loadHistory(days?: number): SessionSummary[];
66
+ /**
67
+ * Format a historical usage report.
68
+ */
69
+ static formatUsageReport(days?: number): string;
70
+ private getPricing;
71
+ private isLocalModel;
72
+ }
73
+ //# sourceMappingURL=telemetry.d.ts.map