llm-cli-gateway 1.17.4 → 1.17.5

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 (64) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/README.md +1 -1
  3. package/dist/approval-manager.js +0 -8
  4. package/dist/async-job-manager.d.ts +0 -113
  5. package/dist/async-job-manager.js +6 -124
  6. package/dist/cache-stats.d.ts +0 -89
  7. package/dist/cache-stats.js +0 -62
  8. package/dist/claude-mcp-config.js +0 -1
  9. package/dist/cli-updater.d.ts +0 -8
  10. package/dist/cli-updater.js +0 -12
  11. package/dist/codex-json-parser.d.ts +0 -20
  12. package/dist/codex-json-parser.js +0 -21
  13. package/dist/config.d.ts +0 -31
  14. package/dist/config.js +2 -72
  15. package/dist/db.d.ts +0 -18
  16. package/dist/db.js +0 -22
  17. package/dist/doctor.d.ts +0 -49
  18. package/dist/doctor.js +0 -47
  19. package/dist/endpoint-exposure.js +0 -1
  20. package/dist/executor.d.ts +0 -19
  21. package/dist/executor.js +3 -38
  22. package/dist/flight-recorder.d.ts +0 -26
  23. package/dist/flight-recorder.js +1 -70
  24. package/dist/gemini-json-parser.d.ts +0 -25
  25. package/dist/gemini-json-parser.js +0 -28
  26. package/dist/health.d.ts +0 -3
  27. package/dist/health.js +0 -3
  28. package/dist/index.d.ts +1 -221
  29. package/dist/index.js +14 -563
  30. package/dist/job-store.d.ts +0 -74
  31. package/dist/job-store.js +1 -73
  32. package/dist/logger.d.ts +0 -7
  33. package/dist/logger.js +0 -6
  34. package/dist/migrate-sessions.d.ts +0 -3
  35. package/dist/migrate-sessions.js +0 -16
  36. package/dist/migrate.js +1 -18
  37. package/dist/mistral-meta-json-parser.js +0 -67
  38. package/dist/model-registry.js +0 -13
  39. package/dist/pricing.d.ts +0 -46
  40. package/dist/pricing.js +0 -47
  41. package/dist/process-monitor.d.ts +0 -15
  42. package/dist/process-monitor.js +2 -31
  43. package/dist/prompt-parts.d.ts +0 -25
  44. package/dist/prompt-parts.js +0 -11
  45. package/dist/provider-status.d.ts +0 -8
  46. package/dist/provider-status.js +0 -11
  47. package/dist/request-helpers.d.ts +0 -334
  48. package/dist/request-helpers.js +1 -229
  49. package/dist/resources.d.ts +0 -20
  50. package/dist/resources.js +1 -34
  51. package/dist/retry.d.ts +0 -45
  52. package/dist/retry.js +3 -40
  53. package/dist/session-manager-pg.d.ts +0 -32
  54. package/dist/session-manager-pg.js +0 -32
  55. package/dist/session-manager.d.ts +0 -21
  56. package/dist/session-manager.js +1 -15
  57. package/dist/stream-json-parser.d.ts +0 -18
  58. package/dist/stream-json-parser.js +0 -22
  59. package/dist/upstream-contracts.d.ts +0 -55
  60. package/dist/upstream-contracts.js +0 -77
  61. package/dist/validation-orchestrator.js +0 -3
  62. package/dist/worktree-manager.d.ts +0 -9
  63. package/dist/worktree-manager.js +0 -21
  64. package/package.json +1 -1
package/dist/db.js CHANGED
@@ -1,7 +1,4 @@
1
1
  import { noopLogger } from "./logger.js";
2
- /**
3
- * Database connection manager for PostgreSQL-backed sessions.
4
- */
5
2
  export class DatabaseConnection {
6
3
  logger;
7
4
  pool = null;
@@ -13,12 +10,8 @@ export class DatabaseConnection {
13
10
  }
14
11
  this.config = config;
15
12
  }
