@clinebot/core 0.0.23 → 0.0.25

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.
@@ -30,6 +30,13 @@ export declare function toRpcProviderModel(modelId: string, info: {
30
30
  capabilities?: string[];
31
31
  thinkingConfig?: unknown;
32
32
  }): RpcProviderModel;
33
+ /**
34
+ * Custom Provider Registry
35
+ *
36
+ * This module manages the registration of custom OpenAI-compatible providers and
37
+ * their models based on local JSON files. It provides functions to read/write the models
38
+ * registry file and to register providers with the LlmsModels system.
39
+ */
33
40
  export declare function registerCustomProvider(providerId: string, entry: StoredModelsFile["providers"][string]): void;
34
41
  export declare function ensureCustomProvidersLoadedSync(manager: ProviderSettingsManager): void;
35
42
  export declare function ensureCustomProvidersLoaded(manager: ProviderSettingsManager): Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"local-provider-registry.d.ts","sourceRoot":"","sources":["../../src/providers/local-provider-registry.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAChF,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,sCAAsC,CAAC;AAEpF,MAAM,MAAM,gBAAgB,GAAG;IAC9B,OAAO,EAAE,CAAC,CAAC;IACX,SAAS,EAAE,MAAM,CAChB,MAAM,EACN;QACC,QAAQ,EAAE;YACT,IAAI,EAAE,MAAM,CAAC;YACb,OAAO,EAAE,MAAM,CAAC;YAChB,cAAc,CAAC,EAAE,MAAM,CAAC;YACxB,YAAY,CAAC,EAAE,qBAAqB,EAAE,CAAC;YACvC,eAAe,CAAC,EAAE,MAAM,CAAC;SACzB,CAAC;QACF,MAAM,EAAE,MAAM,CACb,MAAM,EACN;YACC,EAAE,EAAE,MAAM,CAAC;YACX,IAAI,EAAE,MAAM,CAAC;YACb,cAAc,CAAC,EAAE,OAAO,CAAC;YACzB,mBAAmB,CAAC,EAAE,OAAO,CAAC;YAC9B,iBAAiB,CAAC,EAAE,OAAO,CAAC;SAC5B,CACD,CAAC;KACF,CACD,CAAC;CACF,CAAC;AAYF,wBAAgB,yBAAyB,CACxC,OAAO,EAAE,uBAAuB,GAC9B,MAAM,CAER;AAED,wBAAgB,eAAe,IAAI,gBAAgB,CAElD;AAED,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,gBAAgB,CAmBrE;AAED,wBAAsB,cAAc,CACnC,QAAQ,EAAE,MAAM,GACd,OAAO,CAAC,gBAAgB,CAAC,CAgB3B;AAED,wBAAgB,mBAAmB,CAClC,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,gBAAgB,GACrB,IAAI,CAGN;AAED,wBAAsB,eAAe,CACpC,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,gBAAgB,GACrB,OAAO,CAAC,IAAI,CAAC,CAGf;AAED,wBAAgB,kBAAkB,CACjC,OAAO,EAAE,MAAM,EACf,IAAI,EAAE;IACL,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,cAAc,CAAC,EAAE,OAAO,CAAC;CACzB,GACC,gBAAgB,CASlB;AA2CD,wBAAgB,sBAAsB,CACrC,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE,gBAAgB,CAAC,WAAW,CAAC,CAAC,MAAM,CAAC,GAC1C,IAAI,CA+BN;AAED,wBAAgB,+BAA+B,CAC9C,OAAO,EAAE,uBAAuB,GAC9B,IAAI,CAUN;AAED,wBAAsB,2BAA2B,CAChD,OAAO,EAAE,uBAAuB,GAC9B,OAAO,CAAC,IAAI,CAAC,CAUf"}
