trucontext 0.8.0 → 0.8.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/bin/cli.js CHANGED
@@ -20,6 +20,7 @@ import { relationshipTypesListCommand } from '../src/commands/relationship-types
20
20
  import { docsListCommand, docsShowCommand } from '../src/commands/docs.js';
21
21
  import { registerRootsCommand } from '../src/commands/roots.js';
22
22
  import { registerCuriosityCommand } from '../src/commands/curiosity.js';
23
+ import { usageCommand, usageHistoryCommand, plansCommand } from '../src/commands/billing.js';
23
24
 
24
25
  program
25
26
  .name('trucontext')
@@ -134,12 +135,6 @@ entities.command('edges <entityId>')
134
135
  .description('List edges for an entity')
135
136
  .action(entitiesEdgesCommand);
136
137
 
137
- // Root Nodes
138
- registerRootsCommand(program);
139
-
140
- // Curiosity
141
- registerCuriosityCommand(program);
142
-
143
138
  // Recipes
144
139
  const recipes = program.command('recipes').description('Manage recipes').action(function() { this.help(); });
145
140
  recipes.command('list')
@@ -185,4 +180,22 @@ const docs = program.command('docs').description('Browse API documentation').act
185
180
  docs.command('list').description('List all doc sections').action(docsListCommand);
186
181
  docs.command('show <slug>').description('Show a specific doc section').action(docsShowCommand);
187
182
 
183
+ // Billing & Usage
184
+ const billing = program.command('billing').description('View usage and billing').action(function() { this.help(); });
185
+ billing.command('usage')
186
+ .description('View current usage metrics')
187
+ .option('-p, --period <period>', 'Period: current, today, week, month, year', 'current')
188
+ .action(usageCommand);
189
+ billing.command('history')
190
+ .description('View usage history with time series data')
191
+ .option('-p, --period <period>', 'Period: today, week, month, 6months, year, ytd', 'month')
192
+ .action(usageHistoryCommand);
193
+ billing.command('plans')
194
+ .description('View available plans')
195
+ .action(plansCommand);
196
+
197
+ // Root nodes + Curiosity (registered as subcommand groups)
198
+ registerRootsCommand(program);
199
+ registerCuriosityCommand(program);
200
+
188
201
  program.parse();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "trucontext",
3
- "version": "0.8.0",
3
+ "version": "0.8.1",
4
4
  "description": "TruContext CLI — contextual memory for AI applications",
5
5
  "type": "module",
