llm-cli-gateway 2.13.2 → 2.14.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 (79) hide show
  1. package/CHANGELOG.md +139 -0
  2. package/README.md +68 -29
  3. package/dist/acp/client.d.ts +29 -1
  4. package/dist/acp/client.js +78 -4
  5. package/dist/acp/errors.d.ts +9 -1
  6. package/dist/acp/errors.js +19 -0
  7. package/dist/acp/event-normalizer.d.ts +12 -0
  8. package/dist/acp/event-normalizer.js +16 -0
  9. package/dist/acp/flight-redaction.d.ts +3 -0
  10. package/dist/acp/flight-redaction.js +3 -0
  11. package/dist/acp/permission-bridge.js +11 -5
  12. package/dist/acp/process-manager.d.ts +2 -1
  13. package/dist/acp/process-manager.js +43 -4
  14. package/dist/acp/provider-registry.js +19 -11
  15. package/dist/acp/runtime.d.ts +8 -0
  16. package/dist/acp/runtime.js +47 -4
  17. package/dist/acp/types.d.ts +3083 -55
  18. package/dist/acp/types.js +242 -5
  19. package/dist/async-job-manager.d.ts +38 -1
  20. package/dist/async-job-manager.js +287 -20
  21. package/dist/codex-json-parser.d.ts +1 -0
  22. package/dist/codex-json-parser.js +6 -0
  23. package/dist/config.d.ts +19 -0
  24. package/dist/config.js +84 -1
  25. package/dist/flight-recorder.d.ts +2 -0
  26. package/dist/flight-recorder.js +20 -0
  27. package/dist/gemini-json-parser.d.ts +2 -0
  28. package/dist/gemini-json-parser.js +45 -8
  29. package/dist/grok-json-parser.d.ts +14 -0
  30. package/dist/grok-json-parser.js +156 -0
  31. package/dist/index.d.ts +47 -2
  32. package/dist/index.js +504 -122
  33. package/dist/job-store.d.ts +119 -11
  34. package/dist/job-store.js +372 -42
  35. package/dist/model-registry.d.ts +1 -0
  36. package/dist/model-registry.js +46 -0
  37. package/dist/oauth.js +2 -2
  38. package/dist/postgres-job-store-worker.d.ts +1 -0
  39. package/dist/postgres-job-store-worker.js +444 -0
  40. package/dist/pricing.d.ts +3 -2
  41. package/dist/provider-acp-capabilities.d.ts +52 -0
  42. package/dist/provider-acp-capabilities.js +101 -0
  43. package/dist/provider-admin-tools.d.ts +100 -0
  44. package/dist/provider-admin-tools.js +572 -0
  45. package/dist/provider-capability-cache.d.ts +46 -0
  46. package/dist/provider-capability-cache.js +248 -0
  47. package/dist/provider-capability-discovery.d.ts +85 -0
  48. package/dist/provider-capability-discovery.js +461 -0
  49. package/dist/provider-capability-resolver.d.ts +29 -0
  50. package/dist/provider-capability-resolver.js +92 -0
  51. package/dist/provider-definition-assertions.d.ts +9 -0
  52. package/dist/provider-definition-assertions.js +147 -0
  53. package/dist/provider-definitions.d.ts +127 -0
  54. package/dist/provider-definitions.js +758 -0
  55. package/dist/provider-help-parser.d.ts +34 -0
  56. package/dist/provider-help-parser.js +203 -0
  57. package/dist/provider-model-discovery.d.ts +30 -0
  58. package/dist/provider-model-discovery.js +229 -0
  59. package/dist/provider-output-metadata.d.ts +7 -0
  60. package/dist/provider-output-metadata.js +68 -0
  61. package/dist/provider-schema-builder.d.ts +22 -0
  62. package/dist/provider-schema-builder.js +55 -0
  63. package/dist/provider-surface-generator.d.ts +98 -0
  64. package/dist/provider-surface-generator.js +140 -0
  65. package/dist/provider-tool-capabilities.d.ts +3 -2
  66. package/dist/provider-tool-capabilities.js +77 -62
  67. package/dist/request-helpers.d.ts +37 -4
  68. package/dist/request-helpers.js +134 -0
  69. package/dist/resources.d.ts +6 -1
  70. package/dist/resources.js +64 -192
  71. package/dist/session-manager.js +18 -11
  72. package/dist/sqlite-driver.d.ts +1 -1
  73. package/dist/sqlite-driver.js +2 -1
  74. package/dist/stream-json-parser.d.ts +1 -0
  75. package/dist/stream-json-parser.js +3 -0
  76. package/dist/upstream-contracts.d.ts +17 -1
  77. package/dist/upstream-contracts.js +209 -36
  78. package/npm-shrinkwrap.json +2 -2
  79. package/package.json +8 -3
