@yobekasbah/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/package.json ADDED
@@ -0,0 +1,10 @@
1
+ {
2
+ "name": "@yobekasbah/cli",
3
+ "version": "1.0.0",
4
+ "description": "Kasbah Guard — unified command-line interface. Talks to the local or remote api-server.",
5
+ "bin": { "kasbah": "bin/kasbah.js" },
6
+ "main": "bin/kasbah.js",
7
+ "scripts": { "test": "node bin/kasbah.js --help" },
8
+ "engines": { "node": ">=18" },
9
+ "license": "MIT"
10
+ }
@@ -0,0 +1,230 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ /**
5
+ * Kasbah Setup Validation
6
+ *
7
+ * Comprehensive test suite:
8
+ * 1. API connectivity (Anthropic, Alibaba)
9
+ * 2. MCP server responsiveness
10
+ * 3. SDK instrumentation working
11
+ * 4. Cost tracking enabled
12
+ * 5. Audit receipt generation
13
+ * 6. All model integration
14
+ *
15
+ * Usage:
16
+ * kasbah validate [--verbose] [--models X,Y,Z]
17
+ */
18
+
19
+ const http = require('http');
20
+ const { CostTracker } = require('../kasbah-sdk/src/cost-tracker');
21
+
22
+ const c = (color, text) => {
23
+ const colors = {
24
+ green: '\x1b[32m', red: '\x1b[31m', yellow: '\x1b[33m', cyan: '\x1b[36m',
25
+ dim: '\x1b[2m', reset: '\x1b[0m', bold: '\x1b[1m'
26
+ };
27
+ return (colors[color] || '') + text + colors.reset;
28
+ };
29
+
30
+ const log = (msg, level = 'info') => {
31
+ const prefix = { ok: '✓', err: '✕', warn: '⚠', info: '→' }[level] || '→';
32
+ const color = level === 'ok' ? 'green' : level === 'err' ? 'red' : level === 'warn' ? 'yellow' : 'cyan';
33
+ console.log(c(color, prefix + ' ') + msg);
34
+ };
35
+
36
+ async function testAPIConnectivity() {
37
+ log('Testing API connectivity...');
38
+ const tests = {
39
+ anthropic: !!process.env.ANTHROPIC_API_KEY,
40
+ alibaba: !!process.env.ALIBABA_API_KEY
41
+ };
42
+
43
+ if (!tests.anthropic && !tests.alibaba) {
44
+ log('No API keys found. Set ANTHROPIC_API_KEY or ALIBABA_API_KEY', 'err');
45
+ return { ok: false };
46
+ }
47
+
48
+ let ok = true;
49
+ for (const [provider, found] of Object.entries(tests)) {
50
+ if (found) {
51
+ log(`${provider}: configured`, 'ok');
52
+ } else {
53
+ log(`${provider}: not configured`, 'warn');
54
+ ok = false;
55
+ }
56
+ }
57
+
58
+ return { ok, providers: tests };
59
+ }
60
+
61
+ async function testMCPServer() {
62
+ log('Testing MCP server...');
63
+
64
+ return new Promise((resolve) => {
65
+ const req = http.get('http://127.0.0.1:8788/v1/health', (res) => {
66
+ if (res.statusCode === 200) {
67
+ log('MCP server responsive', 'ok');
68
+ resolve({ ok: true });
69
+ } else {
70
+ log(`MCP server returned ${res.statusCode}`, 'err');
71
+ resolve({ ok: false, status: res.statusCode });
72
+ }
73
+ }).on('error', (e) => {
74
+ log(`MCP server not running: ${e.message}`, 'err');
75
+ resolve({ ok: false, error: e.message });
76
+ });
77
+ });
78
+ }
79
+
80
+ async function testCostTracking() {
81
+ log('Testing cost tracking...');
82
+
83
+ try {
84
+ const tracker = new CostTracker();
85
+
86
+ // Record a test call
87
+ const testCall = tracker.recordCall(
88
+ 'claude-haiku',
89
+ 100, // input tokens
90
+ 50, // output tokens
91
+ 250, // latency ms
92
+ { test: true }
93
+ );
94
+
95
+ if (testCall) {
96
+ log(`Cost tracking: $${testCall.cost.toFixed(6)} recorded`, 'ok');
97
+ const report = tracker.getDailyReport();
98
+ log(`Today's cost: $${report.totalCost.toFixed(4)}`, 'ok');
99
+ return { ok: true, cost: testCall.cost };
100
+ } else {
101
+ log('Cost tracking failed to record', 'err');
102
+ return { ok: false };
103
+ }
104
+ } catch (e) {
105
+ log(`Cost tracking error: ${e.message}`, 'err');
106
+ return { ok: false, error: e.message };
107
+ }
108
+ }
109
+
110
+ async function testAuditReceipts() {
111
+ log('Testing audit receipt generation...');
112
+
113
+ return new Promise((resolve) => {
114
+ const payload = JSON.stringify({
115
+ policyDecision: 'allow',
116
+ sourceDomain: 'claude.ai',
117
+ riskScore: 0
118
+ });
119
+
120
+ const req = http.request({
121
+ hostname: '127.0.0.1',
122
+ port: 8788,
123
+ path: '/v1/audit/receipt',
124
+ method: 'POST',
125
+ headers: { 'Content-Type': 'application/json', 'Content-Length': payload.length }
126
+ }, (res) => {
127
+ let data = '';
128
+ res.on('data', (chunk) => { data += chunk; });
129
+ res.on('end', () => {
130
+ if (res.statusCode === 200) {
131
+ try {
132
+ const receipt = JSON.parse(data);
133
+ if (receipt.receipt || receipt.kasbah_receipt) {
134
+ log('Audit receipt generated', 'ok');
135
+ resolve({ ok: true, receipt });
136
+ } else {
137
+ log('Receipt format unexpected', 'warn');
138
+ resolve({ ok: true, receipt, partial: true });
139
+ }
140
+ } catch (e) {
141
+ log(`Receipt parsing failed: ${e.message}`, 'err');
142
+ resolve({ ok: false });
143
+ }
144
+ } else {
145
+ log(`Receipt endpoint returned ${res.statusCode}`, 'err');
146
+ resolve({ ok: false, status: res.statusCode });
147
+ }
148
+ });
149
+ }).on('error', (e) => {
150
+ log(`Receipt generation failed: ${e.message}`, 'err');
151
+ resolve({ ok: false, error: e.message });
152
+ });
153
+
154
+ req.write(payload);
155
+ req.end();
156
+ });
157
+ }
158
+
159
+ async function testMultiModelSupport() {
160
+ log('Testing multi-model support...');
161
+
162
+ const { PRICING } = require('../kasbah-sdk/src/cost-tracker');
163
+ const models = Object.keys(PRICING);
164
+
165
+ const anthropic = models.filter(m => PRICING[m].provider === 'anthropic');
166
+ const alibaba = models.filter(m => PRICING[m].provider === 'alibaba');
167
+
168
+ console.log(c('cyan', ' Anthropic models:'));
169
+ for (const m of anthropic) {
170
+ const p = PRICING[m];
171
+ log(` ${m}: $${(p.output * 1_000_000).toFixed(2)}/1M output tokens`, 'ok');
172
+ }
173
+
174
+ console.log(c('cyan', ' Alibaba models:'));
175
+ for (const m of alibaba) {
176
+ const p = PRICING[m];
177
+ log(` ${m}: $${(p.output * 1_000_000).toFixed(2)}/1M output tokens`, 'ok');
178
+ }
179
+
180
+ return { ok: true, models: { anthropic, alibaba } };
181
+ }
182
+
183
+ async function main() {
184
+ const argv = process.argv.slice(2);
185
+ const verbose = argv.includes('--verbose');
186
+
187
+ console.log(c('bold', '\nKasbah Setup Validation\n'));
188
+
189
+ const results = {
190
+ timestamp: new Date().toISOString(),
191
+ tests: {}
192
+ };
193
+
194
+ // Run all tests
195
+ results.tests.apiConnectivity = await testAPIConnectivity();
196
+ results.tests.mcpServer = await testMCPServer();
197
+ results.tests.costTracking = await testCostTracking();
198
+ results.tests.auditReceipts = await testAuditReceipts();
199
+ results.tests.multiModel = await testMultiModelSupport();
200
+
201
+ // Summary
202
+ const allOk = Object.values(results.tests).every(t => t.ok);
203
+
204
+ console.log('');
205
+ console.log(c('dim', '═'.repeat(50)));
206
+ if (allOk) {
207
+ log('All tests passed! ✓', 'ok');
208
+ console.log('\nNext steps:');
209
+ console.log(' 1. kasbah status — view cost dashboard');
210
+ console.log(' 2. kasbah dashboard daily — today\'s breakdown');
211
+ console.log(' 3. kasbah dashboard savings — routing recommendations\n');
212
+ } else {
213
+ log('Some tests failed. See above for details.', 'err');
214
+ process.exit(1);
215
+ }
216
+
217
+ if (verbose) {
218
+ console.log('\nDetailed results:');
219
+ console.log(JSON.stringify(results, null, 2));
220
+ }
221
+ }
222
+
223
+ if (require.main === module) {
224
+ main().catch((e) => {
225
+ console.error('Validation failed:', e.message);
226
+ process.exit(1);
227
+ });
228
+ }
229
+
230
+ module.exports = { testAPIConnectivity, testMCPServer, testCostTracking };