trucontext 0.7.1 → 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
@@ -19,6 +19,8 @@ import { recipesListCommand, recipesGetCommand, recipesCreateCommand, recipesDel
19
19
  import { relationshipTypesListCommand } from '../src/commands/relationship-types.js';
20
20
  import { docsListCommand, docsShowCommand } from '../src/commands/docs.js';
21
21
  import { registerRootsCommand } from '../src/commands/roots.js';
22
+ import { registerCuriosityCommand } from '../src/commands/curiosity.js';
23
+ import { usageCommand, usageHistoryCommand, plansCommand } from '../src/commands/billing.js';
22
24
 
23
25
  program
24
26
  .name('trucontext')
@@ -133,9 +135,6 @@ entities.command('edges <entityId>')
133
135
  .description('List edges for an entity')
134
136
  .action(entitiesEdgesCommand);
135
137
 
136
- // Root Nodes
137
- registerRootsCommand(program);
138
-
139
138
  // Recipes
140
139
  const recipes = program.command('recipes').description('Manage recipes').action(function() { this.help(); });
141
140
  recipes.command('list')
@@ -181,4 +180,22 @@ const docs = program.command('docs').description('Browse API documentation').act
181
180
  docs.command('list').description('List all doc sections').action(docsListCommand);
182
181
  docs.command('show <slug>').description('Show a specific doc section').action(docsShowCommand);
183
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
+
184
201
  program.parse();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "trucontext",
3
- "version": "0.7.1",
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
+ }
@@ -0,0 +1,97 @@
1
+ // curiosity.js — Curiosity Engine commands
2
+ //
3
+ // Trigger curiosity analysis and view knowledge gaps for a root node.
4
+ // Curiosity analysis is always scoped to a root node's ego network.
5
+
6
+ import chalk from 'chalk';
7
+ import { controlPlane } from '../client.js';
8
+ import { getConfig, getActiveApp } from '../config.js';
9
+
10
+ export function registerCuriosityCommand(program) {
11
+ const curiosity = program
12
+ .command('curiosity')
13
+ .description('Curiosity engine — discover knowledge gaps')
14
+ .action(function() { this.help(); });
15
+
16
+ // List curiosity packets
17
+ curiosity
18
+ .command('list')
19
+ .description('List curiosity packets for a root node')
20
+ .option('--root <rootId>', 'Root node ID (uses active root if not specified)')
21
+ .option('--status <status>', 'Filter by status: PENDING, DISMISSED', 'PENDING')
22
+ .option('--limit <n>', 'Max packets to return', '20')
23
+ .action(async (opts) => {
24
+ const appId = getActiveApp();
25
+ const config = getConfig();
26
+ const rootId = opts.root || config.activeRoot;
27
+ if (!rootId) {
28
+ console.error(chalk.red('No root specified. Use --root <id> or set active root with: trucontext roots use <id>'));
29
+ process.exit(1);
30
+ }
31
+
32
+ try {
33
+ const res = await controlPlane('GET', `/apps/${appId}/mind/curiosity?root_id=${rootId}&status=${opts.status}&limit=${opts.limit}`);
34
+ const packets = res.data?.packets || [];
35
+
36
+ if (packets.length === 0) {
37
+ console.log(chalk.dim(`No ${opts.status.toLowerCase()} curiosity packets for root: ${rootId}`));
38
+ return;
39
+ }
40
+
41
+ console.log(chalk.bold(`Curiosity Packets (${packets.length}) — root: ${rootId}\n`));
42
+ for (const p of packets) {
43
+ const typeColor = p.curiosity_type === 'EXTERNAL' ? chalk.cyan : chalk.yellow;
44
+ console.log(` ${typeColor(`[${p.gap_type}]`)} ${p.question}`);
45
+ if (p.why_it_matters) console.log(chalk.dim(` Why: ${p.why_it_matters}`));
46
+ if (p.suggested_approach) console.log(chalk.dim(` Approach: ${p.suggested_approach}`));
47
+ console.log(chalk.dim(` Priority: ${p.priority_score?.toFixed(2) || '?'} | Status: ${p.status} | ID: ${p.packet_id}`));
48
+ console.log();
49
+ }
50
+ } catch (err) {
51
+ console.error(chalk.red('Failed:'), err.response?.data?.error || err.message);
52
+ process.exit(1);
53
+ }
54
+ });
55
+
56
+ // Trigger on-demand curiosity evaluation
57
+ curiosity
58
+ .command('trigger')
59
+ .description('Run curiosity analysis on a root node')
60
+ .option('--root <rootId>', 'Root node ID (uses active root if not specified)')
61
+ .action(async (opts) => {
62
+ const appId = getActiveApp();
63
+ const config = getConfig();
64
+ const rootId = opts.root || config.activeRoot;
65
+ if (!rootId) {
66
+ console.error(chalk.red('No root specified. Use --root <id> or set active root with: trucontext roots use <id>'));
67
+ process.exit(1);
68
+ }
69
+
70
+ try {
71
+ console.log(chalk.dim(`Triggering curiosity analysis for root: ${rootId}...`));
72
+ await controlPlane('POST', `/apps/${appId}/mind/curiosity/trigger`, {
73
+ root_id: rootId,
74
+ });
75
+ console.log(chalk.green(`Curiosity analysis started for root: ${rootId}`));
76
+ console.log(chalk.dim(`Results will appear shortly. Check with: trucontext curiosity list --root ${rootId}`));
77
+ } catch (err) {
78
+ console.error(chalk.red('Failed:'), err.response?.data?.error || err.message);
79
+ process.exit(1);
80
+ }
81
+ });
82
+
83
+ // Dismiss a curiosity packet
84
+ curiosity
85
+ .command('dismiss <packetId>')
86
+ .description('Dismiss a curiosity packet')
87
+ .action(async (packetId) => {
88
+ const appId = getActiveApp();
89
+ try {
90
+ await controlPlane('PUT', `/apps/${appId}/mind/curiosity/${packetId}/dismiss`);
91
+ console.log(chalk.green(`Dismissed: ${packetId}`));
92
+ } catch (err) {
93
+ console.error(chalk.red('Failed:'), err.response?.data?.error || err.message);
94
+ process.exit(1);
95
+ }
96
+ });
97
+ }