llm-cli-gateway 2.3.0 → 2.5.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/resources.js CHANGED
@@ -1,5 +1,7 @@
1
+ import { CLI_TYPES, PROVIDER_TYPES } from "./session-manager.js";
1
2
  import { getAvailableCliInfo } from "./model-registry.js";
2
3
  import { computeGlobalCacheStats, computePrefixCacheStats, computeSessionCacheStats, computeTtlRemaining, } from "./cache-stats.js";
4
+ import { buildProviderSubcommandsCompactCatalog, getCliSubcommandContract, serializeCliSubcommandContract, } from "./upstream-contracts.js";
3
5
  export class ResourceProvider {
4
6
  sessionManager;
5
7
  performanceMetrics;
@@ -163,11 +165,26 @@ export class ResourceProvider {
163
165
  priority: 0.9,
164
166
  },
165
167
  },
168
+ {
169
+ uri: "provider-subcommands://catalog",
170
+ name: "Provider Subcommands Catalog",
171
+ title: "Provider Subcommands Catalog",
172
+ description: "Compact read-only catalog of declared provider CLI subcommands",
173
+ mimeType: "application/json",
174
+ annotations: {
175
+ audience: ["user", "assistant"],
176
+ priority: 0.7,
177
+ },
178
+ },
166
179
  ];
167
180
  }
168
181
  async readResource(uri) {
169
182
  if (uri === "sessions://all") {
170
183
  const sessions = await this.sessionManager.listSessions();
184
+ const activeSessions = Object.fromEntries(await Promise.all(PROVIDER_TYPES.map(async (provider) => [
185
+ provider,
186
+ (await this.sessionManager.getActiveSession(provider))?.id || null,
187
+ ])));
171
188
  return {
172
189
  uri,
173
190
  mimeType: "application/json",
@@ -180,13 +197,7 @@ export class ResourceProvider {
180
197
  createdAt: s.createdAt,
181
198
  lastUsedAt: s.lastUsedAt,
182
199
  })),
183
- activeSessions: {
184
- claude: (await this.sessionManager.getActiveSession("claude"))?.id || null,
185
- codex: (await this.sessionManager.getActiveSession("codex"))?.id || null,
186
- gemini: (await this.sessionManager.getActiveSession("gemini"))?.id || null,
187
- grok: (await this.sessionManager.getActiveSession("grok"))?.id || null,
188
- mistral: (await this.sessionManager.getActiveSession("mistral"))?.id || null,
189
- },
200
+ activeSessions,
190
201
  }, null, 2),
191
202
  };
192
203
  }
@@ -302,6 +313,44 @@ export class ResourceProvider {
302
313
  text: JSON.stringify(this.performanceMetrics.snapshot(), null, 2),
303
314
  };
304
315
  }
316
+ if (uri === "provider-subcommands://catalog" || uri === "provider_subcommands://catalog") {
317
+ return {
318
+ uri,
319
+ mimeType: "application/json",
320
+ text: JSON.stringify(buildProviderSubcommandsCompactCatalog()),
321
+ };
322
+ }
323
+ const subcommandResource = parseProviderSubcommandUri(uri);
324
+ if (subcommandResource) {
325
+ const contract = getCliSubcommandContract(subcommandResource.provider, subcommandResource.commandPath);
326
+ if (!contract)
327
+ return null;
328
+ return {
329
+ uri,
330
+ mimeType: "application/json",
331
+ text: JSON.stringify({
332
+ schemaVersion: "provider-subcommand-contract.v1",
333
+ contract: serializeCliSubcommandContract(subcommandResource.provider, contract),
334
+ }, null, 2),
335
+ };
336
+ }
305
337
  return null;
306
338
  }
307
339
  }
340
+ function parseProviderSubcommandUri(uri) {
341
+ const prefix = uri.startsWith("provider-subcommands://")
342
+ ? "provider-subcommands://"
343
+ : uri.startsWith("provider_subcommands://")
344
+ ? "provider_subcommands://"
345
+ : null;
346
+ if (!prefix || uri === `${prefix}catalog`)
347
+ return null;
348
+ const rest = uri.slice(prefix.length);
349
+ const [providerRaw, ...pathParts] = rest.split("/");
350
+ if (!CLI_TYPES.includes(providerRaw) || pathParts.length === 0)
351
+ return null;
352
+ return {
353
+ provider: providerRaw,
354
+ commandPath: pathParts.map(part => decodeURIComponent(part)).filter(Boolean),
355
+ };
356
+ }
@@ -1,16 +1,16 @@
1
1
  import type { Pool } from "pg";