@@ -0,0 +1,248 @@
1
+ import { createHash } from "node:crypto";
2
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
3
+ import os from "node:os";
4
+ import path from "node:path";
5
+ import { z } from "zod/v3";
6
+ export const CAPABILITY_CACHE_SCHEMA_VERSION = "provider-capability-cache.v1";
7
+ export function capabilityCacheDir() {
8
+ const override = process.env.LLM_GATEWAY_CAPABILITY_CACHE_DIR;
9
+ if (override && override.trim().length > 0)
10
+ return override;
11
+ return path.join(os.homedir(), ".llm-cli-gateway", "capability-cache");
12
+ }
13
+ function cacheFilePath(providerId) {
14
+ return path.join(capabilityCacheDir(), `${providerId}.json`);
15
+ }
16
+ export function cacheKeyFields(set) {
17
+ return {
18
+ providerId: set.providerId,
19
+ executablePath: set.executablePath,
20
+ version: set.version,
21
+ rootHelpChecksum: set.checksums.rootHelp,
22
+ subcommandHelpChecksums: set.checksums.subcommandHelp,
23
+ acpInitializeChecksum: set.checksums.acpInitialize,
24
+ modelCatalogChecksum: set.checksums.modelCatalog,
25
+ gatewayVersion: set.gatewayVersion,
26
+ };
27
+ }
28
+ export function computeCacheKey(set) {
29
+ const fields = cacheKeyFields(set);
30
+ const canonical = JSON.stringify([
31
+ fields.providerId,
32
+ fields.executablePath,
33
+ fields.version,
34
+ fields.rootHelpChecksum,
35
+ Object.keys(fields.subcommandHelpChecksums)
36
+ .sort()
37
+ .map(key => [key, fields.subcommandHelpChecksums[key]]),
38
+ fields.acpInitializeChecksum,
39
+ fields.modelCatalogChecksum,
40
+ fields.gatewayVersion,
41
+ ]);
42
+ return createHash("sha256").update(canonical, "utf8").digest("hex");
43
+ }
44
+ const REDACTED = "[REDACTED]";
45
+ const SENSITIVE_KEY_PATTERN = "token|secret|password|passwd|credential|auth|private[_-]?key|api[_-]?key|apikey|" +
46
+ "(?:access|api|client|customer|user|account|org)[_-]?(?:id|key|token|secret)";
47
+ const ACCOUNT_KEY_NAMES = "account|acct";
48
+ const KV_VALUE = "[^\\s\"',;]+";
49
+ const JSON_VALUE = '(?:[^"\\\\]|\\\\.)*';
50
+ const REDACT_VALUE = (match, keyPart) => `${keyPart}${REDACTED}`;
51
+ const REDACT_JSON_VALUE = (match, keyPart) => `${keyPart}"${REDACTED}"`;
52
+ const SECRET_RULES = [
53
+ {
54
+ pattern: /\b(?:sk-ant-|anthropic-|sk-|xai-|gsk_|gh[pousr]_|github_pat_|ya29\.|AKIA|ASIA)[A-Za-z0-9_.-]{8,}/g,
55
+ replacement: REDACTED,
56
+ },
57
+ { pattern: /1\/\/[A-Za-z0-9_.-]{8,}/g, replacement: REDACTED },
58
+ { pattern: /\bAuthorization\s*:\s*(?:Bearer|Basic)\s+\S+/gi, replacement: REDACTED },
59
+ { pattern: /\bBearer\s+[A-Za-z0-9._~+/=-]{6,}/gi, replacement: REDACTED },
60
+ {
61
+ pattern: new RegExp(`("[A-Za-z0-9_.-]*(?:${SENSITIVE_KEY_PATTERN})[A-Za-z0-9_.-]*"\\s*:\\s*)"${JSON_VALUE}"`, "gi"),
62
+ replacement: REDACT_JSON_VALUE,
63
+ },
64
+ {
65
+ pattern: new RegExp(`\\b([A-Za-z0-9_.-]*(?:${SENSITIVE_KEY_PATTERN})[A-Za-z0-9_.-]*\\s*[:=]\\s*)${KV_VALUE}`, "gi"),
66
+ replacement: REDACT_VALUE,
67
+ },
68
+ {
69
+ pattern: new RegExp(`\\b((?:${ACCOUNT_KEY_NAMES})\\s*[:=]\\s*)${KV_VALUE}`, "gi"),
70
+ replacement: REDACT_VALUE,
71
+ },
72
+ { pattern: /\b(?:user|customer|account|acct|org)_[0-9]{6,}\b/g, replacement: REDACTED },
73
+ { pattern: /\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}\b/g, replacement: REDACTED },
74
+ ];
75
+ export function scrubString(value) {
76
+ let out = value;
77
+ for (const rule of SECRET_RULES) {
78
+ out =
79
+ typeof rule.replacement === "string"
80
+ ? out.replace(rule.pattern, rule.replacement)
81
+ : out.replace(rule.pattern, rule.replacement);
82
+ }
83
+ return out;
84
+ }
85
+ export function scrubSecrets(value) {
86
+ if (typeof value === "string")
87
+ return scrubString(value);
88
+ if (Array.isArray(value))
89
+ return value.map(entry => scrubSecrets(entry));
90
+ if (value && typeof value === "object") {
91
+ const out = {};
92
+ for (const [key, entry] of Object.entries(value)) {
93
+ out[key] = scrubSecrets(entry);
94
+ }
95
+ return out;
96
+ }
97
+ return value;
98
+ }
99
+ const ParsedUnmappedSchema = z
100
+ .object({
101
+ kind: z.string(),
102
+ raw: z.string(),
103
+ checksum: z.string(),
104
+ reason: z.string(),
105
+ })
106
+ .passthrough();
107
+ const ParsedHelpSchema = z
108
+ .object({
109
+ flags: z.array(z.object({ name: z.string() }).passthrough()),
110
+ subcommands: z.array(z.object({ name: z.string() }).passthrough()),
111
+ discoveredUnmapped: z.array(ParsedUnmappedSchema),
112
+ checksum: z.string(),
113
+ })
114
+ .passthrough();
115
+ const DiscoveredCapabilitySetSchema = z
116
+ .object({
117
+ providerId: z.string(),
118
+ executable: z.string(),
119
+ executablePath: z.string(),
120
+ version: z.string(),
121
+ rootHelp: ParsedHelpSchema,
122
+ subcommandHelp: z.record(z.string(), ParsedHelpSchema),
123
+ modelCatalog: z
124
+ .object({
125
+ strategy: z.string(),
126
+ argv: z.array(z.string()),
127
+ raw: z.string().nullable(),
128
+ checksum: z.string(),
129
+ evidence: z.string(),
130
+ })
131
+ .passthrough(),
132
+ acpInitialize: z.object({}).passthrough().nullable(),
133
+ checksums: z
134
+ .object({
135
+ version: z.string(),
136
+ rootHelp: z.string(),
137
+ subcommandHelp: z.record(z.string(), z.string()),
138
+ modelCatalog: z.string(),
139
+ acpInitialize: z.string().nullable(),
140
+ })
141
+ .passthrough(),
142
+ sourceEvidence: z.array(z.string()),
143
+ discoveredUnmapped: z.array(ParsedUnmappedSchema),
144
+ status: z.enum(["ok", "degraded", "error"]),
145
+ degradedReason: z.string().optional(),
146
+ gatewayVersion: z.string(),
147
+ discoveredAt: z.string(),
148
+ })
149
+ .passthrough();
150
+ const CachedCapabilityEntrySchema = z
151
+ .object({
152
+ schemaVersion: z.literal(CAPABILITY_CACHE_SCHEMA_VERSION),
153
+ providerId: z.string(),
154
+ cacheKey: z.string(),
155
+ keyFields: z.object({}).passthrough(),
156
+ capabilitySet: DiscoveredCapabilitySetSchema,
157
+ cachedAt: z.string(),
158
+ source: z.literal("discovery"),
159
+ })
160
+ .passthrough();
161
+ export function readCapabilityCache(providerId) {
162
+ const file = cacheFilePath(providerId);
163
+ if (!existsSync(file))
164
+ return null;
165
+ let raw;
166
+ try {
167
+ raw = JSON.parse(readFileSync(file, "utf8"));
168
+ }
169
+ catch {
170
+ return null;
171
+ }
172
+ const parsed = CachedCapabilityEntrySchema.safeParse(raw);
173
+ if (!parsed.success)
174
+ return null;
175
+ if (parsed.data.providerId !== providerId)
176
+ return null;
177
+ if (parsed.data.capabilitySet.providerId !== providerId)
178
+ return null;
179
+ return parsed.data;
180
+ }
181
+ export function writeCapabilityCache(set) {
182
+ const dir = capabilityCacheDir();
183
+ mkdirSync(dir, { recursive: true });
184
+ const scrubbedSet = scrubSecrets(set);
185
+ const entry = {
186
+ schemaVersion: CAPABILITY_CACHE_SCHEMA_VERSION,
187
+ providerId: set.providerId,
188
+ cacheKey: computeCacheKey(set),
189
+ keyFields: scrubSecrets(cacheKeyFields(set)),
190
+ capabilitySet: scrubbedSet,
191
+ cachedAt: new Date().toISOString(),
192
+ source: "discovery",
193
+ };
194
+ const file = cacheFilePath(set.providerId);
195
+ writeFileSync(file, JSON.stringify(entry, null, 2), { encoding: "utf8", mode: 0o600 });
196
+ return entry;
197
+ }
198
+ export function lookupCapabilityCache(freshSet) {
199
+ const entry = readCapabilityCache(freshSet.providerId);
200
+ if (!entry) {
201
+ return {
202
+ hit: false,
203
+ ageMs: null,
204
+ cachedAt: null,
205
+ source: null,
206
+ version: null,
207
+ checksum: null,
208
+ entry: null,
209
+ };
210
+ }
211
+ const freshKey = computeCacheKey(freshSet);
212
+ const hit = entry.cacheKey === freshKey;
213
+ const cachedAtMs = Date.parse(entry.cachedAt);
214
+ return {
215
+ hit,
216
+ ageMs: Number.isFinite(cachedAtMs) ? Date.now() - cachedAtMs : null,
217
+ cachedAt: entry.cachedAt,
218
+ source: entry.source,
219
+ version: entry.keyFields.version,
220
+ checksum: entry.cacheKey,
221
+ entry,
222
+ };
223
+ }
224
+ export function resolveCapabilitySet(freshSet) {
225
+ if (freshSet.status !== "error") {
226
+ writeCapabilityCache(freshSet);
227
+ return { set: freshSet, source: "discovery", degraded: freshSet.status === "degraded" };
228
+ }
229
+ const entry = readCapabilityCache(freshSet.providerId);
230
+ if (entry &&
231
+ entry.keyFields.executablePath === freshSet.executablePath &&
232
+ entry.keyFields.version === freshSet.version &&
233
+ freshSet.version.length > 0) {
234
+ return {
235
+ set: entry.capabilitySet,
236
+ source: "cache",
237
+ degraded: true,
238
+ reason: freshSet.degradedReason ?? "discovery failed; using last valid cached capability set",
239
+ };
240
+ }
241
+ return {
242
+ set: freshSet,
243
+ source: "minimal",
244
+ degraded: true,
245
+ reason: freshSet.degradedReason ??
246
+ "discovery failed and no matching cache exists; minimal prompt surface only",
247
+ };
248
+ }
@@ -0,0 +1,85 @@
1
+ import { type AgentCapabilities } from "./acp/types.js";
2
+ import { type Logger } from "./logger.js";
3
+ import { type CliType, type ProviderDefinition } from "./provider-definitions.js";
4
+ import { type DiscoveredUnmapped, type ParsedHelp } from "./provider-help-parser.js";
5
+ import { type DiscoveryContractDrift } from "./upstream-contracts.js";
6
+ export interface ProbeResult {
7
+ readonly stdout: string;
8
+ readonly stderr: string;
9
+ readonly code: number;
10
+ }
11
+ export type ProbeRunner = (exe: string, argv: readonly string[]) => Promise<ProbeResult>;
12
+ export interface DiscoveredModelCatalog {
13
+ readonly strategy: string;
14
+ readonly argv: readonly string[];
15
+ readonly raw: string | null;
16
+ readonly checksum: string;
17
+ readonly evidence: string;
18
+ }
19
+ export declare const KNOWN_ACP_METHODS: readonly string[];
20
+ export interface ParsedAcpInitialize {
21
+ readonly protocolVersion: number | string | null;
22
+ readonly agentInfo: {
23
+ readonly name?: string;
24
+ readonly version?: string;
25
+ } | null;
26
+ readonly authMethods: readonly string[];
27
+ readonly promptCapabilities: readonly string[];
28
+ readonly mcpCapabilities: readonly string[];
29
+ readonly sessionCapabilities: readonly string[];
30
+ readonly agentCapabilities: AgentCapabilities | null;
31
+ readonly knownMethods: readonly string[];
32
+ readonly extensionMethods: readonly string[];
33
+ readonly checksum: string;
34
+ }
35
+ export interface DiscoveredCapabilitySet {
36
+ readonly providerId: CliType;
37
+ readonly executable: string;
38
+ readonly executablePath: string;
39
+ readonly version: string;
40
+ readonly rootHelp: ParsedHelp;
41
+ readonly subcommandHelp: Readonly<Record<string, ParsedHelp>>;
42
+ readonly modelCatalog: DiscoveredModelCatalog;
43
+ readonly acpInitialize: ParsedAcpInitialize | null;
44
+ readonly checksums: {
45
+ readonly version: string;
46
+ readonly rootHelp: string;
47
+ readonly subcommandHelp: Readonly<Record<string, string>>;
48
+ readonly modelCatalog: string;
49
+ readonly acpInitialize: string | null;
50
+ };
51
+ readonly sourceEvidence: readonly string[];
52
+ readonly discoveredUnmapped: readonly DiscoveredUnmapped[];
53
+ readonly status: "ok" | "degraded" | "error";
54
+ readonly degradedReason?: string;
55
+ readonly gatewayVersion: string;
56
+ readonly discoveredAt: string;
57
+ }
58
+ export interface DiscoveryOptions {
59
+ readonly runner?: ProbeRunner;
60
+ readonly gatewayVersion?: string;
61
+ readonly resolveExecutablePath?: (exe: string) => string;
62
+ readonly logger?: Logger;
63
+ }
64
+ export declare function gatewayVersion(): string;
65
+ export declare function resolveExecutableAbsolutePath(exe: string): string;
66
+ export declare function createDefaultProbeRunner(timeoutMs?: number): ProbeRunner;
67
+ export declare const defaultProbeRunner: ProbeRunner;
68
+ export type AcpInitializeParse = {
69
+ readonly kind: "none";
70
+ } | {
71
+ readonly kind: "ok";
72
+ readonly value: ParsedAcpInitialize;
73
+ } | {
74
+ readonly kind: "invalid";
75
+ readonly reason: string;
76
+ };
77
+ export declare function parseAcpInitialize(text: string): AcpInitializeParse;
78
+ export declare function discoverProviderCapabilities(def: ProviderDefinition, options?: DiscoveryOptions): Promise<DiscoveredCapabilitySet>;
79
+ export declare function discoverAllProviders(options?: DiscoveryOptions): Promise<Map<CliType, DiscoveredCapabilitySet>>;
80
+ export declare function acpMethodAvailability(set: DiscoveredCapabilitySet): {
81
+ readonly acpAvailable: boolean;
82
+ readonly knownMethods: readonly string[];
83
+ readonly extensionMethods: readonly string[];
84
+ };
85
+ export declare function discoveryContractDrift(set: DiscoveredCapabilitySet): DiscoveryContractDrift;