llm-cli-gateway 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.
Files changed (48) hide show
  1. package/CHANGELOG.md +541 -0
  2. package/LICENSE +21 -0
  3. package/README.md +545 -0
  4. package/dist/approval-manager.d.ts +43 -0
  5. package/dist/approval-manager.js +156 -0
  6. package/dist/async-job-manager.d.ts +57 -0
  7. package/dist/async-job-manager.js +334 -0
  8. package/dist/claude-mcp-config.d.ts +8 -0
  9. package/dist/claude-mcp-config.js +161 -0
  10. package/dist/config.d.ts +35 -0
  11. package/dist/config.js +56 -0
  12. package/dist/db.d.ts +48 -0
  13. package/dist/db.js +170 -0
  14. package/dist/executor.d.ts +30 -0
  15. package/dist/executor.js +315 -0
  16. package/dist/health.d.ts +20 -0
  17. package/dist/health.js +32 -0
  18. package/dist/index.d.ts +67 -0
  19. package/dist/index.js +1503 -0
  20. package/dist/logger.d.ts +6 -0
  21. package/dist/logger.js +5 -0
  22. package/dist/metrics.d.ts +23 -0
  23. package/dist/metrics.js +57 -0
  24. package/dist/migrate-sessions.d.ts +12 -0
  25. package/dist/migrate-sessions.js +145 -0
  26. package/dist/migrate.d.ts +2 -0
  27. package/dist/migrate.js +100 -0
  28. package/dist/model-registry.d.ts +10 -0
  29. package/dist/model-registry.js +346 -0
  30. package/dist/optimizer.d.ts +3 -0
  31. package/dist/optimizer.js +183 -0
  32. package/dist/process-monitor.d.ts +54 -0
  33. package/dist/process-monitor.js +146 -0
  34. package/dist/request-helpers.d.ts +25 -0
  35. package/dist/request-helpers.js +32 -0
  36. package/dist/resources.d.ts +26 -0
  37. package/dist/resources.js +201 -0
  38. package/dist/retry.d.ts +72 -0
  39. package/dist/retry.js +146 -0
  40. package/dist/review-integrity.d.ts +50 -0
  41. package/dist/review-integrity.js +283 -0
  42. package/dist/session-manager-pg.d.ts +76 -0
  43. package/dist/session-manager-pg.js +383 -0
  44. package/dist/session-manager.d.ts +62 -0
  45. package/dist/session-manager.js +223 -0
  46. package/dist/stream-json-parser.d.ts +35 -0
  47. package/dist/stream-json-parser.js +94 -0
  48. package/package.json +90 -0
