llm-cli-gateway 2.12.1 → 2.13.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 +46 -0
- package/README.md +177 -19
- package/dist/acp/provider-registry.js +13 -0
- package/dist/api-provider.d.ts +1 -0
- package/dist/api-provider.js +13 -0
- package/dist/approval-manager.d.ts +1 -1
- package/dist/async-job-manager.d.ts +35 -3
- package/dist/async-job-manager.js +344 -46
- package/dist/cli-updater.d.ts +1 -1
- package/dist/cli-updater.js +17 -9
- package/dist/config.d.ts +34 -0
- package/dist/config.js +93 -8
- package/dist/doctor.d.ts +32 -4
- package/dist/doctor.js +80 -13
- package/dist/endpoint-exposure.js +15 -3
- package/dist/executor.js +2 -0
- package/dist/http-transport.d.ts +3 -0
- package/dist/http-transport.js +138 -8
- package/dist/index.d.ts +62 -5
- package/dist/index.js +774 -83
- package/dist/model-registry.d.ts +1 -1
- package/dist/model-registry.js +12 -16
- package/dist/provider-login-guidance.d.ts +12 -0
- package/dist/provider-login-guidance.js +46 -0
- package/dist/provider-status.d.ts +13 -1
- package/dist/provider-status.js +22 -13
- package/dist/provider-tool-capabilities.d.ts +4 -1
- package/dist/provider-tool-capabilities.js +310 -11
- package/dist/provider-types.d.ts +4 -0
- package/dist/provider-types.js +10 -0
- package/dist/resources.d.ts +6 -2
- package/dist/resources.js +72 -8
- package/dist/session-manager.d.ts +3 -5
- package/dist/session-manager.js +4 -2
- package/dist/upstream-contracts.js +169 -0
- package/dist/validation-normalizer.js +5 -4
- package/dist/validation-orchestrator.js +4 -0
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
- package/setup/status.schema.json +68 -3
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export declare const CLI_TYPES: readonly ["claude", "codex", "gemini", "grok", "mistral", "devin", "cursor"];
|
|
2
|
+
export type CliType = (typeof CLI_TYPES)[number];
|
|
3
|
+
export declare const API_PROVIDER_TYPES: readonly ["grok-api"];
|
|
4
|
+
export type KnownApiProviderType = (typeof API_PROVIDER_TYPES)[number];
|
package/dist/resources.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { ISessionManager } from "./session-manager.js";
|
|
|
2
2
|
import { PerformanceMetrics } from "./metrics.js";
|
|
3
3
|
import { FlightRecorderQuery } from "./flight-recorder.js";
|
|
4
4
|
import { type GlobalCacheStats, type PrefixCacheStats, type SessionCacheStats } from "./cache-stats.js";
|
|
5
|
-
import type
|
|
5
|
+
import { type CacheAwarenessConfig, type ProvidersConfig } from "./config.js";
|
|
6
6
|
export interface ResourceDefinition {
|
|
7
7
|
uri: string;
|
|
8
8
|
name: string;
|
|
@@ -25,8 +25,12 @@ export declare class ResourceProvider {
|
|
|
25
25
|
private performanceMetrics;
|
|
26
26
|
private flightRecorder;
|
|
27
27
|
private cacheAwareness;
|
|
28
|
-
|
|
28
|
+
private providers;
|
|
29
|
+
constructor(sessionManager: ISessionManager, performanceMetrics: PerformanceMetrics, flightRecorder?: FlightRecorderQuery, cacheAwareness?: CacheAwarenessConfig | null, providers?: ProvidersConfig | null);
|
|
29
30
|
getFlightRecorderQuery(): FlightRecorderQuery;
|
|
31
|
+
private apiRuntimes;
|
|
32
|
+
private continuityTrackedApiRuntimes;
|
|
33
|
+
private providerCapabilityIds;
|
|
30
34
|
readCacheStateGlobal(opts?: {
|
|
31
35
|
lastNHours?: number;
|
|
32
36
|
}): GlobalCacheStats;
|
package/dist/resources.js
CHANGED
|
@@ -2,22 +2,36 @@ import { CLI_TYPES, PROVIDER_TYPES } from "./session-manager.js";
|
|
|
2
2
|
import { getRequestContext, principalCanAccess, resolveOwnerPrincipal } from "./request-context.js";
|
|
3
3
|
import { getAvailableCliInfo } from "./model-registry.js";
|
|
4
4
|
import { computeGlobalCacheStats, computePrefixCacheStats, computeSessionCacheStats, computeTtlRemaining, } from "./cache-stats.js";
|
|
5
|
+
import { enabledApiProviders, } from "./config.js";
|
|
6
|
+
import { apiContinuityForKind } from "./api-provider.js";
|
|
7
|
+
import { apiProviderCatalogEntry } from "./api-request.js";
|
|
5
8
|
import { buildProviderSubcommandsCompactCatalog, getCliSubcommandContract, serializeCliSubcommandContract, } from "./upstream-contracts.js";
|
|
6
|
-
import { getOneProviderToolCapabilities, getProviderToolCapabilities, providerCapabilityIds, } from "./provider-tool-capabilities.js";
|
|
9
|
+
import { getOneProviderToolCapabilities, getProviderToolCapabilities, knownProviderCapabilityIds, providerCapabilityIds, } from "./provider-tool-capabilities.js";
|
|
7
10
|
export class ResourceProvider {
|
|
8
11
|
sessionManager;
|
|
9
12
|
performanceMetrics;
|
|
10
13
|
flightRecorder;
|
|
11
14
|
cacheAwareness;
|
|
12
|
-
|
|
15
|
+
providers;
|
|
16
|
+
constructor(sessionManager, performanceMetrics, flightRecorder = { queryRequests: () => [] }, cacheAwareness = null, providers = null) {
|
|
13
17
|
this.sessionManager = sessionManager;
|
|
14
18
|
this.performanceMetrics = performanceMetrics;
|
|
15
19
|
this.flightRecorder = flightRecorder;
|
|
16
20
|
this.cacheAwareness = cacheAwareness;
|
|
21
|
+
this.providers = providers;
|
|
17
22
|
}
|
|
18
23
|
getFlightRecorderQuery() {
|
|
19
24
|
return this.flightRecorder;
|
|
20
25
|
}
|
|
26
|
+
apiRuntimes() {
|
|
27
|
+
return this.providers ? enabledApiProviders(this.providers) : [];
|
|
28
|
+
}
|
|
29
|
+
continuityTrackedApiRuntimes() {
|
|
30
|
+
return this.apiRuntimes().filter(rt => apiContinuityForKind(rt.kind) !== "none");
|
|
31
|
+
}
|
|
32
|
+
providerCapabilityIds() {
|
|
33
|
+
return this.providers ? providerCapabilityIds(this.providers) : knownProviderCapabilityIds();
|
|
34
|
+
}
|
|
21
35
|
readCacheStateGlobal(opts = {}) {
|
|
22
36
|
return computeGlobalCacheStats(this.flightRecorder, opts);
|
|
23
37
|
}
|
|
@@ -101,6 +115,17 @@ export class ResourceProvider {
|
|
|
101
115
|
priority: 0.6,
|
|
102
116
|
},
|
|
103
117
|
},
|
|
118
|
+
...this.continuityTrackedApiRuntimes().map(rt => ({
|
|
119
|
+
uri: `sessions://${rt.name}`,
|
|
120
|
+
name: `${rt.name} Sessions`,
|
|
121
|
+
title: `${rt.name} Sessions`,
|
|
122
|
+
description: `List of ${rt.name} API provider conversation sessions`,
|
|
123
|
+
mimeType: "application/json",
|
|
124
|
+
annotations: {
|
|
125
|
+
audience: ["user", "assistant"],
|
|
126
|
+
priority: 0.6,
|
|
127
|
+
},
|
|
128
|
+
})),
|
|
104
129
|
{
|
|
105
130
|
uri: "models://claude",
|
|
106
131
|
name: "Claude Models",
|
|
@@ -156,6 +181,17 @@ export class ResourceProvider {
|
|
|
156
181
|
priority: 0.8,
|
|
157
182
|
},
|
|
158
183
|
},
|
|
184
|
+
...this.apiRuntimes().map(rt => ({
|
|
185
|
+
uri: `models://${rt.name}`,
|
|
186
|
+
name: `${rt.name} Models`,
|
|
187
|
+
title: `${rt.name} Models & Capabilities`,
|
|
188
|
+
description: `Configured ${rt.name} API provider model catalog`,
|
|
189
|
+
mimeType: "application/json",
|
|
190
|
+
annotations: {
|
|
191
|
+
audience: ["user", "assistant"],
|
|
192
|
+
priority: 0.8,
|
|
193
|
+
},
|
|
194
|
+
})),
|
|
159
195
|
{
|
|
160
196
|
uri: "metrics://performance",
|
|
161
197
|
name: "Performance Metrics",
|
|
@@ -189,7 +225,7 @@ export class ResourceProvider {
|
|
|
189
225
|
priority: 0.8,
|
|
190
226
|
},
|
|
191
227
|
},
|
|
192
|
-
...providerCapabilityIds().map(cli => ({
|
|
228
|
+
...this.providerCapabilityIds().map(cli => ({
|
|
193
229
|
uri: `provider-tools://${cli}`,
|
|
194
230
|
name: `${cli} Tool Capabilities`,
|
|
195
231
|
title: `${cli} Tool Capabilities`,
|
|
@@ -338,6 +374,32 @@ export class ResourceProvider {
|
|
|
338
374
|
text: JSON.stringify(cliInfo.mistral, null, 2),
|
|
339
375
|
};
|
|
340
376
|
}
|
|
377
|
+
if (uri.startsWith("models://")) {
|
|
378
|
+
const runtime = this.apiRuntimes().find(rt => `models://${rt.name}` === uri);
|
|
379
|
+
if (runtime) {
|
|
380
|
+
return {
|
|
381
|
+
uri,
|
|
382
|
+
mimeType: "application/json",
|
|
383
|
+
text: JSON.stringify(apiProviderCatalogEntry(runtime), null, 2),
|
|
384
|
+
};
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
if (uri.startsWith("sessions://")) {
|
|
388
|
+
const runtime = this.continuityTrackedApiRuntimes().find(rt => `sessions://${rt.name}` === uri);
|
|
389
|
+
if (runtime) {
|
|
390
|
+
const sessions = this.ownedSessions(await this.sessionManager.listSessions(runtime.name));
|
|
391
|
+
return {
|
|
392
|
+
uri,
|
|
393
|
+
mimeType: "application/json",
|
|
394
|
+
text: JSON.stringify({
|
|
395
|
+
cli: runtime.name,
|
|
396
|
+
total: sessions.length,
|
|
397
|
+
sessions,
|
|
398
|
+
activeSession: await this.ownedActiveId(runtime.name),
|
|
399
|
+
}, null, 2),
|
|
400
|
+
};
|
|
401
|
+
}
|
|
402
|
+
}
|
|
341
403
|
if (uri === "metrics://performance") {
|
|
342
404
|
return {
|
|
343
405
|
uri,
|
|
@@ -356,15 +418,17 @@ export class ResourceProvider {
|
|
|
356
418
|
return {
|
|
357
419
|
uri,
|
|
358
420
|
mimeType: "application/json",
|
|
359
|
-
text: JSON.stringify(getProviderToolCapabilities(), null, 2),
|
|
421
|
+
text: JSON.stringify(getProviderToolCapabilities({ providersConfig: this.providers ?? undefined }), null, 2),
|
|
360
422
|
};
|
|
361
423
|
}
|
|
362
|
-
const providerToolsResource = parseProviderToolsUri(uri);
|
|
424
|
+
const providerToolsResource = parseProviderToolsUri(uri, this.providerCapabilityIds());
|
|
363
425
|
if (providerToolsResource) {
|
|
364
426
|
return {
|
|
365
427
|
uri,
|
|
366
428
|
mimeType: "application/json",
|
|
367
|
-
text: JSON.stringify(getOneProviderToolCapabilities(providerToolsResource.provider
|
|
429
|
+
text: JSON.stringify(getOneProviderToolCapabilities(providerToolsResource.provider, {
|
|
430
|
+
providersConfig: this.providers ?? undefined,
|
|
431
|
+
}), null, 2),
|
|
368
432
|
};
|
|
369
433
|
}
|
|
370
434
|
const subcommandResource = parseProviderSubcommandUri(uri);
|
|
@@ -401,7 +465,7 @@ function parseProviderSubcommandUri(uri) {
|
|
|
401
465
|
commandPath: pathParts.map(part => decodeURIComponent(part)).filter(Boolean),
|
|
402
466
|
};
|
|
403
467
|
}
|
|
404
|
-
function parseProviderToolsUri(uri) {
|
|
468
|
+
function parseProviderToolsUri(uri, providerIds) {
|
|
405
469
|
const prefix = uri.startsWith("provider-tools://")
|
|
406
470
|
? "provider-tools://"
|
|
407
471
|
: uri.startsWith("provider_tools://")
|
|
@@ -410,7 +474,7 @@ function parseProviderToolsUri(uri) {
|
|
|
410
474
|
if (!prefix || uri === `${prefix}catalog`)
|
|
411
475
|
return null;
|
|
412
476
|
const provider = uri.slice(prefix.length);
|
|
413
|
-
if (!
|
|
477
|
+
if (!providerIds.includes(provider))
|
|
414
478
|
return null;
|
|
415
479
|
return { provider: provider };
|
|
416
480
|
}
|
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
import type { Config } from "./config.js";
|
|
2
2
|
import type { DatabaseConnection } from "./db.js";
|
|
3
3
|
import type { Logger } from "./logger.js";
|
|
4
|
-
|
|
5
|
-
export type CliType
|
|
6
|
-
export declare const API_PROVIDER_TYPES: readonly ["grok-api"];
|
|
7
|
-
export type KnownApiProviderType = (typeof API_PROVIDER_TYPES)[number];
|
|
4
|
+
import { API_PROVIDER_TYPES, CLI_TYPES, type CliType, type KnownApiProviderType } from "./provider-types.js";
|
|
5
|
+
export { API_PROVIDER_TYPES, CLI_TYPES, type CliType, type KnownApiProviderType };
|
|
8
6
|
export type ApiProviderType = KnownApiProviderType | (string & {});
|
|
9
7
|
export type ProviderType = CliType | ApiProviderType;
|
|
10
8
|
export type ProviderKind = "cli" | "api";
|
|
11
|
-
export declare const PROVIDER_TYPES: readonly ["claude", "codex", "gemini", "grok", "mistral", "devin", "grok-api"];
|
|
9
|
+
export declare const PROVIDER_TYPES: readonly ["claude", "codex", "gemini", "grok", "mistral", "devin", "cursor", "grok-api"];
|
|
12
10
|
export declare function isCliType(provider: string): provider is CliType;
|
|
13
11
|
export declare function providerKind(provider: ProviderType): ProviderKind;
|
|
14
12
|
export declare function defaultSessionDescription(provider: ProviderType): string;
|
package/dist/session-manager.js
CHANGED
|
@@ -5,8 +5,8 @@ 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
|
import { getRequestContext, resolveOwnerPrincipal } from "./request-context.js";
|
|
8
|
-
|
|
9
|
-
export
|
|
8
|
+
import { API_PROVIDER_TYPES, CLI_TYPES, } from "./provider-types.js";
|
|
9
|
+
export { API_PROVIDER_TYPES, CLI_TYPES };
|
|
10
10
|
export const PROVIDER_TYPES = [...CLI_TYPES, ...API_PROVIDER_TYPES];
|
|
11
11
|
export function isCliType(provider) {
|
|
12
12
|
return CLI_TYPES.includes(provider);
|
|
@@ -20,6 +20,8 @@ const KNOWN_SESSION_DESCRIPTIONS = {
|
|
|
20
20
|
gemini: "Gemini Session",
|
|
21
21
|
grok: "Grok Session",
|
|
22
22
|
mistral: "Mistral Session",
|
|
23
|
+
devin: "Devin Session",
|
|
24
|
+
cursor: "Cursor Session",
|
|
23
25
|
"grok-api": "Grok API Session",
|
|
24
26
|
};
|
|
25
27
|
export function defaultSessionDescription(provider) {
|
|
@@ -70,6 +70,17 @@ export const ACP_ENTRYPOINT_CONTRACTS = {
|
|
|
70
70
|
evidence: 'Native ACP entrypoint `devin acp` (stdio JSON-RPC). Slice D1 manual initialize + session/new smoke passed (protocolVersion 1, agent "Affogato", session created). Third native runtime pilot; routing stays config-gated.',
|
|
71
71
|
docsRef: "docs/plans/first-class-acp-gateway-extension.dag.toml#provider_matrix.devin",
|
|
72
72
|
},
|
|
73
|
+
cursor: {
|
|
74
|
+
cli: "cursor",
|
|
75
|
+
displayName: "Cursor Agent CLI",
|
|
76
|
+
status: "native",
|
|
77
|
+
executable: "cursor-agent",
|
|
78
|
+
entrypointArgs: ["acp"],
|
|
79
|
+
targetVersion: "cursor-agent 2026.06.29-2ad2186",
|
|
80
|
+
probeArgs: [["acp", "--help"]],
|
|
81
|
+
evidence: "Native hidden ACP entrypoint `cursor-agent acp` (stdio JSON-RPC). `cursor-agent acp --help` was verified locally; manual initialize + session/new smoke passed locally (protocolVersion 1, session created; no agentInfo returned). Runtime routing stays config-gated.",
|
|
82
|
+
docsRef: "docs/plans/first-class-acp-gateway-extension.dag.toml#provider_matrix.cursor",
|
|
83
|
+
},
|
|
73
84
|
};
|
|
74
85
|
const PERMISSION_MODES = [
|
|
75
86
|
"default",
|
|
@@ -1826,6 +1837,164 @@ export const UPSTREAM_CLI_CONTRACTS = {
|
|
|
1826
1837
|
},
|
|
1827
1838
|
],
|
|
1828
1839
|
},
|
|
1840
|
+
cursor: {
|
|
1841
|
+
cli: "cursor",
|
|
1842
|
+
executable: "cursor-agent",
|
|
1843
|
+
upstream: "Cursor Agent CLI",
|
|
1844
|
+
upstreamMetadata: {
|
|
1845
|
+
sourceUrls: [
|
|
1846
|
+
"https://cursor.com/cli",
|
|
1847
|
+
"https://cursor.com/docs/cli/acp",
|
|
1848
|
+
"https://cursor.com/docs/cli/reference/parameters",
|
|
1849
|
+
],
|
|
1850
|
+
packageName: "cursor-agent",
|
|
1851
|
+
installDocsUrl: "https://cursor.com/cli",
|
|
1852
|
+
releaseChannel: "vendor",
|
|
1853
|
+
watchCategories: [
|
|
1854
|
+
"flags",
|
|
1855
|
+
"subcommands",
|
|
1856
|
+
"session-resume",
|
|
1857
|
+
"execution-modes",
|
|
1858
|
+
"acp-entrypoint",
|
|
1859
|
+
],
|
|
1860
|
+
},
|
|
1861
|
+
helpArgs: [["--help"]],
|
|
1862
|
+
subcommands: {},
|
|
1863
|
+
maxPositionals: 1,
|
|
1864
|
+
mcpTools: ["cursor_request", "cursor_request_async"],
|
|
1865
|
+
mcpParameters: [
|
|
1866
|
+
"prompt",
|
|
1867
|
+
"model",
|
|
1868
|
+
"mode",
|
|
1869
|
+
"outputFormat",
|
|
1870
|
+
"force",
|
|
1871
|
+
"autoReview",
|
|
1872
|
+
"sandbox",
|
|
1873
|
+
"trust",
|
|
1874
|
+
"workspace",
|
|
1875
|
+
"addDir",
|
|
1876
|
+
"sessionId",
|
|
1877
|
+
"resumeLatest",
|
|
1878
|
+
"createNewSession",
|
|
1879
|
+
],
|
|
1880
|
+
flags: {
|
|
1881
|
+
"--print": {
|
|
1882
|
+
arity: "none",
|
|
1883
|
+
description: "Print responses to console for scripts/non-interactive use",
|
|
1884
|
+
},
|
|
1885
|
+
"--output-format": {
|
|
1886
|
+
arity: "one",
|
|
1887
|
+
values: ["text", "json", "stream-json"],
|
|
1888
|
+
description: "Output format for --print mode",
|
|
1889
|
+
},
|
|
1890
|
+
"--model": { arity: "one", description: "Model to use for the session" },
|
|
1891
|
+
"--mode": {
|
|
1892
|
+
arity: "one",
|
|
1893
|
+
values: ["plan", "ask"],
|
|
1894
|
+
description: "Execution mode (plan or ask)",
|
|
1895
|
+
},
|
|
1896
|
+
"--force": { arity: "none", description: "Force allow commands unless denied" },
|
|
1897
|
+
"--auto-review": { arity: "none", description: "Use Cursor Smart Auto-review" },
|
|
1898
|
+
"--sandbox": {
|
|
1899
|
+
arity: "one",
|
|
1900
|
+
values: ["enabled", "disabled"],
|
|
1901
|
+
description: "Enable or disable Cursor sandbox mode",
|
|
1902
|
+
},
|
|
1903
|
+
"--trust": { arity: "none", description: "Trust workspace in headless mode" },
|
|
1904
|
+
"--workspace": { arity: "one", description: "Workspace directory or saved workspace" },
|
|
1905
|
+
"--add-dir": { arity: "one", description: "Additional workspace root directory" },
|
|
1906
|
+
"--resume": { arity: "one", description: "Resume a specific Cursor chat/session" },
|
|
1907
|
+
"--continue": { arity: "none", description: "Continue the latest Cursor chat" },
|
|
1908
|
+
},
|
|
1909
|
+
acknowledgedUpstreamFlags: [
|
|
1910
|
+
"--api-key",
|
|
1911
|
+
"--header",
|
|
1912
|
+
"-H",
|
|
1913
|
+
"-p",
|
|
1914
|
+
"--plan",
|
|
1915
|
+
"--yolo",
|
|
1916
|
+
"--approve-mcps",
|
|
1917
|
+
"--plugin-dir",
|
|
1918
|
+
"--worktree",
|
|
1919
|
+
"-w",
|
|
1920
|
+
"--worktree-base",
|
|
1921
|
+
"--skip-worktree-setup",
|
|
1922
|
+
"--stream-partial-output",
|
|
1923
|
+
"--list-models",
|
|
1924
|
+
"--version",
|
|
1925
|
+
"-v",
|
|
1926
|
+
"--help",
|
|
1927
|
+
"-h",
|
|
1928
|
+
],
|
|
1929
|
+
env: {
|
|
1930
|
+
CURSOR_API_KEY: {
|
|
1931
|
+
arity: "one",
|
|
1932
|
+
description: "Cursor Agent API key for headless authentication",
|
|
1933
|
+
},
|
|
1934
|
+
},
|
|
1935
|
+
conformanceFixtures: [
|
|
1936
|
+
{
|
|
1937
|
+
id: "cursor-minimal",
|
|
1938
|
+
description: "Minimal print-mode prompt request",
|
|
1939
|
+
args: ["--print", "hello"],
|
|
1940
|
+
expect: "pass",
|
|
1941
|
+
},
|
|
1942
|
+
{
|
|
1943
|
+
id: "cursor-output-format",
|
|
1944
|
+
description: "--output-format json is accepted",
|
|
1945
|
+
args: ["--print", "--output-format", "json", "hello"],
|
|
1946
|
+
expect: "pass",
|
|
1947
|
+
},
|
|
1948
|
+
{
|
|
1949
|
+
id: "cursor-mode",
|
|
1950
|
+
description: "--mode plan is accepted",
|
|
1951
|
+
args: ["--print", "--mode", "plan", "hello"],
|
|
1952
|
+
expect: "pass",
|
|
1953
|
+
},
|
|
1954
|
+
{
|
|
1955
|
+
id: "cursor-high-impact-controls",
|
|
1956
|
+
description: "Model, force, auto-review, sandbox, trust, and workspace controls are accepted",
|
|
1957
|
+
args: [
|
|
1958
|
+
"--print",
|
|
1959
|
+
"--model",
|
|
1960
|
+
"gpt-5",
|
|
1961
|
+
"--force",
|
|
1962
|
+
"--auto-review",
|
|
1963
|
+
"--sandbox",
|
|
1964
|
+
"enabled",
|
|
1965
|
+
"--trust",
|
|
1966
|
+
"--workspace",
|
|
1967
|
+
"/tmp/workspace",
|
|
1968
|
+
"hello",
|
|
1969
|
+
],
|
|
1970
|
+
expect: "pass",
|
|
1971
|
+
},
|
|
1972
|
+
{
|
|
1973
|
+
id: "cursor-mode-invalid",
|
|
1974
|
+
description: "Invalid --mode is rejected",
|
|
1975
|
+
args: ["--print", "--mode", "dangerous", "hello"],
|
|
1976
|
+
expect: "fail",
|
|
1977
|
+
},
|
|
1978
|
+
{
|
|
1979
|
+
id: "cursor-resume",
|
|
1980
|
+
description: "Resume by chat id accepted",
|
|
1981
|
+
args: ["--print", "--resume", "chat-123", "hello"],
|
|
1982
|
+
expect: "pass",
|
|
1983
|
+
},
|
|
1984
|
+
{
|
|
1985
|
+
id: "cursor-add-dir",
|
|
1986
|
+
description: "Repeatable --add-dir is accepted",
|
|
1987
|
+
args: ["--print", "--add-dir", "/tmp/extra", "hello"],
|
|
1988
|
+
expect: "pass",
|
|
1989
|
+
},
|
|
1990
|
+
{
|
|
1991
|
+
id: "cursor-unsupported-flag",
|
|
1992
|
+
description: "Unsupported flag is rejected before spawn",
|
|
1993
|
+
args: ["--print", "--not-a-cursor-flag", "hello"],
|
|
1994
|
+
expect: "fail",
|
|
1995
|
+
},
|
|
1996
|
+
],
|
|
1997
|
+
},
|
|
1829
1998
|
};
|
|
1830
1999
|
export function validateUpstreamCliArgs(cli, args) {
|
|
1831
2000
|
const contract = UPSTREAM_CLI_CONTRACTS[cli];
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
export function normalizeStartedJob(provider, model, snapshot, warning) {
|
|
2
|
+
const inProgress = snapshot.status === "running" || snapshot.status === "queued";
|
|
2
3
|
return {
|
|
3
4
|
provider,
|
|
4
5
|
model,
|
|
5
|
-
status: snapshot.status,
|
|
6
|
-
verdict:
|
|
7
|
-
rationale:
|
|
6
|
+
status: snapshot.status === "queued" ? "running" : snapshot.status,
|
|
7
|
+
verdict: inProgress ? "pending" : null,
|
|
8
|
+
rationale: inProgress ? "Provider job is running asynchronously." : null,
|
|
8
9
|
risks: [],
|
|
9
10
|
rawJobReference: {
|
|
10
11
|
jobId: snapshot.id,
|
|
@@ -34,7 +35,7 @@ export function normalizeJobResult(provider, model, result) {
|
|
|
34
35
|
return {
|
|
35
36
|
provider,
|
|
36
37
|
model,
|
|
37
|
-
status: result.status,
|
|
38
|
+
status: result.status === "queued" ? "running" : result.status,
|
|
38
39
|
verdict: inferVerdict(output, result.status),
|
|
39
40
|
rationale: output ? excerpt(output, 1800) : error,
|
|
40
41
|
risks: extractRisks(output, error),
|
|
@@ -220,6 +220,10 @@ function buildProviderArgs(provider, prompt) {
|
|
|
220
220
|
if (provider === "claude" || provider === "grok" || provider === "mistral") {
|
|
221
221
|
return ["-p", prompt];
|
|
222
222
|
}
|
|
223
|
+
if (provider === "devin")
|
|
224
|
+
return ["-p", prompt];
|
|
225
|
+
if (provider === "cursor")
|
|
226
|
+
return ["--print", "--mode", "ask", "--sandbox", "enabled", prompt];
|
|
223
227
|
if (provider === "codex")
|
|
224
228
|
return ["exec", "--skip-git-repo-check", prompt];
|
|
225
229
|
return [prompt];
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "llm-cli-gateway",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.13.0",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "llm-cli-gateway",
|
|
9
|
-
"version": "2.
|
|
9
|
+
"version": "2.13.0",
|
|
10
10
|
"license": "MIT",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "llm-cli-gateway",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.13.0",
|
|
4
4
|
"mcpName": "io.github.verivus-oss/llm-cli-gateway",
|
|
5
5
|
"description": "MCP server providing unified access to Claude Code, Codex, Gemini, Grok, Mistral Vibe, and Devin CLIs with session management, retry logic, async job orchestration, durable job results, and cross-LLM validation.",
|
|
6
6
|
"license": "MIT",
|
package/setup/status.schema.json
CHANGED
|
@@ -118,7 +118,7 @@
|
|
|
118
118
|
},
|
|
119
119
|
"providers": {
|
|
120
120
|
"type": "object",
|
|
121
|
-
"
|
|
121
|
+
"minProperties": 1,
|
|
122
122
|
"additionalProperties": {
|
|
123
123
|
"type": "object",
|
|
124
124
|
"required": [
|
|
@@ -353,7 +353,7 @@
|
|
|
353
353
|
"catalog": { "const": "provider-tools://catalog" },
|
|
354
354
|
"providers": {
|
|
355
355
|
"type": "object",
|
|
356
|
-
"
|
|
356
|
+
"minProperties": 1,
|
|
357
357
|
"additionalProperties": { "type": "string" }
|
|
358
358
|
}
|
|
359
359
|
},
|
|
@@ -362,7 +362,7 @@
|
|
|
362
362
|
"cache_ttl_ms": { "type": "integer", "minimum": 0 },
|
|
363
363
|
"providers": {
|
|
364
364
|
"type": "object",
|
|
365
|
-
"
|
|
365
|
+
"minProperties": 1,
|
|
366
366
|
"additionalProperties": {
|
|
367
367
|
"type": "object",
|
|
368
368
|
"required": [
|
|
@@ -405,6 +405,71 @@
|
|
|
405
405
|
},
|
|
406
406
|
"additionalProperties": false
|
|
407
407
|
},
|
|
408
|
+
"api_providers": {
|
|
409
|
+
"type": "object",
|
|
410
|
+
"description": "Slice 6: health of enabled [providers.<name>] (kind:api) providers. OMITTED entirely when none are enabled (dormant byte-identical).",
|
|
411
|
+
"required": ["enabled_count", "providers"],
|
|
412
|
+
"properties": {
|
|
413
|
+
"enabled_count": { "type": "integer", "minimum": 1 },
|
|
414
|
+
"providers": {
|
|
415
|
+
"type": "object",
|
|
416
|
+
"additionalProperties": {
|
|
417
|
+
"type": "object",
|
|
418
|
+
"required": [
|
|
419
|
+
"name",
|
|
420
|
+
"kind",
|
|
421
|
+
"base_url",
|
|
422
|
+
"default_model",
|
|
423
|
+
"models",
|
|
424
|
+
"api_key_env",
|
|
425
|
+
"api_key_present",
|
|
426
|
+
"reachable",
|
|
427
|
+
"login_guidance"
|
|
428
|
+
],
|
|
429
|
+
"properties": {
|
|
430
|
+
"name": { "type": "string" },
|
|
431
|
+
"kind": { "enum": ["openai-compatible", "anthropic", "xai-responses"] },
|
|
432
|
+
"base_url": { "type": "string" },
|
|
433
|
+
"default_model": { "type": "string" },
|
|
434
|
+
"models": {
|
|
435
|
+
"type": ["array", "null"],
|
|
436
|
+
"items": { "type": "string" }
|
|
437
|
+
},
|
|
438
|
+
"api_key_env": { "type": ["string", "null"] },
|
|
439
|
+
"api_key_present": { "type": "boolean" },
|
|
440
|
+
"reachable": { "type": ["boolean", "null"] },
|
|
441
|
+
"reachability_error": { "type": "string" },
|
|
442
|
+
"login_guidance": {
|
|
443
|
+
"type": "object",
|
|
444
|
+
"required": [
|
|
445
|
+
"provider",
|
|
446
|
+
"displayName",
|
|
447
|
+
"kind",
|
|
448
|
+
"baseUrl",
|
|
449
|
+
"apiKeyEnv",
|
|
450
|
+
"summary",
|
|
451
|
+
"steps",
|
|
452
|
+
"credentialHandling"
|
|
453
|
+
],
|
|
454
|
+
"properties": {
|
|
455
|
+
"provider": { "type": "string" },
|
|
456
|
+
"displayName": { "type": "string" },
|
|
457
|
+
"kind": { "enum": ["openai-compatible", "anthropic", "xai-responses"] },
|
|
458
|
+
"baseUrl": { "type": "string" },
|
|
459
|
+
"apiKeyEnv": { "type": ["string", "null"] },
|
|
460
|
+
"summary": { "type": "string" },
|
|
461
|
+
"steps": { "type": "array", "items": { "type": "string" } },
|
|
462
|
+
"credentialHandling": { "type": "string" }
|
|
463
|
+
},
|
|
464
|
+
"additionalProperties": false
|
|
465
|
+
}
|
|
466
|
+
},
|
|
467
|
+
"additionalProperties": false
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
},
|
|
471
|
+
"additionalProperties": false
|
|
472
|
+
},
|
|
408
473
|
"upstream": {
|
|
409
474
|
"type": "object",
|
|
410
475
|
"required": [
|