2
- import { Session, CliType } from "./session-manager.js";
2
+ import { Session, ProviderType } from "./session-manager.js";
3
3
  export type { Logger } from "./logger.js";
4
4
  export declare class PostgreSQLSessionManager {
5
5
  private pool;
6
6
  constructor(pool: Pool);
7
- createSession(cli: CliType, description?: string, sessionId?: string): Promise<Session>;
7
+ createSession(cli: ProviderType, description?: string, sessionId?: string): Promise<Session>;
8
8
  getSession(sessionId: string): Promise<Session | null>;
9
- listSessions(cli?: CliType): Promise<Session[]>;
9
+ listSessions(cli?: ProviderType): Promise<Session[]>;
10
10
  deleteSession(sessionId: string): Promise<boolean>;
11
- setActiveSession(cli: CliType, sessionId: string | null): Promise<boolean>;
12
- getActiveSession(cli: CliType): Promise<Session | null>;
11
+ setActiveSession(cli: ProviderType, sessionId: string | null): Promise<boolean>;
12
+ getActiveSession(cli: ProviderType): Promise<Session | null>;
13
13
  updateSessionUsage(sessionId: string): Promise<void>;
14
14
  updateSessionMetadata(sessionId: string, metadata: Record<string, any>): Promise<boolean>;
15
- clearAllSessions(cli?: CliType): Promise<number>;
15
+ clearAllSessions(cli?: ProviderType): Promise<number>;
16
16
  }
@@ -5,6 +5,7 @@ const DEFAULT_SESSION_DESCRIPTIONS = {
5
5
  gemini: "Gemini Session",
6
6
  grok: "Grok Session",
7
7
  mistral: "Mistral Session",
8
+ "grok-api": "Grok API Session",
8
9
  };
