@skaile/forge-settings 1.4.0 → 2.0.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 (75) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/README.md +34 -11
  3. package/dist/module.json +1 -1
  4. package/dist/runtime/ai-sdk/index.d.ts +33 -26
  5. package/dist/runtime/ai-sdk/index.js +34 -16
  6. package/dist/runtime/components/ForgeProvidersPanel.d.vue.ts +2 -2
  7. package/dist/runtime/components/ForgeProvidersPanel.vue.d.ts +2 -2
  8. package/dist/runtime/editor/index.d.ts +55 -16
  9. package/dist/runtime/editor/index.js +82 -40
  10. package/dist/runtime/model/selector.d.ts +16 -0
  11. package/dist/runtime/model/selector.js +12 -0
  12. package/dist/runtime/server/credential-store.d.ts +16 -4
  13. package/dist/runtime/server/credential-store.js +3 -4
  14. package/dist/runtime/server/env-layers.d.ts +1 -1
  15. package/dist/runtime/server/env-layers.js +3 -3
  16. package/dist/runtime/server/index.d.ts +7 -8
  17. package/dist/runtime/server/index.js +1 -3
  18. package/dist/runtime/server/provider-discovery.d.ts +28 -8
  19. package/dist/runtime/server/provider-discovery.js +57 -33
  20. package/dist/runtime/server/provider-layers.d.ts +1 -1
  21. package/dist/runtime/server/provider-layers.js +3 -5
  22. package/dist/runtime/server/provider-service.d.ts +2 -2
  23. package/dist/runtime/server/provider-write.d.ts +1 -1
  24. package/dist/runtime/server/provider-write.js +3 -3
  25. package/dist/runtime/server/voice-provider.d.ts +61 -0
  26. package/dist/runtime/server/voice-provider.js +31 -0
  27. package/dist/runtime/shared/data-zone.d.ts +28 -26
  28. package/dist/runtime/shared/data-zone.js +4 -4
  29. package/dist/runtime/shared/harness-providers.d.ts +7 -7
  30. package/dist/runtime/shared/provider-accounts.d.ts +1 -1
  31. package/dist/runtime/shared/provider-registry.d.ts +19 -220
  32. package/dist/runtime/shared/provider-registry.js +25 -81
  33. package/dist/runtime/vue/providers-panel-model.d.ts +4 -4
  34. package/dist/runtime/{server → workspaces}/harness-settings.d.ts +3 -3
  35. package/dist/runtime/workspaces/index.d.ts +16 -0
  36. package/dist/runtime/workspaces/index.js +5 -0
  37. package/dist/runtime/workspaces/language-model.d.ts +45 -0
  38. package/dist/runtime/workspaces/language-model.js +26 -0
  39. package/dist/runtime/workspaces/model-endpoint.d.ts +38 -0
  40. package/dist/runtime/workspaces/model-endpoint.js +54 -0
  41. package/dist/runtime/workspaces/provider-settings.d.ts +35 -0
  42. package/dist/runtime/{server → workspaces}/provider-settings.js +0 -31
  43. package/dist/runtime/{server → workspaces}/settings-defaults.d.ts +1 -1
  44. package/dist/runtime/{shared → workspaces}/settings-types.d.ts +4 -4
  45. package/package.json +13 -4
  46. package/src/runtime/ai-sdk/index.ts +88 -47
  47. package/src/runtime/components/ForgeProvidersPanel.vue +2 -2
  48. package/src/runtime/editor/index.ts +158 -63
  49. package/src/runtime/model/selector.ts +31 -0
  50. package/src/runtime/server/credential-store.ts +20 -10
  51. package/src/runtime/server/env-layers.ts +4 -4
  52. package/src/runtime/server/index.ts +7 -8
  53. package/src/runtime/server/provider-discovery.ts +114 -48
  54. package/src/runtime/server/provider-layers.ts +6 -6
  55. package/src/runtime/server/provider-service.ts +2 -2
  56. package/src/runtime/server/provider-write.ts +3 -3
  57. package/src/runtime/server/voice-provider.ts +110 -0
  58. package/src/runtime/shared/data-zone.ts +40 -33
  59. package/src/runtime/shared/harness-providers.ts +7 -7
  60. package/src/runtime/shared/provider-accounts.ts +1 -1
  61. package/src/runtime/shared/provider-registry.ts +82 -314
  62. package/src/runtime/vue/providers-panel-model.ts +5 -5
  63. package/src/runtime/vue/useProvidersPanel.ts +5 -2
  64. package/src/runtime/{server → workspaces}/harness-settings.ts +3 -3
  65. package/src/runtime/workspaces/index.ts +17 -0
  66. package/src/runtime/workspaces/language-model.ts +82 -0
  67. package/src/runtime/workspaces/model-endpoint.ts +100 -0
  68. package/src/runtime/workspaces/provider-settings.ts +96 -0
  69. package/src/runtime/{server → workspaces}/settings-defaults.ts +1 -1
  70. package/src/runtime/{shared → workspaces}/settings-types.ts +4 -4
  71. package/dist/runtime/server/provider-settings.d.ts +0 -78
  72. package/src/runtime/server/provider-settings.ts +0 -192
  73. /package/dist/runtime/{server → workspaces}/harness-settings.js +0 -0
  74. /package/dist/runtime/{server → workspaces}/settings-defaults.js +0 -0
  75. /package/dist/runtime/{shared → workspaces}/settings-types.js +0 -0
