llm-cli-gateway 2.13.1 → 2.14.0-rc.1
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 +140 -0
- package/README.md +53 -26
- package/dist/acp/client.d.ts +29 -1
- package/dist/acp/client.js +78 -4
- package/dist/acp/errors.d.ts +9 -1
- package/dist/acp/errors.js +19 -0
- package/dist/acp/event-normalizer.d.ts +12 -0
- package/dist/acp/event-normalizer.js +16 -0
- package/dist/acp/flight-redaction.d.ts +3 -0
- package/dist/acp/flight-redaction.js +3 -0
- package/dist/acp/permission-bridge.js +11 -5
- package/dist/acp/process-manager.d.ts +2 -1
- package/dist/acp/process-manager.js +43 -4
- package/dist/acp/provider-registry.js +19 -11
- package/dist/acp/runtime.d.ts +8 -0
- package/dist/acp/runtime.js +47 -4
- package/dist/acp/types.d.ts +3083 -55
- package/dist/acp/types.js +242 -5
- package/dist/async-job-manager.d.ts +2 -0
- package/dist/async-job-manager.js +11 -0
- package/dist/codex-json-parser.d.ts +1 -0
- package/dist/codex-json-parser.js +6 -0
- package/dist/config.d.ts +16 -0
- package/dist/config.js +105 -19
- package/dist/connector-setup.d.ts +39 -0
- package/dist/connector-setup.js +86 -0
- package/dist/doctor.d.ts +39 -1
- package/dist/doctor.js +182 -3
- package/dist/flight-recorder.d.ts +2 -0
- package/dist/flight-recorder.js +20 -0
- package/dist/gemini-json-parser.d.ts +2 -0
- package/dist/gemini-json-parser.js +45 -8
- package/dist/grok-json-parser.d.ts +14 -0
- package/dist/grok-json-parser.js +156 -0
- package/dist/http-transport.js +27 -3
- package/dist/index.d.ts +48 -2
- package/dist/index.js +633 -144
- package/dist/job-store.d.ts +45 -7
- package/dist/job-store.js +138 -17
- package/dist/model-registry.d.ts +1 -0
- package/dist/model-registry.js +46 -0
- package/dist/oauth.js +17 -16
- package/dist/postgres-job-store-worker.d.ts +1 -0
- package/dist/postgres-job-store-worker.js +327 -0
- package/dist/pricing.d.ts +3 -2
- package/dist/provider-acp-capabilities.d.ts +52 -0
- package/dist/provider-acp-capabilities.js +101 -0
- package/dist/provider-admin-tools.d.ts +100 -0
- package/dist/provider-admin-tools.js +572 -0
- package/dist/provider-capability-cache.d.ts +46 -0
- package/dist/provider-capability-cache.js +248 -0
- package/dist/provider-capability-discovery.d.ts +85 -0
- package/dist/provider-capability-discovery.js +461 -0
- package/dist/provider-capability-resolver.d.ts +29 -0
- package/dist/provider-capability-resolver.js +92 -0
- package/dist/provider-definition-assertions.d.ts +9 -0
- package/dist/provider-definition-assertions.js +147 -0
- package/dist/provider-definitions.d.ts +127 -0
- package/dist/provider-definitions.js +758 -0
- package/dist/provider-help-parser.d.ts +34 -0
- package/dist/provider-help-parser.js +203 -0
- package/dist/provider-model-discovery.d.ts +30 -0
- package/dist/provider-model-discovery.js +229 -0
- package/dist/provider-output-metadata.d.ts +7 -0
- package/dist/provider-output-metadata.js +68 -0
- package/dist/provider-schema-builder.d.ts +22 -0
- package/dist/provider-schema-builder.js +55 -0
- package/dist/provider-surface-generator.d.ts +98 -0
- package/dist/provider-surface-generator.js +140 -0
- package/dist/provider-tool-capabilities.d.ts +3 -2
- package/dist/provider-tool-capabilities.js +77 -62
- package/dist/remote-url.d.ts +28 -0
- package/dist/remote-url.js +61 -0
- package/dist/request-helpers.d.ts +37 -4
- package/dist/request-helpers.js +134 -0
- package/dist/resources.d.ts +6 -1
- package/dist/resources.js +67 -193
- package/dist/session-manager.d.ts +2 -0
- package/dist/session-manager.js +34 -8
- package/dist/stream-json-parser.d.ts +1 -0
- package/dist/stream-json-parser.js +3 -0
- package/dist/upstream-contracts.d.ts +17 -1
- package/dist/upstream-contracts.js +209 -36
- package/dist/workspace-registry.d.ts +9 -0
- package/dist/workspace-registry.js +24 -4
- package/npm-shrinkwrap.json +2 -2
- package/package.json +8 -3
- package/setup/status.schema.json +75 -0
package/dist/job-store.d.ts
CHANGED
|
@@ -214,18 +214,56 @@ export declare class MemoryJobStore implements JobStore {
|
|
|
214
214
|
evictExpired(): number;
|
|
215
215
|
close(): void;
|
|
216
216
|
}
|
|
217
|
-
export declare class PostgresJobStore implements JobStore {
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
217
|
+
export declare class PostgresJobStore implements JobStore, ValidationRunStore {
|
|
218
|
+
private logger;
|
|
219
|
+
private worker;
|
|
220
|
+
private tmpDir;
|
|
221
|
+
private nextRequestId;
|
|
222
|
+
private closed;
|
|
223
|
+
constructor(dsn: string, logger?: Logger, options?: {
|
|
224
|
+
retentionMs?: number;
|
|
225
|
+
dedupWindowMs?: number;
|
|
226
|
+
});
|
|
227
|
+
private syncCall;
|
|
228
|
+
recordStart(input: {
|
|
229
|
+
id: string;
|
|
230
|
+
correlationId: string;
|
|
231
|
+
requestKey: string;
|
|
232
|
+
cli: string;
|
|
233
|
+
args: string[];
|
|
234
|
+
outputFormat?: string;
|
|
235
|
+
startedAt: string;
|
|
236
|
+
pid: number | null;
|
|
237
|
+
ownerPrincipal?: string | null;
|
|
238
|
+
transport?: JobTransport;
|
|
239
|
+
payloadJson?: string | null;
|
|
240
|
+
}): void;
|
|
241
|
+
recordOutput(id: string, stdout: string, stderr: string, outputTruncated: boolean): void;
|
|
242
|
+
recordComplete(input: {
|
|
243
|
+
id: string;
|
|
244
|
+
status: Exclude<JobStoreStatus, "running">;
|
|
245
|
+
exitCode: number | null;
|
|
246
|
+
stdout: string;
|
|
247
|
+
stderr: string;
|
|
248
|
+
outputTruncated: boolean;
|
|
249
|
+
error: string | null;
|
|
250
|
+
finishedAt: string;
|
|
251
|
+
httpStatus?: number | null;
|
|
252
|
+
}): void;
|
|
253
|
+
getById(id: string): JobRecord | null;
|
|
254
|
+
findByRequestKey(requestKey: string): JobRecord | null;
|
|
224
255
|
markOrphanedOnStartup(): {
|
|
225
256
|
count: number;
|
|
226
257
|
orphaned: Array<OrphanedJobSnapshot>;
|
|
227
258
|
};
|
|
228
259
|
evictExpired(): number;
|
|
260
|
+
recordValidationRun(run: ValidationRunRecord): void;
|
|
261
|
+
getValidationRun(validationId: string): ValidationRunRecord | null;
|
|
262
|
+
setValidationJudgeLink(validationId: string, judgeLink: ValidationRunLink): void;
|
|
263
|
+
setValidationRunStatus(validationId: string, status: ValidationRunRecord["status"]): void;
|
|
264
|
+
getValidationRunIdByJobId(jobId: string): string | null;
|
|
265
|
+
recordValidationReceipt(receipt: ValidationReceiptRecord): void;
|
|
266
|
+
getValidationReceipt(validationId: string): ValidationReceiptRecord | null;
|
|
229
267
|
close(): void;
|
|
230
268
|
}
|
|
231
269
|
export declare function createJobStore(config: PersistenceConfig, logger?: Logger): JobStore | null;
|
package/dist/job-store.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import { chmodSync } from "fs";
|
|
1
|
+
import { chmodSync, existsSync, mkdtempSync, readFileSync, rmSync } from "fs";
|
|
2
2
|
import os from "os";
|
|
3
3
|
import path from "path";
|
|
4
4
|
import { createHash } from "crypto";
|
|
5
|
+
import { Worker } from "worker_threads";
|
|
6
|
+
import { fileURLToPath } from "url";
|
|
5
7
|
import { openDatabase } from "./sqlite-driver.js";
|
|
6
8
|
import { noopLogger } from "./logger.js";
|
|
7
9
|
export function resolveJobStoreDbPath() {
|
|
@@ -550,34 +552,153 @@ export class MemoryJobStore {
|
|
|
550
552
|
}
|
|
551
553
|
}
|
|
552
554
|
export class PostgresJobStore {
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
555
|
+
logger;
|
|
556
|
+
worker;
|
|
557
|
+
tmpDir;
|
|
558
|
+
nextRequestId = 0;
|
|
559
|
+
closed = false;
|
|
560
|
+
constructor(dsn, logger = noopLogger, options = {}) {
|
|
561
|
+
this.logger = logger;
|
|
562
|
+
if (!dsn) {
|
|
563
|
+
throw new Error("PostgresJobStore requires a non-empty DSN");
|
|
564
|
+
}
|
|
565
|
+
this.tmpDir = mkdtempSync(path.join(os.tmpdir(), "llm-gateway-pg-job-store-"));
|
|
566
|
+
this.worker = new Worker(resolvePostgresWorkerUrl(), {
|
|
567
|
+
execArgv: [],
|
|
568
|
+
workerData: {
|
|
569
|
+
dsn,
|
|
570
|
+
retentionMs: options.retentionMs ?? resolveJobRetentionMs(),
|
|
571
|
+
dedupWindowMs: options.dedupWindowMs ?? resolveDedupWindowMs(),
|
|
572
|
+
farFutureIso: FAR_FUTURE_ISO,
|
|
573
|
+
connectionTimeoutMillis: 5000,
|
|
574
|
+
},
|
|
575
|
+
});
|
|
576
|
+
try {
|
|
577
|
+
this.syncCall("init");
|
|
578
|
+
}
|
|
579
|
+
catch (err) {
|
|
580
|
+
this.closed = true;
|
|
581
|
+
void this.worker.terminate();
|
|
582
|
+
rmSync(this.tmpDir, { recursive: true, force: true });
|
|
583
|
+
throw err;
|
|
584
|
+
}
|
|
556
585
|
}
|
|
557
|
-
|
|
558
|
-
|
|
586
|
+
syncCall(method, ...args) {
|
|
587
|
+
if (this.closed && method !== "close") {
|
|
588
|
+
throw new Error("PostgresJobStore is closed");
|
|
589
|
+
}
|
|
590
|
+
const shared = new SharedArrayBuffer(4);
|
|
591
|
+
const state = new Int32Array(shared);
|
|
592
|
+
const resultPath = path.join(this.tmpDir, `result-${process.pid}-${++this.nextRequestId}.json`);
|
|
593
|
+
this.worker.postMessage({ method, args, resultPath, shared });
|
|
594
|
+
const wait = Atomics.wait(state, 0, 0, method === "init" ? 10_000 : 30_000);
|
|
595
|
+
if (wait !== "ok" && wait !== "not-equal") {
|
|
596
|
+
throw new Error(`PostgresJobStore ${method} wait failed: ${wait}`);
|
|
597
|
+
}
|
|
598
|
+
if (Atomics.load(state, 0) === 2) {
|
|
599
|
+
throw new Error(`PostgresJobStore ${method} worker could not write its result`);
|
|
600
|
+
}
|
|
601
|
+
let payload;
|
|
602
|
+
try {
|
|
603
|
+
payload = JSON.parse(readFileSync(resultPath, "utf8"));
|
|
604
|
+
}
|
|
605
|
+
finally {
|
|
606
|
+
rmSync(resultPath, { force: true });
|
|
607
|
+
}
|
|
608
|
+
if (!payload.ok) {
|
|
609
|
+
const err = new Error(payload.error.message);
|
|
610
|
+
if (payload.error.stack)
|
|
611
|
+
err.stack = payload.error.stack;
|
|
612
|
+
throw err;
|
|
613
|
+
}
|
|
614
|
+
return payload.value;
|
|
559
615
|
}
|
|
560
|
-
|
|
561
|
-
|
|
616
|
+
recordStart(input) {
|
|
617
|
+
this.syncCall("recordStart", input);
|
|
562
618
|
}
|
|
563
|
-
|
|
564
|
-
|
|
619
|
+
recordOutput(id, stdout, stderr, outputTruncated) {
|
|
620
|
+
this.syncCall("recordOutput", id, stdout, stderr, outputTruncated);
|
|
565
621
|
}
|
|
566
|
-
|
|
567
|
-
|
|
622
|
+
recordComplete(input) {
|
|
623
|
+
this.syncCall("recordComplete", input);
|
|
568
624
|
}
|
|
569
|
-
|
|
570
|
-
|
|
625
|
+
getById(id) {
|
|
626
|
+
const row = this.syncCall("getById", id);
|
|
627
|
+
return row ? rowToRecord(row) : null;
|
|
628
|
+
}
|
|
629
|
+
findByRequestKey(requestKey) {
|
|
630
|
+
const row = this.syncCall("findByRequestKey", requestKey);
|
|
631
|
+
return row ? rowToRecord(row) : null;
|
|
571
632
|
}
|
|
572
633
|
markOrphanedOnStartup() {
|
|
573
|
-
|
|
634
|
+
const result = this.syncCall("markOrphanedOnStartup");
|
|
635
|
+
return {
|
|
636
|
+
count: result.count,
|
|
637
|
+
orphaned: result.orphaned.map(row => ({
|
|
638
|
+
id: row.id,
|
|
639
|
+
correlationId: row.correlation_id,
|
|
640
|
+
startedAt: row.started_at,
|
|
641
|
+
stdout: row.stdout ?? "",
|
|
642
|
+
stderr: row.stderr ?? "",
|
|
643
|
+
exitCode: row.exit_code,
|
|
644
|
+
transport: row.transport ?? "process",
|
|
645
|
+
httpStatus: row.http_status ?? null,
|
|
646
|
+
})),
|
|
647
|
+
};
|
|
574
648
|
}
|
|
575
649
|
evictExpired() {
|
|
576
|
-
|
|
650
|
+
return this.syncCall("evictExpired");
|
|
651
|
+
}
|
|
652
|
+
recordValidationRun(run) {
|
|
653
|
+
this.syncCall("recordValidationRun", run);
|
|
654
|
+
}
|
|
655
|
+
getValidationRun(validationId) {
|
|
656
|
+
const row = this.syncCall("getValidationRun", validationId);
|
|
657
|
+
return row ? rowToValidationRunRecord(row) : null;
|
|
658
|
+
}
|
|
659
|
+
setValidationJudgeLink(validationId, judgeLink) {
|
|
660
|
+
this.syncCall("setValidationJudgeLink", validationId, judgeLink);
|
|
661
|
+
}
|
|
662
|
+
setValidationRunStatus(validationId, status) {
|
|
663
|
+
this.syncCall("setValidationRunStatus", validationId, status);
|
|
664
|
+
}
|
|
665
|
+
getValidationRunIdByJobId(jobId) {
|
|
666
|
+
return this.syncCall("getValidationRunIdByJobId", jobId);
|
|
667
|
+
}
|
|
668
|
+
recordValidationReceipt(receipt) {
|
|
669
|
+
this.syncCall("recordValidationReceipt", receipt);
|
|
670
|
+
}
|
|
671
|
+
getValidationReceipt(validationId) {
|
|
672
|
+
const row = this.syncCall("getValidationReceipt", validationId);
|
|
673
|
+
return row ? rowToValidationReceiptRecord(row) : null;
|
|
577
674
|
}
|
|
578
675
|
close() {
|
|
676
|
+
if (this.closed)
|
|
677
|
+
return;
|
|
678
|
+
try {
|
|
679
|
+
this.syncCall("close");
|
|
680
|
+
}
|
|
681
|
+
catch (err) {
|
|
682
|
+
this.logger.error("PostgresJobStore close failed", err);
|
|
683
|
+
}
|
|
684
|
+
finally {
|
|
685
|
+
this.closed = true;
|
|
686
|
+
void this.worker.terminate();
|
|
687
|
+
rmSync(this.tmpDir, { recursive: true, force: true });
|
|
688
|
+
}
|
|
579
689
|
}
|
|
580
690
|
}
|
|
691
|
+
function resolvePostgresWorkerUrl() {
|
|
692
|
+
const sibling = new URL("./postgres-job-store-worker.js", import.meta.url);
|
|
693
|
+
if (existsSync(fileURLToPath(sibling)))
|
|
694
|
+
return sibling;
|
|
695
|
+
if (import.meta.url.endsWith("/src/job-store.ts")) {
|
|
696
|
+
const built = new URL("../dist/postgres-job-store-worker.js", import.meta.url);
|
|
697
|
+
if (existsSync(fileURLToPath(built)))
|
|
698
|
+
return built;
|
|
699
|
+
}
|
|
700
|
+
throw new Error("PostgresJobStore worker module is missing. Run `npm run build` before using backend = 'postgres'.");
|
|
701
|
+
}
|
|
581
702
|
export function createJobStore(config, logger = noopLogger) {
|
|
582
703
|
const opts = {
|
|
583
704
|
retentionMs: config.retentionDays * 24 * 60 * 60 * 1000,
|
|
@@ -589,7 +710,7 @@ export function createJobStore(config, logger = noopLogger) {
|
|
|
589
710
|
case "memory":
|
|
590
711
|
return new MemoryJobStore(opts);
|
|
591
712
|
case "postgres":
|
|
592
|
-
return new PostgresJobStore(config.dsn ?? "", logger);
|
|
713
|
+
return new PostgresJobStore(config.dsn ?? "", logger, opts);
|
|
593
714
|
case "sqlite":
|
|
594
715
|
default:
|
|
595
716
|
if (!config.path) {
|
package/dist/model-registry.d.ts
CHANGED
|
@@ -17,6 +17,7 @@ export interface CliInfo {
|
|
|
17
17
|
aliases?: Record<string, string>;
|
|
18
18
|
modelMetadata?: Record<string, ModelMetadata>;
|
|
19
19
|
warnings?: string[];
|
|
20
|
+
defaultAgent?: string;
|
|
20
21
|
}
|
|
21
22
|
export type CliInfoMap = Record<CliType, CliInfo>;
|
|
22
23
|
export declare function getCliInfo(forceRefresh?: boolean): CliInfoMap;
|
package/dist/model-registry.js
CHANGED
|
@@ -139,6 +139,7 @@ function cloneInfo(source) {
|
|
|
139
139
|
aliases: source.aliases ? { ...source.aliases } : undefined,
|
|
140
140
|
modelMetadata: source.modelMetadata ? { ...source.modelMetadata } : {},
|
|
141
141
|
warnings: source.warnings ? [...source.warnings] : [],
|
|
142
|
+
defaultAgent: source.defaultAgent,
|
|
142
143
|
};
|
|
143
144
|
Object.keys(cloned.models).forEach(model => {
|
|
144
145
|
cloned.modelMetadata[model] = cloned.modelMetadata[model] ?? {
|
|
@@ -373,6 +374,7 @@ function applyGeminiOverrides(info) {
|
|
|
373
374
|
}
|
|
374
375
|
function applyGrokOverrides(info) {
|
|
375
376
|
const envDefault = process.env.GROK_DEFAULT_MODEL;
|
|
377
|
+
applyGrokConfig(info);
|
|
376
378
|
addEnvModels(info, "GROK_MODELS");
|
|
377
379
|
addEnvAliases(info, "grok", "GROK_MODEL_ALIASES");
|
|
378
380
|
addGlobalEnvAliases(info, "grok");
|
|
@@ -381,8 +383,48 @@ function applyGrokOverrides(info) {
|
|
|
381
383
|
}
|
|
382
384
|
info.modelOrder = buildOrder(info, info.defaultModel);
|
|
383
385
|
}
|
|
386
|
+
function applyGrokConfig(info) {
|
|
387
|
+
const configPath = process.env.GROK_CONFIG_PATH || path.join(homedir(), ".grok", "config.toml");
|
|
388
|
+
if (!existsSync(configPath)) {
|
|
389
|
+
return;
|
|
390
|
+
}
|
|
391
|
+
try {
|
|
392
|
+
const parsed = parseToml(readFileSync(configPath, "utf-8"));
|
|
393
|
+
const models = readRecordProperty(parsed, "models");
|
|
394
|
+
Object.entries(models).forEach(([key, value]) => {
|
|
395
|
+
if (typeof value !== "string") {
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
398
|
+
if (key === "default") {
|
|
399
|
+
setDefaultModel(info, value, `${configPath} [models].default`, "config");
|
|
400
|
+
return;
|
|
401
|
+
}
|
|
402
|
+
addModel(info, value, `Custom Grok model from config.toml [models].${key}`, {
|
|
403
|
+
source: "config",
|
|
404
|
+
sourceDetail: `${configPath} [models].${key}`,
|
|
405
|
+
confidence: "medium",
|
|
406
|
+
});
|
|
407
|
+
});
|
|
408
|
+
const ui = readRecordProperty(parsed, "ui");
|
|
409
|
+
const forkModel = readStringProperty(ui, "fork_secondary_model");
|
|
410
|
+
if (forkModel) {
|
|
411
|
+
addModel(info, forkModel, "Configured Grok fork/secondary model", {
|
|
412
|
+
source: "config",
|
|
413
|
+
sourceDetail: `${configPath} [ui].fork_secondary_model`,
|
|
414
|
+
confidence: "medium",
|
|
415
|
+
});
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
catch (error) {
|
|
419
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
420
|
+
addWarning(info, `Could not parse Grok config ${configPath}: ${message}`);
|
|
421
|
+
}
|
|
422
|
+
}
|
|
384
423
|
function applyMistralOverrides(info) {
|
|
385
424
|
const vibeConfig = readVibeConfig(info);
|
|
425
|
+
if (vibeConfig.defaultAgent) {
|
|
426
|
+
info.defaultAgent = vibeConfig.defaultAgent;
|
|
427
|
+
}
|
|
386
428
|
addVibeModelEntries(info, parseVibeModels(process.env.VIBE_MODELS, "VIBE_MODELS"), "env");
|
|
387
429
|
addVibeModelEntries(info, vibeConfig.models, "config");
|
|
388
430
|
addEnvModels(info, "MISTRAL_MODELS");
|
|
@@ -426,6 +468,10 @@ function readVibeConfig(info) {
|
|
|
426
468
|
if (activeModel) {
|
|
427
469
|
result.activeModel = activeModel.trim();
|
|
428
470
|
}
|
|
471
|
+
const defaultAgent = readStringProperty(parsed, "default_agent");
|
|
472
|
+
if (defaultAgent) {
|
|
473
|
+
result.defaultAgent = defaultAgent.trim();
|
|
474
|
+
}
|
|
429
475
|
result.models = parseVibeModelArray(readRecordOrArrayProperty(parsed, "models"), configPath);
|
|
430
476
|
}
|
|
431
477
|
catch (error) {
|
package/dist/oauth.js
CHANGED
|
@@ -2,6 +2,7 @@ import { createHash, randomBytes, randomUUID, scryptSync, timingSafeEqual } from
|
|
|
2
2
|
import { URLSearchParams } from "node:url";
|
|
3
3
|
import { readCappedRawBody, maxOAuthBodyBytes } from "./request-limits.js";
|
|
4
4
|
import { issueOAuthAccessToken, timingSafeStringEqual, } from "./auth.js";
|
|
5
|
+
import { joinBaseAndPath, OAUTH_AUTHORIZE_PATH, OAUTH_TOKEN_PATH, OAUTH_REGISTER_PATH, OAUTH_PROTECTED_RESOURCE_METADATA_PATH, OAUTH_AUTHORIZATION_SERVER_METADATA_PATH, OPENID_CONFIGURATION_PATH, } from "./remote-url.js";
|
|
5
6
|
export const OAUTH_CODE_TTL_MS = 5 * 60 * 1000;
|
|
6
7
|
const GENERATED_SECRET_BYTES = 32;
|
|
7
8
|
const SCRYPT_N = 32768;
|
|
@@ -227,17 +228,17 @@ export class OAuthServer {
|
|
|
227
228
|
}
|
|
228
229
|
}
|
|
229
230
|
resourceMetadataUrl(baseUrl) {
|
|
230
|
-
return
|
|
231
|
+
return joinBaseAndPath(baseUrl, OAUTH_PROTECTED_RESOURCE_METADATA_PATH);
|
|
231
232
|
}
|
|
232
233
|
isOAuthPath(pathname) {
|
|
233
|
-
return (pathname.startsWith(
|
|
234
|
-
pathname.startsWith(
|
|
235
|
-
pathname ===
|
|
234
|
+
return (pathname.startsWith(OAUTH_PROTECTED_RESOURCE_METADATA_PATH) ||
|
|
235
|
+
pathname.startsWith(OAUTH_AUTHORIZATION_SERVER_METADATA_PATH) ||
|
|
236
|
+
pathname === OPENID_CONFIGURATION_PATH ||
|
|
236
237
|
pathname.startsWith("/oauth/"));
|
|
237
238
|
}
|
|
238
239
|
async handle(ctx) {
|
|
239
240
|
const { req, res, url, baseUrl } = ctx;
|
|
240
|
-
if (url.pathname.startsWith(
|
|
241
|
+
if (url.pathname.startsWith(OAUTH_PROTECTED_RESOURCE_METADATA_PATH)) {
|
|
241
242
|
if (req.method !== "GET") {
|
|
242
243
|
methodNotAllowed(res);
|
|
243
244
|
return true;
|
|
@@ -245,8 +246,8 @@ export class OAuthServer {
|
|
|
245
246
|
jsonResponse(res, 200, this.protectedResourceMetadata(baseUrl));
|
|
246
247
|
return true;
|
|
247
248
|
}
|
|
248
|
-
if (url.pathname.startsWith(
|
|
249
|
-
url.pathname ===
|
|
249
|
+
if (url.pathname.startsWith(OAUTH_AUTHORIZATION_SERVER_METADATA_PATH) ||
|
|
250
|
+
url.pathname === OPENID_CONFIGURATION_PATH) {
|
|
250
251
|
if (req.method !== "GET") {
|
|
251
252
|
methodNotAllowed(res);
|
|
252
253
|
return true;
|
|
@@ -254,15 +255,15 @@ export class OAuthServer {
|
|
|
254
255
|
jsonResponse(res, 200, this.authorizationServerMetadata(baseUrl));
|
|
255
256
|
return true;
|
|
256
257
|
}
|
|
257
|
-
if (url.pathname ===
|
|
258
|
+
if (url.pathname === OAUTH_REGISTER_PATH) {
|
|
258
259
|
await this.handleRegister(req, res);
|
|
259
260
|
return true;
|
|
260
261
|
}
|
|
261
|
-
if (url.pathname ===
|
|
262
|
+
if (url.pathname === OAUTH_AUTHORIZE_PATH) {
|
|
262
263
|
await this.handleAuthorize(req, res);
|
|
263
264
|
return true;
|
|
264
265
|
}
|
|
265
|
-
if (url.pathname ===
|
|
266
|
+
if (url.pathname === OAUTH_TOKEN_PATH) {
|
|
266
267
|
await this.handleToken(req, res);
|
|
267
268
|
return true;
|
|
268
269
|
}
|
|
@@ -270,18 +271,18 @@ export class OAuthServer {
|
|
|
270
271
|
}
|
|
271
272
|
protectedResourceMetadata(baseUrl) {
|
|
272
273
|
return {
|
|
273
|
-
resource:
|
|
274
|
+
resource: joinBaseAndPath(baseUrl, this.opts.protectedPath),
|
|
274
275
|
authorization_servers: [baseUrl],
|
|
275
|
-
scopes_supported: ["mcp", "workspace:admin"],
|
|
276
|
+
scopes_supported: ["mcp", "workspace:admin", "cli:admin"],
|
|
276
277
|
bearer_methods_supported: ["header"],
|
|
277
278
|
};
|
|
278
279
|
}
|
|
279
280
|
authorizationServerMetadata(baseUrl) {
|
|
280
281
|
return {
|
|
281
282
|
issuer: baseUrl,
|
|
282
|
-
authorization_endpoint:
|
|
283
|
-
token_endpoint:
|
|
284
|
-
registration_endpoint:
|
|
283
|
+
authorization_endpoint: joinBaseAndPath(baseUrl, OAUTH_AUTHORIZE_PATH),
|
|
284
|
+
token_endpoint: joinBaseAndPath(baseUrl, OAUTH_TOKEN_PATH),
|
|
285
|
+
registration_endpoint: joinBaseAndPath(baseUrl, OAUTH_REGISTER_PATH),
|
|
285
286
|
response_types_supported: ["code"],
|
|
286
287
|
grant_types_supported: ["authorization_code"],
|
|
287
288
|
token_endpoint_auth_methods_supported: this.opts.config.allowPublicClients
|
|
@@ -290,7 +291,7 @@ export class OAuthServer {
|
|
|
290
291
|
code_challenge_methods_supported: this.opts.config.allowPlainPkce
|
|
291
292
|
? ["S256", "plain"]
|
|
292
293
|
: ["S256"],
|
|
293
|
-
scopes_supported: ["mcp", "workspace:admin"],
|
|
294
|
+
scopes_supported: ["mcp", "workspace:admin", "cli:admin"],
|
|
294
295
|
};
|
|
295
296
|
}
|
|
296
297
|
registrationAllowedByPolicy(req, params) {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|