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.
Files changed (88) hide show
  1. package/CHANGELOG.md +140 -0
  2. package/README.md +53 -26
  3. package/dist/acp/client.d.ts +29 -1
  4. package/dist/acp/client.js +78 -4
  5. package/dist/acp/errors.d.ts +9 -1
  6. package/dist/acp/errors.js +19 -0
  7. package/dist/acp/event-normalizer.d.ts +12 -0
  8. package/dist/acp/event-normalizer.js +16 -0
  9. package/dist/acp/flight-redaction.d.ts +3 -0
  10. package/dist/acp/flight-redaction.js +3 -0
  11. package/dist/acp/permission-bridge.js +11 -5
  12. package/dist/acp/process-manager.d.ts +2 -1
  13. package/dist/acp/process-manager.js +43 -4
  14. package/dist/acp/provider-registry.js +19 -11
  15. package/dist/acp/runtime.d.ts +8 -0
  16. package/dist/acp/runtime.js +47 -4
  17. package/dist/acp/types.d.ts +3083 -55
  18. package/dist/acp/types.js +242 -5
  19. package/dist/async-job-manager.d.ts +2 -0
  20. package/dist/async-job-manager.js +11 -0
  21. package/dist/codex-json-parser.d.ts +1 -0
  22. package/dist/codex-json-parser.js +6 -0
  23. package/dist/config.d.ts +16 -0
  24. package/dist/config.js +105 -19
  25. package/dist/connector-setup.d.ts +39 -0
  26. package/dist/connector-setup.js +86 -0
  27. package/dist/doctor.d.ts +39 -1
  28. package/dist/doctor.js +182 -3
  29. package/dist/flight-recorder.d.ts +2 -0
  30. package/dist/flight-recorder.js +20 -0
  31. package/dist/gemini-json-parser.d.ts +2 -0
  32. package/dist/gemini-json-parser.js +45 -8
  33. package/dist/grok-json-parser.d.ts +14 -0
  34. package/dist/grok-json-parser.js +156 -0
  35. package/dist/http-transport.js +27 -3
  36. package/dist/index.d.ts +48 -2
  37. package/dist/index.js +633 -144
  38. package/dist/job-store.d.ts +45 -7
  39. package/dist/job-store.js +138 -17
  40. package/dist/model-registry.d.ts +1 -0
  41. package/dist/model-registry.js +46 -0
  42. package/dist/oauth.js +17 -16
  43. package/dist/postgres-job-store-worker.d.ts +1 -0
  44. package/dist/postgres-job-store-worker.js +327 -0
  45. package/dist/pricing.d.ts +3 -2
  46. package/dist/provider-acp-capabilities.d.ts +52 -0
  47. package/dist/provider-acp-capabilities.js +101 -0
  48. package/dist/provider-admin-tools.d.ts +100 -0
  49. package/dist/provider-admin-tools.js +572 -0
  50. package/dist/provider-capability-cache.d.ts +46 -0
  51. package/dist/provider-capability-cache.js +248 -0
  52. package/dist/provider-capability-discovery.d.ts +85 -0
  53. package/dist/provider-capability-discovery.js +461 -0
  54. package/dist/provider-capability-resolver.d.ts +29 -0
  55. package/dist/provider-capability-resolver.js +92 -0
  56. package/dist/provider-definition-assertions.d.ts +9 -0
  57. package/dist/provider-definition-assertions.js +147 -0
  58. package/dist/provider-definitions.d.ts +127 -0
  59. package/dist/provider-definitions.js +758 -0
  60. package/dist/provider-help-parser.d.ts +34 -0
  61. package/dist/provider-help-parser.js +203 -0
  62. package/dist/provider-model-discovery.d.ts +30 -0
  63. package/dist/provider-model-discovery.js +229 -0
  64. package/dist/provider-output-metadata.d.ts +7 -0
  65. package/dist/provider-output-metadata.js +68 -0
  66. package/dist/provider-schema-builder.d.ts +22 -0
  67. package/dist/provider-schema-builder.js +55 -0
  68. package/dist/provider-surface-generator.d.ts +98 -0
  69. package/dist/provider-surface-generator.js +140 -0
  70. package/dist/provider-tool-capabilities.d.ts +3 -2
  71. package/dist/provider-tool-capabilities.js +77 -62
  72. package/dist/remote-url.d.ts +28 -0
  73. package/dist/remote-url.js +61 -0
  74. package/dist/request-helpers.d.ts +37 -4
  75. package/dist/request-helpers.js +134 -0
  76. package/dist/resources.d.ts +6 -1
  77. package/dist/resources.js +67 -193
  78. package/dist/session-manager.d.ts +2 -0
  79. package/dist/session-manager.js +34 -8
  80. package/dist/stream-json-parser.d.ts +1 -0
  81. package/dist/stream-json-parser.js +3 -0
  82. package/dist/upstream-contracts.d.ts +17 -1
  83. package/dist/upstream-contracts.js +209 -36
  84. package/dist/workspace-registry.d.ts +9 -0
  85. package/dist/workspace-registry.js +24 -4
  86. package/npm-shrinkwrap.json +2 -2
  87. package/package.json +8 -3
  88. package/setup/status.schema.json +75 -0
