@cloudverse/aix-cli 1.0.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.
package/README.md ADDED
@@ -0,0 +1,91 @@
1
+ # AIX CLI
2
+
3
+ Command-line interface for the AIX Brain Decision Engine. Decision-only surface — no gateway behavior, no prompt proxying.
4
+
5
+ ## Setup
6
+
7
+ ```bash
8
+ # From repo root
9
+ cd packages/aix-cli
10
+
11
+ # Set environment
12
+ export AIX_API_URL=http://localhost:5000 # or https://aix.cloudverse.ai
13
+ export AIX_TOKEN=your_api_key_here # AIX API key or legacy key
14
+ ```
15
+
16
+ ## Commands
17
+
18
+ ### `aix decide`
19
+
20
+ Request a routing decision from the AIX Brain.
21
+
22
+ ```bash
23
+ # Basic decision
24
+ npx tsx src/index.ts decide --execution-class conversation
25
+
26
+ # With priority and constraints
27
+ npx tsx src/index.ts decide \
28
+ --execution-class code_generation \
29
+ --priority quality \
30
+ --max-cost-usd 0.05 \
31
+ --max-latency-ms 3000
32
+
33
+ # With token estimates for cost simulation
34
+ npx tsx src/index.ts decide \
35
+ --execution-class conversation \
36
+ --input-tokens 1000 \
37
+ --output-tokens 500
38
+
39
+ # JSON output
40
+ npx tsx src/index.ts decide --execution-class conversation --format json
41
+
42
+ # From intent JSON file
43
+ npx tsx src/index.ts decide --intent-json ./my-request.json
44
+ ```
45
+
46
+ ### `aix explain <decision_id>`
47
+
48
+ Get explanation and trace for a past decision.
49
+
50
+ ```bash
51
+ npx tsx src/index.ts explain dec_abc123
52
+ npx tsx src/index.ts explain dec_abc123 --format json
53
+ ```
54
+
55
+ ### `aix health`
56
+
57
+ Check AIX Brain health and coverage.
58
+
59
+ ```bash
60
+ npx tsx src/index.ts health
61
+ ```
62
+
63
+ ### `aix policy show`
64
+
65
+ Show current org routing policy.
66
+
67
+ ```bash
68
+ npx tsx src/index.ts policy show
69
+ ```
70
+
71
+ ### `aix catalog status`
72
+
73
+ Show catalog status and coverage.
74
+
75
+ ```bash
76
+ npx tsx src/index.ts catalog status
77
+ ```
78
+
79
+ ## Environment Variables
80
+
81
+ | Variable | Required | Default | Description |
82
+ |----------|----------|---------|-------------|
83
+ | `AIX_API_URL` | No | `http://localhost:5000` | AIX server base URL |
84
+ | `AIX_TOKEN` | Yes* | — | API key for authentication |
85
+
86
+ *Not required if server allows unauthenticated access in local dev mode.
87
+
88
+ ## Output Formats
89
+
90
+ - `--format pretty` (default): Human-readable with color coding
91
+ - `--format json`: Raw JSON for piping to other tools
@@ -0,0 +1,5 @@
1
+ export declare function formatDecision(d: any): void;
2
+ export declare function formatExplanation(d: any): void;
3
+ export declare function formatHealth(h: any): void;
4
+ export declare function formatPolicy(p: any): void;
5
+ export declare function formatCatalogStatus(c: any): void;
@@ -0,0 +1,164 @@
1
+ const GREEN = '\x1b[32m';
2
+ const YELLOW = '\x1b[33m';
3
+ const RED = '\x1b[31m';
4
+ const CYAN = '\x1b[36m';
5
+ const DIM = '\x1b[2m';
6
+ const BOLD = '\x1b[1m';
7
+ const RESET = '\x1b[0m';
8
+ function check(ok) {
9
+ return ok ? `${GREEN}OK${RESET}` : `${YELLOW}WARN${RESET}`;
10
+ }
11
+ function statusIcon(status) {
12
+ if (status === 'ok' || status === 'healthy')
13
+ return `${GREEN}OK${RESET}`;
14
+ if (status === 'degraded')
15
+ return `${YELLOW}DEGRADED${RESET}`;
16
+ return `${RED}FAILED${RESET}`;
17
+ }
18
+ export function formatDecision(d) {
19
+ console.log(`\n${BOLD}AIX Brain Decision${RESET}`);
20
+ console.log(`${'─'.repeat(50)}`);
21
+ console.log(` Status: ${statusIcon(d.decision_status)} ${d.degraded_reason || d.degradation_reason || ''}`);
22
+ console.log(` Provider: ${CYAN}${d.provider}${RESET}`);
23
+ console.log(` Model: ${CYAN}${d.model_name || d.model_class}${RESET}`);
24
+ console.log(` Class: ${d.model_class}`);
25
+ console.log(` Confidence: ${d.confidence?.toFixed(2) || 'N/A'}`);
26
+ console.log(` Data Used: ${d.data_used}`);
27
+ console.log(` Decision: ${DIM}${d.decision_id}${RESET}`);
28
+ if (d.reason) {
29
+ console.log(`\n ${DIM}Reason: ${d.reason}${RESET}`);
30
+ }
31
+ if (d.explanation) {
32
+ console.log(` ${DIM}Explanation: ${d.explanation}${RESET}`);
33
+ }
34
+ if (d.score_breakdown) {
35
+ console.log(`\n ${BOLD}Score Breakdown${RESET}`);
36
+ const sb = d.score_breakdown;
37
+ console.log(` Cost: ${sb.cost?.toFixed(3) || '-'}`);
38
+ console.log(` Latency: ${sb.latency?.toFixed(3) || '-'}`);
39
+ console.log(` QSR: ${sb.qsr?.toFixed(3) || '-'}`);
40
+ console.log(` Reliability: ${sb.reliability?.toFixed(3) || '-'}`);
41
+ console.log(` Total: ${BOLD}${sb.total?.toFixed(3) || '-'}${RESET}`);
42
+ if (sb.cost_source) {
43
+ console.log(` Cost Source: ${sb.cost_source}`);
44
+ }
45
+ }
46
+ if (d.alternatives && d.alternatives.length > 0) {
47
+ console.log(`\n ${BOLD}Top Alternatives${RESET}`);
48
+ d.alternatives.slice(0, 3).forEach((alt, i) => {
49
+ console.log(` ${i + 1}. ${alt.provider}/${alt.model_name || alt.model_class} (score: ${alt.score?.toFixed(3)}, data: ${alt.data_used})`);
50
+ });
51
+ }
52
+ console.log('');
53
+ }
54
+ export function formatExplanation(d) {
55
+ console.log(`\n${BOLD}Decision Explanation${RESET}`);
56
+ console.log(`${'─'.repeat(50)}`);
57
+ console.log(` Decision ID: ${d.id || d.decision_id}`);
58
+ console.log(` Execution Class: ${d.execution_class}`);
59
+ console.log(` Provider: ${CYAN}${d.provider}${RESET}`);
60
+ console.log(` Model: ${CYAN}${d.model_name || d.model_class}${RESET}`);
61
+ console.log(` Confidence: ${d.confidence?.toFixed?.(2) || d.confidence || 'N/A'}`);
62
+ console.log(` Data Used: ${d.data_used}`);
63
+ console.log(` Created: ${d.created_at || 'N/A'}`);
64
+ if (d.reason) {
65
+ console.log(`\n ${BOLD}Reason${RESET}`);
66
+ console.log(` ${d.reason}`);
67
+ }
68
+ if (d.score_breakdown) {
69
+ console.log(`\n ${BOLD}Score Breakdown${RESET}`);
70
+ console.log(` ${JSON.stringify(d.score_breakdown, null, 4).split('\n').join('\n ')}`);
71
+ }
72
+ console.log('');
73
+ }
74
+ export function formatHealth(h) {
75
+ console.log(`\n${BOLD}AIX Brain Health${RESET}`);
76
+ console.log(`${'─'.repeat(50)}`);
77
+ console.log(` Overall: ${statusIcon(h.status)}`);
78
+ if (h.execution_class_coverage) {
79
+ const ec = h.execution_class_coverage;
80
+ console.log(`\n ${BOLD}Execution Class Coverage${RESET}`);
81
+ console.log(` Total classes: ${ec.total}`);
82
+ console.log(` With capabilities: ${ec.with_capability_rows} ${check(ec.with_capability_rows > 0)}`);
83
+ if (ec.details && ec.details.length > 0) {
84
+ ec.details.forEach((d) => {
85
+ const icon = d.supported_rows > 0 ? `${GREEN}OK${RESET}` : `${RED}--${RESET}`;
86
+ console.log(` ${icon} ${d.name} (${d.supported_rows} providers)`);
87
+ });
88
+ }
89
+ }
90
+ if (h.routable_models) {
91
+ console.log(`\n ${BOLD}Routable Models${RESET}`);
92
+ console.log(` Active: ${h.routable_models.total} ${check(h.routable_models.total > 0)}`);
93
+ }
94
+ if (h.provider_defaults) {
95
+ console.log(`\n ${BOLD}Provider Defaults${RESET}`);
96
+ console.log(` Count: ${h.provider_defaults.count}`);
97
+ console.log(` Coverage: ${check(h.provider_defaults.coverage === 'ok')}`);
98
+ console.log(` Safe Fallback: ${check(h.provider_defaults.safe_fallback === 'ok')}`);
99
+ }
100
+ if (h.pricing) {
101
+ console.log(`\n ${BOLD}Pricing${RESET}`);
102
+ console.log(` Coverage: ${h.pricing.coverage}`);
103
+ console.log(` Last Sync: ${h.pricing.last_sync || 'never'}`);
104
+ }
105
+ console.log(`\n Discovered Models: ${h.discovered_models || 0}`);
106
+ console.log('');
107
+ }
108
+ export function formatPolicy(p) {
109
+ console.log(`\n${BOLD}AIX Brain Routing Policy${RESET}`);
110
+ console.log(`${'─'.repeat(50)}`);
111
+ console.log(` Org: ${p.org_id}`);
112
+ console.log(` Source: ${p.source}`);
113
+ if (p.policy) {
114
+ const pol = p.policy;
115
+ if (pol.allowed_providers && pol.allowed_providers.length > 0) {
116
+ console.log(`\n ${BOLD}Allowed Providers${RESET}`);
117
+ pol.allowed_providers.forEach((prov) => {
118
+ console.log(` - ${prov}`);
119
+ });
120
+ }
121
+ else {
122
+ console.log(`\n Allowed Providers: ${DIM}all (no restrictions)${RESET}`);
123
+ }
124
+ if (pol.default_priority) {
125
+ console.log(` Default Priority: ${pol.default_priority}`);
126
+ }
127
+ if (pol.mode) {
128
+ console.log(` Routing Mode: ${pol.mode}`);
129
+ }
130
+ if (pol.budget_guard) {
131
+ console.log(`\n ${BOLD}Budget Guard${RESET}`);
132
+ console.log(` ${JSON.stringify(pol.budget_guard, null, 4).split('\n').join('\n ')}`);
133
+ }
134
+ }
135
+ console.log(`\n Connected Providers: ${p.connected_providers_count}`);
136
+ if (p.connected_providers && p.connected_providers.length > 0) {
137
+ p.connected_providers.forEach((prov) => {
138
+ console.log(` - ${prov}`);
139
+ });
140
+ }
141
+ console.log('');
142
+ }
143
+ export function formatCatalogStatus(c) {
144
+ console.log(`\n${BOLD}AIX Brain Catalog Status${RESET}`);
145
+ console.log(`${'─'.repeat(50)}`);
146
+ console.log(` Discovered Models: ${c.discovered_model_count}`);
147
+ console.log(` Routable Models: ${c.routable_model_count}`);
148
+ console.log(` Provider Defaults: ${c.defaults_count}`);
149
+ if (c.activation_gates) {
150
+ const ag = c.activation_gates;
151
+ console.log(`\n ${BOLD}Activation Gates${RESET}`);
152
+ console.log(` Pricing Coverage: ${check(ag.has_pricing_coverage)}`);
153
+ console.log(` Capability Rows: ${check(ag.has_capability_rows)}`);
154
+ console.log(` Execution Classes: ${ag.execution_classes_with_coverage}/${ag.execution_classes_total} with coverage`);
155
+ }
156
+ if (c.pricing) {
157
+ console.log(`\n ${BOLD}Pricing Catalog${RESET}`);
158
+ console.log(` LLM Models: ${c.pricing.llm_models}`);
159
+ console.log(` GPU SKUs: ${c.pricing.gpu_skus}`);
160
+ console.log(` Providers: ${c.pricing.providers}`);
161
+ console.log(` Last Sync: ${c.pricing.last_sync_at || 'never'}`);
162
+ }
163
+ console.log('');
164
+ }
package/dist/http.d.ts ADDED
@@ -0,0 +1,12 @@
1
+ export interface HttpConfig {
2
+ baseUrl: string;
3
+ token: string | null;
4
+ }
5
+ export declare function getConfig(): HttpConfig;
6
+ export interface HttpResponse<T = any> {
7
+ ok: boolean;
8
+ status: number;
9
+ data: T;
10
+ }
11
+ export declare function request<T = any>(method: 'GET' | 'POST', path: string, body?: any): Promise<HttpResponse<T>>;
12
+ export declare function exitWithError(message: string, details?: string): never;
package/dist/http.js ADDED
@@ -0,0 +1,37 @@
1
+ const DEFAULT_BASE_URL = 'http://localhost:5000';
2
+ export function getConfig() {
3
+ return {
4
+ baseUrl: (process.env.AIX_API_URL || DEFAULT_BASE_URL).replace(/\/$/, ''),
5
+ token: process.env.AIX_TOKEN || null,
6
+ };
7
+ }
8
+ export async function request(method, path, body) {
9
+ const config = getConfig();
10
+ const url = `${config.baseUrl}${path}`;
11
+ const headers = {
12
+ 'Content-Type': 'application/json',
13
+ 'Accept': 'application/json',
14
+ };
15
+ if (config.token) {
16
+ headers['Authorization'] = `Bearer ${config.token}`;
17
+ }
18
+ const res = await fetch(url, {
19
+ method,
20
+ headers,
21
+ body: body ? JSON.stringify(body) : undefined,
22
+ });
23
+ let data;
24
+ try {
25
+ data = await res.json();
26
+ }
27
+ catch {
28
+ data = { error: `HTTP ${res.status}: ${res.statusText}` };
29
+ }
30
+ return { ok: res.ok, status: res.status, data };
31
+ }
32
+ export function exitWithError(message, details) {
33
+ console.error(`\x1b[31mError:\x1b[0m ${message}`);
34
+ if (details)
35
+ console.error(` ${details}`);
36
+ process.exit(1);
37
+ }
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,144 @@
1
+ #!/usr/bin/env node
2
+ import { Command } from 'commander';
3
+ import { request, exitWithError } from './http.js';
4
+ import { formatDecision, formatExplanation, formatHealth, formatPolicy, formatCatalogStatus } from './formatters.js';
5
+ const program = new Command();
6
+ program
7
+ .name('aix')
8
+ .description('AIX Brain Decision Engine CLI — decision-only surface')
9
+ .version('1.0.0');
10
+ program
11
+ .command('decide')
12
+ .description('Request a routing decision from the AIX Brain')
13
+ .requiredOption('--execution-class <string>', 'Execution class (e.g., conversation, code_generation)')
14
+ .option('--priority <string>', 'Scoring priority: cost | balanced | quality | latency', 'balanced')
15
+ .option('--allow-substitution <boolean>', 'Allow model substitution', 'true')
16
+ .option('--max-cost-usd <number>', 'Max cost constraint in USD')
17
+ .option('--max-latency-ms <number>', 'Max latency constraint in ms')
18
+ .option('--min-qsr <number>', 'Minimum quality score ratio')
19
+ .option('--compliance-tier <string>', 'Compliance tier (passed through)')
20
+ .option('--input-tokens <number>', 'Estimated input tokens for cost simulation')
21
+ .option('--output-tokens <number>', 'Estimated output tokens for cost simulation')
22
+ .option('--intent-json <path>', 'Path to JSON file with full request payload')
23
+ .option('--format <string>', 'Output format: pretty | json', 'pretty')
24
+ .action(async (opts) => {
25
+ let payload;
26
+ if (opts.intentJson) {
27
+ const fs = await import('fs');
28
+ try {
29
+ const raw = fs.readFileSync(opts.intentJson, 'utf-8');
30
+ payload = JSON.parse(raw);
31
+ }
32
+ catch (err) {
33
+ exitWithError(`Failed to read intent JSON file: ${opts.intentJson}`, err.message);
34
+ }
35
+ }
36
+ else {
37
+ payload = {
38
+ execution_class: opts.executionClass,
39
+ intent: {
40
+ priority: opts.priority,
41
+ constraints: {},
42
+ },
43
+ process_model: {
44
+ allow_model_substitution: opts.allowSubstitution === 'true',
45
+ },
46
+ };
47
+ if (opts.maxCostUsd)
48
+ payload.intent.constraints.max_cost_usd = parseFloat(opts.maxCostUsd);
49
+ if (opts.maxLatencyMs)
50
+ payload.intent.constraints.max_latency_ms = parseInt(opts.maxLatencyMs);
51
+ if (opts.minQsr)
52
+ payload.intent.constraints.min_qsr = parseFloat(opts.minQsr);
53
+ if (opts.complianceTier)
54
+ payload.compliance_tier = opts.complianceTier;
55
+ if (opts.inputTokens || opts.outputTokens) {
56
+ payload.token_estimates = {};
57
+ if (opts.inputTokens)
58
+ payload.token_estimates.input_tokens = parseInt(opts.inputTokens);
59
+ if (opts.outputTokens)
60
+ payload.token_estimates.output_tokens = parseInt(opts.outputTokens);
61
+ }
62
+ }
63
+ const res = await request('POST', '/api/brain/decide', payload);
64
+ if (!res.ok) {
65
+ exitWithError(`Decision failed (HTTP ${res.status})`, JSON.stringify(res.data, null, 2));
66
+ }
67
+ if (opts.format === 'json') {
68
+ console.log(JSON.stringify(res.data, null, 2));
69
+ }
70
+ else {
71
+ formatDecision(res.data);
72
+ }
73
+ });
74
+ program
75
+ .command('explain <decision_id>')
76
+ .description('Get explanation and trace for a past decision')
77
+ .option('--format <string>', 'Output format: pretty | json', 'pretty')
78
+ .action(async (decisionId, opts) => {
79
+ const res = await request('GET', `/api/brain/decisions/${decisionId}`);
80
+ if (!res.ok) {
81
+ exitWithError(`Failed to fetch decision (HTTP ${res.status})`, JSON.stringify(res.data, null, 2));
82
+ }
83
+ if (opts.format === 'json') {
84
+ console.log(JSON.stringify(res.data, null, 2));
85
+ }
86
+ else {
87
+ formatExplanation(res.data);
88
+ }
89
+ });
90
+ program
91
+ .command('health')
92
+ .description('Check AIX Brain health and coverage')
93
+ .option('--format <string>', 'Output format: pretty | json', 'pretty')
94
+ .action(async (opts) => {
95
+ const res = await request('GET', '/api/brain/health');
96
+ if (!res.ok) {
97
+ exitWithError(`Health check failed (HTTP ${res.status})`, JSON.stringify(res.data, null, 2));
98
+ }
99
+ if (opts.format === 'json') {
100
+ console.log(JSON.stringify(res.data, null, 2));
101
+ }
102
+ else {
103
+ formatHealth(res.data);
104
+ }
105
+ });
106
+ const policyCmd = program
107
+ .command('policy')
108
+ .description('Brain routing policy commands');
109
+ policyCmd
110
+ .command('show')
111
+ .description('Show current org routing policy')
112
+ .option('--format <string>', 'Output format: pretty | json', 'pretty')
113
+ .action(async (opts) => {
114
+ const res = await request('GET', '/api/brain/policy');
115
+ if (!res.ok) {
116
+ exitWithError(`Policy fetch failed (HTTP ${res.status})`, JSON.stringify(res.data, null, 2));
117
+ }
118
+ if (opts.format === 'json') {
119
+ console.log(JSON.stringify(res.data, null, 2));
120
+ }
121
+ else {
122
+ formatPolicy(res.data);
123
+ }
124
+ });
125
+ const catalogCmd = program
126
+ .command('catalog')
127
+ .description('Brain catalog commands');
128
+ catalogCmd
129
+ .command('status')
130
+ .description('Show catalog status and coverage')
131
+ .option('--format <string>', 'Output format: pretty | json', 'pretty')
132
+ .action(async (opts) => {
133
+ const res = await request('GET', '/api/brain/catalog/status');
134
+ if (!res.ok) {
135
+ exitWithError(`Catalog status failed (HTTP ${res.status})`, JSON.stringify(res.data, null, 2));
136
+ }
137
+ if (opts.format === 'json') {
138
+ console.log(JSON.stringify(res.data, null, 2));
139
+ }
140
+ else {
141
+ formatCatalogStatus(res.data);
142
+ }
143
+ });
144
+ program.parse();
package/package.json ADDED
@@ -0,0 +1,40 @@
1
+ {
2
+ "name": "@cloudverse/aix-cli",
3
+ "version": "1.0.0",
4
+ "description": "AIX Brain Decision Engine CLI - decision-only surface for AI workload routing",
5
+ "type": "module",
6
+ "bin": {
7
+ "aix": "./dist/index.js"
8
+ },
9
+ "main": "dist/index.js",
10
+ "files": [
11
+ "dist",
12
+ "README.md"
13
+ ],
14
+ "scripts": {
15
+ "build": "tsc",
16
+ "dev": "tsx src/index.ts",
17
+ "clean": "rm -rf dist",
18
+ "prepublishOnly": "npm run build"
19
+ },
20
+ "dependencies": {
21
+ "commander": "^12.0.0"
22
+ },
23
+ "devDependencies": {
24
+ "typescript": "^5.4.0",
25
+ "tsx": "^4.7.0",
26
+ "@types/node": "^20.0.0"
27
+ },
28
+ "keywords": ["aix", "cloudverse", "cli", "brain", "decision", "llm", "routing", "ai", "gpu"],
29
+ "license": "MIT",
30
+ "repository": {
31
+ "type": "git",
32
+ "url": "https://github.com/cloudverse-ai/aix-cli"
33
+ },
34
+ "engines": {
35
+ "node": ">=18"
36
+ },
37
+ "publishConfig": {
38
+ "access": "public"
39
+ }
40
+ }