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 +20 -3
- package/package.json +1 -1
- package/src/commands/billing.js +134 -0
- package/src/commands/curiosity.js +97 -0
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
|
@@ -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
|
+
}
|