@@ -0,0 +1,6 @@
1
+ export interface Logger {
2
+ info(message: string, meta?: unknown): void;
3
+ error(message: string, meta?: unknown): void;
4
+ debug(message: string, meta?: unknown): void;
5
+ }
6
+ export declare const noopLogger: Logger;
package/dist/logger.js ADDED
@@ -0,0 +1,5 @@
1
+ export const noopLogger = {
2
+ info: () => { },
3
+ error: () => { },
4
+ debug: () => { }
5
+ };
@@ -0,0 +1,23 @@
1
+ import { type CliType } from "./session-manager.js";
2
+ export interface ToolMetricsSnapshot {
3
+ requestCount: number;
4
+ successCount: number;
5
+ failureCount: number;
6
+ averageResponseTimeMs: number;
7
+ successRate: number;
8
+ failureRate: number;
9
+ }
10
+ export interface PerformanceMetricsSnapshot {
11
+ totalRequests: number;
12
+ totalSuccesses: number;
13
+ totalFailures: number;
14
+ overallSuccessRate: number;
15
+ overallFailureRate: number;
16
+ byTool: Record<CliType, ToolMetricsSnapshot>;
17
+ generatedAt: string;
18
+ }
19
+ export declare class PerformanceMetrics {
20
+ private metrics;
21
+ recordRequest(cli: CliType, durationMs: number, success: boolean): void;
22
+ snapshot(): PerformanceMetricsSnapshot;
23
+ }
@@ -0,0 +1,57 @@
1
+ import { CLI_TYPES } from "./session-manager.js";
2
+ const createEmptyMetrics = () => Object.fromEntries(CLI_TYPES.map(cli => [cli, { requestCount: 0, successCount: 0, failureCount: 0, totalResponseTimeMs: 0 }]));
3
+ export class PerformanceMetrics {
4
+ metrics = createEmptyMetrics();
5
+ recordRequest(cli, durationMs, success) {
6
+ const metrics = this.metrics[cli];
7
+ metrics.requestCount += 1;
8
+ const normalizedDurationMs = Number.isFinite(durationMs) ? Math.max(0, durationMs) : 0;
9
+ metrics.totalResponseTimeMs += normalizedDurationMs;
10
+ if (success) {
11
+ metrics.successCount += 1;
12
+ }
13
+ else {
14
+ metrics.failureCount += 1;
15
+ }
16
+ }
17
+ snapshot() {
18
+ const byTool = {};
19
+ let totalRequests = 0;
20
+ let totalSuccesses = 0;
21
+ let totalFailures = 0;
22
+ for (const cli of CLI_TYPES) {
23
+ const metrics = this.metrics[cli];
24
+ const averageResponseTimeMs = metrics.requestCount > 0
25
+ ? metrics.totalResponseTimeMs / metrics.requestCount
26
+ : 0;
27
+ const successRate = metrics.requestCount > 0
28
+ ? metrics.successCount / metrics.requestCount
29
+ : 0;
30
+ const failureRate = metrics.requestCount > 0
31
+ ? metrics.failureCount / metrics.requestCount
32
+ : 0;
33
+ byTool[cli] = {
34
+ requestCount: metrics.requestCount,
35
+ successCount: metrics.successCount,
36
+ failureCount: metrics.failureCount,
37
+ averageResponseTimeMs,
38
+ successRate,
39
+ failureRate
40
+ };
41
+ totalRequests += metrics.requestCount;
42
+ totalSuccesses += metrics.successCount;
43
+ totalFailures += metrics.failureCount;
44
+ }
45
+ const overallSuccessRate = totalRequests > 0 ? totalSuccesses / totalRequests : 0;
46
+ const overallFailureRate = totalRequests > 0 ? totalFailures / totalRequests : 0;
47
+ return {
48
+ totalRequests,
49
+ totalSuccesses,
50
+ totalFailures,
51
+ overallSuccessRate,
52
+ overallFailureRate,
53
+ byTool,
54
+ generatedAt: new Date().toISOString()
55
+ };
56
+ }
57
+ }
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env node
2
+ import { PostgreSQLSessionManager } from "./session-manager-pg.js";
3
+ interface MigrationResult {
4
+ migrated: number;
5
+ failed: number;
6
+ errors: string[];
7
+ }
8
+ /**
9
+ * Migrate sessions from file-based storage to PostgreSQL
10
+ */
11
+ export declare function migrateFromFile(filePath: string, pgManager: PostgreSQLSessionManager): Promise<MigrationResult>;
12
+ export {};
@@ -0,0 +1,145 @@
1
+ #!/usr/bin/env node
2
+ import { readFileSync } from "fs";
3
+ import { homedir } from "os";
4
+ import { join } from "path";
5
+ import { PostgreSQLSessionManager } from "./session-manager-pg.js";
6
+ import { loadConfig } from "./config.js";
7
+ import { createDatabaseConnection } from "./db.js";
8
+ // Simple console logger for migration script
9
+ const logger = {
10
+ info: (message, meta) => console.error(`[INFO] ${message}`, meta || ""),
11
+ error: (message, meta) => console.error(`[ERROR] ${message}`, meta || ""),
12
+ debug: (message, meta) => console.error(`[DEBUG] ${message}`, meta || "")
13
+ };
14
+ /**
15
+ * Migrate sessions from file-based storage to PostgreSQL
16
+ */
17
+ export async function migrateFromFile(filePath, pgManager) {
18
+ const result = {
19
+ migrated: 0,
20
+ failed: 0,
21
+ errors: []
22
+ };
23
+ // Read file-based sessions
24
+ let fileData;
25
+ try {
26
+ const fileContent = readFileSync(filePath, "utf-8");
27
+ fileData = JSON.parse(fileContent);
28
+ }
29
+ catch (error) {
30
+ throw new Error(`Failed to read sessions file: ${error instanceof Error ? error.message : String(error)}`);
31
+ }
32
+ console.error(`Found ${Object.keys(fileData.sessions).length} sessions to migrate`);
33
+ // Migrate sessions
34
+ for (const [id, session] of Object.entries(fileData.sessions)) {
35
+ try {
36
+ await pgManager.createSession(session.cli, session.description, session.id);
37
+ // Migrate metadata if present
38
+ if (session.metadata) {
39
+ await pgManager.updateSessionMetadata(id, session.metadata);
40
+ }
41
+ result.migrated++;
42
+ console.error(`✓ Migrated session ${id} (${session.cli})`);
43
+ }
44
+ catch (error) {
45
+ result.failed++;
46
+ const errorMsg = `Failed to migrate session ${id}: ${error instanceof Error ? error.message : String(error)}`;
47
+ result.errors.push(errorMsg);
48
+ console.error(`✗ ${errorMsg}`);
49
+ }
50
+ }
51
+ // Restore active sessions
52
+ console.error("\nRestoring active sessions...");
53
+ for (const [cli, sessionId] of Object.entries(fileData.activeSession)) {
54
+ if (sessionId) {
55
+ try {
56
+ await pgManager.setActiveSession(cli, sessionId);
57
+ console.error(`✓ Set active session for ${cli}: ${sessionId}`);
58
+ }
59
+ catch (error) {
60
+ const errorMsg = `Failed to set active session for ${cli}: ${error instanceof Error ? error.message : String(error)}`;
61
+ result.errors.push(errorMsg);
62
+ console.error(`✗ ${errorMsg}`);
63
+ }
64
+ }
65
+ }
66
+ return result;
67
+ }
68
+ /**
69
+ * Main CLI entry point
70
+ */
71
+ async function main() {
72
+ const args = process.argv.slice(2);
73
+ if (args.length === 0 || args[0] === "--help" || args[0] === "-h") {
74
+ console.error(`
75
+ Usage: node dist/migrate-sessions.js --from <sessions.json>
76
+
77
+ Migrate sessions from file-based storage to PostgreSQL.
78
+
79
+ Options:
80
+ --from <path> Path to sessions.json file (default: ~/.llm-cli-gateway/sessions.json)
81
+ --help, -h Show this help message
82
+
83
+ Environment Variables:
84
+ DATABASE_URL PostgreSQL connection string (required)
85
+ REDIS_URL Redis connection string (required)
86
+ `);
87
+ process.exit(args[0] === "--help" || args[0] === "-h" ? 0 : 1);
88
+ }
89
+ // Parse arguments
90
+ let filePath = join(homedir(), ".llm-cli-gateway", "sessions.json");
91
+ for (let i = 0; i < args.length; i++) {
92
+ if (args[i] === "--from" && args[i + 1]) {
93
+ filePath = args[i + 1];
94
+ i++;
95
+ }
96
+ }
97
+ console.error(`Migration Configuration:`);
98
+ console.error(` Source: ${filePath}`);
99
+ console.error(` DATABASE_URL: ${process.env.DATABASE_URL ? "[set]" : "[not set]"}`);
100
+ console.error(` REDIS_URL: ${process.env.REDIS_URL ? "[set]" : "[not set]"}`);
101
+ console.error("");
102
+ // Load config
103
+ const config = loadConfig();
104
+ if (!config.database || !config.redis) {
105
+ console.error("ERROR: DATABASE_URL and REDIS_URL must be set");
106
+ process.exit(1);
107
+ }
108
+ // Connect to database
109
+ console.error("Connecting to database...");
110
+ const db = await createDatabaseConnection(config, logger);
111
+ const pgManager = new PostgreSQLSessionManager(db.getPool(), db.getRedis(), config.cacheTtl, logger);
112
+ console.error("✓ Connected to database\n");
113
+ try {
114
+ // Run migration
115
+ console.error("Starting migration...\n");
116
+ const result = await migrateFromFile(filePath, pgManager);
117
+ console.error("\n" + "=".repeat(50));
118
+ console.error("Migration Summary:");
119
+ console.error(` Migrated: ${result.migrated}`);
120
+ console.error(` Failed: ${result.failed}`);
121
+ if (result.errors.length > 0) {
122
+ console.error("\nErrors:");
123
+ result.errors.forEach(err => console.error(` - ${err}`));
124
+ }
125
+ if (result.failed === 0) {
126
+ console.error("\n✓ Migration completed successfully!");
127
+ process.exit(0);
128
+ }
129
+ else {
130
+ console.error("\n⚠ Migration completed with errors");
131
+ process.exit(1);
132
+ }
133
+ }
134
+ catch (error) {
135
+ console.error("\nERROR:", error instanceof Error ? error.message : String(error));
136
+ process.exit(1);
137
+ }
138
+ finally {
139
+ await db.disconnect();
140
+ }
141
+ }
142
+ // Run if executed directly
143
+ if (import.meta.url === `file://${process.argv[1]}`) {
144
+ main();
145
+ }
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -0,0 +1,100 @@
1
+ #!/usr/bin/env node
2
+ import { Pool } from "pg";
3
+ import { readFileSync, readdirSync } from "fs";
4
+ import { join, dirname } from "path";
5
+ import { fileURLToPath } from "url";
6
+ const __filename = fileURLToPath(import.meta.url);
7
+ const __dirname = dirname(__filename);
8
+ /**
9
+ * Load all migration files from migrations directory
10
+ */
11
+ function loadMigrations() {
12
+ const migrationsDir = join(__dirname, "..", "migrations");
13
+ const files = readdirSync(migrationsDir)
14
+ .filter(f => f.endsWith(".sql"))
15
+ .sort(); // Ensures migrations run in order
16
+ return files.map(file => {
17
+ const match = file.match(/^(\d+)_(.+)\.sql$/);
18
+ if (!match) {
19
+ throw new Error(`Invalid migration filename: ${file}`);
20
+ }
21
+ const version = parseInt(match[1], 10);
22
+ const name = match[2];
23
+ const sql = readFileSync(join(migrationsDir, file), "utf-8");
24
+ return { version, name, sql };
25
+ });
26
+ }
27
+ /**
28
+ * Get list of applied migrations
29
+ */
30
+ async function getAppliedMigrations(pool) {
31
+ // First, ensure schema_migrations table exists
32
+ await pool.query(`
33
+ CREATE TABLE IF NOT EXISTS schema_migrations (
34
+ version INTEGER PRIMARY KEY,
35
+ name VARCHAR(255) NOT NULL,
36
+ applied_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
37
+ )
38
+ `);
39
+ const result = await pool.query("SELECT version FROM schema_migrations ORDER BY version");
40
+ return result.rows.map(row => row.version);
41
+ }
42
+ /**
43
+ * Run a single migration
44
+ */
45
+ async function runMigration(pool, migration) {
46
+ console.error(`Running migration ${migration.version}: ${migration.name}...`);
47
+ const client = await pool.connect();
48
+ try {
49
+ await client.query("BEGIN");
50
+ await client.query(migration.sql);
51
+ await client.query("COMMIT");
52
+ console.error(`✓ Migration ${migration.version} completed`);
53
+ }
54
+ catch (error) {
55
+ await client.query("ROLLBACK");
56
+ throw error;
57
+ }
58
+ finally {
59
+ client.release();
60
+ }
61
+ }
62
+ /**
63
+ * Main migration runner
64
+ */
65
+ async function main() {
66
+ const databaseUrl = process.env.DATABASE_URL;
67
+ if (!databaseUrl) {
68
+ console.error("ERROR: DATABASE_URL environment variable not set");
69
+ process.exit(1);
70
+ }
71
+ const pool = new Pool({ connectionString: databaseUrl });
72
+ try {
73
+ // Load migrations
74
+ const migrations = loadMigrations();
75
+ console.error(`Found ${migrations.length} migration(s)`);
76
+ // Get applied migrations
77
+ const applied = await getAppliedMigrations(pool);
78
+ console.error(`${applied.length} migration(s) already applied`);
79
+ // Filter pending migrations
80
+ const pending = migrations.filter(m => !applied.includes(m.version));
81
+ if (pending.length === 0) {
82
+ console.error("✓ All migrations up to date");
83
+ return;
84
+ }
85
+ console.error(`Running ${pending.length} pending migration(s)...`);
86
+ // Run pending migrations
87
+ for (const migration of pending) {
88
+ await runMigration(pool, migration);
89
+ }
90
+ console.error("✓ All migrations completed successfully");
91
+ }
92
+ catch (error) {
93
+ console.error("ERROR:", error instanceof Error ? error.message : String(error));
94
+ process.exit(1);
95
+ }
96
+ finally {
97
+ await pool.end();
98
+ }
99
+ }
100
+ main();
@@ -0,0 +1,10 @@
1
+ import type { CliType } from "./session-manager.js";
2
+ export interface CliInfo {
3
+ description: string;
4
+ models: Record<string, string>;
5
+ defaultModel?: string;
6
+ modelOrder?: string[];
7
+ }
8
+ export type CliInfoMap = Record<CliType, CliInfo>;
9
+ export declare function getCliInfo(forceRefresh?: boolean): CliInfoMap;
10
+ export declare function resolveModelAlias(cli: CliType, model: string | undefined, info: CliInfoMap): string | undefined;