6
6
  "bin": {
@@ -0,0 +1,134 @@
1
+ // billing.js — Usage and billing commands
2
+ //
3
+ // View API call counts, token usage, storage, and processing time.
4
+ // Data is tracked per-app per-period (daily/monthly).
5
+
6
+ import chalk from 'chalk';
7
+ import { controlPlane } from '../client.js';
8
+ import { getActiveApp } from '../config.js';
9
+
10
+ function requireApp() {
11
+ const appId = getActiveApp();
12
+ if (!appId) {
13
+ console.error(chalk.red('No active app. Run: trucontext use <app>'));
14
+ process.exit(1);
15
+ }
16
+ return appId;
17
+ }
18
+
19
+ function formatNumber(n) {
20
+ if (n === null || n === undefined) return '0';
21
+ if (n >= 1_000_000) return `${(n / 1_000_000).toFixed(1)}M`;
22
+ if (n >= 1_000) return `${(n / 1_000).toFixed(1)}K`;
23
+ return String(n);
24
+ }
25
+
26
+ function formatBytes(bytes) {
27
+ if (!bytes) return '0 B';
28
+ if (bytes >= 1_073_741_824) return `${(bytes / 1_073_741_824).toFixed(1)} GB`;
29
+ if (bytes >= 1_048_576) return `${(bytes / 1_048_576).toFixed(1)} MB`;
30
+ if (bytes >= 1024) return `${(bytes / 1024).toFixed(1)} KB`;
31
+ return `${bytes} B`;
32
+ }
33
+
34
+ function formatMs(ms) {
35
+ if (!ms) return '0.0 min';
36
+ if (ms >= 60_000) return `${(ms / 60_000).toFixed(1)} min`;
37
+ return `${(ms / 1000).toFixed(1)} sec`;
38
+ }
39
+
40
+ export async function usageCommand(options) {
41
+ try {
42
+ const appId = requireApp();
43
+ const period = options.period || 'current';
44
+ const res = await controlPlane('GET', `/billing/usage/${appId}?period=${period}`);
45
+ const usage = res.data?.data || res.data || {};
46
+
47
+ if (!usage || (!usage.apiCalls && !usage.llmInputTokens && !usage.processingMs)) {
48
+ console.log(chalk.yellow('No usage data available.'));
49
+ return;
50
+ }
51
+
52
+ console.log(chalk.bold('Usage Summary') + chalk.dim(` — ${period}`));
53
+ console.log();
54
+ console.log(` API Calls: ${chalk.bold(formatNumber(usage.apiCalls || 0))}${usage.limits?.apiCalls ? chalk.dim(` of ${formatNumber(usage.limits.apiCalls)}`) : ''}`);
55
+ console.log(` Tokens: ${chalk.bold(formatNumber((usage.llmInputTokens || 0) + (usage.llmOutputTokens || 0)))}${usage.limits?.llmTokens ? chalk.dim(` of ${formatNumber(usage.limits.llmTokens)}`) : ''}`);
56
+ console.log(` Input: ${chalk.dim(formatNumber(usage.llmInputTokens || 0))}`);
57
+ console.log(` Output: ${chalk.dim(formatNumber(usage.llmOutputTokens || 0))}`);
58
+ console.log(` Compute Units: ${chalk.bold(formatNumber(usage.computeUnits || 0))}`);
59
+ console.log(` Storage: ${chalk.bold(formatBytes(usage.storageBytes || 0))}${usage.limits?.storageGb ? chalk.dim(` of ${usage.limits.storageGb} GB`) : ''}`);
60
+ console.log(` Processing: ${chalk.bold(formatMs(usage.processingMs || 0))}${usage.limits?.processingMinutes ? chalk.dim(` of ${usage.limits.processingMinutes} min`) : ''}`);
61
+
62
+ if (usage.planTier) {
63
+ console.log();
64
+ console.log(` Plan: ${chalk.cyan(usage.planTier)}`);
65
+ }
66
+ } catch (err) {
67
+ console.error(chalk.red(`Failed: ${err.message}`));
68
+ process.exit(1);
69
+ }
70
+ }
71
+
72
+ export async function usageHistoryCommand(options) {
73
+ try {
74
+ const appId = requireApp();
75
+ const period = options.period || 'month';
76
+ const res = await controlPlane('GET', `/billing/usage/${appId}/history?period=${period}&sources=true`);
77
+ const historyData = res.data?.data || res.data || {};
78
+ const history = historyData.history || [];
79
+
80
+ if (history.length === 0) {
81
+ console.log(chalk.yellow(`No usage history for period: ${period}`));
82
+ return;
83
+ }
84
+
85
+ console.log(chalk.bold(`Usage History`) + chalk.dim(` — ${period} (${history.length} entries)`));
86
+ console.log();
87
+ console.log(chalk.dim(' Date API Calls Tokens Source'));
88
+ console.log(chalk.dim(' ──── ───────── ────── ──────'));
89
+
90
+ for (const entry of history) {
91
+ const date = entry.date || entry.period || '—';
92
+ const calls = formatNumber(entry.apiCalls || 0);
93
+ const tokens = formatNumber((entry.inputTokens || 0) + (entry.outputTokens || 0));
94
+ const source = entry.source || '—';
95
+ console.log(` ${date.padEnd(18)} ${calls.padEnd(12)} ${tokens.padEnd(12)} ${chalk.dim(source)}`);
96
+ }
97
+ } catch (err) {
98
+ console.error(chalk.red(`Failed: ${err.message}`));
99
+ process.exit(1);
100
+ }
101
+ }
102
+
103
+ export async function plansCommand() {
104
+ try {
105
+ const res = await controlPlane('GET', '/billing/plans');
106
+ const plansData = res.data?.data || res.data || {};
107
+ const plans = plansData.plans || [];
108
+
109
+ if (plans.length === 0) {
110
+ console.log(chalk.yellow('No plans available.'));
111
+ return;
112
+ }
113
+
114
+ console.log(chalk.bold('Available Plans'));
115
+ console.log();
116
+
117
+ for (const plan of plans) {
118
+ const current = plan.current ? chalk.green(' (current)') : '';
119
+ console.log(` ${chalk.bold(plan.name || plan.tier)}${current}`);
120
+ if (plan.price) console.log(` Price: ${plan.price}`);
121
+ if (plan.description) console.log(` ${chalk.dim(plan.description)}`);
122
+ if (plan.limits) {
123
+ const limits = plan.limits;
124
+ if (limits.apiCalls) console.log(` API Calls: ${formatNumber(limits.apiCalls)}/mo`);
125
+ if (limits.tokens) console.log(` Tokens: ${formatNumber(limits.tokens)}/mo`);
126
+ if (limits.storageBytes) console.log(` Storage: ${formatBytes(limits.storageBytes)}`);
127
+ }
128
+ console.log();
129
+ }
130
+ } catch (err) {
131
+ console.error(chalk.red(`Failed: ${err.message}`));
132
+ process.exit(1);
133
+ }
134
+ }