llm-cli-gateway 1.17.4 → 1.17.6
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/CHANGELOG.md +35 -0
- package/README.md +1 -1
- package/dist/approval-manager.js +0 -8
- package/dist/async-job-manager.d.ts +0 -113
- package/dist/async-job-manager.js +6 -124
- package/dist/cache-stats.d.ts +0 -89
- package/dist/cache-stats.js +0 -62
- package/dist/claude-mcp-config.js +0 -1
- package/dist/cli-updater.d.ts +0 -8
- package/dist/cli-updater.js +0 -12
- package/dist/codex-json-parser.d.ts +0 -20
- package/dist/codex-json-parser.js +0 -21
- package/dist/config.d.ts +0 -31
- package/dist/config.js +2 -72
- package/dist/db.d.ts +0 -18
- package/dist/db.js +0 -22
- package/dist/doctor.d.ts +0 -49
- package/dist/doctor.js +0 -47
- package/dist/endpoint-exposure.js +0 -1
- package/dist/executor.d.ts +0 -19
- package/dist/executor.js +3 -38
- package/dist/flight-recorder.d.ts +0 -26
- package/dist/flight-recorder.js +1 -70
- package/dist/gemini-json-parser.d.ts +0 -25
- package/dist/gemini-json-parser.js +0 -28
- package/dist/health.d.ts +0 -3
- package/dist/health.js +0 -3
- package/dist/index.d.ts +1 -221
- package/dist/index.js +14 -563
- package/dist/job-store.d.ts +0 -74
- package/dist/job-store.js +1 -73
- package/dist/logger.d.ts +0 -7
- package/dist/logger.js +0 -6
- package/dist/migrate-sessions.d.ts +0 -3
- package/dist/migrate-sessions.js +0 -16
- package/dist/migrate.js +1 -18
- package/dist/mistral-meta-json-parser.js +0 -67
- package/dist/model-registry.js +0 -13
- package/dist/pricing.d.ts +0 -46
- package/dist/pricing.js +0 -47
- package/dist/process-monitor.d.ts +0 -15
- package/dist/process-monitor.js +2 -31
- package/dist/prompt-parts.d.ts +0 -25
- package/dist/prompt-parts.js +0 -11
- package/dist/provider-status.d.ts +0 -8
- package/dist/provider-status.js +0 -11
- package/dist/request-helpers.d.ts +0 -334
- package/dist/request-helpers.js +1 -229
- package/dist/resources.d.ts +0 -20
- package/dist/resources.js +1 -34
- package/dist/retry.d.ts +0 -45
- package/dist/retry.js +3 -40
- package/dist/session-manager-pg.d.ts +0 -32
- package/dist/session-manager-pg.js +0 -32
- package/dist/session-manager.d.ts +0 -21
- package/dist/session-manager.js +1 -15
- package/dist/stream-json-parser.d.ts +0 -18
- package/dist/stream-json-parser.js +0 -22
- package/dist/upstream-contracts.d.ts +0 -55
- package/dist/upstream-contracts.js +0 -77
- package/dist/validation-orchestrator.js +0 -3
- package/dist/worktree-manager.d.ts +0 -9
- package/dist/worktree-manager.js +0 -21
- 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")) {
|
package/dist/executor.d.ts
CHANGED
|
@@ -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),
|
|
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);
|
|
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,
|
|
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
|
}
|