@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.
- package/CHANGELOG.md +31 -0
- package/README.md +34 -11
- package/dist/module.json +1 -1
- package/dist/runtime/ai-sdk/index.d.ts +33 -26
- package/dist/runtime/ai-sdk/index.js +34 -16
- package/dist/runtime/components/ForgeProvidersPanel.d.vue.ts +2 -2
- package/dist/runtime/components/ForgeProvidersPanel.vue.d.ts +2 -2
- package/dist/runtime/editor/index.d.ts +55 -16
- package/dist/runtime/editor/index.js +82 -40
- package/dist/runtime/model/selector.d.ts +16 -0
- package/dist/runtime/model/selector.js +12 -0
- package/dist/runtime/server/credential-store.d.ts +16 -4
- package/dist/runtime/server/credential-store.js +3 -4
- package/dist/runtime/server/env-layers.d.ts +1 -1
- package/dist/runtime/server/env-layers.js +3 -3
- package/dist/runtime/server/index.d.ts +7 -8
- package/dist/runtime/server/index.js +1 -3
- package/dist/runtime/server/provider-discovery.d.ts +28 -8
- package/dist/runtime/server/provider-discovery.js +57 -33
- package/dist/runtime/server/provider-layers.d.ts +1 -1
- package/dist/runtime/server/provider-layers.js +3 -5
- package/dist/runtime/server/provider-service.d.ts +2 -2
- package/dist/runtime/server/provider-write.d.ts +1 -1
- package/dist/runtime/server/provider-write.js +3 -3
- package/dist/runtime/server/voice-provider.d.ts +61 -0
- package/dist/runtime/server/voice-provider.js +31 -0
- package/dist/runtime/shared/data-zone.d.ts +28 -26
- package/dist/runtime/shared/data-zone.js +4 -4
- package/dist/runtime/shared/harness-providers.d.ts +7 -7
- package/dist/runtime/shared/provider-accounts.d.ts +1 -1
- package/dist/runtime/shared/provider-registry.d.ts +19 -220
- package/dist/runtime/shared/provider-registry.js +25 -81
- package/dist/runtime/vue/providers-panel-model.d.ts +4 -4
- package/dist/runtime/{server → workspaces}/harness-settings.d.ts +3 -3
- package/dist/runtime/workspaces/index.d.ts +16 -0
- package/dist/runtime/workspaces/index.js +5 -0
- package/dist/runtime/workspaces/language-model.d.ts +45 -0
- package/dist/runtime/workspaces/language-model.js +26 -0
- package/dist/runtime/workspaces/model-endpoint.d.ts +38 -0
- package/dist/runtime/workspaces/model-endpoint.js +54 -0
- package/dist/runtime/workspaces/provider-settings.d.ts +35 -0
- package/dist/runtime/{server → workspaces}/provider-settings.js +0 -31
- package/dist/runtime/{server → workspaces}/settings-defaults.d.ts +1 -1
- package/dist/runtime/{shared → workspaces}/settings-types.d.ts +4 -4
- package/package.json +13 -4
- package/src/runtime/ai-sdk/index.ts +88 -47
- package/src/runtime/components/ForgeProvidersPanel.vue +2 -2
- package/src/runtime/editor/index.ts +158 -63
- package/src/runtime/model/selector.ts +31 -0
- package/src/runtime/server/credential-store.ts +20 -10
- package/src/runtime/server/env-layers.ts +4 -4
- package/src/runtime/server/index.ts +7 -8
- package/src/runtime/server/provider-discovery.ts +114 -48
- package/src/runtime/server/provider-layers.ts +6 -6
- package/src/runtime/server/provider-service.ts +2 -2
- package/src/runtime/server/provider-write.ts +3 -3
- package/src/runtime/server/voice-provider.ts +110 -0
- package/src/runtime/shared/data-zone.ts +40 -33
- package/src/runtime/shared/harness-providers.ts +7 -7
- package/src/runtime/shared/provider-accounts.ts +1 -1
- package/src/runtime/shared/provider-registry.ts +82 -314
- package/src/runtime/vue/providers-panel-model.ts +5 -5
- package/src/runtime/vue/useProvidersPanel.ts +5 -2
- package/src/runtime/{server → workspaces}/harness-settings.ts +3 -3
- package/src/runtime/workspaces/index.ts +17 -0
- package/src/runtime/workspaces/language-model.ts +82 -0
- package/src/runtime/workspaces/model-endpoint.ts +100 -0
- package/src/runtime/workspaces/provider-settings.ts +96 -0
- package/src/runtime/{server → workspaces}/settings-defaults.ts +1 -1
- package/src/runtime/{shared → workspaces}/settings-types.ts +4 -4
- package/dist/runtime/server/provider-settings.d.ts +0 -78
- package/src/runtime/server/provider-settings.ts +0 -192
- /package/dist/runtime/{server → workspaces}/harness-settings.js +0 -0
- /package/dist/runtime/{server → workspaces}/settings-defaults.js +0 -0
- /package/dist/runtime/{shared → workspaces}/settings-types.js +0 -0
|
@@ -1,242 +1,74 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The AI provider registry vocabulary, as forge-settings speaks it.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* these types and vice versa.
|
|
4
|
+
* The schema is `@skaile/provider-registry`'s: every record type, the
|
|
5
|
+
* enumerations and the credential-reference grammar below are re-exported from
|
|
6
|
+
* there, so an entry this package reads is the same type the runtime resolves
|
|
7
|
+
* and there is one validator, not a mirror. What this module adds are Forge's
|
|
8
|
+
* view helpers: reading either registry shape in registry order, filtering by
|
|
9
|
+
* kind, voice selection and the credential editor's wording.
|
|
11
10
|
*
|
|
12
|
-
* Browser-safe on purpose — no `node:` imports
|
|
13
|
-
* registry
|
|
11
|
+
* Browser-safe on purpose — no `node:` imports, and only the browser-safe root
|
|
12
|
+
* of `@skaile/provider-registry` — so the settings modal renders registry
|
|
13
|
+
* entries with the same types the server resolves them with.
|
|
14
14
|
*
|
|
15
15
|
* Vocabulary: these are **AI provider** records. They are unrelated to the
|
|
16
16
|
* platform's connector `ProviderType`/`ProviderLink` (git, SharePoint, Drive).
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
export type
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
export
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
/** Union of {@link AI_PROVIDER_CREDENTIAL_KINDS}. */
|
|
73
|
-
export type AIProviderCredentialKind = (typeof AI_PROVIDER_CREDENTIAL_KINDS)[number];
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* Where an endpoint processes data: `eu` inside the EU, `eu_option` only once
|
|
77
|
-
* the account's EU-processing setting is on, `transfer` outside the EU,
|
|
78
|
-
* `local` on the operator's own machine or network. Upstream
|
|
79
|
-
* `AI_PROVIDER_DATA_ZONES` (`@skaile/workspaces` 9.1).
|
|
80
|
-
*/
|
|
81
|
-
export const DATA_ZONES = ["eu", "eu_option", "transfer", "local"] as const;
|
|
82
|
-
|
|
83
|
-
/** Union of {@link DATA_ZONES}. */
|
|
84
|
-
export type DataZone = (typeof DATA_ZONES)[number];
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* Which data zones a host allows: `any` all, `eu_options` EU, local and
|
|
88
|
-
* `eu_option` entries with `eu_processing: true`, `eu` only EU and local.
|
|
89
|
-
* Upstream `AI_PROVIDER_DATA_ZONE_RULES` (`@skaile/workspaces` 9.1).
|
|
90
|
-
*/
|
|
91
|
-
export const DATA_ZONE_RULES = ["any", "eu_options", "eu"] as const;
|
|
92
|
-
|
|
93
|
-
/** Union of {@link DATA_ZONE_RULES}. */
|
|
94
|
-
export type DataZoneRule = (typeof DATA_ZONE_RULES)[number];
|
|
95
|
-
|
|
96
|
-
// ── Record ─────────────────────────────────────────────────────────────────
|
|
97
|
-
|
|
98
|
-
/** `transport: bedrock` routing parameters. */
|
|
99
|
-
export interface AIProviderBedrockParams {
|
|
100
|
-
/** AWS region, e.g. `us-east-1`. */
|
|
101
|
-
region: string;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
/** `transport: vertex` routing parameters. */
|
|
105
|
-
export interface AIProviderVertexParams {
|
|
106
|
-
/** GCP project id. */
|
|
107
|
-
project: string;
|
|
108
|
-
/** Vertex location, e.g. `europe-west4`. */
|
|
109
|
-
location: string;
|
|
110
|
-
/** Model publisher; defaults to `google`. */
|
|
111
|
-
publisher?: string;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
/** `transport: foundry` routing parameters. */
|
|
115
|
-
export interface AIProviderFoundryParams {
|
|
116
|
-
/** Azure AI Foundry resource name. */
|
|
117
|
-
resource: string;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
/** `transport: azure-openai` routing parameters. */
|
|
121
|
-
export interface AIProviderAzureOpenAIParams {
|
|
122
|
-
/** Azure OpenAI resource name. */
|
|
123
|
-
resource: string;
|
|
124
|
-
/** Deployment the request is routed to. */
|
|
125
|
-
deployment: string;
|
|
126
|
-
/** `api-version` query value. */
|
|
127
|
-
api_version: string;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
/** Voice parameters, typed per preset: Deepgram `{model, language}`, ElevenLabs `{voice_id, output_format}`. */
|
|
131
|
-
export type AIProviderVoiceParams = Record<string, string | number | boolean>;
|
|
132
|
-
|
|
133
|
-
/** The `transport` → `params` discriminated union. */
|
|
134
|
-
export type AIProviderTransportSlice =
|
|
135
|
-
| { transport?: "direct"; params?: Record<string, never> }
|
|
136
|
-
| { transport: "bedrock"; params: AIProviderBedrockParams }
|
|
137
|
-
| { transport: "vertex"; params: AIProviderVertexParams }
|
|
138
|
-
| { transport: "foundry"; params: AIProviderFoundryParams }
|
|
139
|
-
| { transport: "azure-openai"; params: AIProviderAzureOpenAIParams };
|
|
140
|
-
|
|
141
|
-
/** Reference to a secret — never the secret itself. See {@link parseCredentialRef}. */
|
|
142
|
-
export interface AIProviderCredential {
|
|
143
|
-
kind: AIProviderCredentialKind;
|
|
144
|
-
/**
|
|
145
|
-
* `env:NAME` · `cmd:<shell>` · `platform:<configId>` · `keyring:<service>/<account>`.
|
|
146
|
-
* A literal secret here is a validation error, not a warning.
|
|
147
|
-
*/
|
|
148
|
-
ref?: string;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
/** Per-model capability facts, keyed by the logical (`model_map`) id. */
|
|
152
|
-
export interface AIProviderCapabilities {
|
|
153
|
-
context_window?: number;
|
|
154
|
-
tools?: boolean;
|
|
155
|
-
images?: boolean;
|
|
156
|
-
thinking_max?: number;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
/** omp-compatible discovery descriptor. */
|
|
160
|
-
export interface AIProviderDiscovery {
|
|
161
|
-
type: string;
|
|
162
|
-
/** Path appended to `base_url` when the surface default does not apply. */
|
|
163
|
-
path?: string;
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
/** Fields every entry carries, whatever its `kind`. */
|
|
167
|
-
export interface AIProviderEntryCommon {
|
|
168
|
-
/** Display name. Never load-bearing. */
|
|
169
|
-
label?: string;
|
|
170
|
-
/** Catalog preset this entry inherits from. */
|
|
171
|
-
preset?: string;
|
|
172
|
-
/** Literal endpoint; wins over the preset value and the transport derivation. */
|
|
173
|
-
base_url?: string;
|
|
174
|
-
/** Static, non-secret request headers. */
|
|
175
|
-
headers?: Record<string, string>;
|
|
176
|
-
credential?: AIProviderCredential;
|
|
177
|
-
/** Default `true`; read it through {@link isEntryEnabled}. */
|
|
178
|
-
enabled?: boolean;
|
|
179
|
-
/** Set by the control plane: lower layers cannot override this entry. */
|
|
180
|
-
locked?: boolean;
|
|
181
|
-
/** Where the endpoint processes data; overrides the preset's zone. Read it through `entryDataZone`. */
|
|
182
|
-
data_zone?: DataZone;
|
|
183
|
-
/** The account's EU-processing setting is on; only an `eu_option` entry reads it. */
|
|
184
|
-
eu_processing?: boolean;
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
/** An `llm`/`embed` entry — the full record. */
|
|
188
|
-
export type AIProviderLlmEntry = AIProviderEntryCommon &
|
|
189
|
-
AIProviderTransportSlice & {
|
|
190
|
-
kind: "llm" | "embed";
|
|
191
|
-
/** Required unless a `preset` supplies it. */
|
|
192
|
-
surface?: AIProviderSurface;
|
|
193
|
-
/** Azure's `api-version` lives here. */
|
|
194
|
-
query_params?: Record<string, string>;
|
|
195
|
-
/** Logical id → provider-side id. */
|
|
196
|
-
model_map?: Record<string, string>;
|
|
197
|
-
/** Keyed by the logical id, i.e. the `model_map` key. */
|
|
198
|
-
capabilities?: Record<string, AIProviderCapabilities>;
|
|
199
|
-
discovery?: AIProviderDiscovery;
|
|
200
|
-
/**
|
|
201
|
-
* The models this entry may be used with; absent = every model, `[]` =
|
|
202
|
-
* none. Logical ids of `model_map`, or provider-side ids (what a
|
|
203
|
-
* `discovery` entry lists). Read it through {@link isModelAllowed}.
|
|
204
|
-
*/
|
|
205
|
-
allowed_models?: string[];
|
|
206
|
-
};
|
|
207
|
-
|
|
208
|
-
/**
|
|
209
|
-
* An `stt`/`tts` entry — the reduced record. `surface`, `transport`,
|
|
210
|
-
* `model_map`, `capabilities` and `discovery` are `llm`-only: a TTS voice is
|
|
211
|
-
* not a wire protocol. The voice defaults forge apps used to hardcode at their
|
|
212
|
-
* call sites (Deepgram model/language, ElevenLabs voice id/output format) live
|
|
213
|
-
* in `params`.
|
|
214
|
-
*/
|
|
215
|
-
export type AIProviderVoiceEntry = AIProviderEntryCommon & {
|
|
216
|
-
kind: AIProviderVoiceKind;
|
|
217
|
-
params?: AIProviderVoiceParams;
|
|
218
|
-
};
|
|
219
|
-
|
|
220
|
-
/** One registry entry. */
|
|
221
|
-
export type AIProviderEntry = AIProviderLlmEntry | AIProviderVoiceEntry;
|
|
222
|
-
|
|
223
|
-
/** Entry id → entry. Iteration order is registry order. */
|
|
224
|
-
export type AIProviderRegistry = Record<string, AIProviderEntry>;
|
|
225
|
-
|
|
226
|
-
/** Configuration layers, lowest precedence first. */
|
|
227
|
-
export type AIProviderLayerId = "catalog" | "user" | "workspace" | "runtime" | "session";
|
|
228
|
-
|
|
229
|
-
/** The merged, layered view the runtime hands to a session. */
|
|
230
|
-
export interface MergedAIProviderRegistry {
|
|
231
|
-
version: 1;
|
|
232
|
-
providers: AIProviderRegistry;
|
|
233
|
-
/** Registry order: highest-precedence layer first, document order within a layer. */
|
|
234
|
-
order: string[];
|
|
235
|
-
/** Highest-precedence layer that contributed to each entry. */
|
|
236
|
-
origin: Record<string, AIProviderLayerId>;
|
|
237
|
-
/** Entries frozen by `locked: true`. */
|
|
238
|
-
locked: string[];
|
|
239
|
-
}
|
|
19
|
+
import type {
|
|
20
|
+
AIProviderEntry,
|
|
21
|
+
AIProviderKind,
|
|
22
|
+
AIProviderLayerId,
|
|
23
|
+
AIProviderLlmEntry,
|
|
24
|
+
AIProviderRegistry,
|
|
25
|
+
AIProviderVoiceEntry,
|
|
26
|
+
AIProviderVoiceKind,
|
|
27
|
+
MergedAIProviderRegistry,
|
|
28
|
+
} from "@skaile/provider-registry";
|
|
29
|
+
import { isEntryEnabled, parseCredentialRef } from "@skaile/provider-registry";
|
|
30
|
+
|
|
31
|
+
export type {
|
|
32
|
+
AIProviderAzureOpenAIParams,
|
|
33
|
+
AIProviderBedrockParams,
|
|
34
|
+
AIProviderCapabilities,
|
|
35
|
+
AIProviderCredential,
|
|
36
|
+
AIProviderCredentialKind,
|
|
37
|
+
AIProviderCredentialRef,
|
|
38
|
+
AIProviderDataZone,
|
|
39
|
+
AIProviderDataZoneRule,
|
|
40
|
+
AIProviderDiscovery,
|
|
41
|
+
AIProviderEntry,
|
|
42
|
+
AIProviderEntryCommon,
|
|
43
|
+
AIProviderFoundryParams,
|
|
44
|
+
AIProviderKind,
|
|
45
|
+
AIProviderLayerId,
|
|
46
|
+
AIProviderLlmEntry,
|
|
47
|
+
AIProviderModelAllowListFields,
|
|
48
|
+
AIProviderRegistry,
|
|
49
|
+
AIProviderSurface,
|
|
50
|
+
AIProviderTransport,
|
|
51
|
+
AIProviderTransportSlice,
|
|
52
|
+
AIProviderVertexParams,
|
|
53
|
+
AIProviderVoiceEntry,
|
|
54
|
+
AIProviderVoiceKind,
|
|
55
|
+
AIProviderVoiceParams,
|
|
56
|
+
MergedAIProviderRegistry,
|
|
57
|
+
} from "@skaile/provider-registry";
|
|
58
|
+
export {
|
|
59
|
+
AI_PROVIDER_CREDENTIAL_KINDS,
|
|
60
|
+
AI_PROVIDER_DATA_ZONE_RULES,
|
|
61
|
+
AI_PROVIDER_DATA_ZONES,
|
|
62
|
+
AI_PROVIDER_KINDS,
|
|
63
|
+
AI_PROVIDER_SURFACES,
|
|
64
|
+
AI_PROVIDER_TRANSPORTS,
|
|
65
|
+
AI_PROVIDER_VOICE_KINDS,
|
|
66
|
+
allowedModelIds,
|
|
67
|
+
formatCredentialRef,
|
|
68
|
+
isEntryEnabled,
|
|
69
|
+
isModelAllowed,
|
|
70
|
+
parseCredentialRef,
|
|
71
|
+
} from "@skaile/provider-registry";
|
|
240
72
|
|
|
241
73
|
/** Either shape a consumer may be handed. */
|
|
242
74
|
export type AIProviderRegistryView = AIProviderRegistry | MergedAIProviderRegistry;
|
|
@@ -255,9 +87,25 @@ export function isMergedRegistry(
|
|
|
255
87
|
return Array.isArray((registry as MergedAIProviderRegistry).order);
|
|
256
88
|
}
|
|
257
89
|
|
|
258
|
-
/**
|
|
259
|
-
|
|
260
|
-
|
|
90
|
+
/**
|
|
91
|
+
* The layered shape of a view. A merged registry is returned as is; a bare
|
|
92
|
+
* entry map becomes one layer in its own iteration order, reported as the
|
|
93
|
+
* `runtime` layer (the host handed it over in memory) with nothing locked.
|
|
94
|
+
* The model constructor resolves against this shape only.
|
|
95
|
+
*/
|
|
96
|
+
export function asMergedRegistry(
|
|
97
|
+
registry: AIProviderRegistryView,
|
|
98
|
+
layer: AIProviderLayerId = "runtime",
|
|
99
|
+
): MergedAIProviderRegistry {
|
|
100
|
+
if (isMergedRegistry(registry)) return registry;
|
|
101
|
+
const order = Object.keys(registry);
|
|
102
|
+
return {
|
|
103
|
+
version: 1,
|
|
104
|
+
providers: registry,
|
|
105
|
+
order,
|
|
106
|
+
origin: Object.fromEntries(order.map((id) => [id, layer])),
|
|
107
|
+
locked: [],
|
|
108
|
+
};
|
|
261
109
|
}
|
|
262
110
|
|
|
263
111
|
/** Entries in registry order, as `[id, entry]` pairs. Both registry shapes accepted. */
|
|
@@ -293,33 +141,6 @@ export function isVoiceEntry(entry: AIProviderEntry): entry is AIProviderVoiceEn
|
|
|
293
141
|
return entry.kind === "stt" || entry.kind === "tts";
|
|
294
142
|
}
|
|
295
143
|
|
|
296
|
-
/** A thinking-level suffix omp takes on a selector (`sonnet:high`); not part of the model id. */
|
|
297
|
-
const THINKING_SUFFIX = /:(off|minimal|low|medium|high|xhigh)$/i;
|
|
298
|
-
|
|
299
|
-
/**
|
|
300
|
-
* Whether an entry's `allowed_models` lets it be used with `modelId`. Absent
|
|
301
|
-
* allows every model, `[]` none. `modelId` and the list may each hold the
|
|
302
|
-
* logical id (a `model_map` key) or the provider-side id; a trailing
|
|
303
|
-
* thinking-level suffix (`:high`) is ignored. Mirrors `isModelAllowed` of
|
|
304
|
-
* `@skaile/workspaces/core` 9.1, which enforces it at model resolution.
|
|
305
|
-
*/
|
|
306
|
-
export function isModelAllowed(
|
|
307
|
-
entry: { allowed_models?: readonly string[]; model_map?: Record<string, string> },
|
|
308
|
-
modelId: string,
|
|
309
|
-
): boolean {
|
|
310
|
-
const list = entry.allowed_models;
|
|
311
|
-
if (list === undefined) return true;
|
|
312
|
-
const matches = (id: string): boolean => {
|
|
313
|
-
if (list.includes(id)) return true;
|
|
314
|
-
const mapped = entry.model_map?.[id];
|
|
315
|
-
if (mapped !== undefined && list.includes(mapped)) return true;
|
|
316
|
-
return list.some((allowed) => entry.model_map?.[allowed] === id);
|
|
317
|
-
};
|
|
318
|
-
if (matches(modelId)) return true;
|
|
319
|
-
const base = modelId.replace(THINKING_SUFFIX, "");
|
|
320
|
-
return base !== modelId && matches(base);
|
|
321
|
-
}
|
|
322
|
-
|
|
323
144
|
// ── Voice selection ────────────────────────────────────────────────────────
|
|
324
145
|
|
|
325
146
|
/**
|
|
@@ -352,62 +173,9 @@ export function selectVoiceEntryId(ids: readonly string[], wanted?: string): str
|
|
|
352
173
|
|
|
353
174
|
// ── Credential references ──────────────────────────────────────────────────
|
|
354
175
|
|
|
355
|
-
/**
|
|
356
|
-
const ENV_NAME = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
357
|
-
|
|
358
|
-
/** The four reference forms. */
|
|
176
|
+
/** The four reference forms, as a credential editor's refusal distinguishes them. */
|
|
359
177
|
const CREDENTIAL_REF = /^(env|cmd|platform|keyring):([\s\S]+)$/;
|
|
360
178
|
|
|
361
|
-
/** A parsed `credential.ref`. */
|
|
362
|
-
export type AIProviderCredentialRef =
|
|
363
|
-
| { form: "env"; name: string }
|
|
364
|
-
| { form: "cmd"; command: string }
|
|
365
|
-
| { form: "platform"; configId: string }
|
|
366
|
-
| { form: "keyring"; service: string; account: string };
|
|
367
|
-
|
|
368
|
-
/**
|
|
369
|
-
* Parse a `credential.ref`.
|
|
370
|
-
*
|
|
371
|
-
* Returns `undefined` for anything that is not one of the four reference forms
|
|
372
|
-
* — which, in a registry entry or a credential editor, means someone typed a
|
|
373
|
-
* literal secret.
|
|
374
|
-
*/
|
|
375
|
-
export function parseCredentialRef(ref: string): AIProviderCredentialRef | undefined {
|
|
376
|
-
const match = CREDENTIAL_REF.exec(ref.trim());
|
|
377
|
-
if (!match) return undefined;
|
|
378
|
-
const body = match[2].trim();
|
|
379
|
-
if (body === "") return undefined;
|
|
380
|
-
switch (match[1]) {
|
|
381
|
-
case "env":
|
|
382
|
-
return ENV_NAME.test(body) ? { form: "env", name: body } : undefined;
|
|
383
|
-
case "cmd":
|
|
384
|
-
return { form: "cmd", command: body };
|
|
385
|
-
case "platform":
|
|
386
|
-
return { form: "platform", configId: body };
|
|
387
|
-
case "keyring": {
|
|
388
|
-
const slash = body.indexOf("/");
|
|
389
|
-
if (slash <= 0 || slash === body.length - 1) return undefined;
|
|
390
|
-
return { form: "keyring", service: body.slice(0, slash), account: body.slice(slash + 1) };
|
|
391
|
-
}
|
|
392
|
-
default:
|
|
393
|
-
return undefined;
|
|
394
|
-
}
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
/** Render a parsed reference back to its `<form>:<body>` string. */
|
|
398
|
-
export function formatCredentialRef(ref: AIProviderCredentialRef): string {
|
|
399
|
-
switch (ref.form) {
|
|
400
|
-
case "env":
|
|
401
|
-
return `env:${ref.name}`;
|
|
402
|
-
case "cmd":
|
|
403
|
-
return `cmd:${ref.command}`;
|
|
404
|
-
case "platform":
|
|
405
|
-
return `platform:${ref.configId}`;
|
|
406
|
-
case "keyring":
|
|
407
|
-
return `keyring:${ref.service}/${ref.account}`;
|
|
408
|
-
}
|
|
409
|
-
}
|
|
410
|
-
|
|
411
179
|
/** The reference forms, spelled the way a credential editor offers them. */
|
|
412
180
|
export const CREDENTIAL_REF_FORMS = [
|
|
413
181
|
"env:NAME",
|
|
@@ -13,11 +13,11 @@ import type {
|
|
|
13
13
|
} from "../shared/provider-accounts";
|
|
14
14
|
import type {
|
|
15
15
|
AIProviderCredentialKind,
|
|
16
|
+
AIProviderDataZone,
|
|
17
|
+
AIProviderDataZoneRule,
|
|
16
18
|
AIProviderKind,
|
|
17
19
|
AIProviderSurface,
|
|
18
20
|
AIProviderTransport,
|
|
19
|
-
DataZone,
|
|
20
|
-
DataZoneRule,
|
|
21
21
|
} from "../shared/provider-registry";
|
|
22
22
|
import type { ProvidersPanelClient } from "./providers-client";
|
|
23
23
|
|
|
@@ -68,7 +68,7 @@ export interface ProvidersPanelProps {
|
|
|
68
68
|
* The host's data-zone rule ("Where may AI process data?"). Rows outside it
|
|
69
69
|
* show a muted note naming the rule. Absent or `any`: no row is marked.
|
|
70
70
|
*/
|
|
71
|
-
dataZoneRule?:
|
|
71
|
+
dataZoneRule?: AIProviderDataZoneRule;
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
/** Emits both variants declare. */
|
|
@@ -107,7 +107,7 @@ export interface ProvidersPanelEntry {
|
|
|
107
107
|
/** The scope's own document's values for the editable fields the dialog does not show. */
|
|
108
108
|
layerFields?: Record<string, unknown>;
|
|
109
109
|
/** Where the entry processes data. Absent (an older host): the preset's zone, else `transfer`. */
|
|
110
|
-
dataZone?:
|
|
110
|
+
dataZone?: AIProviderDataZone;
|
|
111
111
|
/** The entry's `eu_processing` switch. */
|
|
112
112
|
euProcessing?: boolean;
|
|
113
113
|
/** The entry's `allowed_models`; absent = every model, `[]` = none. */
|
|
@@ -181,7 +181,7 @@ export interface ProvidersPanelRow extends ProvidersPanelEntry {
|
|
|
181
181
|
/** "Renew" is offered next to the expiry: the credential can be changed here. */
|
|
182
182
|
canRenew: boolean;
|
|
183
183
|
/** The entry's effective data zone. */
|
|
184
|
-
zone:
|
|
184
|
+
zone: AIProviderDataZone;
|
|
185
185
|
/** The zone chip: EU / EU option / Transfer / Local. */
|
|
186
186
|
zoneLabel: string;
|
|
187
187
|
/** "EU processing is on for this account" is offered: an `eu_option` entry the panel may write. */
|
|
@@ -55,9 +55,9 @@ import {
|
|
|
55
55
|
import {
|
|
56
56
|
AI_PROVIDER_SURFACES,
|
|
57
57
|
type AIProviderCredentialKind,
|
|
58
|
+
type AIProviderDataZone,
|
|
58
59
|
type AIProviderKind,
|
|
59
60
|
type AIProviderSurface,
|
|
60
|
-
type DataZone,
|
|
61
61
|
validateCredentialRef,
|
|
62
62
|
} from "../shared/provider-registry";
|
|
63
63
|
import {
|
|
@@ -466,7 +466,10 @@ export function useProvidersPanel(
|
|
|
466
466
|
}
|
|
467
467
|
|
|
468
468
|
/** The muted note of a row the host's data-zone rule keeps out; `null` when it does not. */
|
|
469
|
-
function blockedNoteOf(
|
|
469
|
+
function blockedNoteOf(
|
|
470
|
+
zone: AIProviderDataZone,
|
|
471
|
+
euProcessing: boolean | undefined,
|
|
472
|
+
): string | null {
|
|
470
473
|
const rule = props.dataZoneRule;
|
|
471
474
|
if (!rule || rule === "any") return null;
|
|
472
475
|
return dataZoneRefusal(rule, zone, euProcessing) ?? null;
|
|
@@ -34,8 +34,8 @@ import {
|
|
|
34
34
|
harnessRoleIds,
|
|
35
35
|
isExperimentalHarness,
|
|
36
36
|
} from "../shared/harnesses";
|
|
37
|
-
import type {
|
|
38
|
-
import type { SkaileSettings } from "
|
|
37
|
+
import type { AIProviderDataZoneRule } from "../shared/provider-registry";
|
|
38
|
+
import type { SkaileSettings } from "./settings-types";
|
|
39
39
|
|
|
40
40
|
/** The slice of {@link SkaileSettings} that selects, configures and locks harnesses. */
|
|
41
41
|
export type HarnessSettings = Pick<
|
|
@@ -126,7 +126,7 @@ export interface HarnessRowProviders {
|
|
|
126
126
|
/** `GET /api/providers` rows (or any {@link HarnessProviderCandidate}s), in registry order. */
|
|
127
127
|
candidates: readonly HarnessProviderCandidate[];
|
|
128
128
|
/** The host's data-zone rule. Absent: `any`. */
|
|
129
|
-
dataZoneRule?:
|
|
129
|
+
dataZoneRule?: AIProviderDataZoneRule;
|
|
130
130
|
}
|
|
131
131
|
|
|
132
132
|
/**
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@skaile/forge-settings/workspaces` — the Skaile Workspaces integration:
|
|
3
|
+
* the Workspaces `settings.json` document (`SkaileSettings`, defaults, legacy
|
|
4
|
+
* migrations), harness settings, project-registry model resolution with the
|
|
5
|
+
* Workspaces keyring location, and the address of a running Workspaces model
|
|
6
|
+
* endpoint.
|
|
7
|
+
*
|
|
8
|
+
* The only entry that needs the optional peer `@skaile/workspaces`; the generic
|
|
9
|
+
* entries (`/server`, `/vue`, `/nuxt`, `/ai-sdk`, `/editor`) never import it.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
export * from "./harness-settings";
|
|
13
|
+
export * from "./language-model";
|
|
14
|
+
export * from "./model-endpoint";
|
|
15
|
+
export * from "./provider-settings";
|
|
16
|
+
export * from "./settings-defaults";
|
|
17
|
+
export type { DomainConfig, SkaileSettings } from "./settings-types";
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Project-oriented model resolution for a Skaile Workspaces host: load the
|
|
3
|
+
* project's merged provider registry once (user + workspace layers + catalog),
|
|
4
|
+
* keep the forge keyring where Workspaces hosts always kept it, and delegate to
|
|
5
|
+
* the generic `resolveRegistryLanguageModel` (`@skaile/forge-settings/ai-sdk`).
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { join } from "node:path";
|
|
9
|
+
import type { MergedAIProviderRegistry } from "@skaile/provider-registry";
|
|
10
|
+
import { loadMergedProviderRegistry } from "@skaile/workspaces/sdk/bridge";
|
|
11
|
+
import { globalSkaileDir } from "@skaile/workspaces/sdk/core";
|
|
12
|
+
import {
|
|
13
|
+
type ResolvedLanguageModel,
|
|
14
|
+
type ResolveRegistryLanguageModelOptions,
|
|
15
|
+
resolveRegistryLanguageModel,
|
|
16
|
+
} from "../ai-sdk";
|
|
17
|
+
import { ModelResolutionError } from "../model/errors";
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* The forge keyring of a Workspaces host: `FORGE_CREDENTIAL_STORE` of `env`
|
|
21
|
+
* when set, else `<skaile home>/forge-credentials.json`, beside the user's
|
|
22
|
+
* `settings.json` (follows `$SKAILE_HOME`). Generic entries never assume this
|
|
23
|
+
* home; a Workspaces host passes it as the store `path` (or pins it into
|
|
24
|
+
* `FORGE_CREDENTIAL_STORE`).
|
|
25
|
+
*/
|
|
26
|
+
export function workspaceCredentialStorePath(
|
|
27
|
+
env?: Readonly<Record<string, string | undefined>>,
|
|
28
|
+
): string {
|
|
29
|
+
return env?.FORGE_CREDENTIAL_STORE || join(globalSkaileDir(), "forge-credentials.json");
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* The merged provider registry of `projectDir` — the user layer, the
|
|
34
|
+
* project's `.skaile/providers.yml` and the catalog presets they name.
|
|
35
|
+
*
|
|
36
|
+
* @throws {ModelResolutionError} `registry_invalid` on any error diagnostic.
|
|
37
|
+
*/
|
|
38
|
+
export function loadWorkspaceProviderRegistry(projectDir: string): MergedAIProviderRegistry {
|
|
39
|
+
const { merged, diagnostics } = loadMergedProviderRegistry({ projectDir });
|
|
40
|
+
const errors = diagnostics
|
|
41
|
+
.filter((d) => d.severity === "error")
|
|
42
|
+
.map((d) => (d.path ? `${d.path}: ${d.message}` : d.message));
|
|
43
|
+
if (errors.length > 0) {
|
|
44
|
+
throw new ModelResolutionError("registry_invalid", `providers registry: ${errors.join("; ")}`);
|
|
45
|
+
}
|
|
46
|
+
return merged;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** Options for {@link resolveWorkspaceLanguageModel}. */
|
|
50
|
+
export interface ResolveWorkspaceLanguageModelOptions
|
|
51
|
+
extends Omit<ResolveRegistryLanguageModelOptions, "registry"> {
|
|
52
|
+
/** Workspace whose `.skaile/providers.yml` is the workspace layer. */
|
|
53
|
+
projectDir: string;
|
|
54
|
+
/**
|
|
55
|
+
* An already merged (e.g. transform-proxy-routed) registry. When set,
|
|
56
|
+
* `projectDir` is not read for providers; the selector, the policy check
|
|
57
|
+
* and the model all resolve against this registry.
|
|
58
|
+
*/
|
|
59
|
+
registry?: MergedAIProviderRegistry;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* The AI SDK model for `selector`, resolved against the registry of
|
|
64
|
+
* `projectDir` (read once) or the given `registry`.
|
|
65
|
+
*
|
|
66
|
+
* `keyring:` references read the store at `path`, else the environment's
|
|
67
|
+
* `FORGE_CREDENTIAL_STORE`, else {@link workspaceCredentialStorePath}.
|
|
68
|
+
*
|
|
69
|
+
* @throws {ModelResolutionError} `registry_invalid` (only when read from disk),
|
|
70
|
+
* plus every failure of `resolveRegistryLanguageModel`.
|
|
71
|
+
*/
|
|
72
|
+
export async function resolveWorkspaceLanguageModel(
|
|
73
|
+
selector: string,
|
|
74
|
+
options: ResolveWorkspaceLanguageModelOptions,
|
|
75
|
+
): Promise<ResolvedLanguageModel> {
|
|
76
|
+
const { projectDir, registry, ...rest } = options;
|
|
77
|
+
return resolveRegistryLanguageModel(selector, {
|
|
78
|
+
...rest,
|
|
79
|
+
registry: registry ?? loadWorkspaceProviderRegistry(projectDir),
|
|
80
|
+
path: options.path ?? workspaceCredentialStorePath(options.env),
|
|
81
|
+
});
|
|
82
|
+
}
|