@@ -0,0 +1,54 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { join } from "node:path";
3
+ import { isPlainObject } from "../shared/guards.js";
4
+ export const WORKSPACE_MODEL_ENDPOINT_URL_ENV = "SKAILE_MODEL_ENDPOINT_URL";
5
+ export const WORKSPACE_MODEL_ENDPOINT_TOKEN_ENV = "SKAILE_MODEL_ENDPOINT_TOKEN";
6
+ const PREFIX = "Skaile model endpoint configuration";
7
+ function isHttpUrl(value) {
8
+ try {
9
+ const { protocol } = new URL(value);
10
+ return protocol === "http:" || protocol === "https:";
11
+ } catch {
12
+ return false;
13
+ }
14
+ }
15
+ export async function readWorkspaceModelEndpoint(options) {
16
+ const envUrl = options.env[WORKSPACE_MODEL_ENDPOINT_URL_ENV]?.trim() ?? "";
17
+ const envToken = options.env[WORKSPACE_MODEL_ENDPOINT_TOKEN_ENV]?.trim() ?? "";
18
+ if (envUrl || envToken) {
19
+ if (!envUrl || !envToken) {
20
+ throw new Error(
21
+ `${PREFIX}: ${WORKSPACE_MODEL_ENDPOINT_URL_ENV} and ${WORKSPACE_MODEL_ENDPOINT_TOKEN_ENV} must be set together`
22
+ );
23
+ }
24
+ if (!isHttpUrl(envUrl)) {
25
+ throw new Error(`${PREFIX}: ${WORKSPACE_MODEL_ENDPOINT_URL_ENV} is not an http(s) URL`);
26
+ }
27
+ return { url: envUrl, token: envToken };
28
+ }
29
+ if (options.projectDir === void 0) return null;
30
+ const file = join(options.projectDir, ".skaile", "model-endpoint.json");
31
+ let text;
32
+ try {
33
+ text = await readFile(file, "utf8");
34
+ } catch (error) {
35
+ if (error.code === "ENOENT") return null;
36
+ throw new Error(`${PREFIX}: ${file} could not be read`);
37
+ }
38
+ let parsed;
39
+ try {
40
+ parsed = JSON.parse(text);
41
+ } catch {
42
+ throw new Error(`${PREFIX}: ${file} is not valid JSON`);
43
+ }
44
+ const record = isPlainObject(parsed) ? parsed : {};
45
+ const url = typeof record.url === "string" ? record.url.trim() : "";
46
+ const token = typeof record.token === "string" ? record.token.trim() : "";
47
+ if (!url || !token) {
48
+ throw new Error(`${PREFIX}: ${file} needs a non-empty "url" and "token"`);
49
+ }
50
+ if (!isHttpUrl(url)) {
51
+ throw new Error(`${PREFIX}: ${file} "url" is not an http(s) URL`);
52
+ }
53
+ return { url, token };
54
+ }
@@ -0,0 +1,35 @@
1
+ /**
2
+ * The legacy `settings.json` migration of a Workspaces host.
3
+ *
4
+ * A `settings.json` written before the 5.0.0 registry migration holds
5
+ * `provider` + a free-text `model`, and an `apiKeys` map of secrets. On first
6
+ * load the pair collapses into one `"<providerId>/<modelId>"` selector, and the
7
+ * `apiKeys` map is dropped with a warning — a plaintext key is never
8
+ * auto-written anywhere; the user moves it into `.env`.
9
+ *
10
+ * Voice selection over the same settings fields is generic:
11
+ * `resolveVoiceProvider` in `@skaile/forge-settings/server`.
12
+ */
13
+ /** What {@link migrateLegacyProviderSettings} did to one settings file. */
14
+ export interface LegacySettingsMigration {
15
+ /** The file that was inspected. */
16
+ path: string;
17
+ /** Whether the file was rewritten. */
18
+ changed: boolean;
19
+ /** The selector `provider` + `model` collapsed into, when that happened. */
20
+ model?: string;
21
+ /** Everything dropped or left untouched, and why. Secret-free. */
22
+ warnings: string[];
23
+ }
24
+ /**
25
+ * Migrate one legacy `settings.json` in place. Idempotent: a file with no
26
+ * legacy `provider` and no `apiKeys` is left byte-identical.
27
+ *
28
+ * An `apiKeys` map is removed from the file and never stored anywhere else:
29
+ * the registry holds references only, and a plaintext key has no safe
30
+ * automatic destination. Each non-empty entry is reported through `warnings`
31
+ * (without the key) so the user can move it into `.env`.
32
+ *
33
+ * @param settingsPath - Absolute path to a `settings.json`.
34
+ */
35
+ export declare function migrateLegacyProviderSettings(settingsPath: string): LegacySettingsMigration;
@@ -1,9 +1,4 @@
1
1
  import { existsSync, readFileSync, writeFileSync } from "node:fs";
