chainlesschain 0.37.8 → 0.37.10
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/README.md +403 -8
- package/bin/chainlesschain.js +4 -0
- package/package.json +7 -2
- package/src/commands/agent.js +30 -0
- package/src/commands/ask.js +114 -0
- package/src/commands/audit.js +286 -0
- package/src/commands/auth.js +387 -0
- package/src/commands/browse.js +184 -0
- package/src/commands/chat.js +35 -0
- package/src/commands/db.js +152 -0
- package/src/commands/did.js +376 -0
- package/src/commands/encrypt.js +233 -0
- package/src/commands/export.js +125 -0
- package/src/commands/git.js +215 -0
- package/src/commands/import.js +259 -0
- package/src/commands/instinct.js +202 -0
- package/src/commands/llm.js +288 -0
- package/src/commands/mcp.js +302 -0
- package/src/commands/memory.js +282 -0
- package/src/commands/note.js +489 -0
- package/src/commands/org.js +505 -0
- package/src/commands/p2p.js +274 -0
- package/src/commands/plugin.js +398 -0
- package/src/commands/search.js +237 -0
- package/src/commands/session.js +238 -0
- package/src/commands/skill.js +479 -0
- package/src/commands/sync.js +249 -0
- package/src/commands/tokens.js +214 -0
- package/src/commands/wallet.js +416 -0
- package/src/index.js +65 -0
- package/src/lib/audit-logger.js +364 -0
- package/src/lib/bm25-search.js +322 -0
- package/src/lib/browser-automation.js +216 -0
- package/src/lib/crypto-manager.js +246 -0
- package/src/lib/did-manager.js +270 -0
- package/src/lib/ensure-utf8.js +59 -0
- package/src/lib/git-integration.js +220 -0
- package/src/lib/instinct-manager.js +190 -0
- package/src/lib/knowledge-exporter.js +302 -0
- package/src/lib/knowledge-importer.js +293 -0
- package/src/lib/llm-providers.js +325 -0
- package/src/lib/mcp-client.js +413 -0
- package/src/lib/memory-manager.js +211 -0
- package/src/lib/note-versioning.js +244 -0
- package/src/lib/org-manager.js +424 -0
- package/src/lib/p2p-manager.js +317 -0
- package/src/lib/pdf-parser.js +96 -0
- package/src/lib/permission-engine.js +374 -0
- package/src/lib/plan-mode.js +333 -0
- package/src/lib/platform.js +15 -0
- package/src/lib/plugin-manager.js +312 -0
- package/src/lib/response-cache.js +156 -0
- package/src/lib/session-manager.js +189 -0
- package/src/lib/sync-manager.js +347 -0
- package/src/lib/token-tracker.js +200 -0
- package/src/lib/wallet-manager.js +348 -0
- package/src/repl/agent-repl.js +912 -0
- package/src/repl/chat-repl.js +262 -0
- package/src/runtime/bootstrap.js +159 -0
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sync commands
|
|
3
|
+
* chainlesschain sync status|push|pull|conflicts|resolve|log|clear
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import chalk from "chalk";
|
|
7
|
+
import { logger } from "../lib/logger.js";
|
|
8
|
+
import { bootstrap, shutdown } from "../runtime/bootstrap.js";
|
|
9
|
+
import {
|
|
10
|
+
getSyncStatus,
|
|
11
|
+
pushResources,
|
|
12
|
+
pullResources,
|
|
13
|
+
getConflicts,
|
|
14
|
+
resolveConflict,
|
|
15
|
+
getSyncLog,
|
|
16
|
+
clearSyncData,
|
|
17
|
+
registerResource,
|
|
18
|
+
getAllSyncStates,
|
|
19
|
+
} from "../lib/sync-manager.js";
|
|
20
|
+
|
|
21
|
+
export function registerSyncCommand(program) {
|
|
22
|
+
const sync = program
|
|
23
|
+
.command("sync")
|
|
24
|
+
.description("File and knowledge synchronization");
|
|
25
|
+
|
|
26
|
+
// sync status
|
|
27
|
+
sync
|
|
28
|
+
.command("status", { isDefault: true })
|
|
29
|
+
.description("Show sync status")
|
|
30
|
+
.option("--json", "Output as JSON")
|
|
31
|
+
.action(async (options) => {
|
|
32
|
+
try {
|
|
33
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
34
|
+
if (!ctx.db) {
|
|
35
|
+
logger.error("Database not available");
|
|
36
|
+
process.exit(1);
|
|
37
|
+
}
|
|
38
|
+
const db = ctx.db.getDatabase();
|
|
39
|
+
const status = getSyncStatus(db);
|
|
40
|
+
|
|
41
|
+
if (options.json) {
|
|
42
|
+
console.log(JSON.stringify(status, null, 2));
|
|
43
|
+
} else {
|
|
44
|
+
logger.log(chalk.bold("Sync Status:\n"));
|
|
45
|
+
logger.log(` ${chalk.bold("Resources:")} ${status.totalResources}`);
|
|
46
|
+
logger.log(
|
|
47
|
+
` ${chalk.bold("Pending:")} ${chalk.yellow(status.pending)}`,
|
|
48
|
+
);
|
|
49
|
+
logger.log(
|
|
50
|
+
` ${chalk.bold("Synced:")} ${chalk.green(status.synced)}`,
|
|
51
|
+
);
|
|
52
|
+
logger.log(
|
|
53
|
+
` ${chalk.bold("Conflicts:")} ${status.conflicts > 0 ? chalk.red(status.conflicts) : "0"}`,
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
await shutdown();
|
|
58
|
+
} catch (err) {
|
|
59
|
+
logger.error(`Failed: ${err.message}`);
|
|
60
|
+
process.exit(1);
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
// sync push
|
|
65
|
+
sync
|
|
66
|
+
.command("push")
|
|
67
|
+
.description("Push local changes to remote")
|
|
68
|
+
.option("--type <type>", "Resource type to push")
|
|
69
|
+
.action(async (options) => {
|
|
70
|
+
try {
|
|
71
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
72
|
+
if (!ctx.db) {
|
|
73
|
+
logger.error("Database not available");
|
|
74
|
+
process.exit(1);
|
|
75
|
+
}
|
|
76
|
+
const db = ctx.db.getDatabase();
|
|
77
|
+
const result = pushResources(db, options.type);
|
|
78
|
+
logger.success(`Pushed ${result.pushed}/${result.total} resources`);
|
|
79
|
+
await shutdown();
|
|
80
|
+
} catch (err) {
|
|
81
|
+
logger.error(`Failed: ${err.message}`);
|
|
82
|
+
process.exit(1);
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
// sync pull
|
|
87
|
+
sync
|
|
88
|
+
.command("pull")
|
|
89
|
+
.description("Pull remote changes to local")
|
|
90
|
+
.option("--type <type>", "Resource type to pull")
|
|
91
|
+
.action(async (options) => {
|
|
92
|
+
try {
|
|
93
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
94
|
+
if (!ctx.db) {
|
|
95
|
+
logger.error("Database not available");
|
|
96
|
+
process.exit(1);
|
|
97
|
+
}
|
|
98
|
+
const db = ctx.db.getDatabase();
|
|
99
|
+
const result = pullResources(db, options.type);
|
|
100
|
+
logger.success(
|
|
101
|
+
`Checked ${result.checked} resources, updated ${result.updated}`,
|
|
102
|
+
);
|
|
103
|
+
await shutdown();
|
|
104
|
+
} catch (err) {
|
|
105
|
+
logger.error(`Failed: ${err.message}`);
|
|
106
|
+
process.exit(1);
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
// sync conflicts
|
|
111
|
+
sync
|
|
112
|
+
.command("conflicts")
|
|
113
|
+
.description("Show sync conflicts")
|
|
114
|
+
.option("--all", "Include resolved conflicts")
|
|
115
|
+
.option("--json", "Output as JSON")
|
|
116
|
+
.action(async (options) => {
|
|
117
|
+
try {
|
|
118
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
119
|
+
if (!ctx.db) {
|
|
120
|
+
logger.error("Database not available");
|
|
121
|
+
process.exit(1);
|
|
122
|
+
}
|
|
123
|
+
const db = ctx.db.getDatabase();
|
|
124
|
+
const conflicts = getConflicts(db, { resolved: options.all });
|
|
125
|
+
|
|
126
|
+
if (options.json) {
|
|
127
|
+
console.log(JSON.stringify(conflicts, null, 2));
|
|
128
|
+
} else if (conflicts.length === 0) {
|
|
129
|
+
logger.info("No conflicts");
|
|
130
|
+
} else {
|
|
131
|
+
logger.log(chalk.bold(`Conflicts (${conflicts.length}):\n`));
|
|
132
|
+
for (const c of conflicts) {
|
|
133
|
+
const resolved = c.resolution
|
|
134
|
+
? chalk.green("[resolved]")
|
|
135
|
+
: chalk.red("[unresolved]");
|
|
136
|
+
logger.log(` ${chalk.cyan(c.id)} ${resolved}`);
|
|
137
|
+
logger.log(
|
|
138
|
+
` ${chalk.gray(`${c.resource_type}/${c.resource_id}`)}`,
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
await shutdown();
|
|
144
|
+
} catch (err) {
|
|
145
|
+
logger.error(`Failed: ${err.message}`);
|
|
146
|
+
process.exit(1);
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
// sync resolve
|
|
151
|
+
sync
|
|
152
|
+
.command("resolve")
|
|
153
|
+
.description("Resolve a sync conflict")
|
|
154
|
+
.argument("<conflict-id>", "Conflict ID")
|
|
155
|
+
.option("--use <side>", "Use local or remote version", "local")
|
|
156
|
+
.action(async (conflictId, options) => {
|
|
157
|
+
try {
|
|
158
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
159
|
+
if (!ctx.db) {
|
|
160
|
+
logger.error("Database not available");
|
|
161
|
+
process.exit(1);
|
|
162
|
+
}
|
|
163
|
+
const db = ctx.db.getDatabase();
|
|
164
|
+
const ok = resolveConflict(db, conflictId, options.use);
|
|
165
|
+
|
|
166
|
+
if (ok) {
|
|
167
|
+
logger.success(`Conflict resolved (${options.use})`);
|
|
168
|
+
} else {
|
|
169
|
+
logger.error(`Conflict not found: ${conflictId}`);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
await shutdown();
|
|
173
|
+
} catch (err) {
|
|
174
|
+
logger.error(`Failed: ${err.message}`);
|
|
175
|
+
process.exit(1);
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
// sync log
|
|
180
|
+
sync
|
|
181
|
+
.command("log")
|
|
182
|
+
.description("Show sync operation log")
|
|
183
|
+
.option("--limit <n>", "Number of entries", "20")
|
|
184
|
+
.option("--json", "Output as JSON")
|
|
185
|
+
.action(async (options) => {
|
|
186
|
+
try {
|
|
187
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
188
|
+
if (!ctx.db) {
|
|
189
|
+
logger.error("Database not available");
|
|
190
|
+
process.exit(1);
|
|
191
|
+
}
|
|
192
|
+
const db = ctx.db.getDatabase();
|
|
193
|
+
const entries = getSyncLog(db, { limit: parseInt(options.limit) });
|
|
194
|
+
|
|
195
|
+
if (options.json) {
|
|
196
|
+
console.log(JSON.stringify(entries, null, 2));
|
|
197
|
+
} else if (entries.length === 0) {
|
|
198
|
+
logger.info("No sync log entries");
|
|
199
|
+
} else {
|
|
200
|
+
logger.log(chalk.bold(`Sync Log (${entries.length}):\n`));
|
|
201
|
+
for (const e of entries) {
|
|
202
|
+
const statusColor =
|
|
203
|
+
e.status === "success" ? chalk.green : chalk.red;
|
|
204
|
+
logger.log(
|
|
205
|
+
` ${chalk.gray(e.created_at)} ${chalk.bold(e.operation)} ${statusColor(e.status)}`,
|
|
206
|
+
);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
await shutdown();
|
|
211
|
+
} catch (err) {
|
|
212
|
+
logger.error(`Failed: ${err.message}`);
|
|
213
|
+
process.exit(1);
|
|
214
|
+
}
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
// sync clear
|
|
218
|
+
sync
|
|
219
|
+
.command("clear")
|
|
220
|
+
.description("Clear all sync data")
|
|
221
|
+
.option("--force", "Skip confirmation")
|
|
222
|
+
.action(async (options) => {
|
|
223
|
+
try {
|
|
224
|
+
if (!options.force) {
|
|
225
|
+
const { confirm } = await import("@inquirer/prompts");
|
|
226
|
+
const ok = await confirm({
|
|
227
|
+
message: "Clear all sync data? This cannot be undone.",
|
|
228
|
+
});
|
|
229
|
+
if (!ok) {
|
|
230
|
+
logger.info("Cancelled");
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
236
|
+
if (!ctx.db) {
|
|
237
|
+
logger.error("Database not available");
|
|
238
|
+
process.exit(1);
|
|
239
|
+
}
|
|
240
|
+
const db = ctx.db.getDatabase();
|
|
241
|
+
clearSyncData(db);
|
|
242
|
+
logger.success("Sync data cleared");
|
|
243
|
+
await shutdown();
|
|
244
|
+
} catch (err) {
|
|
245
|
+
logger.error(`Failed: ${err.message}`);
|
|
246
|
+
process.exit(1);
|
|
247
|
+
}
|
|
248
|
+
});
|
|
249
|
+
}
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Token usage tracking commands
|
|
3
|
+
* chainlesschain tokens [show|breakdown|recent|reset]
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import chalk from "chalk";
|
|
7
|
+
import { logger } from "../lib/logger.js";
|
|
8
|
+
import { bootstrap, shutdown } from "../runtime/bootstrap.js";
|
|
9
|
+
import {
|
|
10
|
+
getUsageStats,
|
|
11
|
+
getTodayStats,
|
|
12
|
+
getCostBreakdown,
|
|
13
|
+
getRecentUsage,
|
|
14
|
+
} from "../lib/token-tracker.js";
|
|
15
|
+
import {
|
|
16
|
+
getCacheStats,
|
|
17
|
+
clearCache,
|
|
18
|
+
clearExpired,
|
|
19
|
+
} from "../lib/response-cache.js";
|
|
20
|
+
|
|
21
|
+
export function registerTokensCommand(program) {
|
|
22
|
+
const tokens = program
|
|
23
|
+
.command("tokens")
|
|
24
|
+
.description("LLM token usage tracking and response cache");
|
|
25
|
+
|
|
26
|
+
// tokens show (default)
|
|
27
|
+
tokens
|
|
28
|
+
.command("show", { isDefault: true })
|
|
29
|
+
.description("Show token usage summary")
|
|
30
|
+
.option("--period <period>", "Time period: today, week, month, all", "all")
|
|
31
|
+
.option("--json", "Output as JSON")
|
|
32
|
+
.action(async (options) => {
|
|
33
|
+
try {
|
|
34
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
35
|
+
if (!ctx.db) {
|
|
36
|
+
logger.error("Database not available");
|
|
37
|
+
process.exit(1);
|
|
38
|
+
}
|
|
39
|
+
const db = ctx.db.getDatabase();
|
|
40
|
+
|
|
41
|
+
let statsOptions = {};
|
|
42
|
+
const now = new Date();
|
|
43
|
+
if (options.period === "today") {
|
|
44
|
+
statsOptions.startDate = now.toISOString().slice(0, 10);
|
|
45
|
+
} else if (options.period === "week") {
|
|
46
|
+
const weekAgo = new Date(now - 7 * 24 * 60 * 60 * 1000);
|
|
47
|
+
statsOptions.startDate = weekAgo.toISOString().slice(0, 10);
|
|
48
|
+
} else if (options.period === "month") {
|
|
49
|
+
const monthAgo = new Date(now - 30 * 24 * 60 * 60 * 1000);
|
|
50
|
+
statsOptions.startDate = monthAgo.toISOString().slice(0, 10);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const stats = getUsageStats(db, statsOptions);
|
|
54
|
+
const todayStats = getTodayStats(db);
|
|
55
|
+
|
|
56
|
+
if (options.json) {
|
|
57
|
+
console.log(JSON.stringify({ stats, today: todayStats }, null, 2));
|
|
58
|
+
} else {
|
|
59
|
+
logger.log(chalk.bold(`Token Usage (${options.period}):\n`));
|
|
60
|
+
logger.log(` Total calls: ${chalk.cyan(stats.total_calls)}`);
|
|
61
|
+
logger.log(
|
|
62
|
+
` Input tokens: ${chalk.cyan(stats.total_input_tokens.toLocaleString())}`,
|
|
63
|
+
);
|
|
64
|
+
logger.log(
|
|
65
|
+
` Output tokens: ${chalk.cyan(stats.total_output_tokens.toLocaleString())}`,
|
|
66
|
+
);
|
|
67
|
+
logger.log(
|
|
68
|
+
` Total tokens: ${chalk.cyan(stats.total_tokens.toLocaleString())}`,
|
|
69
|
+
);
|
|
70
|
+
logger.log(
|
|
71
|
+
` Total cost: ${chalk.yellow("$" + stats.total_cost_usd.toFixed(4))}`,
|
|
72
|
+
);
|
|
73
|
+
logger.log(
|
|
74
|
+
` Avg response: ${chalk.gray(Math.round(stats.avg_response_time_ms) + "ms")}`,
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
if (options.period !== "today") {
|
|
78
|
+
logger.log(chalk.bold("\n Today:"));
|
|
79
|
+
logger.log(
|
|
80
|
+
` Calls: ${chalk.cyan(todayStats.total_calls)} Tokens: ${chalk.cyan(todayStats.total_tokens.toLocaleString())} Cost: ${chalk.yellow("$" + todayStats.total_cost_usd.toFixed(4))}`,
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
await shutdown();
|
|
86
|
+
} catch (err) {
|
|
87
|
+
logger.error(`Failed to get token stats: ${err.message}`);
|
|
88
|
+
process.exit(1);
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
// tokens breakdown
|
|
93
|
+
tokens
|
|
94
|
+
.command("breakdown")
|
|
95
|
+
.description("Show cost breakdown by provider/model")
|
|
96
|
+
.option("--json", "Output as JSON")
|
|
97
|
+
.action(async (options) => {
|
|
98
|
+
try {
|
|
99
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
100
|
+
if (!ctx.db) {
|
|
101
|
+
logger.error("Database not available");
|
|
102
|
+
process.exit(1);
|
|
103
|
+
}
|
|
104
|
+
const db = ctx.db.getDatabase();
|
|
105
|
+
const breakdown = getCostBreakdown(db);
|
|
106
|
+
|
|
107
|
+
if (options.json) {
|
|
108
|
+
console.log(JSON.stringify(breakdown, null, 2));
|
|
109
|
+
} else if (breakdown.length === 0) {
|
|
110
|
+
logger.info("No usage data yet");
|
|
111
|
+
} else {
|
|
112
|
+
logger.log(chalk.bold("Cost Breakdown:\n"));
|
|
113
|
+
for (const row of breakdown) {
|
|
114
|
+
logger.log(
|
|
115
|
+
` ${chalk.cyan(row.provider)}/${chalk.white(row.model)}`,
|
|
116
|
+
);
|
|
117
|
+
logger.log(
|
|
118
|
+
` Calls: ${row.calls} Tokens: ${row.total_tokens.toLocaleString()} Cost: ${chalk.yellow("$" + row.cost_usd.toFixed(4))}`,
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
await shutdown();
|
|
124
|
+
} catch (err) {
|
|
125
|
+
logger.error(`Failed: ${err.message}`);
|
|
126
|
+
process.exit(1);
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
// tokens recent
|
|
131
|
+
tokens
|
|
132
|
+
.command("recent")
|
|
133
|
+
.description("Show recent LLM calls")
|
|
134
|
+
.option("-n, --limit <n>", "Number of entries", "10")
|
|
135
|
+
.option("--json", "Output as JSON")
|
|
136
|
+
.action(async (options) => {
|
|
137
|
+
try {
|
|
138
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
139
|
+
if (!ctx.db) {
|
|
140
|
+
logger.error("Database not available");
|
|
141
|
+
process.exit(1);
|
|
142
|
+
}
|
|
143
|
+
const db = ctx.db.getDatabase();
|
|
144
|
+
const entries = getRecentUsage(
|
|
145
|
+
db,
|
|
146
|
+
Math.max(1, parseInt(options.limit) || 10),
|
|
147
|
+
);
|
|
148
|
+
|
|
149
|
+
if (options.json) {
|
|
150
|
+
console.log(JSON.stringify(entries, null, 2));
|
|
151
|
+
} else if (entries.length === 0) {
|
|
152
|
+
logger.info("No recent usage");
|
|
153
|
+
} else {
|
|
154
|
+
logger.log(chalk.bold("Recent LLM Calls:\n"));
|
|
155
|
+
for (const e of entries) {
|
|
156
|
+
logger.log(
|
|
157
|
+
` ${chalk.gray(e.created_at)} ${chalk.cyan(e.provider)}/${chalk.white(e.model)} ${e.total_tokens} tokens ${chalk.yellow("$" + e.cost_usd.toFixed(4))} ${chalk.gray(e.response_time_ms + "ms")}`,
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
await shutdown();
|
|
163
|
+
} catch (err) {
|
|
164
|
+
logger.error(`Failed: ${err.message}`);
|
|
165
|
+
process.exit(1);
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
// tokens cache
|
|
170
|
+
tokens
|
|
171
|
+
.command("cache")
|
|
172
|
+
.description("Show response cache statistics")
|
|
173
|
+
.option("--clear", "Clear all cached responses")
|
|
174
|
+
.option("--cleanup", "Remove expired entries only")
|
|
175
|
+
.option("--json", "Output as JSON")
|
|
176
|
+
.action(async (options) => {
|
|
177
|
+
try {
|
|
178
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
179
|
+
if (!ctx.db) {
|
|
180
|
+
logger.error("Database not available");
|
|
181
|
+
process.exit(1);
|
|
182
|
+
}
|
|
183
|
+
const db = ctx.db.getDatabase();
|
|
184
|
+
|
|
185
|
+
if (options.clear) {
|
|
186
|
+
clearCache(db);
|
|
187
|
+
logger.success("Response cache cleared");
|
|
188
|
+
} else if (options.cleanup) {
|
|
189
|
+
const removed = clearExpired(db);
|
|
190
|
+
logger.success(`Removed ${removed} expired cache entries`);
|
|
191
|
+
} else {
|
|
192
|
+
const stats = getCacheStats(db);
|
|
193
|
+
if (options.json) {
|
|
194
|
+
console.log(JSON.stringify(stats, null, 2));
|
|
195
|
+
} else {
|
|
196
|
+
logger.log(chalk.bold("Response Cache:\n"));
|
|
197
|
+
logger.log(` Entries: ${chalk.cyan(stats.total_entries)}`);
|
|
198
|
+
logger.log(` Total hits: ${chalk.cyan(stats.total_hits)}`);
|
|
199
|
+
logger.log(
|
|
200
|
+
` Tokens saved: ${chalk.green(stats.total_tokens_saved.toLocaleString())}`,
|
|
201
|
+
);
|
|
202
|
+
logger.log(
|
|
203
|
+
` Expired: ${chalk.gray(stats.expired_entries)}`,
|
|
204
|
+
);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
await shutdown();
|
|
209
|
+
} catch (err) {
|
|
210
|
+
logger.error(`Failed: ${err.message}`);
|
|
211
|
+
process.exit(1);
|
|
212
|
+
}
|
|
213
|
+
});
|
|
214
|
+
}
|