16
- /**
17
- * Initialize connection to PostgreSQL.
18
- */
19
13
  async connect() {
20
14
  const { Pool } = await importOptionalPg();
21
- // Initialize PostgreSQL pool
22
15
  const poolConfig = {
23
16
  connectionString: this.config.database.connectionString,
24
17
  max: this.config.database.pool.max,
@@ -27,7 +20,6 @@ export class DatabaseConnection {
27
20
  statement_timeout: this.config.database.pool.statementTimeout,
28
21
  };
29
22
  this.pool = new Pool(poolConfig);
30
- // Test PostgreSQL connection
31
23
  try {
32
24
  const client = await this.pool.connect();
33
25
  await client.query("SELECT 1");
@@ -39,9 +31,6 @@ export class DatabaseConnection {
39
31
  throw new Error(`Failed to connect to PostgreSQL: ${error instanceof Error ? error.message : String(error)}`);
40
32
  }
41
33
  }
42
- /**
43
- * Graceful shutdown - close all connections
44
- */
45
34
  async disconnect() {
46
35
  this.logger.info("Disconnecting database connections");
47
36
  const errors = [];
@@ -58,14 +47,10 @@ export class DatabaseConnection {
58
47
  throw new Error(`Disconnect errors: ${errors.map(e => e.message).join("; ")}`);
59
48
  }
60
49
  }
61
- /**
62
- * Health check for PostgreSQL.
63
- */
64
50
  async healthCheck() {
65
51
  const result = {
66
52
  postgres: { connected: false, latency: 0 },
67
53
  };
68
- // Check PostgreSQL
69
54
  if (this.pool) {
70
55
  const pgStart = Date.now();
71
56
  let client = null;
@@ -79,7 +64,6 @@ export class DatabaseConnection {
79
64
  result.postgres.connected = false;
80
65
  }
81
66
  finally {
82
- // Always release the client to prevent connection leaks
83
67
  if (client) {
84
68
  client.release();
85
69
  }
@@ -90,9 +74,6 @@ export class DatabaseConnection {
90
74
  });
91
75
  return result;
92
76
  }
93
- /**
94
- * Get PostgreSQL pool
95
- */
96
77
  getPool() {
97
78
  if (!this.pool) {
98
79
  throw new Error("PostgreSQL pool not initialized");
@@ -111,9 +92,6 @@ async function importOptionalPg() {
111
92
  throw error;
112
93
  }
113
94
  }
114
- /**
115
- * Factory function to create and connect DatabaseConnection
116
- */
117
95
  export async function createDatabaseConnection(config, logger) {
118
96
  const db = new DatabaseConnection(config, logger);
119
97
  await db.connect();
package/dist/doctor.d.ts CHANGED
@@ -3,15 +3,6 @@ import { type ProviderLoginStatus } from "./provider-status.js";
3
3
  import type { FlightRecorderQuery } from "./flight-recorder.js";
4
4
  import { type CacheAwarenessConfig } from "./config.js";
5
5
  export type CliType = "claude" | "codex" | "gemini" | "grok" | "mistral";
6
- /**
7
- * Slice 3 cross-cutting: doctor report block summarising the gateway's
8
- * cache-awareness posture. Always PRESENT in the report (zeroed when the
9
- * flight recorder has no rows for the last 24h).
10
- *
11
- * `enabled_features` is an empty array (NOT omitted) when all flags are
12
- * off so callers can distinguish "configured but dormant" from
13
- * "cache_awareness block missing".
14
- */
15
6
  export interface CacheAwarenessReport {
16
7
  enabled_features: Array<"anthropic_cache_control" | "ttl_warnings">;
17
8
  last_24h: {
@@ -33,42 +24,20 @@ export interface VibeSessionLoggingStatus {
33
24
  note: string;
34
25
  }
35
26
  export interface GeminiConfigStatus {
36
- /** Presence of a project-local `GEMINI.md` in the gateway's cwd. */
37
27
  project_gemini_md_present: boolean;
38
28
  project_gemini_md_path: string;
39
- /** Presence of `~/.gemini/GEMINI.md`. */
40
29
  user_gemini_md_present: boolean;
41
30
  user_gemini_md_path: string;
42
- /** Presence and contents of `~/.gemini/settings.json` `mcpServers` block. */
43
31
  settings_json_present: boolean;
44
32
  settings_json_path: string;
45
33
  mcp_servers_registered: string[];
46
- /** Per-server reconciliation against the gateway's `--allowed-mcp-server-names` whitelist. */
47
34
  mcp_reconciliation: {
48
35
  whitelisted: string[];
49
36
  missing_from_settings: string[];
50
37
  };
51
38
  next_actions: string[];
52
39
  }
53
- /**
54
- * Probe ~/.vibe/config.toml to see whether session_logging is enabled. Current
55
- * Mistral Vibe defaults session logging to enabled; an explicit
56
- * `[session_logging] enabled = false` disables `--continue` / `--resume`.
57
- * The probe is read-only: the gateway never mutates this file.
58
- */
59
40
  export declare function checkVibeSessionLogging(home?: string): VibeSessionLoggingStatus;
60
- /**
61
- * U27: Probe Gemini's project/user config locations.
62
- *
63
- * - `./GEMINI.md` (gateway cwd) and `~/.gemini/GEMINI.md` are documented
64
- * "context" surfaces. Missing both means Gemini has no project-specific
65
- * guidance.
66
- * - `~/.gemini/settings.json` defines registered MCP servers (`mcpServers`
67
- * block). The gateway tracks its own whitelist (`CLAUDE_MCP_SERVER_NAMES`)
68
- * and surfaces a reconciliation warning for each whitelisted server not
69
- * present in settings.json so callers don't ship requests for unregistered
70
- * servers.
71
- */
72
41
  export declare function checkGeminiConfig(cwd?: string, home?: string, whitelist?: readonly string[]): GeminiConfigStatus;
73
42
  export interface DoctorReport {
74
43
  schema_version: "1.0";
@@ -136,35 +105,17 @@ export interface DoctorReport {
136
105
  note: string;
137
106
  recommendation: string;
138
107
  how_to_check: string;
139
- /** Whether the expensive installed binary probe was performed (requires --probe-upstream). */
140
108
  probed: boolean;
141
- /** Cheap installed versions (always present when CLIs are detected). */
142
109
  installed_versions: Partial<Record<CliType, string | null>>;
143
- /** Lightweight declared contracts (always present, no spawning). */
144
110
  contracts: ReturnType<typeof import("./upstream-contracts.js").buildUpstreamContractReport>;
145
- /** Full probed report only when --probe-upstream was used. */
146
111
  probe_report?: ReturnType<typeof import("./upstream-contracts.js").buildUpstreamContractReport>;
147
112
  };
148
113
  next_actions: string[];
149
114
  }
150
115
  export interface CreateDoctorReportOptions {
151
116
  env?: NodeJS.ProcessEnv;
152
- /**
153
- * Optional read access to the flight recorder. Drives the
154
- * cache_awareness.last_24h and per_cli aggregates. When absent, those
155
- * blocks report zeroed aggregates (still PRESENT in the report).
156
- */
157
117
  flightRecorder?: FlightRecorderQuery;
158
- /**
159
- * Optional CacheAwarenessConfig. Drives `enabled_features`. When
160
- * absent, `enabled_features` is empty (all behaviour considered off).
161
- */
162
118
  cacheAwareness?: CacheAwarenessConfig;
163
- /**
164
- * When true, perform the (potentially slow) installed CLI --help probe
165
- * for upstream contract drift detection. This is opt-in because it
166
- * spawns the real provider CLIs.
167
- */
168
119
  probeUpstream?: boolean;
169
120
  }
170
121
  export declare function createDoctorReport(envOrOptions?: NodeJS.ProcessEnv | CreateDoctorReportOptions): DoctorReport;
package/dist/doctor.js CHANGED
@@ -10,12 +10,6 @@ import { loadCacheAwarenessConfig } from "./config.js";
10
10
  import { computeGlobalCacheStats } from "./cache-stats.js";
11
11
  import { FlightRecorder, resolveFlightRecorderDbPath } from "./flight-recorder.js";
12
12
  import { buildUpstreamContractReport } from "./upstream-contracts.js";
13
- /**
14
- * Probe ~/.vibe/config.toml to see whether session_logging is enabled. Current
15
- * Mistral Vibe defaults session logging to enabled; an explicit
16
- * `[session_logging] enabled = false` disables `--continue` / `--resume`.
17
- * The probe is read-only: the gateway never mutates this file.
18
- */
19
13
  export function checkVibeSessionLogging(home = homedir()) {
20
14
  const configPath = join(home, ".vibe", "config.toml");
21
15
  if (!existsSync(configPath)) {
@@ -56,10 +50,6 @@ export function checkVibeSessionLogging(home = homedir()) {
56
50
  };
57
51
  }
58
52
  }
59
- /**
60
- * Tiny TOML probe focused on `[session_logging] enabled = ...`. Avoids pulling
61
- * in the full `toml` parser when only one boolean is needed.
62
- */
63
53
  function parseVibeSessionLoggingEnabled(text) {
64
54
  const lines = text.split(/\r?\n/);
65
55
  let inSection = false;
@@ -84,7 +74,6 @@ function parseVibeSessionLoggingEnabled(text) {
84
74
  }
85
75
  }
86
76
  else {
87
- // Allow dotted form: session_logging.enabled = true
88
77
  const dotted = line.match(/^session_logging\.enabled\s*=\s*(.+)$/);
89
78
  if (dotted) {
90
79
  const value = dotted[1].trim().toLowerCase();
@@ -98,18 +87,6 @@ function parseVibeSessionLoggingEnabled(text) {
98
87
  }
99
88
  return undefined;
100
89
  }
101
- /**
102
- * U27: Probe Gemini's project/user config locations.
103
- *
104
- * - `./GEMINI.md` (gateway cwd) and `~/.gemini/GEMINI.md` are documented
105
- * "context" surfaces. Missing both means Gemini has no project-specific
106
- * guidance.
107
- * - `~/.gemini/settings.json` defines registered MCP servers (`mcpServers`
108
- * block). The gateway tracks its own whitelist (`CLAUDE_MCP_SERVER_NAMES`)
109
- * and surfaces a reconciliation warning for each whitelisted server not
110
- * present in settings.json so callers don't ship requests for unregistered
111
- * servers.
112
- */
113
90
  export function checkGeminiConfig(cwd = process.cwd(), home = homedir(), whitelist = CLAUDE_MCP_SERVER_NAMES) {
114
91
  const projectGeminiMd = join(cwd, "GEMINI.md");
115
92
  const userGeminiMd = join(home, ".gemini", "GEMINI.md");
@@ -127,7 +104,6 @@ export function checkGeminiConfig(cwd = process.cwd(), home = homedir(), whiteli
127
104
  }
128
105
  }
129
106
  catch {
130
- // Best-effort: leave list empty so the next_action surfaces the gap.
131
107
  }
132
108
  }
133
109
  const missingFromSettings = whitelist.filter(name => !mcpServersRegistered.includes(name));
@@ -165,7 +141,6 @@ function packageVersion() {
165
141
  return parsed.version || "unknown";
166
142
  }
167
143
  catch {
168
- // Try next candidate.
169
144
  }
170
145
  }
171
146
  return "unknown";
@@ -204,10 +179,6 @@ function chatGPTConnectorUrl(env, rawPublicUrl) {
204
179
  return null;
205
180
  }
206
181
  }
207
- /**
208
- * Build the cache_awareness block. ALWAYS present in the report; fields
209
- * are zeroed when the flight recorder is missing or empty.
210
- */
211
182
  function buildCacheAwarenessReport(opts) {
212
183
  const enabled = [];
213
184
  if (opts.cacheAwareness?.emitAnthropicCacheControl) {
@@ -264,7 +235,6 @@ function buildCacheAwarenessReport(opts) {
264
235
  };
265
236
  }
266
237
  export function createDoctorReport(envOrOptions = process.env) {
267
- // Preserve back-compat: previous signature accepted a bare `env` object.
268
238
  const opts = isCreateDoctorReportOptions(envOrOptions)
269
239
  ? envOrOptions
270
240
  : { env: envOrOptions };
@@ -351,14 +321,10 @@ export function createDoctorReport(envOrOptions = process.env) {
351
321
  report.next_actions.push(`${name}: ${provider.login_guidance.summary}`);
352
322
  }
353
323
  }
354
- // Mistral-specific: surface the session_logging toggle BEFORE a --continue/--resume
355
- // request fails opaquely. The check is read-only; the gateway never mutates the file.
356
324
  const vibeStatus = report.client_config.vibe_session_logging;
357
325
  if (report.providers.mistral.cli_available && !vibeStatus.session_logging_enabled) {
358
326
  report.next_actions.push(`mistral: ${vibeStatus.note}`);
359
327
  }
360
- // U27: surface Gemini config gaps (missing GEMINI.md, missing settings.json,
361
- // MCP-server whitelist drift) only when Gemini CLI is actually installed.
362
328
  if (report.providers.gemini.cli_available) {
363
329
  for (const action of report.client_config.gemini_config.next_actions) {
364
330
  report.next_actions.push(`gemini: ${action}`);
@@ -367,7 +333,6 @@ export function createDoctorReport(envOrOptions = process.env) {
367
333
  if (report.next_actions.length === 0) {
368
334
  report.next_actions.push("Run a client setup guide and verify with doctor --json after each step.");
369
335
  }
370
- // Upstream drift detection recommendation — surfaced for habitual use after provider upgrades.
371
336
  const hasAnyCli = Object.values(report.providers).some(p => p.cli_available);
372
337
  if (hasAnyCli) {
373
338
  if (report.upstream.probed) {
@@ -382,17 +347,12 @@ export function createDoctorReport(envOrOptions = process.env) {
382
347
  return report;
383
348
  }
384
349
  export function printDoctorJson(opts = {}) {
385
- // Load cache-awareness config + open the flight recorder so the doctor
386
- // command can populate cache_awareness.last_24h. Both are best-effort —
387
- // failures degrade to the zeroed block (buildCacheAwarenessReport
388
- // handles missing deps).
389
350
  let cacheAwareness;
390
351
  let flightRecorder;
391
352
  try {
392
353
  cacheAwareness = loadCacheAwarenessConfig();
393
354
  }
394
355
  catch {
395
- // ignore
396
356
  }
397
357
  try {
398
358
  const dbPath = resolveFlightRecorderDbPath();
@@ -400,7 +360,6 @@ export function printDoctorJson(opts = {}) {
400
360
  flightRecorder = new FlightRecorder(dbPath);
401
361
  }
402
362
  catch {
403
- // ignore
404
363
  }
405
364
  const report = createDoctorReport({
406
365
  env: process.env,
@@ -414,16 +373,10 @@ export function printDoctorJson(opts = {}) {
414
373
  flightRecorder.close();
415
374
  }
416
375
  catch {
417
- // best effort
418
376
  }
419
377
  }
420
378
  }
421
379
  function isCreateDoctorReportOptions(value) {
422
- // CreateDoctorReportOptions carries either `env` (an object) or
423
- // `flightRecorder` (an object). A NodeJS.ProcessEnv is a flat
424
- // Record<string, string|undefined> — even if a shell happens to export
425
- // `env=production` or `flightRecorder=...`, the value at that key is a
426
- // STRING, not an object, so the typeof checks here cannot collide.
427
380
  if (value === null || typeof value !== "object")
428
381
  return false;
429
382
  if (Object.prototype.hasOwnProperty.call(value, "flightRecorder")) {
@@ -112,7 +112,6 @@ function inferTunnelProvider(rawUrl) {
112
112
  return host;
113
113
  }
114
114
  catch {
115
- // Treat unparsable URLs as not classified.
116
115
  }
117
116
  return "";
118
117
  }
@@ -5,15 +5,7 @@ export interface ExecuteOptions {
5
5
  idleTimeout?: number;
6
6
  cwd?: string;
7
7
  logger?: Logger;
8
- /** Extra environment variables to inject; merged after PATH. */
9
8
  env?: NodeJS.ProcessEnv;
10
- /**
11
- * Slice κ: optional UTF-8 payload to write to the child's stdin
12
- * immediately after spawn. When provided, stdio for stdin switches
13
- * from "ignore" to "pipe" so the CLI can read the payload (used by
14
- * `claude --input-format stream-json`). Undefined preserves the
15
- * legacy stdio:["ignore","pipe","pipe"] shape.
16
- */
17
9
  stdin?: string;
18
10
  }
19
11
  export interface ExecuteResult {
@@ -36,18 +28,7 @@ export declare function resolveCommandForSpawn(command: string, args: string[],
36
28
  export declare function shouldDetachProviderProcess(platform?: NodeJS.Platform): boolean;
37
29
  export declare function registerProcessGroup(pid: number): void;
38
30
  export declare function unregisterProcessGroup(pid: number): void;
39
- /**
40
- * Kill all active process groups. Called on gateway shutdown.
41
- * Sends SIGTERM to all groups, waits 3s, then SIGKILL survivors.
42
- * Returns a Promise that resolves after SIGKILL escalation completes.
43
- * The returned Promise keeps the event loop alive (no .unref()),
44
- * ensuring the process does NOT exit before SIGKILL fires.
45
- */
46
31
  export declare function killAllProcessGroups(): Promise<void>;
47
- /**
48
- * Kill an entire process group. Falls back to killing just the process
49
- * if the group kill fails (e.g., pid not yet assigned).
50
- */
51
32
  export declare function killProcessGroup(proc: ChildProcess, signal: NodeJS.Signals): boolean;
52
33
  export declare function spawnCliProcess(command: string, args: string[], options: {
53
34
  cwd?: string;
package/dist/executor.js CHANGED
@@ -73,14 +73,13 @@ function windowsCommonCliPaths(env, home) {
73
73
  export function buildExtendedPath(env = process.env, home = homedir(), nodePath = process.execPath, platform = process.platform) {
74
74
  const additionalPaths = [
75
75
  pathJoinFor(platform, home, ".local", "bin"),
76
- dirnameFor(platform, nodePath), // Current node's bin directory
76
+ dirnameFor(platform, nodePath),
77
77
  "/usr/local/bin",
78
78
  "/usr/bin",
79
79
  ];
80
80
  if (platform === "win32") {
81
81
  additionalPaths.push(...windowsCommonCliPaths(env, home));
82
82
  }
83
- // Add all nvm node version bin directories
84
83
  const nvmPath = getNvmPath();
85
84
  if (nvmPath) {
86
85
  additionalPaths.push(nvmPath);
@@ -90,7 +89,6 @@ export function buildExtendedPath(env = process.env, home = homedir(), nodePath
90
89
  .filter(Boolean)
91
90
  .join(pathDelimiterFor(platform));
92
91
  }
93
- // Extend PATH to include common locations for CLI tools.
94
92
  export function getExtendedPath() {
95
93
  return buildExtendedPath();
96
94
  }
@@ -143,11 +141,6 @@ export function resolveCommandForSpawn(command, args, options = {}) {
143
141
  "/d",
144
142
  "/s",
145
143
  "/c",
146
- // Windows .cmd/.bat shims require cmd.exe. `buildWindowsCmdCommand`
147
- // applies CommandLineToArgvW quoting and cmd metacharacter escaping
148
- // to every dynamic segment before it reaches this shell boundary.
149
- //
150
- // codeql[js/shell-command-constructed-from-input]
151
144
  `"${buildWindowsCmdCommand(resolved, args)}"`,
152
145
  ],
153
146
  windowsVerbatimArguments: true,
@@ -156,7 +149,6 @@ export function resolveCommandForSpawn(command, args, options = {}) {
156
149
  return { command: resolved, args };
157
150
  }
158
151
  function buildWindowsCmdCommand(command, args) {
159
- // codeql[js/shell-command-constructed-from-input]
160
152
  return [escapeWindowsCmdCommand(command), ...args.map(escapeWindowsCmdArgument)].join(" ");
161
153
  }
162
154
  const WINDOWS_CMD_META_CHARS = new Set([
@@ -182,11 +174,6 @@ const WINDOWS_CMD_META_CHARS = new Set([
182
174
  function escapeWindowsCmdCommand(value) {
183
175
  return escapeWindowsCmdMetaChars(win32.normalize(value));
184
176
  }
185
- // CommandLineToArgvW rules: a run of N backslashes before a literal " must be
186
- // doubled and followed by \" (yielding 2N+1 backslashes total, so the parser
187
- // strips N and keeps the quote as literal); a run of N backslashes immediately
188
- // before the closing " must be doubled (2N) so the quote still terminates the
189
- // arg. Then wrap in quotes and caret-escape cmd.exe metacharacters.
190
177
  function escapeWindowsCmdArgument(value) {
191
178
  return escapeWindowsCmdMetaChars(quoteWindowsArgForCommandLineToArgv(`${value}`));
192
179
  }
@@ -237,12 +224,8 @@ function resolveWindowsCommandPath(command, envPath) {
237
224
  }
238
225
  return null;
239
226
  }
240
- /** Registry of active detached process groups for shutdown cleanup. */
241
227
  const activeProcessGroups = new Set();
242
228
  export function shouldDetachProviderProcess(platform = process.platform) {
243
- // On Windows, detached console children can flash visible cmd/conhost windows
244
- // when provider CLIs are native console apps or .cmd shims. Keep them in the
245
- // gateway process tree and rely on hidden-window spawn plus taskkill cleanup.
246
229
  return platform !== "win32";
247
230
  }
248
231
  export function registerProcessGroup(pid) {
@@ -251,13 +234,6 @@ export function registerProcessGroup(pid) {
251
234
  export function unregisterProcessGroup(pid) {
252
235
  activeProcessGroups.delete(pid);
253
236
  }
254
- /**
255
- * Kill all active process groups. Called on gateway shutdown.
256
- * Sends SIGTERM to all groups, waits 3s, then SIGKILL survivors.
257
- * Returns a Promise that resolves after SIGKILL escalation completes.
258
- * The returned Promise keeps the event loop alive (no .unref()),
259
- * ensuring the process does NOT exit before SIGKILL fires.
260
- */
261
237
  export function killAllProcessGroups() {
262
238
  if (activeProcessGroups.size === 0)
263
239
  return Promise.resolve();
@@ -270,7 +246,6 @@ export function killAllProcessGroups() {
270
246
  process.kill(-pid, "SIGTERM");
271
247
  }
272
248
  catch {
273
- /* ESRCH ok */
274
249
  }
275
250
  }
276
251
  }
@@ -285,19 +260,14 @@ export function killAllProcessGroups() {
285
260
  process.kill(-pid, "SIGKILL");
286
261
  }
287
262
  catch {
288
- /* ESRCH ok */
289
263
  }
290
264
  }
291
265
  }
292
266
  activeProcessGroups.clear();
293
267
  resolve();
294
- }, 3000); // No .unref() — keeps event loop alive through escalation
268
+ }, 3000);
295
269
  });
296
270
  }
297
- /**
298
- * Kill an entire process group. Falls back to killing just the process
299
- * if the group kill fails (e.g., pid not yet assigned).
300
- */
301
271
  export function killProcessGroup(proc, signal) {
302
272
  if (proc.pid) {
303
273
  if (process.platform === "win32") {
@@ -308,7 +278,6 @@ export function killProcessGroup(proc, signal) {
308
278
  return true;
309
279
  }
310
280
  catch (err) {
311
- // ESRCH = process/group already dead — not an error
312
281
  if (err.code !== "ESRCH") {
313
282
  try {
314
283
  return proc.kill(signal);
@@ -376,7 +345,6 @@ export async function executeCli(command, args, options = {}) {
376
345
  let exited = false;
377
346
  let outputSize = 0;
378
347
  let settled = false;
379
- // Single cleanup flag to prevent double-unregister
380
348
  let groupCleaned = false;
381
349
  const cleanupProcessGroup = () => {
382
350
  if (groupCleaned)
@@ -399,7 +367,6 @@ export async function executeCli(command, args, options = {}) {
399
367
  }, 5000);
400
368
  }, timeoutMs)
401
369
  : undefined;
402
- // Idle timeout: kill process if no stdout/stderr activity for idleMs
403
370
  const idleMs = typeof idleTimeout === "number" && Number.isFinite(idleTimeout) && idleTimeout > 0
404
371
  ? idleTimeout
405
372
  : undefined;
@@ -421,7 +388,6 @@ export async function executeCli(command, args, options = {}) {
421
388
  }, 5000);
422
389
  }, idleMs);
423
390
  };
424
- // Start idle timer immediately (covers case where process never outputs)
425
391
  resetIdleTimer();
426
392
  const finalizeReject = (error) => {
427
393
  if (settled) {
@@ -475,7 +441,6 @@ export async function executeCli(command, args, options = {}) {
475
441
  if (idleTimerId) {
476
442
  clearTimeout(idleTimerId);
477
443
  }
478
- // Unregister process group on clean exit (no kill was issued)
479
444
  if (!timedOut && !idledOut && !overflowed) {
480
445
  cleanupProcessGroup();
481
446
  }
@@ -489,7 +454,7 @@ export async function executeCli(command, args, options = {}) {
489
454
  const result = {
490
455
  stdout,
491
456
  stderr: stderr + `\nProcess timed out after ${timeoutMs}ms`,
492
- code: 124, // Standard timeout exit code
457
+ code: 124,
493
458
  };
494
459
  const error = new Error(result.stderr);
495
460
  error.code = 124;
@@ -8,12 +8,6 @@ export interface FlightLogStart {
8
8
  asyncJobId?: string;
9
9
  stablePrefixHash?: string;
10
10
  stablePrefixTokens?: number;
11
- /**
12
- * Slice κ: number of caller-supplied prompt-parts content blocks
13
- * that the gateway emitted with an explicit `cache_control`
14
- * breakpoint on this request. `null` (default) for non-κ requests,
15
- * including pre-κ rows after a v4 migration of a legacy DB.
16
- */
17
11
  cacheControlBlocks?: number;
18
12
  }
19
13
  export interface FlightLogResult {
@@ -45,21 +39,6 @@ export declare class FlightRecorder {
45
39
  constructor(dbPath: string);
46
40
  logStart(entry: FlightLogStart): void;
47
41
  logComplete(correlationId: string, result: FlightLogResult): void;
48
- /**
49
- * Read-only query over the requests + gateway_metadata tables. Used by
50
- * cache-stats / MCP resources / doctor without exposing a second SQLite
51
- * connection. better-sqlite3 in WAL mode handles concurrent readers
52
- * inside a single process safely.
53
- *
54
- * Safety:
55
- * - Caller MUST pass parameterised SQL — direct string interpolation of
56
- * untrusted values is unsafe.
57
- * - The compiled statement's `.readonly` flag is checked at runtime;
58
- * anything that can mutate rows (INSERT/UPDATE/DELETE, including the
59
- * `RETURNING` forms that better-sqlite3 surfaces via `.all()`) throws.
60
- * This blocks the writer-disguised-as-reader vector codex-r1/F3
61
- * flagged, even when the caller is internal gateway code.
62
- */
63
42
  queryRequests<T = Record<string, unknown>>(sql: string, ...params: unknown[]): T[];
64
43
  flush(): void;
65
44
  close(): void;
@@ -72,11 +51,6 @@ export declare class NoopFlightRecorder {
72
51
  close(): void;
73
52
  }
74
53
  export type FlightRecorderLike = FlightRecorder | NoopFlightRecorder;
75
- /**
76
- * Read-only subset of FlightRecorder used by cache-stats / MCP resources /
77
- * doctor. Accepts either FlightRecorder or NoopFlightRecorder; the noop
78
- * returns `[]` from every query so downstream aggregation is empty by design.
79
- */
80
54
  export interface FlightRecorderQuery {
81
55
  queryRequests<T = Record<string, unknown>>(sql: string, ...params: unknown[]): T[];
82
56
  }