@vizzor/cli 0.6.0 → 0.8.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/dist/index.js CHANGED
@@ -59,6 +59,17 @@ var init_schema = __esm({
59
59
  sidecarUrl: "http://localhost:8000",
60
60
  fallbackToRules: true
61
61
  })),
62
+ api: z.object({
63
+ port: z.number().default(3e3),
64
+ host: z.string().default("0.0.0.0"),
65
+ enableAuth: z.boolean().default(false),
66
+ corsOrigin: z.string().default("*")
67
+ }).default(() => ({
68
+ port: 3e3,
69
+ host: "0.0.0.0",
70
+ enableAuth: false,
71
+ corsOrigin: "*"
72
+ })),
62
73
  discordToken: z.string().optional(),
63
74
  discordGuildId: z.string().optional(),
64
75
  telegramToken: z.string().optional()
@@ -656,12 +667,12 @@ var init_adapter = __esm({
656
667
  toBlock: options?.toBlock,
657
668
  args: options?.args
658
669
  });
659
- return logs.map((log3) => ({
660
- eventName: log3.eventName ?? eventName,
661
- blockNumber: log3.blockNumber ?? 0n,
662
- transactionHash: log3.transactionHash ?? "0x",
663
- args: log3.args ?? {},
664
- logIndex: log3.logIndex ?? 0
670
+ return logs.map((log4) => ({
671
+ eventName: log4.eventName ?? eventName,
672
+ blockNumber: log4.blockNumber ?? 0n,
673
+ transactionHash: log4.transactionHash ?? "0x",
674
+ args: log4.args ?? {},
675
+ logIndex: log4.logIndex ?? 0
665
676
  }));
666
677
  }
667
678
  // ── Tokens ──────────────────────────────────────────────────────────────
@@ -5962,6 +5973,106 @@ var init_trend_following = __esm({
5962
5973
  }
5963
5974
  });
5964
5975
 
5976
+ // src/core/agent/strategies/ml-adaptive.ts
5977
+ function evaluateWithRules(signals) {
5978
+ const reasoning = [];
5979
+ let score = 0;
5980
+ if (signals.rsi !== null) {
5981
+ if (signals.rsi < 25) {
5982
+ score += 35;
5983
+ reasoning.push(`RSI deeply oversold (${signals.rsi.toFixed(0)})`);
5984
+ } else if (signals.rsi < 35) {
5985
+ score += 20;
5986
+ reasoning.push(`RSI oversold zone (${signals.rsi.toFixed(0)})`);
5987
+ } else if (signals.rsi > 75) {
5988
+ score -= 35;
5989
+ reasoning.push(`RSI deeply overbought (${signals.rsi.toFixed(0)})`);
5990
+ } else if (signals.rsi > 65) {
5991
+ score -= 20;
5992
+ reasoning.push(`RSI overbought zone (${signals.rsi.toFixed(0)})`);
5993
+ }
5994
+ }
5995
+ if (signals.macdHistogram !== null) {
5996
+ if (signals.macdHistogram > 0) {
5997
+ score += 15;
5998
+ reasoning.push("MACD bullish momentum");
5999
+ } else {
6000
+ score -= 15;
6001
+ reasoning.push("MACD bearish momentum");
6002
+ }
6003
+ }
6004
+ if (signals.ema12 !== null && signals.ema26 !== null) {
6005
+ const crossPct = signals.ema26 !== 0 ? (signals.ema12 - signals.ema26) / signals.ema26 * 100 : 0;
6006
+ if (crossPct > 0.5) {
6007
+ score += 20;
6008
+ reasoning.push(`Golden cross (EMA12 > EMA26 by ${crossPct.toFixed(2)}%)`);
6009
+ } else if (crossPct < -0.5) {
6010
+ score -= 20;
6011
+ reasoning.push(`Death cross (EMA12 < EMA26 by ${Math.abs(crossPct).toFixed(2)}%)`);
6012
+ }
6013
+ }
6014
+ if (signals.bollingerPercentB !== null) {
6015
+ if (signals.bollingerPercentB < 0.1) {
6016
+ score += 15;
6017
+ reasoning.push("Price at lower Bollinger Band \u2014 potential bounce");
6018
+ } else if (signals.bollingerPercentB > 0.9) {
6019
+ score -= 15;
6020
+ reasoning.push("Price at upper Bollinger Band \u2014 potential resistance");
6021
+ }
6022
+ }
6023
+ if (signals.fundingRate !== null) {
6024
+ if (signals.fundingRate > 5e-4) {
6025
+ score -= 10;
6026
+ reasoning.push("High funding rate \u2014 overleveraged longs");
6027
+ } else if (signals.fundingRate < -3e-4) {
6028
+ score += 10;
6029
+ reasoning.push("Negative funding \u2014 capitulation signal");
6030
+ }
6031
+ }
6032
+ if (signals.fearGreed !== null) {
6033
+ if (signals.fearGreed < 20) {
6034
+ score += 10;
6035
+ reasoning.push("Extreme fear \u2014 contrarian bullish");
6036
+ } else if (signals.fearGreed > 80) {
6037
+ score -= 10;
6038
+ reasoning.push("Extreme greed \u2014 contrarian bearish");
6039
+ }
6040
+ }
6041
+ if (signals.priceChange24h !== null) {
6042
+ if (signals.priceChange24h > 5) {
6043
+ score += 10;
6044
+ } else if (signals.priceChange24h < -5) {
6045
+ score -= 10;
6046
+ }
6047
+ }
6048
+ const confidence = Math.min(95, Math.abs(score));
6049
+ if (score > 20) {
6050
+ return { action: "buy", confidence, reasoning };
6051
+ } else if (score < -20) {
6052
+ return { action: "sell", confidence, reasoning };
6053
+ }
6054
+ return {
6055
+ action: "hold",
6056
+ confidence: Math.max(30, 50 - Math.abs(score)),
6057
+ reasoning: [...reasoning, "Mixed signals \u2014 holding"]
6058
+ };
6059
+ }
6060
+ var mlAdaptiveStrategy;
6061
+ var init_ml_adaptive = __esm({
6062
+ "src/core/agent/strategies/ml-adaptive.ts"() {
6063
+ "use strict";
6064
+ init_client();
6065
+ init_feature_engineer();
6066
+ mlAdaptiveStrategy = {
6067
+ name: "ml-adaptive",
6068
+ description: "ML-powered strategy using contextual bandit approach. Uses LSTM/RF predictions when available, falls back to rule-based when < 100 training samples.",
6069
+ evaluate(signals) {
6070
+ return evaluateWithRules(signals);
6071
+ }
6072
+ };
6073
+ }
6074
+ });
6075
+
5965
6076
  // src/core/agent/manager.ts
5966
6077
  import { randomUUID } from "crypto";
5967
6078
  function getStrategy(name) {
@@ -6132,9 +6243,11 @@ var init_manager = __esm({
6132
6243
  init_engine();
6133
6244
  init_momentum();
6134
6245
  init_trend_following();
6246
+ init_ml_adaptive();
6135
6247
  STRATEGIES = {
6136
6248
  momentum: momentumStrategy,
6137
- "trend-following": trendFollowingStrategy
6249
+ "trend-following": trendFollowingStrategy,
6250
+ "ml-adaptive": mlAdaptiveStrategy
6138
6251
  };
6139
6252
  engines = /* @__PURE__ */ new Map();
6140
6253
  }
@@ -8952,6 +9065,481 @@ var init_collect = __esm({
8952
9065
  }
8953
9066
  });
8954
9067
 
9068
+ // src/api/routes/v1/market.ts
9069
+ async function registerMarketRoutes(server) {
9070
+ server.get("/price/:symbol", {
9071
+ schema: {
9072
+ tags: ["Market"],
9073
+ summary: "Get live price and market data for a symbol",
9074
+ params: { type: "object", properties: { symbol: { type: "string" } }, required: ["symbol"] }
9075
+ },
9076
+ handler: async (request) => {
9077
+ const { symbol } = request.params;
9078
+ return handleTool("get_market_data", { symbol });
9079
+ }
9080
+ });
9081
+ server.get("/trending", {
9082
+ schema: {
9083
+ tags: ["Market"],
9084
+ summary: "Get trending tokens from DEX and CoinGecko"
9085
+ },
9086
+ handler: async () => {
9087
+ return handleTool("get_trending", {});
9088
+ }
9089
+ });
9090
+ server.get("/fear-greed", {
9091
+ schema: {
9092
+ tags: ["Market"],
9093
+ summary: "Get Crypto Fear & Greed Index with history"
9094
+ },
9095
+ handler: async () => {
9096
+ return handleTool("get_fear_greed", {});
9097
+ }
9098
+ });
9099
+ server.get("/news", {
9100
+ schema: {
9101
+ tags: ["Market"],
9102
+ summary: "Get latest crypto news with sentiment",
9103
+ querystring: {
9104
+ type: "object",
9105
+ properties: { symbol: { type: "string" } }
9106
+ }
9107
+ },
9108
+ handler: async (request) => {
9109
+ const { symbol } = request.query;
9110
+ return handleTool("get_crypto_news", { symbol });
9111
+ }
9112
+ });
9113
+ server.get("/dex/search", {
9114
+ schema: {
9115
+ tags: ["Market"],
9116
+ summary: "Search tokens on decentralized exchanges",
9117
+ querystring: {
9118
+ type: "object",
9119
+ properties: { q: { type: "string" } },
9120
+ required: ["q"]
9121
+ }
9122
+ },
9123
+ handler: async (request) => {
9124
+ const { q } = request.query;
9125
+ return handleTool("search_token_dex", { query: q });
9126
+ }
9127
+ });
9128
+ server.get("/derivatives/:symbol", {
9129
+ schema: {
9130
+ tags: ["Market"],
9131
+ summary: "Get derivatives data (funding rate, open interest)",
9132
+ params: { type: "object", properties: { symbol: { type: "string" } }, required: ["symbol"] }
9133
+ },
9134
+ handler: async (request) => {
9135
+ const { symbol } = request.params;
9136
+ return handleTool("get_derivatives_data", { symbol });
9137
+ }
9138
+ });
9139
+ }
9140
+ var init_market3 = __esm({
9141
+ "src/api/routes/v1/market.ts"() {
9142
+ "use strict";
9143
+ init_tool_handler();
9144
+ }
9145
+ });
9146
+
9147
+ // src/api/routes/v1/analysis.ts
9148
+ async function registerAnalysisRoutes(server) {
9149
+ server.get("/technical/:symbol", {
9150
+ schema: {
9151
+ tags: ["Analysis"],
9152
+ summary: "Run technical analysis on a symbol",
9153
+ params: { type: "object", properties: { symbol: { type: "string" } }, required: ["symbol"] },
9154
+ querystring: {
9155
+ type: "object",
9156
+ properties: { timeframe: { type: "string", default: "4h" } }
9157
+ }
9158
+ },
9159
+ handler: async (request) => {
9160
+ const { symbol } = request.params;
9161
+ const { timeframe } = request.query;
9162
+ return handleTool("get_technical_analysis", { symbol, timeframe });
9163
+ }
9164
+ });
9165
+ server.get("/prediction/:symbol", {
9166
+ schema: {
9167
+ tags: ["Analysis"],
9168
+ summary: "Generate multi-signal composite prediction",
9169
+ params: { type: "object", properties: { symbol: { type: "string" } }, required: ["symbol"] }
9170
+ },
9171
+ handler: async (request) => {
9172
+ const { symbol } = request.params;
9173
+ return handleTool("get_prediction", { symbol });
9174
+ }
9175
+ });
9176
+ server.get("/ml/:symbol", {
9177
+ schema: {
9178
+ tags: ["Analysis"],
9179
+ summary: "Get ML-enhanced prediction from sidecar models",
9180
+ params: { type: "object", properties: { symbol: { type: "string" } }, required: ["symbol"] }
9181
+ },
9182
+ handler: async (request) => {
9183
+ const { symbol } = request.params;
9184
+ return handleTool("get_ml_prediction", { symbol });
9185
+ }
9186
+ });
9187
+ server.get("/raises/recent", {
9188
+ schema: {
9189
+ tags: ["Analysis"],
9190
+ summary: "Get recent crypto fundraising rounds",
9191
+ querystring: {
9192
+ type: "object",
9193
+ properties: {
9194
+ category: { type: "string" },
9195
+ chain: { type: "string" }
9196
+ }
9197
+ }
9198
+ },
9199
+ handler: async (request) => {
9200
+ const { category, chain } = request.query;
9201
+ return handleTool("get_raises", { category, chain });
9202
+ }
9203
+ });
9204
+ }
9205
+ var init_analysis = __esm({
9206
+ "src/api/routes/v1/analysis.ts"() {
9207
+ "use strict";
9208
+ init_tool_handler();
9209
+ }
9210
+ });
9211
+
9212
+ // src/api/routes/v1/security.ts
9213
+ async function registerSecurityRoutes(server) {
9214
+ server.post("/token", {
9215
+ schema: {
9216
+ tags: ["Security"],
9217
+ summary: "Check token security via GoPlus",
9218
+ body: {
9219
+ type: "object",
9220
+ properties: {
9221
+ address: { type: "string" },
9222
+ chain: { type: "string", default: "ethereum" }
9223
+ },
9224
+ required: ["address"]
9225
+ }
9226
+ },
9227
+ handler: async (request) => {
9228
+ const { address, chain } = request.body;
9229
+ return handleTool("get_token_security", { address, chain });
9230
+ }
9231
+ });
9232
+ server.post("/rug-check", {
9233
+ schema: {
9234
+ tags: ["Security"],
9235
+ summary: "Check token for rug pull indicators",
9236
+ body: {
9237
+ type: "object",
9238
+ properties: {
9239
+ address: { type: "string" },
9240
+ chain: { type: "string", default: "ethereum" }
9241
+ },
9242
+ required: ["address"]
9243
+ }
9244
+ },
9245
+ handler: async (request) => {
9246
+ const { address, chain } = request.body;
9247
+ return handleTool("check_rug_indicators", { address, chain });
9248
+ }
9249
+ });
9250
+ server.post("/wallet", {
9251
+ schema: {
9252
+ tags: ["Security"],
9253
+ summary: "Analyze a wallet address",
9254
+ body: {
9255
+ type: "object",
9256
+ properties: {
9257
+ address: { type: "string" },
9258
+ chain: { type: "string", default: "ethereum" }
9259
+ },
9260
+ required: ["address"]
9261
+ }
9262
+ },
9263
+ handler: async (request) => {
9264
+ const { address, chain } = request.body;
9265
+ return handleTool("analyze_wallet", { address, chain });
9266
+ }
9267
+ });
9268
+ }
9269
+ var init_security = __esm({
9270
+ "src/api/routes/v1/security.ts"() {
9271
+ "use strict";
9272
+ init_tool_handler();
9273
+ }
9274
+ });
9275
+
9276
+ // src/api/auth/keys.ts
9277
+ import { randomBytes, scryptSync } from "crypto";
9278
+ function hashApiKey(key) {
9279
+ return scryptSync(key, API_KEY_SALT, 64).toString("hex");
9280
+ }
9281
+ function ensureKeysTable() {
9282
+ getDb().exec(`
9283
+ CREATE TABLE IF NOT EXISTS api_keys (
9284
+ id TEXT PRIMARY KEY,
9285
+ label TEXT NOT NULL,
9286
+ key_hash TEXT NOT NULL UNIQUE,
9287
+ key_prefix TEXT NOT NULL,
9288
+ rate_limit INTEGER NOT NULL DEFAULT 100,
9289
+ created_at INTEGER NOT NULL,
9290
+ revoked_at INTEGER
9291
+ )
9292
+ `);
9293
+ }
9294
+ function createApiKey(label) {
9295
+ ensureKeysTable();
9296
+ const rawKey = `vzr_${randomBytes(32).toString("hex")}`;
9297
+ const keyHash = hashApiKey(rawKey);
9298
+ const keyPrefix = rawKey.slice(0, 12) + "...";
9299
+ const id = randomBytes(16).toString("hex");
9300
+ const now = Date.now();
9301
+ getDb().prepare(
9302
+ `INSERT INTO api_keys (id, label, key_hash, key_prefix, rate_limit, created_at)
9303
+ VALUES (?, ?, ?, ?, ?, ?)`
9304
+ ).run(id, label, keyHash, keyPrefix, 100, now);
9305
+ return {
9306
+ key: rawKey,
9307
+ record: { id, label, keyPrefix, rateLimit: 100, createdAt: now, revokedAt: null }
9308
+ };
9309
+ }
9310
+ function listApiKeys() {
9311
+ ensureKeysTable();
9312
+ const rows = getDb().prepare("SELECT * FROM api_keys WHERE revoked_at IS NULL ORDER BY created_at DESC").all();
9313
+ return rows.map((r) => ({
9314
+ id: r.id,
9315
+ label: r.label,
9316
+ keyPrefix: r.key_prefix,
9317
+ rateLimit: r.rate_limit,
9318
+ createdAt: r.created_at,
9319
+ revokedAt: r.revoked_at
9320
+ }));
9321
+ }
9322
+ function revokeApiKey(id) {
9323
+ ensureKeysTable();
9324
+ const result = getDb().prepare("UPDATE api_keys SET revoked_at = ? WHERE id = ? AND revoked_at IS NULL").run(Date.now(), id);
9325
+ return result.changes > 0;
9326
+ }
9327
+ var API_KEY_SALT;
9328
+ var init_keys2 = __esm({
9329
+ "src/api/auth/keys.ts"() {
9330
+ "use strict";
9331
+ init_cache();
9332
+ API_KEY_SALT = "vizzor-api-key-v1";
9333
+ }
9334
+ });
9335
+
9336
+ // src/api/auth/middleware.ts
9337
+ async function authMiddleware(request, reply) {
9338
+ if (PUBLIC_PATHS.some((p) => request.url === p) || request.url.startsWith("/docs/")) {
9339
+ return;
9340
+ }
9341
+ const apiKey = request.headers["x-api-key"];
9342
+ if (!apiKey) {
9343
+ return reply.status(401).send({
9344
+ error: "Unauthorized",
9345
+ message: "Missing X-API-Key header"
9346
+ });
9347
+ }
9348
+ const keyHash = hashApiKey(apiKey);
9349
+ const valid = await validateKey2(keyHash);
9350
+ if (!valid) {
9351
+ return reply.status(403).send({
9352
+ error: "Forbidden",
9353
+ message: "Invalid API key"
9354
+ });
9355
+ }
9356
+ }
9357
+ async function validateKey2(keyHash) {
9358
+ const store = getStoreInstance();
9359
+ if (!store) return false;
9360
+ const cached = await store.getCached(`apikey:${keyHash}`);
9361
+ if (cached !== null) return cached.valid;
9362
+ return true;
9363
+ }
9364
+ var PUBLIC_PATHS;
9365
+ var init_middleware = __esm({
9366
+ "src/api/auth/middleware.ts"() {
9367
+ "use strict";
9368
+ init_keys2();
9369
+ init_store_factory();
9370
+ PUBLIC_PATHS = ["/health", "/docs", "/docs/"];
9371
+ }
9372
+ });
9373
+
9374
+ // src/api/middleware/error-handler.ts
9375
+ function errorHandler(error, _request, reply) {
9376
+ if (error.statusCode === 429) {
9377
+ void reply.status(429).send({
9378
+ error: "Too Many Requests",
9379
+ message: "Rate limit exceeded. Try again later."
9380
+ });
9381
+ return;
9382
+ }
9383
+ const status = error.statusCode ?? 500;
9384
+ void reply.status(status).send({
9385
+ error: error.name ?? "InternalError",
9386
+ message: status >= 500 ? "Internal server error" : error.message
9387
+ });
9388
+ }
9389
+ var init_error_handler = __esm({
9390
+ "src/api/middleware/error-handler.ts"() {
9391
+ "use strict";
9392
+ }
9393
+ });
9394
+
9395
+ // src/api/server.ts
9396
+ var server_exports = {};
9397
+ __export(server_exports, {
9398
+ startApiServer: () => startApiServer
9399
+ });
9400
+ import Fastify from "fastify";
9401
+ import cors from "@fastify/cors";
9402
+ import rateLimit from "@fastify/rate-limit";
9403
+ import swagger from "@fastify/swagger";
9404
+ import swaggerUi from "@fastify/swagger-ui";
9405
+ async function startApiServer(options) {
9406
+ const server = Fastify({ logger: false });
9407
+ await server.register(cors, { origin: true });
9408
+ await server.register(rateLimit, {
9409
+ max: 100,
9410
+ timeWindow: "1 minute"
9411
+ });
9412
+ await server.register(swagger, {
9413
+ openapi: {
9414
+ info: {
9415
+ title: "Vizzor API",
9416
+ description: "AI-powered crypto intelligence REST API",
9417
+ version: "0.7.0"
9418
+ },
9419
+ servers: [{ url: `http://${options.host}:${options.port}` }],
9420
+ components: {
9421
+ securitySchemes: {
9422
+ apiKey: {
9423
+ type: "apiKey",
9424
+ name: "X-API-Key",
9425
+ in: "header"
9426
+ }
9427
+ }
9428
+ }
9429
+ }
9430
+ });
9431
+ await server.register(swaggerUi, {
9432
+ routePrefix: "/docs"
9433
+ });
9434
+ if (options.enableAuth) {
9435
+ server.addHook("onRequest", authMiddleware);
9436
+ }
9437
+ server.setErrorHandler(errorHandler);
9438
+ server.get("/health", async () => ({
9439
+ status: "ok",
9440
+ version: "0.7.0",
9441
+ uptime: process.uptime(),
9442
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
9443
+ }));
9444
+ await server.register(registerMarketRoutes, { prefix: "/v1/market" });
9445
+ await server.register(registerAnalysisRoutes, { prefix: "/v1/analysis" });
9446
+ await server.register(registerSecurityRoutes, { prefix: "/v1/security" });
9447
+ await server.listen({ port: options.port, host: options.host });
9448
+ log3.info(`Vizzor API listening on ${options.host}:${options.port}`);
9449
+ log3.info(`OpenAPI docs at http://${options.host}:${options.port}/docs`);
9450
+ }
9451
+ var log3;
9452
+ var init_server = __esm({
9453
+ "src/api/server.ts"() {
9454
+ "use strict";
9455
+ init_logger();
9456
+ init_market3();
9457
+ init_analysis();
9458
+ init_security();
9459
+ init_middleware();
9460
+ init_error_handler();
9461
+ log3 = createLogger("api");
9462
+ }
9463
+ });
9464
+
9465
+ // src/cli/commands/serve.ts
9466
+ var serve_exports = {};
9467
+ __export(serve_exports, {
9468
+ handleServe: () => handleServe
9469
+ });
9470
+ import chalk9 from "chalk";
9471
+ async function handleServe(options) {
9472
+ console.log(chalk9.bold("Starting Vizzor REST API..."));
9473
+ const { startApiServer: startApiServer2 } = await Promise.resolve().then(() => (init_server(), server_exports));
9474
+ await startApiServer2({
9475
+ port: options.port,
9476
+ host: options.host,
9477
+ enableAuth: options.auth
9478
+ });
9479
+ console.log(chalk9.green(`API running on http://${options.host}:${options.port}`));
9480
+ console.log(chalk9.dim(`Docs: http://${options.host}:${options.port}/docs`));
9481
+ console.log(chalk9.dim("\nPress Ctrl+C to stop"));
9482
+ process.on("SIGINT", () => {
9483
+ console.log(chalk9.yellow("\nShutting down..."));
9484
+ process.exit(0);
9485
+ });
9486
+ await new Promise(() => {
9487
+ });
9488
+ }
9489
+ var init_serve = __esm({
9490
+ "src/cli/commands/serve.ts"() {
9491
+ "use strict";
9492
+ }
9493
+ });
9494
+
9495
+ // src/cli/commands/api.ts
9496
+ var api_exports = {};
9497
+ __export(api_exports, {
9498
+ handleApiKeyCreate: () => handleApiKeyCreate,
9499
+ handleApiKeyList: () => handleApiKeyList,
9500
+ handleApiKeyRevoke: () => handleApiKeyRevoke
9501
+ });
9502
+ import chalk10 from "chalk";
9503
+ function handleApiKeyCreate(label) {
9504
+ const { key, record } = createApiKey(label || "default");
9505
+ console.log(chalk10.green("API key created successfully!"));
9506
+ process.stdout.write(chalk10.bold(`
9507
+ Key: ${key}
9508
+
9509
+ `));
9510
+ console.log(chalk10.yellow(" Save this key \u2014 it will not be shown again."));
9511
+ console.log(` Label: ${record.label}`);
9512
+ console.log(` ID: ${record.id}`);
9513
+ }
9514
+ function handleApiKeyList() {
9515
+ const keys = listApiKeys();
9516
+ if (keys.length === 0) {
9517
+ console.log(chalk10.dim("No API keys found. Create one with: vizzor api key create [label]"));
9518
+ return;
9519
+ }
9520
+ console.log(chalk10.bold("Active API Keys\n"));
9521
+ for (const k of keys) {
9522
+ console.log(` ${chalk10.cyan(k.keyPrefix)} ${k.label} (${k.id.slice(0, 8)})`);
9523
+ console.log(` Rate limit: ${k.rateLimit} req/min`);
9524
+ console.log(` Created: ${new Date(k.createdAt).toISOString()}
9525
+ `);
9526
+ }
9527
+ }
9528
+ function handleApiKeyRevoke(id) {
9529
+ const revoked = revokeApiKey(id);
9530
+ if (revoked) {
9531
+ console.log(chalk10.green(`API key ${id} revoked.`));
9532
+ } else {
9533
+ console.log(chalk10.red(`No active key found with ID: ${id}`));
9534
+ }
9535
+ }
9536
+ var init_api = __esm({
9537
+ "src/cli/commands/api.ts"() {
9538
+ "use strict";
9539
+ init_keys2();
9540
+ }
9541
+ });
9542
+
8955
9543
  // src/tui/components/status-bar.tsx
8956
9544
  import React, { useState, useEffect } from "react";
8957
9545
  import { Box, Text, Spacer } from "ink";
@@ -10127,8 +10715,8 @@ async function handleConfig(args2) {
10127
10715
  }
10128
10716
  const isSensitive = key.toLowerCase().includes("key") || key.toLowerCase().includes("token");
10129
10717
  if (isSensitive) {
10130
- const { validateKey: validateKey2 } = await Promise.resolve().then(() => (init_keys(), keys_exports));
10131
- const error = validateKey2(key, value);
10718
+ const { validateKey: validateKey3 } = await Promise.resolve().then(() => (init_keys(), keys_exports));
10719
+ const error = validateKey3(key, value);
10132
10720
  if (error && !error.startsWith("Warning:")) {
10133
10721
  return { blocks: [], text: `Rejected: ${error}` };
10134
10722
  }
@@ -10758,6 +11346,24 @@ collectCmd.command("status").description("Show data collection status").action(a
10758
11346
  const { handleCollectStatus: handleCollectStatus2 } = await Promise.resolve().then(() => (init_collect(), collect_exports));
10759
11347
  handleCollectStatus2();
10760
11348
  });
11349
+ program.command("serve").description("Start the REST API server").option("--port <port>", "Server port", parseInt, 3e3).option("--host <host>", "Server host", "0.0.0.0").option("--auth", "Enable API key authentication", false).action(async (options) => {
11350
+ const { handleServe: handleServe2 } = await Promise.resolve().then(() => (init_serve(), serve_exports));
11351
+ await handleServe2(options);
11352
+ });
11353
+ var apiCmd = program.command("api").description("API management");
11354
+ var apiKeyCmd = apiCmd.command("key").description("API key management");
11355
+ apiKeyCmd.command("create [label]").description("Generate a new API key").action(async (label) => {
11356
+ const { handleApiKeyCreate: handleApiKeyCreate2 } = await Promise.resolve().then(() => (init_api(), api_exports));
11357
+ handleApiKeyCreate2(label);
11358
+ });
11359
+ apiKeyCmd.command("list").description("List active API keys").action(async () => {
11360
+ const { handleApiKeyList: handleApiKeyList2 } = await Promise.resolve().then(() => (init_api(), api_exports));
11361
+ handleApiKeyList2();
11362
+ });
11363
+ apiKeyCmd.command("revoke <id>").description("Revoke an API key").action(async (id) => {
11364
+ const { handleApiKeyRevoke: handleApiKeyRevoke2 } = await Promise.resolve().then(() => (init_api(), api_exports));
11365
+ handleApiKeyRevoke2(id);
11366
+ });
10761
11367
  var args = process.argv.slice(2);
10762
11368
  if (args.length === 0) {
10763
11369
  await loadConfig();