llm-cli-gateway 2.14.0-rc.1 → 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.
- package/CHANGELOG.md +42 -0
- package/README.md +18 -6
- package/dist/async-job-manager.d.ts +36 -1
- package/dist/async-job-manager.js +276 -20
- package/dist/config.d.ts +11 -0
- package/dist/config.js +37 -1
- package/dist/index.js +20 -3
- package/dist/job-store.d.ts +75 -5
- package/dist/job-store.js +248 -39
- package/dist/postgres-job-store-worker.js +124 -7
- package/dist/sqlite-driver.d.ts +1 -1
- package/dist/sqlite-driver.js +2 -1
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -291,9 +291,17 @@ function getJobStore(runtimeLogger = logger) {
|
|
|
291
291
|
return jobStore;
|
|
292
292
|
}
|
|
293
293
|
function newAsyncJobManager(metrics, runtimeLogger, store = getJobStore(runtimeLogger), fr = getFlightRecorder(runtimeLogger)) {
|
|
294
|
+
const pc = getPersistenceConfig(runtimeLogger);
|
|
294
295
|
return new AsyncJobManager(runtimeLogger, (cli, durationMs, success) => {
|
|
295
296
|
metrics.recordRequest(cli, durationMs, success);
|
|
296
|
-
}, store, fr, getLimitsConfig(runtimeLogger).jobs
|
|
297
|
+
}, store, fr, getLimitsConfig(runtimeLogger).jobs, true, {
|
|
298
|
+
instanceHeartbeatMs: pc.instanceHeartbeatMs,
|
|
299
|
+
instanceLeaseTtlMs: pc.instanceLeaseTtlMs,
|
|
300
|
+
httpJobGraceMs: pc.httpJobGraceMs,
|
|
301
|
+
orphanSweepIntervalMs: pc.orphanSweepIntervalMs,
|
|
302
|
+
instanceGcMs: pc.instanceGcMs,
|
|
303
|
+
role: process.argv.includes("--transport=http") ? "http" : "stdio",
|
|
304
|
+
});
|
|
297
305
|
}
|
|
298
306
|
function getAsyncJobManager(runtimeLogger = logger) {
|
|
299
307
|
asyncJobManager ??= newAsyncJobManager(performanceMetrics, runtimeLogger);
|
|
@@ -573,7 +581,7 @@ async function awaitJobOrDefer(cli, args, corrId, idleTimeoutMs, outputFormat, f
|
|
|
573
581
|
}
|
|
574
582
|
const deferralAvailable = runtime.persistence.backend !== "none" &&
|
|
575
583
|
runtime.persistence.asyncJobsEnabled &&
|
|
576
|
-
runtime.asyncJobManager.
|
|
584
|
+
runtime.asyncJobManager.canAdmitDurableJobs();
|
|
577
585
|
if (SYNC_DEADLINE_MS === 0 || !deferralAvailable) {
|
|
578
586
|
const command = providerCommandName(cli);
|
|
579
587
|
let slot;
|
|
@@ -662,7 +670,7 @@ async function awaitApiJobOrDefer(provider, apiRequest, corrId, runtime = resolv
|
|
|
662
670
|
};
|
|
663
671
|
const deferralAvailable = runtime.persistence.backend !== "none" &&
|
|
664
672
|
runtime.persistence.asyncJobsEnabled &&
|
|
665
|
-
runtime.asyncJobManager.
|
|
673
|
+
runtime.asyncJobManager.canAdmitDurableJobs();
|
|
666
674
|
if (SYNC_DEADLINE_MS === 0 || !deferralAvailable || forceInline) {
|
|
667
675
|
let slot;
|
|
668
676
|
try {
|
|
@@ -8282,6 +8290,15 @@ function registerHealthResource(server) {
|
|
|
8282
8290
|
async function shutdown(signal) {
|
|
8283
8291
|
logger.info(`Received ${signal}, shutting down gracefully...`);
|
|
8284
8292
|
try {
|
|
8293
|
+
if (asyncJobManager) {
|
|
8294
|
+
try {
|
|
8295
|
+
await asyncJobManager.dispose({ timeoutMs: 5000 });
|
|
8296
|
+
logger.info("Async job manager disposed");
|
|
8297
|
+
}
|
|
8298
|
+
catch (err) {
|
|
8299
|
+
logger.error("Async job manager dispose failed", err);
|
|
8300
|
+
}
|
|
8301
|
+
}
|
|
8285
8302
|
await killAllProcessGroups();
|
|
8286
8303
|
logger.info("All process groups terminated");
|
|
8287
8304
|
if (activeHttpGateway) {
|
package/dist/job-store.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { Logger } from "./logger.js";
|
|
2
2
|
import type { PersistenceConfig } from "./config.js";
|
|
3
|
-
export type JobStoreStatus = "running" | "completed" | "failed" | "canceled" | "orphaned";
|
|
3
|
+
export type JobStoreStatus = "queued" | "running" | "completed" | "failed" | "canceled" | "orphaned";
|
|
4
|
+
export type JobStoreActiveStatus = Extract<JobStoreStatus, "queued" | "running">;
|
|
4
5
|
export type JobTransport = "process" | "http";
|
|
5
6
|
export interface JobRecord {
|
|
6
7
|
id: string;
|
|
@@ -23,11 +24,26 @@ export interface JobRecord {
|
|
|
23
24
|
transport: JobTransport;
|
|
24
25
|
httpStatus: number | null;
|
|
25
26
|
payloadJson: string | null;
|
|
27
|
+
ownerInstance: string | null;
|
|
28
|
+
leaseDeadline: number | null;
|
|
26
29
|
}
|
|
27
30
|
export declare function resolveJobStoreDbPath(): string | null;
|
|
28
31
|
export declare function resolveJobRetentionMs(): number;
|
|
29
32
|
export declare function resolveDedupWindowMs(): number;
|
|
30
33
|
export declare function computeRequestKey(cli: string, args: string[], extra?: string): string;
|
|
34
|
+
export interface GatewayInstanceMeta {
|
|
35
|
+
instanceId: string;
|
|
36
|
+
role: string | null;
|
|
37
|
+
hostname: string | null;
|
|
38
|
+
pid: number | null;
|
|
39
|
+
}
|
|
40
|
+
export interface SweepCandidate {
|
|
41
|
+
id: string;
|
|
42
|
+
pid: number | null;
|
|
43
|
+
transport: JobTransport;
|
|
44
|
+
ownerInstance: string | null;
|
|
45
|
+
hostname: string | null;
|
|
46
|
+
}
|
|
31
47
|
export interface JobStore {
|
|
32
48
|
recordStart(input: {
|
|
33
49
|
id: string;
|
|
@@ -39,13 +55,23 @@ export interface JobStore {
|
|
|
39
55
|
startedAt: string;
|
|
40
56
|
pid: number | null;
|
|
41
57
|
ownerPrincipal?: string | null;
|
|
58
|
+
ownerInstance?: string | null;
|
|
42
59
|
transport?: JobTransport;
|
|
43
60
|
payloadJson?: string | null;
|
|
44
61
|
}): void;
|
|
62
|
+
markRunning(id: string, opts: {
|
|
63
|
+
pid: number | null;
|
|
64
|
+
}): boolean;
|
|
65
|
+
registerInstance(meta: GatewayInstanceMeta): void;
|
|
66
|
+
heartbeat(instanceId: string): void;
|
|
67
|
+
deregisterInstance(instanceId: string): void;
|
|
68
|
+
selectStaleProcessCandidates(leaseTtlMs: number, httpJobGraceMs: number): SweepCandidate[];
|
|
69
|
+
recoverStaleJobs(leaseTtlMs: number, httpJobGraceMs: number, liveConfirmedIds?: string[]): OrphanedJobSnapshot[];
|
|
70
|
+
gcInstances(instanceGcMs: number): number;
|
|
45
71
|
recordOutput(id: string, stdout: string, stderr: string, outputTruncated: boolean): void;
|
|
46
72
|
recordComplete(input: {
|
|
47
73
|
id: string;
|
|
48
|
-
status: Exclude<JobStoreStatus, "running">;
|
|
74
|
+
status: Exclude<JobStoreStatus, "running" | "queued">;
|
|
49
75
|
exitCode: number | null;
|
|
50
76
|
stdout: string;
|
|
51
77
|
stderr: string;
|
|
@@ -117,6 +143,7 @@ export declare class SqliteJobStore implements JobStore, ValidationRunStore {
|
|
|
117
143
|
private db;
|
|
118
144
|
private retentionMs;
|
|
119
145
|
private dedupWindowMs;
|
|
146
|
+
private leaseTtlMs;
|
|
120
147
|
private insertStmt;
|
|
121
148
|
private updateOutputStmt;
|
|
122
149
|
private updateCompleteStmt;
|
|
@@ -125,9 +152,19 @@ export declare class SqliteJobStore implements JobStore, ValidationRunStore {
|
|
|
125
152
|
private selectRunningOrphansStmt;
|
|
126
153
|
private markOrphanedStmt;
|
|
127
154
|
private deleteExpiredStmt;
|
|
155
|
+
private markRunningStmt;
|
|
156
|
+
private registerInstanceStmt;
|
|
157
|
+
private heartbeatInstanceStmt;
|
|
158
|
+
private heartbeatJobsStmt;
|
|
159
|
+
private deregisterInstanceStmt;
|
|
160
|
+
private selectStaleCandidatesStmt;
|
|
161
|
+
private orphanExpiredStmt;
|
|
162
|
+
private advanceLeaseStmt;
|
|
163
|
+
private gcInstancesStmt;
|
|
128
164
|
constructor(dbPath: string, logger?: Logger, options?: {
|
|
129
165
|
retentionMs?: number;
|
|
130
166
|
dedupWindowMs?: number;
|
|
167
|
+
leaseTtlMs?: number;
|
|
131
168
|
});
|
|
132
169
|
recordStart(input: {
|
|
133
170
|
id: string;
|
|
@@ -139,13 +176,23 @@ export declare class SqliteJobStore implements JobStore, ValidationRunStore {
|
|
|
139
176
|
startedAt: string;
|
|
140
177
|
pid: number | null;
|
|
141
178
|
ownerPrincipal?: string | null;
|
|
179
|
+
ownerInstance?: string | null;
|
|
142
180
|
transport?: JobTransport;
|
|
143
181
|
payloadJson?: string | null;
|
|
144
182
|
}): void;
|
|
183
|
+
markRunning(id: string, opts: {
|
|
184
|
+
pid: number | null;
|
|
185
|
+
}): boolean;
|
|
186
|
+
registerInstance(meta: GatewayInstanceMeta): void;
|
|
187
|
+
heartbeat(instanceId: string): void;
|
|
188
|
+
deregisterInstance(instanceId: string): void;
|
|
189
|
+
selectStaleProcessCandidates(_leaseTtlMs: number, _httpJobGraceMs: number): SweepCandidate[];
|
|
190
|
+
recoverStaleJobs(leaseTtlMs: number, httpJobGraceMs: number, liveConfirmedIds?: string[]): OrphanedJobSnapshot[];
|
|
191
|
+
gcInstances(instanceGcMs: number): number;
|
|
145
192
|
recordOutput(id: string, stdout: string, stderr: string, outputTruncated: boolean): void;
|
|
146
193
|
recordComplete(input: {
|
|
147
194
|
id: string;
|
|
148
|
-
status: Exclude<JobStoreStatus, "running">;
|
|
195
|
+
status: Exclude<JobStoreStatus, "running" | "queued">;
|
|
149
196
|
exitCode: number | null;
|
|
150
197
|
stdout: string;
|
|
151
198
|
stderr: string;
|
|
@@ -176,9 +223,11 @@ export declare class MemoryJobStore implements JobStore {
|
|
|
176
223
|
private rows;
|
|
177
224
|
private retentionMs;
|
|
178
225
|
private dedupWindowMs;
|
|
226
|
+
private leaseTtlMs;
|
|
179
227
|
constructor(options?: {
|
|
180
228
|
retentionMs?: number;
|
|
181
229
|
dedupWindowMs?: number;
|
|
230
|
+
leaseTtlMs?: number;
|
|
182
231
|
});
|
|
183
232
|
recordStart(input: {
|
|
184
233
|
id: string;
|
|
@@ -190,13 +239,23 @@ export declare class MemoryJobStore implements JobStore {
|
|
|
190
239
|
startedAt: string;
|
|
191
240
|
pid: number | null;
|
|
192
241
|
ownerPrincipal?: string | null;
|
|
242
|
+
ownerInstance?: string | null;
|
|
193
243
|
transport?: JobTransport;
|
|
194
244
|
payloadJson?: string | null;
|
|
195
245
|
}): void;
|
|
246
|
+
markRunning(id: string, opts: {
|
|
247
|
+
pid: number | null;
|
|
248
|
+
}): boolean;
|
|
249
|
+
registerInstance(_meta: GatewayInstanceMeta): void;
|
|
250
|
+
heartbeat(instanceId: string): void;
|
|
251
|
+
deregisterInstance(_instanceId: string): void;
|
|
252
|
+
selectStaleProcessCandidates(_leaseTtlMs: number, _httpJobGraceMs: number): SweepCandidate[];
|
|
253
|
+
recoverStaleJobs(_leaseTtlMs: number, _httpJobGraceMs: number, _liveConfirmedIds?: string[]): OrphanedJobSnapshot[];
|
|
254
|
+
gcInstances(_instanceGcMs: number): number;
|
|
196
255
|
recordOutput(id: string, stdout: string, stderr: string, outputTruncated: boolean): void;
|
|
197
256
|
recordComplete(input: {
|
|
198
257
|
id: string;
|
|
199
|
-
status: Exclude<JobStoreStatus, "running">;
|
|
258
|
+
status: Exclude<JobStoreStatus, "running" | "queued">;
|
|
200
259
|
exitCode: number | null;
|
|
201
260
|
stdout: string;
|
|
202
261
|
stderr: string;
|
|
@@ -223,6 +282,7 @@ export declare class PostgresJobStore implements JobStore, ValidationRunStore {
|
|
|
223
282
|
constructor(dsn: string, logger?: Logger, options?: {
|
|
224
283
|
retentionMs?: number;
|
|
225
284
|
dedupWindowMs?: number;
|
|
285
|
+
leaseTtlMs?: number;
|
|
226
286
|
});
|
|
227
287
|
private syncCall;
|
|
228
288
|
recordStart(input: {
|
|
@@ -235,13 +295,23 @@ export declare class PostgresJobStore implements JobStore, ValidationRunStore {
|
|
|
235
295
|
startedAt: string;
|
|
236
296
|
pid: number | null;
|
|
237
297
|
ownerPrincipal?: string | null;
|
|
298
|
+
ownerInstance?: string | null;
|
|
238
299
|
transport?: JobTransport;
|
|
239
300
|
payloadJson?: string | null;
|
|
240
301
|
}): void;
|
|
302
|
+
markRunning(id: string, opts: {
|
|
303
|
+
pid: number | null;
|
|
304
|
+
}): boolean;
|
|
305
|
+
registerInstance(meta: GatewayInstanceMeta): void;
|
|
306
|
+
heartbeat(instanceId: string): void;
|
|
307
|
+
deregisterInstance(instanceId: string): void;
|
|
308
|
+
selectStaleProcessCandidates(leaseTtlMs: number, httpJobGraceMs: number): SweepCandidate[];
|
|
309
|
+
recoverStaleJobs(leaseTtlMs: number, httpJobGraceMs: number, liveConfirmedIds?: string[]): OrphanedJobSnapshot[];
|
|
310
|
+
gcInstances(instanceGcMs: number): number;
|
|
241
311
|
recordOutput(id: string, stdout: string, stderr: string, outputTruncated: boolean): void;
|
|
242
312
|
recordComplete(input: {
|
|
243
313
|
id: string;
|
|
244
|
-
status: Exclude<JobStoreStatus, "running">;
|
|
314
|
+
status: Exclude<JobStoreStatus, "running" | "queued">;
|
|
245
315
|
exitCode: number | null;
|
|
246
316
|
stdout: string;
|
|
247
317
|
stderr: string;
|