9
10
  export class PostgreSQLSessionManager {
10
11
  pool;
@@ -3,9 +3,13 @@ import type { DatabaseConnection } from "./db.js";
3
3
  import type { Logger } from "./logger.js";
4
4
  export declare const CLI_TYPES: readonly ["claude", "codex", "gemini", "grok", "mistral"];
5
5
  export type CliType = (typeof CLI_TYPES)[number];
6
+ export declare const API_PROVIDER_TYPES: readonly ["grok-api"];
7
+ export type ApiProviderType = (typeof API_PROVIDER_TYPES)[number];
8
+ export declare const PROVIDER_TYPES: readonly ["claude", "codex", "gemini", "grok", "mistral", "grok-api"];
9
+ export type ProviderType = (typeof PROVIDER_TYPES)[number];
6
10
  export interface Session {
7
11
  id: string;
8
- cli: CliType;
12
+ cli: ProviderType;
9
13
  createdAt: string;
10
14
  lastUsedAt: string;
11
15
  description?: string;
@@ -13,7 +17,7 @@ export interface Session {
13
17
  }
14
18
  export interface SessionStorage {
15
19
  sessions: Record<string, Session>;
16
- activeSession: Record<CliType, string | null>;
20
+ activeSession: Record<ProviderType, string | null>;
17
21
  }
18
22
  export type SessionCleanupHook = (session: Session) => void | Promise<void>;
19
23
  export declare class FileSessionManager {
@@ -32,27 +36,27 @@ export declare class FileSessionManager {
32
36
  private ensureStorageDirectory;
33
37
  private loadStorage;
34
38
  private saveStorage;
35
- createSession(cli: CliType, description?: string, sessionId?: string): Session;
39
+ createSession(cli: ProviderType, description?: string, sessionId?: string): Session;
36
40
  getSession(sessionId: string): Session | null;
37
- listSessions(cli?: CliType): Session[];
41
+ listSessions(cli?: ProviderType): Session[];
38
42
  deleteSession(sessionId: string): boolean;
39
- setActiveSession(cli: CliType, sessionId: string | null): boolean;
40
- getActiveSession(cli: CliType): Session | null;
43
+ setActiveSession(cli: ProviderType, sessionId: string | null): boolean;
44
+ getActiveSession(cli: ProviderType): Session | null;
41
45
  updateSessionUsage(sessionId: string): void;
42
46
  updateSessionMetadata(sessionId: string, metadata: Record<string, any>): boolean;
43
- clearAllSessions(cli?: CliType): number;
47
+ clearAllSessions(cli?: ProviderType): number;
44
48
  }
45
49
  export declare const SessionManager: typeof FileSessionManager;
46
50
  export interface ISessionManager {
47
- createSession(cli: CliType, description?: string, sessionId?: string): Session | Promise<Session>;
51
+ createSession(cli: ProviderType, description?: string, sessionId?: string): Session | Promise<Session>;
48
52
  getSession(sessionId: string): Session | null | Promise<Session | null>;
49
- listSessions(cli?: CliType): Session[] | Promise<Session[]>;
53
+ listSessions(cli?: ProviderType): Session[] | Promise<Session[]>;
50
54
  deleteSession(sessionId: string): boolean | Promise<boolean>;
51
- setActiveSession(cli: CliType, sessionId: string | null): boolean | Promise<boolean>;
52
- getActiveSession(cli: CliType): Session | null | Promise<Session | null>;
55
+ setActiveSession(cli: ProviderType, sessionId: string | null): boolean | Promise<boolean>;
56
+ getActiveSession(cli: ProviderType): Session | null | Promise<Session | null>;
53
57
  updateSessionUsage(sessionId: string): void | Promise<void>;
54
58
  updateSessionMetadata(sessionId: string, metadata: Record<string, any>): boolean | Promise<boolean>;
55
- clearAllSessions(cli?: CliType): number | Promise<number>;
59
+ clearAllSessions(cli?: ProviderType): number | Promise<number>;
56
60
  }
57
61
  export declare function createSessionManager(config?: Config, db?: DatabaseConnection, logger?: Logger, opts?: {
58
62
  cleanupHook?: SessionCleanupHook;
@@ -5,13 +5,16 @@ import { existsSync, mkdirSync, readFileSync, writeFileSync, renameSync, openSyn
5
5
  import { DEFAULT_SESSION_TTL_SECONDS } from "./config.js";
6
6
  import { noopLogger } from "./logger.js";
7
7
  export const CLI_TYPES = ["claude", "codex", "gemini", "grok", "mistral"];
8
- const createEmptyActiveSessions = () => Object.fromEntries(CLI_TYPES.map(cli => [cli, null]));
8
+ export const API_PROVIDER_TYPES = ["grok-api"];
9
+ export const PROVIDER_TYPES = [...CLI_TYPES, ...API_PROVIDER_TYPES];
10
+ const createEmptyActiveSessions = () => Object.fromEntries(PROVIDER_TYPES.map(provider => [provider, null]));
9
11
  const DEFAULT_SESSION_DESCRIPTIONS = {
10
12
  claude: "Claude Session",
11
13
  codex: "Codex Session",
12
14
  gemini: "Gemini Session",
13
15
  grok: "Grok Session",
14
16
  mistral: "Mistral Session",
17
+ "grok-api": "Grok API Session",
15
18
  };
16
19
  export class FileSessionManager {
17
20
  storagePath;
@@ -21,6 +21,7 @@ export interface CliContract {
21
21
  upstream: string;
22
22
  helpArgs: string[][];
23
23
  flags: Record<string, CliFlagContract>;
24
+ subcommands?: Record<string, CliSubcommandContract>;
24
25
  env?: Record<string, CliFlagContract>;
25
26
  mcpTools: readonly string[];
26
27
  mcpParameters: readonly string[];
@@ -43,6 +44,25 @@ export interface CliContractFixture {
43
44
  env?: Record<string, string>;
44
45
  expect: "pass" | "fail";
45
46
  }
47
+ export type CliSubcommandRisk = "read_only" | "writes_local_config" | "auth" | "network" | "starts_server" | "updates_binary" | "destructive" | "executes_agent";
48
+ export type CliSubcommandExposure = "tracked_only" | "mcp_readonly" | "mcp_requires_approval" | "not_exposed";
49
+ export type CliSubcommandTier = "catalog" | "inspect" | "execute_candidate" | "diagnostic";
50
+ export type CliSubcommandTokenCost = "tiny" | "small" | "medium" | "large";
51
+ export interface CliSubcommandContract {
52
+ commandPath: readonly string[];
53
+ helpArgs: readonly string[][];
54
+ flags: Record<string, CliFlagContract>;
55
+ maxPositionals: number;
56
+ acknowledgedUpstreamFlags?: readonly string[];
57
+ aliases?: readonly string[];
58
+ children?: Record<string, CliSubcommandContract>;
59
+ risk: CliSubcommandRisk;
60
+ exposure: CliSubcommandExposure;
61
+ tier: CliSubcommandTier;
62
+ tokenCost: CliSubcommandTokenCost;
63
+ summary: string;
64
+ conformanceFixtures: readonly CliContractFixture[];
65
+ }
46
66
  export interface ContractViolation {
47
67
  cli: CliType;
48
68
  arg?: string;
@@ -53,9 +73,55 @@ export interface ContractValidationResult {
53
73
  ok: boolean;
54
74
  violations: ContractViolation[];
55
75
  }
76
+ export interface SubcommandContractValidationResult extends ContractValidationResult {
77
+ commandPath: readonly string[];
78
+ risk?: CliSubcommandRisk;
79
+ exposure?: CliSubcommandExposure;
80
+ tier?: CliSubcommandTier;
81
+ }
82
+ export interface ProviderSubcommandCatalogRow {
83
+ provider: CliType;
84
+ commandPath: readonly string[];
85
+ aliases: readonly string[];
86
+ tier: CliSubcommandTier;
87
+ risk: CliSubcommandRisk;
88
+ exposure: CliSubcommandExposure;
89
+ tokenCost: CliSubcommandTokenCost;
90
+ summary: string;
91
+ driftStatus: "unknown" | "clean" | "drift";
92
+ resourceUri: string;
93
+ }
94
+ export interface ProviderSubcommandCompactCatalog {
95
+ schemaVersion: "provider-subcommands-catalog.v1";
96
+ columns: readonly [
97
+ "provider",
98
+ "commandPath",
99
+ "aliases",
100
+ "tier",
101
+ "risk",
102
+ "exposure",
103
+ "tokenCost",
104
+ "summary",
105
+ "driftStatus",
106
+ "resourceUri"
107
+ ];
108
+ rows: readonly (readonly string[])[];
109
+ }
56
110
  export declare const UPSTREAM_CLI_CONTRACTS: Record<CliType, CliContract>;
57
111
  export declare function validateUpstreamCliArgs(cli: CliType, args: readonly string[]): ContractValidationResult;
58
112
  export declare function assertUpstreamCliArgs(cli: CliType, args: readonly string[]): void;
113
+ export declare function flattenCliSubcommands(subcommands: Record<string, CliSubcommandContract> | undefined): CliSubcommandContract[];
114
+ export declare function getCliSubcommandContract(cli: CliType, commandPath: readonly string[]): CliSubcommandContract | null;
115
+ export declare function serializeCliSubcommandContract(cli: CliType, contract: CliSubcommandContract): Record<string, unknown>;
116
+ export declare function listProviderSubcommands(options?: {
117
+ provider?: CliType;
118
+ tier?: CliSubcommandTier;
119
+ risk?: CliSubcommandRisk;
120
+ exposure?: CliSubcommandExposure;
121
+ commandPathPrefix?: readonly string[];
122
+ }): ProviderSubcommandCatalogRow[];
123
+ export declare function buildProviderSubcommandsCompactCatalog(options?: Parameters<typeof listProviderSubcommands>[0]): ProviderSubcommandCompactCatalog;
124
+ export declare function validateUpstreamCliSubcommandArgs(cli: CliType, commandPath: readonly string[], args: readonly string[]): SubcommandContractValidationResult;
59
125
  export declare function validateUpstreamCliEnv(cli: CliType, env: Record<string, string> | undefined): ContractValidationResult;
60
126
  export declare function assertUpstreamCliEnv(cli: CliType, env: Record<string, string> | undefined): void;
61
127
  export declare function extractDiscoveredFlags(helpText: string): readonly string[];
@@ -66,6 +132,23 @@ export interface FlagDriftResult {
66
132
  warnings: string[];
67
133
  }
68
134
  export declare function computeFlagDrift(contract: CliContract, helpText: string, discoveredFlags: readonly string[]): FlagDriftResult;
135
+ export declare function computeSubcommandFlagDrift(contract: CliSubcommandContract, executable: string, helpText: string, discoveredFlags: readonly string[]): FlagDriftResult;
136
+ export interface InstalledCliSubcommandProbe {
137
+ commandPath: readonly string[];
138
+ checkedHelpCommands: string[][];
139
+ available: boolean;
140
+ missingFlags: string[];
141
+ extraFlags: readonly string[];
142
+ acknowledgedExtraFlags: readonly string[];
143
+ discoveredFlags: readonly string[];
144
+ helpHash?: string;
145
+ probedAt: string;
146
+ warnings: string[];
147
+ risk: CliSubcommandRisk;
148
+ exposure: CliSubcommandExposure;
149
+ tier: CliSubcommandTier;
150
+ summary: string;
151
+ }
69
152
  export interface InstalledCliContractProbe {
70
153
  cli: CliType;
71
154
  executable: string;
@@ -79,6 +162,7 @@ export interface InstalledCliContractProbe {
79
162
  discoveredFlags: readonly string[];
80
163
  helpHash?: string;
81
164
  versionHint?: string;
165
+ subcommands: Record<string, InstalledCliSubcommandProbe>;
82
166
  probedAt: string;
83
167
  warnings: string[];
84
168
  }