2
- import {
3
- entriesOfKind,
4
- selectVoiceEntryId,
5
- VOICE_OVERRIDE_PARAM
6
- } from "../shared/provider-registry.js";
7
2
  export function migrateLegacyProviderSettings(settingsPath) {
8
3
  const result = {
9
4
  path: settingsPath,
@@ -53,29 +48,3 @@ export function migrateLegacyProviderSettings(settingsPath) {
53
48
  }
54
49
  return result;
55
50
  }
56
- export function resolveVoiceProvider(registry, kind, settings = {}, overrides = {}) {
57
- const candidates = entriesOfKind(registry, kind);
58
- if (candidates.length === 0) return void 0;
59
- const warnings = [];
60
- const wanted = (kind === "stt" ? settings.sttProvider : settings.ttsProvider)?.trim();
61
- const ids = candidates.map(([id]) => id);
62
- const providerId = selectVoiceEntryId(ids, wanted);
63
- const entry = candidates[ids.indexOf(providerId)][1];
64
- if (wanted && wanted !== providerId) {
65
- warnings.push(
66
- `settings name '${wanted}' for ${kind}, which is not an enabled ${kind} entry \u2014 using '${providerId}' instead`
67
- );
68
- }
69
- const fallback = wanted !== providerId;
70
- const settingsOverride = {};
71
- const override = (kind === "stt" ? settings.sttLanguage : settings.ttsVoice)?.trim();
72
- if (override) settingsOverride[VOICE_OVERRIDE_PARAM[kind]] = override;
73
- return {
74
- providerId,
75
- entry,
76
- ...entry.base_url ? { baseUrl: entry.base_url } : {},
77
- params: { ...entry.params, ...settingsOverride, ...overrides },
78
- fallback,
79
- warnings
80
- };
81
- }
@@ -4,7 +4,7 @@
4
4
  * Storage stays with the host: this module reads and writes nothing except the
5
5
  * one `settings.json` {@link migrateLegacyHarnessSettings} is pointed at.
6
6
  */
7
- import type { SkaileSettings } from "../shared/settings-types.js";
7
+ import type { SkaileSettings } from "./settings-types.js";
8
8
  /**
9
9
  * Merged default settings for forge apps.
10
10
  *
@@ -1,9 +1,9 @@
1
1
  /**
2
- * The settings document type forge apps share.
2
+ * The settings document type forge apps on Skaile Workspaces share.
3
3
  *
4
- * A leaf module: it imports types only, so the harness and provider settings
5
- * modules read `SkaileSettings` from here instead of from the broad server
6
- * entry. Browser-safe.
4
+ * It extends the Workspaces `settings.json` type, so it lives in the explicit
5
+ * `@skaile/forge-settings/workspaces` entry: the generic entries never see it.
6
+ * A leaf module: it imports types only.
7
7
  */
8
8
  import type { SkaileSettings as CoreSettings } from "@skaile/workspaces/sdk/core";
9
9
  /** Skill domain enablement configuration for a forge app installation. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@skaile/forge-settings",
3
- "version": "1.4.0",
3
+ "version": "2.0.1",
4
4
  "description": "Provider and settings operations for Skaile forge apps: provider registry layers, credential store, env layers, model discovery, the providers panel, and editor / AI SDK model adapters.",
5
5
  "type": "module",
6
6
  "packageManager": "bun@1.3.9",
@@ -26,6 +26,10 @@
26
26
  "./editor": {
27
27
  "types": "./dist/runtime/editor/index.d.ts",
28
28
  "import": "./dist/runtime/editor/index.js"
29
+ },
30
+ "./workspaces": {
31
+ "types": "./dist/runtime/workspaces/index.d.ts",
32
+ "import": "./dist/runtime/workspaces/index.js"
29
33
  }
30
34
  },
31
35
  "files": [
@@ -55,7 +59,7 @@
55
59
  },
56
60
  "dependencies": {
57
61
  "@nuxt/kit": "^4.0.0",
58
- "@skaile/workspaces": "^9.1.0",
62
+ "@skaile/provider-registry": "^0.1.0",
59
63
  "ofetch": "^1.4.0",
60
64
  "yaml": "^2.9.0"
61
65
  },
@@ -64,7 +68,8 @@
64
68
  "@ai-sdk/openai": "^4.0.0",
65
69
  "@ai-sdk/openai-compatible": "^3.0.0",
66
70
  "@nuxt/ui": "^4.11.1",
67
- "@skaile/forge-editor": "^6.0.0",
71
+ "@skaile/forge-editor": "^6.0.0 || ^7.0.0",
72
+ "@skaile/workspaces": "^7.8.0 || ^8.0.0 || ^9.0.0 || ^10.0.0",
68
73
  "ai": "^7.0.0",
69
74
  "nuxt": "^4.0.0",
70
75
  "vue": "^3.5.0"
@@ -85,6 +90,9 @@
85
90
  "@skaile/forge-editor": {
86
91
  "optional": true
87
92
  },
93
+ "@skaile/workspaces": {
94
+ "optional": true
95
+ },
88
96
  "ai": {
89
97
  "optional": true
90
98
  },
@@ -101,7 +109,8 @@
101
109
  "@nuxt/module-builder": "^1.0.0",
102
110
  "@nuxt/schema": "^4.0.0",
103
111
  "@nuxt/ui": "^4.11.1",
104
- "@skaile/forge-editor": "^6.4.2",
112
+ "@skaile/forge-editor": "^7.0.0",
113
+ "@skaile/workspaces": "^10.0.0",
105
114
  "@types/bun": "^1.2.0",
106
115
  "@types/node": "^22.0.0",
107
116
  "ai": "^7.0.155",
@@ -1,33 +1,43 @@
1
1
  /**
2
2
  * `@skaile/forge-settings/ai-sdk` — the AI SDK language model of a registry
3
- * selector, resolved by `@skaile/workspaces/ai-sdk` and gated by the host's
4
- * credential policy.
3
+ * selector, built by `@skaile/provider-registry/ai-sdk` against one registry
4
+ * snapshot the host passes, with the credential resolved under the host's
5
+ * policy.
5
6
  *
6
- * Workspaces owns endpoint, header and credential wiring for every surface;
7
- * this module only decides *whether* an entry may be resolved and against
8
- * *which* environment. It is not a service locator: the caller passes the
9
- * selector (role-to-selector mapping belongs to the host's settings document)
10
- * and the environment explicitly.
7
+ * The shared constructor owns endpoint, header and SDK wiring for every
8
+ * surface it supports; this module decides *whether* an entry may be resolved
9
+ * and supplies its credential — through the same policy-checked resolver model
10
+ * discovery uses, so a model that lists is a model that calls with the same
11
+ * key. It is not a service locator: the caller passes the selector
12
+ * (role-to-selector mapping belongs to the host's settings document), the
13
+ * registry and the environment explicitly. Nothing is read from disk here;
14
+ * a Skaile Workspaces host that resolves against a project's registry uses
15
+ * `resolveWorkspaceLanguageModel` from `@skaile/forge-settings/workspaces`.
11
16
  *
12
17
  * Provider SDKs (`@ai-sdk/anthropic`, `@ai-sdk/openai`,
13
- * `@ai-sdk/openai-compatible`) are optional peers of Workspaces: install the
14
- * one the selected surface needs.
18
+ * `@ai-sdk/openai-compatible`) are optional peers: install the one the
19
+ * selected surface needs.
15
20
  */
16
21
 
17
- import { type MergedAIProviderRegistry, parseModelRef } from "@skaile/workspaces/core";
18
- import { type ResolvedLanguageModel, resolveLanguageModel } from "@skaile/workspaces/ai-sdk";
19
- import { loadMergedProviderRegistry } from "@skaile/workspaces/sdk/bridge";
22
+ import { type MergedAIProviderRegistry, parseModelRef } from "@skaile/provider-registry";
23
+ import {
24
+ type EntryCredentialResolver,
25
+ type ResolvedLanguageModel,
26
+ resolveLanguageModel,
27
+ } from "@skaile/provider-registry/ai-sdk";
28
+ import { assertEntryAllowed, ModelResolutionError } from "../model/errors";
29
+ import { credentialStoreOptions, selectorProviderId } from "../model/selector";
20
30
  import type { CredentialPolicy } from "../server/credential-policy";
21
- import { ModelResolutionError, assertEntryAllowed } from "../model/errors";
31
+ import { resolveEntryCredential } from "../server/credential-store";
22
32
 
23
- export { ModelResolutionError } from "../model/errors";
24
33
  export type { ModelResolutionCode } from "../model/errors";
34
+ export { ModelResolutionError } from "../model/errors";
25
35
  export type { ResolvedLanguageModel };
26
36
 
27
37
  /** Options for {@link resolveRegistryLanguageModel}. */
28
38
  export interface ResolveRegistryLanguageModelOptions {
29
- /** Workspace whose `.skaile/providers.yml` is the workspace layer. */
30
- projectDir: string;
39
+ /** The one merged registry snapshot the selector resolves against. */
40
+ registry: MergedAIProviderRegistry;
31
41
  /**
32
42
  * The environment `env:` credential references are read from. Required, so a
33
43
  * host never reads the ambient process by accident.
@@ -40,48 +50,91 @@ export interface ResolveRegistryLanguageModelOptions {
40
50
  * before anything is read; a refused one is a `policy_refused` failure.
41
51
  */
42
52
  policy?: CredentialPolicy;
43
- /**
44
- * An already merged (e.g. transform-proxy-routed) registry. When set,
45
- * `projectDir` is not read for providers, the policy check runs on this
46
- * registry, and it is handed to Workspaces as the registry to resolve against.
47
- */
48
- registry?: MergedAIProviderRegistry;
53
+ /** Local encrypted store for `keyring:` references; else `FORGE_CREDENTIAL_STORE` of `env`. */
54
+ path?: string;
55
+ /** 64-char hex key of the local store; else `FORGE_CREDENTIAL_KEY` of `env`. */
56
+ key?: string;
57
+ }
58
+
59
+ /**
60
+ * The credential resolver discovery and invocation share: the policy refuses
61
+ * an entry before any reference is read, then the reference resolves from
62
+ * `env` or the local store. Secret-free errors only.
63
+ */
64
+ function policyCredentialResolver(
65
+ options: Pick<ResolveRegistryLanguageModelOptions, "env" | "policy" | "path" | "key">,
66
+ ): EntryCredentialResolver {
67
+ return (entry, providerId) => {
68
+ assertEntryAllowed(entry, providerId, options.policy);
69
+ const resolved = resolveEntryCredential(entry, credentialStoreOptions(options.env, options));
70
+ return resolved.error && !resolved.value
71
+ ? { error: resolved.error }
72
+ : { ...(resolved.value !== undefined ? { value: resolved.value } : {}) };
73
+ };
49
74
  }
50
75
 
51
76
  /**
52
77
  * The AI SDK model for `selector` (`<providerId>/<modelId>`, a bare model id or
53
- * `@role`), resolved by Workspaces against the registry on disk, or the given
54
- * `registry`.
78
+ * `@role`), resolved against `options.registry` only.
55
79
  *
56
- * @throws {ModelResolutionError} `registry_invalid` (only when read from disk) ·
57
- * `unknown_selector` · `policy_refused` · `unresolved` (credential, endpoint or
58
- * provider SDK; the message is fixed and never carries Workspaces' text, which
59
- * stays on `cause`).
80
+ * Strict: a selector naming a provider that is missing from the registry's
81
+ * enabled `llm` entries (and that no entry maps as a model id), disabled, or an
82
+ * unassigned `@role` fails — nothing is rerouted to another endpoint and
83
+ * nothing is sent. A bare model id without `/` resolves as the registry orders it.
84
+ *
85
+ * @throws {ModelResolutionError} `unknown_selector` (no enabled entry serves the
86
+ * selector, or the named entry is disabled / not an `llm` entry) ·
87
+ * `policy_refused` · `unresolved` (credential, endpoint or provider SDK; the
88
+ * message is fixed, the underlying error stays on `cause`).
60
89
  */
61
90
  export async function resolveRegistryLanguageModel(
62
91
  selector: string,
63
92
  options: ResolveRegistryLanguageModelOptions,
64
93
  ): Promise<ResolvedLanguageModel> {
65
- const merged = options.registry ?? loadRegistry(options.projectDir);
94
+ const merged = options.registry;
95
+
96
+ const head = selectorProviderId(selector);
97
+ const named = head === undefined ? undefined : merged.providers[head];
98
+ if (
99
+ head !== undefined &&
100
+ named !== undefined &&
101
+ (named.kind !== "llm" || named.enabled === false)
102
+ ) {
103
+ throw new ModelResolutionError(
104
+ "unknown_selector",
105
+ `provider '${head}' is ${named.kind !== "llm" ? "not an llm entry" : "disabled"}`,
106
+ { providerId: head },
107
+ );
108
+ }
66
109
 
67
110
  const ref = parseModelRef(selector, merged, options.roles);
68
- if (!ref) {
111
+ // `<head>/<model>` that no entry is named by or maps: the general resolver would
112
+ // hand it to the first entry in registry order, i.e. send it (and that entry's
113
+ // credential) to an endpoint the caller did not name.
114
+ if (!ref || (head !== undefined && named === undefined && ref.fallback === true)) {
69
115
  throw new ModelResolutionError("unknown_selector", `no provider serves '${selector}'`);
70
116
  }
117
+ if (ref.role !== undefined && !options.roles?.[ref.role]) {
118
+ throw new ModelResolutionError(
119
+ "unknown_selector",
120
+ `role '@${ref.role}' is not assigned to a model`,
121
+ );
122
+ }
71
123
  const entry = merged.providers[ref.providerId];
72
124
  if (entry) assertEntryAllowed(entry, ref.providerId, options.policy);
73
125
 
74
126
  try {
75
127
  return await resolveLanguageModel(selector, {
76
- projectDir: options.projectDir,
128
+ registry: merged,
77
129
  env: options.env,
78
130
  ...(options.roles ? { roles: options.roles } : {}),
79
- ...(options.registry ? { registry: options.registry } : {}),
131
+ resolveCredential: policyCredentialResolver(options),
80
132
  });
81
133
  } catch (cause) {
82
- // Workspaces' message is not ours to vouch for: it can echo a resolved
83
- // credential or an endpoint with a key in its query. The message is fixed;
84
- // the original error stays on `cause` for a host that logs it server-side.
134
+ if (cause instanceof ModelResolutionError) throw cause;
135
+ // The constructor's message is not ours to vouch for: an SDK error can echo
136
+ // an endpoint with a key in its query. The message is fixed; the original
137
+ // error stays on `cause` for a host that logs it server-side.
85
138
  throw new ModelResolutionError(
86
139
  "unresolved",
87
140
  `provider '${ref.providerId}': credential, endpoint or provider SDK could not be resolved`,
@@ -89,15 +142,3 @@ export async function resolveRegistryLanguageModel(
89
142
  );
90
143
  }
91
144
  }
92
-
93
- /** The merged registry of `projectDir`; `registry_invalid` on any error diagnostic. */
94
- function loadRegistry(projectDir: string): MergedAIProviderRegistry {
95
- const { merged, diagnostics } = loadMergedProviderRegistry({ projectDir });
96
- const errors = diagnostics
97
- .filter((d) => d.severity === "error")
98
- .map((d) => (d.path ? `${d.path}: ${d.message}` : d.message));
99
- if (errors.length > 0) {
100
- throw new ModelResolutionError("registry_invalid", `providers registry: ${errors.join("; ")}`);
101
- }
102
- return merged;
103
- }
@@ -709,7 +709,7 @@
709
709
 
710
710
  <script setup lang="ts">
711
711
  import { computed } from "vue";
712
- import type { DataZoneRule } from "../shared/provider-registry";
712
+ import type { AIProviderDataZoneRule } from "../shared/provider-registry";
713
713
  import type { ProvidersPanelClient } from "../vue/providers-client";
714
714
  import {
715
715
  AZURE_ACCOUNT,
@@ -754,7 +754,7 @@ const props = withDefaults(
754
754
  * The host's data-zone rule: rows outside it show a muted note naming the
755
755
  * rule (`provider-blocked-<id>`). Absent or `any`: none.
756
756
  */
757
- dataZoneRule?: DataZoneRule;
757
+ dataZoneRule?: AIProviderDataZoneRule;
758
758
  }>(),
759
759
  // Without a default Vue casts an absent boolean to `false`; the panel tells absent from `false`.
760
760
  { environmentEnabled: undefined, note: undefined, expert: false },