1
+ {"version":3,"file":"local-provider-registry.d.ts","sourceRoot":"","sources":["../../src/providers/local-provider-registry.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAChF,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,sCAAsC,CAAC;AAEpF,MAAM,MAAM,gBAAgB,GAAG;IAC9B,OAAO,EAAE,CAAC,CAAC;IACX,SAAS,EAAE,MAAM,CAChB,MAAM,EACN;QACC,QAAQ,EAAE;YACT,IAAI,EAAE,MAAM,CAAC;YACb,OAAO,EAAE,MAAM,CAAC;YAChB,cAAc,CAAC,EAAE,MAAM,CAAC;YACxB,YAAY,CAAC,EAAE,qBAAqB,EAAE,CAAC;YACvC,eAAe,CAAC,EAAE,MAAM,CAAC;SACzB,CAAC;QACF,MAAM,EAAE,MAAM,CACb,MAAM,EACN;YACC,EAAE,EAAE,MAAM,CAAC;YACX,IAAI,EAAE,MAAM,CAAC;YACb,cAAc,CAAC,EAAE,OAAO,CAAC;YACzB,mBAAmB,CAAC,EAAE,OAAO,CAAC;YAC9B,iBAAiB,CAAC,EAAE,OAAO,CAAC;SAC5B,CACD,CAAC;KACF,CACD,CAAC;CACF,CAAC;AAYF,wBAAgB,yBAAyB,CACxC,OAAO,EAAE,uBAAuB,GAC9B,MAAM,CAER;AAED,wBAAgB,eAAe,IAAI,gBAAgB,CAElD;AAED,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,gBAAgB,CAmBrE;AAED,wBAAsB,cAAc,CACnC,QAAQ,EAAE,MAAM,GACd,OAAO,CAAC,gBAAgB,CAAC,CAgB3B;AAED,wBAAgB,mBAAmB,CAClC,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,gBAAgB,GACrB,IAAI,CAGN;AAED,wBAAsB,eAAe,CACpC,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,gBAAgB,GACrB,OAAO,CAAC,IAAI,CAAC,CAGf;AAED,wBAAgB,kBAAkB,CACjC,OAAO,EAAE,MAAM,EACf,IAAI,EAAE;IACL,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,cAAc,CAAC,EAAE,OAAO,CAAC;CACzB,GACC,gBAAgB,CASlB;AA2CD;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CACrC,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE,gBAAgB,CAAC,WAAW,CAAC,CAAC,MAAM,CAAC,GAC1C,IAAI,CAgCN;AAED,wBAAgB,+BAA+B,CAC9C,OAAO,EAAE,uBAAuB,GAC9B,IAAI,CAUN;AAED,wBAAsB,2BAA2B,CAChD,OAAO,EAAE,uBAAuB,GAC9B,OAAO,CAAC,IAAI,CAAC,CAUf"}
@@ -1,13 +1,39 @@
1
1
  import * as LlmsProviders from "@clinebot/llms/providers";
2
- import type { RpcAddProviderActionRequest, RpcOAuthProviderId, RpcProviderListItem, RpcProviderModel, RpcSaveProviderSettingsActionRequest } from "@clinebot/shared";
2
+ import type { RpcAddProviderActionRequest, RpcOAuthProviderId, RpcProviderCapability, RpcProviderListItem, RpcProviderModel, RpcSaveProviderSettingsActionRequest } from "@clinebot/shared";
3
3
  import type { ProviderSettingsManager } from "../storage/provider-settings-manager";
4
4
  export { ensureCustomProvidersLoaded } from "./local-provider-registry";
5
+ export interface UpdateLocalProviderRequest {
6
+ providerId: string;
7
+ name?: string;
8
+ baseUrl?: string;
9
+ apiKey?: string | null;
10
+ headers?: Record<string, string> | null;
11
+ timeoutMs?: number | null;
12
+ models?: string[];
13
+ defaultModelId?: string | null;
14
+ modelsSourceUrl?: string | null;
15
+ capabilities?: RpcProviderCapability[] | null;
16
+ }
17
+ export interface DeleteLocalProviderRequest {
18
+ providerId: string;
19
+ }
5
20
  export declare function addLocalProvider(manager: ProviderSettingsManager, request: Omit<RpcAddProviderActionRequest, "action">): Promise<{
6
21
  providerId: string;
7
22
  settingsPath: string;
8
23
  modelsPath: string;
9
24
  modelsCount: number;
10
25
  }>;
26
+ export declare function updateLocalProvider(manager: ProviderSettingsManager, request: UpdateLocalProviderRequest): Promise<{
27
+ providerId: string;
28
+ settingsPath: string;
29
+ modelsPath: string;
30
+ modelsCount: number;
31
+ }>;
32
+ export declare function deleteLocalProvider(manager: ProviderSettingsManager, request: DeleteLocalProviderRequest): Promise<{
33
+ providerId: string;
34
+ settingsPath: string;
35
+ modelsPath: string;
36
+ }>;
11
37
  export declare function listLocalProviders(manager: ProviderSettingsManager): Promise<{
12
38
  providers: RpcProviderListItem[];
13
39
  settingsPath: string;
@@ -1 +1 @@
1
- {"version":3,"file":"local-provider-service.d.ts","sourceRoot":"","sources":["../../src/providers/local-provider-service.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,aAAa,MAAM,0BAA0B,CAAC;AAC1D,OAAO,KAAK,EACX,2BAA2B,EAC3B,kBAAkB,EAClB,mBAAmB,EACnB,gBAAgB,EAChB,oCAAoC,EACpC,MAAM,kBAAkB,CAAC;AAK1B,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,sCAAsC,CAAC;AASpF,OAAO,EAAE,2BAA2B,EAAE,MAAM,2BAA2B,CAAC;AAuJxE,wBAAsB,gBAAgB,CACrC,OAAO,EAAE,uBAAuB,EAChC,OAAO,EAAE,IAAI,CAAC,2BAA2B,EAAE,QAAQ,CAAC,GAClD,OAAO,CAAC;IACV,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;CACpB,CAAC,CAyFD;AAED,wBAAsB,kBAAkB,CACvC,OAAO,EAAE,uBAAuB,GAC9B,OAAO,CAAC;IAAE,SAAS,EAAE,mBAAmB,EAAE,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,CAAC,CAoCrE;AAED,wBAAsB,sBAAsB,CAC3C,UAAU,EAAE,MAAM,EAClB,MAAM,CAAC,EAAE,aAAa,CAAC,cAAc,GACnC,OAAO,CAAC;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,gBAAgB,EAAE,CAAA;CAAE,CAAC,CAK7D;AAED,wBAAgB,yBAAyB,CACxC,OAAO,EAAE,uBAAuB,EAChC,OAAO,EAAE,IAAI,CAAC,oCAAoC,EAAE,QAAQ,CAAC,GAC3D;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,CA4DhE;AAED,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,GAAG,kBAAkB,CAQ3E;AAWD,wBAAsB,kBAAkB,CACvC,UAAU,EAAE,kBAAkB,EAC9B,QAAQ,EAAE,aAAa,CAAC,gBAAgB,GAAG,SAAS,EACpD,OAAO,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,GAC5B,OAAO,CAAC;IACV,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC,CAkBD;AAED,wBAAgB,iCAAiC,CAChD,OAAO,EAAE,uBAAuB,EAChC,UAAU,EAAE,kBAAkB,EAC9B,QAAQ,EAAE,aAAa,CAAC,gBAAgB,GAAG,SAAS,EACpD,WAAW,EAAE;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;CACnB,GACC,aAAa,CAAC,gBAAgB,CAkBhC;AAED,wBAAgB,0BAA0B,CACzC,QAAQ,EAAE,aAAa,CAAC,gBAAgB,GAAG,SAAS,GAClD,MAAM,GAAG,SAAS,CAGpB"}
1
+ {"version":3,"file":"local-provider-service.d.ts","sourceRoot":"","sources":["../../src/providers/local-provider-service.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"runtime-builder.d.ts","sourceRoot":"","sources":["../../src/runtime/runtime-builder.ts"],"names":[],"mappings":"AA6BA,OAAO,KAAK,EACX,cAAc,EACd,mBAAmB,EACnB,YAAY,IAAI,kBAAkB,EAClC,MAAM,mBAAmB,CAAC;AAgB3B,wBAAgB,cAAc,IAAI,MAAM,CAEvC;AAqTD,qBAAa,qBAAsB,YAAW,cAAc;IAC3D,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAA6B;IAEjE,KAAK,CAAC,KAAK,EAAE,mBAAmB,GAAG,kBAAkB;CA2QrD"}
1
+ {"version":3,"file":"runtime-builder.d.ts","sourceRoot":"","sources":["../../src/runtime/runtime-builder.ts"],"names":[],"mappings":"AA6BA,OAAO,KAAK,EACX,cAAc,EACd,mBAAmB,EACnB,YAAY,IAAI,kBAAkB,EAClC,MAAM,mBAAmB,CAAC;AAgB3B,wBAAgB,cAAc,IAAI,MAAM,CAEvC;AAqTD,qBAAa,qBAAsB,YAAW,cAAc;IAC3D,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAA6B;IAEjE,KAAK,CAAC,KAAK,EAAE,mBAAmB,GAAG,kBAAkB;CA0QrD"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@clinebot/core",
3
3
  "description": "Cline Core SDK for Node Runtime",
4
- "version": "0.0.23",
4
+ "version": "0.0.25",
5
5
  "type": "module",
6
6
  "types": "./dist/index.d.ts",
7
7
  "main": "./dist/index.js",
@@ -36,8 +36,8 @@
36
36
  "test:watch": "vitest --config vitest.config.ts"
37
37
  },
38
38
  "dependencies": {
39
- "@clinebot/agents": "0.0.23",
40
- "@clinebot/llms": "0.0.23",
39
+ "@clinebot/agents": "0.0.25",
40
+ "@clinebot/llms": "0.0.25",
41
41
  "@opentelemetry/api": "^1.9.0",
42
42
  "@opentelemetry/api-logs": "^0.56.0",
43
43
  "@opentelemetry/exporter-logs-otlp-http": "^0.56.0",
@@ -54,8 +54,8 @@
54
54
  "zod": "^4.3.6"
55
55
  },
56
56
  "devDependencies": {
57
- "@clinebot/rpc": "0.0.23",
58
- "@clinebot/shared": "0.0.23"
57
+ "@clinebot/rpc": "0.0.25",
58
+ "@clinebot/shared": "0.0.25"
59
59
  },
60
60
  "engines": {
61
61
  "node": ">=20"
package/src/index.ts CHANGED
@@ -246,6 +246,8 @@ export {
246
246
  } from "./mcp";
247
247
  export {
248
248
  addLocalProvider,
249
+ type DeleteLocalProviderRequest,
250
+ deleteLocalProvider,
249
251
  ensureCustomProvidersLoaded,
250
252
  getLocalProviderModels,
251
253
  listLocalProviders,
@@ -254,6 +256,8 @@ export {
254
256
  resolveLocalClineAuthToken,
255
257
  saveLocalProviderOAuthCredentials,
256
258
  saveLocalProviderSettings,
259
+ type UpdateLocalProviderRequest,
260
+ updateLocalProvider,
257
261
  } from "./providers/local-provider-service";
258
262
  export type { AvailableRuntimeCommand } from "./runtime/commands";
259
263
  export {
@@ -167,6 +167,13 @@ function toModelCapabilities(
167
167
  return [...next];
168
168
  }
169
169
 
170
+ /**
171
+ * Custom Provider Registry
172
+ *
173
+ * This module manages the registration of custom OpenAI-compatible providers and
174
+ * their models based on local JSON files. It provides functions to read/write the models
175
+ * registry file and to register providers with the LlmsModels system.
176
+ */
170
177
  export function registerCustomProvider(
171
178
  providerId: string,
172
179
  entry: StoredModelsFile["providers"][string],
@@ -195,6 +202,7 @@ export function registerCustomProvider(
195
202
  id: providerId,
196
203
  name: entry.provider.name.trim() || titleCaseFromId(providerId),
197
204
  protocol: "openai-chat",
205
+ client: "openai-compatible",
198
206
  baseUrl: entry.provider.baseUrl,
199
207
  defaultModelId,
200
208
  capabilities: toProviderCapabilities(entry.provider.capabilities),
@@ -4,13 +4,19 @@ import path from "node:path";
4
4
  import * as LlmsModels from "@clinebot/llms/models";
5
5
  import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
6
6
  import { ProviderSettingsManager } from "../storage/provider-settings-manager";
7
+ import {
8
+ readModelsFile,
9
+ resolveModelsRegistryPath,
10
+ } from "./local-provider-registry";
7
11
  import {
8
12
  addLocalProvider,
13
+ deleteLocalProvider,
9
14
  getLocalProviderModels,
10
15
  listLocalProviders,
11
16
  normalizeOAuthProvider,
12
17
  resolveLocalClineAuthToken,
13
18
  saveLocalProviderSettings,
19
+ updateLocalProvider,
14
20
  } from "./local-provider-service";
15
21
 
16
22
  // ---------------------------------------------------------------------------
@@ -267,12 +273,13 @@ describe("addLocalProvider – validation", () => {
267
273
  ).rejects.toThrow("name is required");
268
274
  });
269
275
 
270
- it("throws when baseUrl is empty", async () => {
276
+ it("throws when baseUrl is empty and apiKey is provided", async () => {
271
277
  await expect(
272
278
  addLocalProvider(manager, {
273
279
  providerId: "my-provider2",
274
280
  name: "My Provider",
275
281
  baseUrl: " ",
282
+ apiKey: "present-key",
276
283
  models: ["m"],
277
284
  }),
278
285
  ).rejects.toThrow("baseUrl is required");
@@ -309,6 +316,71 @@ describe("addLocalProvider – validation", () => {
309
316
  });
310
317
  });
311
318
 
319
+ // ===========================================================================
320
+ // addLocalProvider – delete compatibility path
321
+ // ===========================================================================
322
+
323
+ describe("addLocalProvider – delete compatibility", () => {
324
+ let manager: ProviderSettingsManager;
325
+ let cleanup: () => void;
326
+
327
+ beforeEach(async () => {
328
+ ({ manager, cleanup } = makeTempManager());
329
+ await addLocalProvider(manager, {
330
+ providerId: "legacy-delete-provider",
331
+ name: "Legacy Delete Provider",
332
+ baseUrl: "https://example.invalid/v1",
333
+ models: ["legacy-model"],
334
+ });
335
+ manager.saveProviderSettings(
336
+ {
337
+ provider: "legacy-delete-provider",
338
+ baseUrl: "https://example.invalid/v1",
339
+ model: "legacy-model",
340
+ },
341
+ { setLastUsed: true },
342
+ );
343
+ });
344
+
345
+ afterEach(() => cleanup());
346
+
347
+ it("treats empty baseUrl and apiKey as a delete request for existing provider", async () => {
348
+ const result = await addLocalProvider(manager, {
349
+ providerId: "legacy-delete-provider",
350
+ name: "Ignored",
351
+ baseUrl: " ",
352
+ apiKey: " ",
353
+ models: [],
354
+ });
355
+
356
+ expect(result.providerId).toBe("legacy-delete-provider");
357
+ expect(result.modelsCount).toBe(0);
358
+ expect(LlmsModels.hasProvider("legacy-delete-provider")).toBe(false);
359
+ expect(
360
+ manager.getProviderSettings("legacy-delete-provider"),
361
+ ).toBeUndefined();
362
+ expect(manager.getLastUsedProviderSettings()).toBeUndefined();
363
+ });
364
+
365
+ it("is a no-op delete when provider is already missing", async () => {
366
+ await deleteLocalProvider(manager, {
367
+ providerId: "legacy-delete-provider",
368
+ });
369
+
370
+ await expect(
371
+ addLocalProvider(manager, {
372
+ providerId: "legacy-delete-provider",
373
+ name: "Ignored",
374
+ baseUrl: " ",
375
+ models: [],
376
+ }),
377
+ ).resolves.toMatchObject({
378
+ providerId: "legacy-delete-provider",
379
+ modelsCount: 0,
380
+ });
381
+ });
382
+ });
383
+
312
384
  // ===========================================================================
313
385
  // addLocalProvider – defaultModelId selection
314
386
  // ===========================================================================
@@ -568,6 +640,192 @@ describe("saveLocalProviderSettings", () => {
568
640
  });
569
641
  });
570
642
 
643
+ // ===========================================================================
644
+ // updateLocalProvider
645
+ // ===========================================================================
646
+
647
+ describe("updateLocalProvider", () => {
648
+ let manager: ProviderSettingsManager;
649
+ let cleanup: () => void;
650
+
651
+ beforeEach(async () => {
652
+ ({ manager, cleanup } = makeTempManager());
653
+ await addLocalProvider(manager, {
654
+ providerId: "editable-provider",
655
+ name: "Editable Provider",
656
+ baseUrl: "https://example.invalid/v1",
657
+ apiKey: "seed-key",
658
+ models: ["model-a", "model-b"],
659
+ defaultModelId: "model-a",
660
+ });
661
+ });
662
+
663
+ afterEach(() => cleanup());
664
+
665
+ it("updates provider metadata and model registry", async () => {
666
+ await updateLocalProvider(manager, {
667
+ providerId: "editable-provider",
668
+ name: "Renamed Provider",
669
+ baseUrl: "https://api.example.invalid/v2",
670
+ models: ["model-c", "model-d"],
671
+ defaultModelId: "model-d",
672
+ capabilities: ["vision", "reasoning"],
673
+ });
674
+
675
+ const provider = await LlmsModels.getProvider("editable-provider");
676
+ expect(provider?.name).toBe("Renamed Provider");
677
+ expect(provider?.baseUrl).toBe("https://api.example.invalid/v2");
678
+ expect(provider?.defaultModelId).toBe("model-d");
679
+
680
+ const { models } = await getLocalProviderModels("editable-provider");
681
+ expect(models.map((model) => model.id).sort()).toEqual([
682
+ "model-c",
683
+ "model-d",
684
+ ]);
685
+ expect(models.find((model) => model.id === "model-c")?.supportsVision).toBe(
686
+ true,
687
+ );
688
+ expect(
689
+ models.find((model) => model.id === "model-c")?.supportsReasoning,
690
+ ).toBe(true);
691
+ });
692
+
693
+ it("updates provider settings and can clear optional fields", async () => {
694
+ await updateLocalProvider(manager, {
695
+ providerId: "editable-provider",
696
+ baseUrl: "https://api.example.invalid/v3",
697
+ apiKey: "",
698
+ headers: null,
699
+ timeoutMs: null,
700
+ });
701
+
702
+ const settings = manager.getProviderSettings("editable-provider");
703
+ expect(settings?.baseUrl).toBe("https://api.example.invalid/v3");
704
+ expect(settings).not.toHaveProperty("apiKey");
705
+ expect(settings).not.toHaveProperty("headers");
706
+ expect(settings).not.toHaveProperty("timeout");
707
+ });
708
+
709
+ it("clears capabilities with null and does not treat [] as a clear sentinel", async () => {
710
+ await updateLocalProvider(manager, {
711
+ providerId: "editable-provider",
712
+ capabilities: ["vision"],
713
+ });
714
+ const modelsPath = resolveModelsRegistryPath(manager);
715
+ let modelsState = await readModelsFile(modelsPath);
716
+ expect(
717
+ modelsState.providers["editable-provider"]?.provider.capabilities,
718
+ ).toEqual(["vision"]);
719
+
720
+ await updateLocalProvider(manager, {
721
+ providerId: "editable-provider",
722
+ capabilities: [],
723
+ });
724
+ modelsState = await readModelsFile(modelsPath);
725
+ expect(
726
+ modelsState.providers["editable-provider"]?.provider.capabilities,
727
+ ).toEqual([]);
728
+
729
+ await updateLocalProvider(manager, {
730
+ providerId: "editable-provider",
731
+ capabilities: null,
732
+ });
733
+ modelsState = await readModelsFile(modelsPath);
734
+ expect(
735
+ modelsState.providers["editable-provider"]?.provider.capabilities,
736
+ ).toBeUndefined();
737
+ });
738
+
739
+ it("allows modelsSourceUrl: null to clear URL while preserving existing models", async () => {
740
+ const fetchMock = vi.fn().mockResolvedValue({
741
+ ok: true,
742
+ json: async () => ["remote-a", "remote-b"],
743
+ });
744
+ vi.stubGlobal("fetch", fetchMock);
745
+
746
+ await updateLocalProvider(manager, {
747
+ providerId: "editable-provider",
748
+ models: ["model-a", "model-b"],
749
+ modelsSourceUrl: "https://example.invalid/models",
750
+ });
751
+
752
+ await updateLocalProvider(manager, {
753
+ providerId: "editable-provider",
754
+ modelsSourceUrl: null,
755
+ });
756
+
757
+ const modelsState = await readModelsFile(
758
+ resolveModelsRegistryPath(manager),
759
+ );
760
+ expect(
761
+ modelsState.providers["editable-provider"]?.provider.modelsSourceUrl,
762
+ ).toBeUndefined();
763
+
764
+ const { models } = await getLocalProviderModels("editable-provider");
765
+ expect(models.map((model) => model.id).sort()).toEqual([
766
+ "model-a",
767
+ "model-b",
768
+ "remote-a",
769
+ "remote-b",
770
+ ]);
771
+ expect(fetchMock).toHaveBeenCalledTimes(1);
772
+ });
773
+
774
+ it("throws when updating a provider that does not exist in custom registry", async () => {
775
+ await expect(
776
+ updateLocalProvider(manager, {
777
+ providerId: "missing-provider",
778
+ name: "Nope",
779
+ }),
780
+ ).rejects.toThrow('"missing-provider" does not exist');
781
+ });
782
+ });
783
+
784
+ // ===========================================================================
785
+ // deleteLocalProvider
786
+ // ===========================================================================
787
+
788
+ describe("deleteLocalProvider", () => {
789
+ let manager: ProviderSettingsManager;
790
+ let cleanup: () => void;
791
+
792
+ beforeEach(async () => {
793
+ ({ manager, cleanup } = makeTempManager());
794
+ await addLocalProvider(manager, {
795
+ providerId: "delete-me-provider",
796
+ name: "Delete Me",
797
+ baseUrl: "https://example.invalid/v1",
798
+ models: ["delete-model"],
799
+ });
800
+ manager.saveProviderSettings(
801
+ {
802
+ provider: "delete-me-provider",
803
+ baseUrl: "https://example.invalid/v1",
804
+ model: "delete-model",
805
+ },
806
+ { setLastUsed: true },
807
+ );
808
+ });
809
+
810
+ afterEach(() => cleanup());
811
+
812
+ it("removes provider from registry and local settings", async () => {
813
+ await deleteLocalProvider(manager, {
814
+ providerId: "delete-me-provider",
815
+ });
816
+
817
+ expect(LlmsModels.hasProvider("delete-me-provider")).toBe(false);
818
+ expect(manager.getProviderSettings("delete-me-provider")).toBeUndefined();
819
+ expect(manager.getLastUsedProviderSettings()).toBeUndefined();
820
+ });
821
+
822
+ it("throws when deleting an unknown provider", async () => {
823
+ await expect(
824
+ deleteLocalProvider(manager, { providerId: "missing-provider" }),
825
+ ).rejects.toThrow('"missing-provider" does not exist');
826
+ });
827
+ });
828
+
571
829
  // ===========================================================================
572
830
  // listLocalProviders
573
831
  // ===========================================================================