@@ -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
- constructor(_dsn: string, _logger?: Logger);
219
- recordStart(): void;
220
- recordOutput(): void;
221
- recordComplete(): void;
222
- getById(): JobRecord | null;
223
- findByRequestKey(): JobRecord | null;
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
- constructor(_dsn, _logger = noopLogger) {
554
- throw new Error("PostgresJobStore is not yet implemented. Use backend = 'sqlite' (single-instance) or " +
555
- "backend = 'memory' (ephemeral) until the Postgres backend ships.");
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
- recordStart() {
558
- throw new Error("not implemented");
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
- recordOutput() {
561
- throw new Error("not implemented");
616
+ recordStart(input) {
617
+ this.syncCall("recordStart", input);
562
618
  }
563
- recordComplete() {
564
- throw new Error("not implemented");
619
+ recordOutput(id, stdout, stderr, outputTruncated) {
620
+ this.syncCall("recordOutput", id, stdout, stderr, outputTruncated);
565
621
  }
566
- getById() {
567
- throw new Error("not implemented");
622
+ recordComplete(input) {
623
+ this.syncCall("recordComplete", input);
568
624
  }
569
- findByRequestKey() {
570
- throw new Error("not implemented");
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
- throw new Error("not implemented");
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
- throw new Error("not implemented");
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) {
@@ -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;
@@ -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 `${baseUrl}/.well-known/oauth-protected-resource`;
231
+ return joinBaseAndPath(baseUrl, OAUTH_PROTECTED_RESOURCE_METADATA_PATH);
231
232
  }
232
233
  isOAuthPath(pathname) {
233
- return (pathname.startsWith("/.well-known/oauth-protected-resource") ||
234
- pathname.startsWith("/.well-known/oauth-authorization-server") ||
235
- pathname === "/.well-known/openid-configuration" ||
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("/.well-known/oauth-protected-resource")) {
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("/.well-known/oauth-authorization-server") ||
249
- url.pathname === "/.well-known/openid-configuration") {
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 === "/oauth/register") {
258
+ if (url.pathname === OAUTH_REGISTER_PATH) {
258
259
  await this.handleRegister(req, res);
259
260
  return true;
260
261
  }
261
- if (url.pathname === "/oauth/authorize") {
262
+ if (url.pathname === OAUTH_AUTHORIZE_PATH) {
262
263
  await this.handleAuthorize(req, res);
263
264
  return true;
264
265
  }
265
- if (url.pathname === "/oauth/token") {
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: `${baseUrl}${this.opts.protectedPath}`,
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: `${baseUrl}/oauth/authorize`,
283
- token_endpoint: `${baseUrl}/oauth/token`,
284
- registration_endpoint: `${baseUrl}/oauth/register`,
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 {};