@skaile/forge-settings 1.3.3 → 2.0.0

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 +40 -0
  2. package/README.md +52 -9
  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 +13 -0
  7. package/dist/runtime/components/ForgeProvidersPanel.vue +461 -78
  8. package/dist/runtime/components/ForgeProvidersPanel.vue.d.ts +13 -0
  9. package/dist/runtime/editor/index.d.ts +55 -16
  10. package/dist/runtime/editor/index.js +82 -40
  11. package/dist/runtime/model/selector.d.ts +16 -0
  12. package/dist/runtime/model/selector.js +12 -0
  13. package/dist/runtime/server/credential-store.d.ts +16 -4
  14. package/dist/runtime/server/credential-store.js +3 -4
  15. package/dist/runtime/server/env-layers.d.ts +1 -1
  16. package/dist/runtime/server/env-layers.js +3 -3
  17. package/dist/runtime/server/index.d.ts +9 -8
  18. package/dist/runtime/server/index.js +3 -3
  19. package/dist/runtime/server/provider-discovery.d.ts +28 -8
  20. package/dist/runtime/server/provider-discovery.js +57 -33
  21. package/dist/runtime/server/provider-layers.d.ts +1 -1
  22. package/dist/runtime/server/provider-layers.js +3 -5
  23. package/dist/runtime/server/provider-service.d.ts +26 -2
  24. package/dist/runtime/server/provider-service.js +16 -1
  25. package/dist/runtime/server/provider-write.d.ts +26 -10
  26. package/dist/runtime/server/provider-write.js +75 -7
  27. package/dist/runtime/server/voice-provider.d.ts +61 -0
  28. package/dist/runtime/server/voice-provider.js +31 -0
  29. package/dist/runtime/shared/data-zone.d.ts +68 -0
  30. package/dist/runtime/shared/data-zone.js +79 -0
  31. package/dist/runtime/shared/harness-providers.d.ts +42 -3
  32. package/dist/runtime/shared/harness-providers.js +31 -8
  33. package/dist/runtime/shared/harnesses.d.ts +7 -0
  34. package/dist/runtime/shared/harnesses.js +18 -0
  35. package/dist/runtime/shared/provider-accounts.d.ts +215 -0
  36. package/dist/runtime/shared/provider-accounts.js +255 -0
  37. package/dist/runtime/shared/provider-registry.d.ts +19 -182
  38. package/dist/runtime/shared/provider-registry.js +25 -65
  39. package/dist/runtime/vue/index.d.ts +2 -0
  40. package/dist/runtime/vue/index.js +2 -0
  41. package/dist/runtime/vue/providers-panel-model.d.ts +98 -5
  42. package/dist/runtime/vue/providers-panel-model.js +13 -1
  43. package/dist/runtime/vue/useProvidersPanel.d.ts +66 -5
  44. package/dist/runtime/vue/useProvidersPanel.js +417 -29
  45. package/dist/runtime/{server → workspaces}/harness-settings.d.ts +51 -13
  46. package/dist/runtime/{server → workspaces}/harness-settings.js +93 -9
  47. package/dist/runtime/workspaces/index.d.ts +16 -0
  48. package/dist/runtime/workspaces/index.js +5 -0
  49. package/dist/runtime/workspaces/language-model.d.ts +45 -0
  50. package/dist/runtime/workspaces/language-model.js +26 -0
  51. package/dist/runtime/workspaces/model-endpoint.d.ts +38 -0
  52. package/dist/runtime/workspaces/model-endpoint.js +54 -0
  53. package/dist/runtime/workspaces/provider-settings.d.ts +35 -0
  54. package/dist/runtime/{server → workspaces}/provider-settings.js +0 -31
  55. package/dist/runtime/{server → workspaces}/settings-defaults.d.ts +1 -1
  56. package/dist/runtime/{shared → workspaces}/settings-types.d.ts +11 -4
  57. package/package.json +11 -2
  58. package/src/runtime/ai-sdk/index.ts +88 -47
  59. package/src/runtime/components/ForgeProvidersPanel.vue +477 -78
  60. package/src/runtime/editor/index.ts +158 -63
  61. package/src/runtime/model/selector.ts +31 -0
  62. package/src/runtime/server/credential-store.ts +20 -10
  63. package/src/runtime/server/env-layers.ts +4 -4
  64. package/src/runtime/server/index.ts +9 -8
  65. package/src/runtime/server/provider-discovery.ts +114 -48
  66. package/src/runtime/server/provider-layers.ts +6 -6
  67. package/src/runtime/server/provider-service.ts +53 -1
  68. package/src/runtime/server/provider-write.ts +132 -18
  69. package/src/runtime/server/voice-provider.ts +110 -0
  70. package/src/runtime/shared/data-zone.ts +179 -0
  71. package/src/runtime/shared/harness-providers.ts +78 -7
  72. package/src/runtime/shared/harnesses.ts +25 -0
  73. package/src/runtime/shared/provider-accounts.ts +526 -0
  74. package/src/runtime/shared/provider-registry.ts +82 -256
  75. package/src/runtime/vue/index.ts +2 -0
  76. package/src/runtime/vue/providers-panel-model.ts +123 -5
  77. package/src/runtime/vue/useProvidersPanel.ts +589 -37
  78. package/src/runtime/{server → workspaces}/harness-settings.ts +166 -22
  79. package/src/runtime/workspaces/index.ts +17 -0
  80. package/src/runtime/workspaces/language-model.ts +82 -0
  81. package/src/runtime/workspaces/model-endpoint.ts +100 -0
  82. package/src/runtime/workspaces/provider-settings.ts +96 -0
  83. package/src/runtime/{server → workspaces}/settings-defaults.ts +1 -1
  84. package/src/runtime/{shared → workspaces}/settings-types.ts +11 -4
  85. package/dist/runtime/server/provider-settings.d.ts +0 -78
  86. package/src/runtime/server/provider-settings.ts +0 -192
  87. /package/dist/runtime/{server → workspaces}/settings-defaults.js +0 -0
  88. /package/dist/runtime/{shared → workspaces}/settings-types.js +0 -0
@@ -1,78 +0,0 @@
1
- /**
2
- * Where a forge app's settings meet the AI provider registry.
3
- *
4
- * Two jobs:
5
- *
6
- * 1. **Migration.** A `settings.json` written before the 5.0.0 registry migration holds
7
- * `provider` + a free-text `model`, and an `apiKeys` map of secrets. On
8
- * first load the pair collapses into one `"<providerId>/<modelId>"`
9
- * selector, and the `apiKeys` map is dropped with a warning — a plaintext
10
- * key is never auto-written anywhere; the user moves it into `.env`.
11
- * 2. **Voice selection.** `sttProvider`/`ttsProvider` are registry ids of
12
- * `kind: stt|tts` entries; the endpoint and the voice parameters live on
13
- * the entry, and `sttLanguage`/`ttsVoice` are per-request overrides of two
14
- * of those parameters. Resolving all of that used to be three apps'
15
- * worth of hardcoded endpoints and voice ids.
16
- */
17
- import type { AIProviderVoiceEntry, AIProviderVoiceKind, AIProviderVoiceParams, AIProviderRegistryView } from "../shared/provider-registry.js";
18
- import type { SkaileSettings } from "../shared/settings-types.js";
19
- /** What {@link migrateLegacyProviderSettings} did to one settings file. */
20
- export interface LegacySettingsMigration {
21
- /** The file that was inspected. */
22
- path: string;
23
- /** Whether the file was rewritten. */
24
- changed: boolean;
25
- /** The selector `provider` + `model` collapsed into, when that happened. */
26
- model?: string;
27
- /** Everything dropped or left untouched, and why. Secret-free. */
28
- warnings: string[];
29
- }
30
- /**
31
- * Migrate one legacy `settings.json` in place. Idempotent: a file with no
32
- * legacy `provider` and no `apiKeys` is left byte-identical.
33
- *
34
- * An `apiKeys` map is removed from the file and never stored anywhere else:
35
- * the registry holds references only, and a plaintext key has no safe
36
- * automatic destination. Each non-empty entry is reported through `warnings`
37
- * (without the key) so the user can move it into `.env`.
38
- *
39
- * @param settingsPath - Absolute path to a `settings.json`.
40
- */
41
- export declare function migrateLegacyProviderSettings(settingsPath: string): LegacySettingsMigration;
42
- /** Settings fields that name a voice entry and override its parameters. */
43
- export type VoiceSettings = Pick<SkaileSettings, "sttProvider" | "ttsProvider" | "sttLanguage" | "ttsVoice">;
44
- /** The `kind: stt|tts` entry a request runs against, with its parameters resolved. */
45
- export interface VoiceProviderSelection {
46
- /** Registry id of the entry. */
47
- providerId: string;
48
- entry: AIProviderVoiceEntry;
49
- /** Endpoint from the entry; absent when the entry inherits its preset's default. */
50
- baseUrl?: string;
51
- /**
52
- * The entry's `params` with overrides applied — Deepgram `{model, language}`,
53
- * ElevenLabs `{voice_id, output_format}`. This is the only place a forge app
54
- * should read voice defaults from.
55
- */
56
- params: AIProviderVoiceParams;
57
- /** True when settings named no usable entry and registry order decided. */
58
- fallback: boolean;
59
- /** Non-fatal notes, e.g. settings naming an entry the registry does not have. */
60
- warnings: string[];
61
- }
62
- /**
63
- * Resolve which registry entry serves speech-to-text or text-to-speech.
64
- *
65
- * Selection is `settings.sttProvider`/`settings.ttsProvider`, falling back to
66
- * the first enabled entry of that kind in registry order when the setting is
67
- * absent or names an entry that is gone — a missing setting must not silence an
68
- * app that has a perfectly good entry configured.
69
- *
70
- * Parameter precedence, lowest first: the entry's `params`, then the settings
71
- * overrides (`sttLanguage` → `language`, `ttsVoice` → `voice_id`), then
72
- * `overrides` from the request. The per-request override is the contract
73
- * forge-concept already honoured and the other apps silently dropped.
74
- *
75
- * @returns The selection, or `undefined` when the registry has no enabled entry
76
- * of that kind.
77
- */
78
- export declare function resolveVoiceProvider(registry: AIProviderRegistryView, kind: AIProviderVoiceKind, settings?: VoiceSettings, overrides?: AIProviderVoiceParams): VoiceProviderSelection | undefined;
@@ -1,192 +0,0 @@
1
- /**
2
- * Where a forge app's settings meet the AI provider registry.
3
- *
4
- * Two jobs:
5
- *
6
- * 1. **Migration.** A `settings.json` written before the 5.0.0 registry migration holds
7
- * `provider` + a free-text `model`, and an `apiKeys` map of secrets. On
8
- * first load the pair collapses into one `"<providerId>/<modelId>"`
9
- * selector, and the `apiKeys` map is dropped with a warning — a plaintext
10
- * key is never auto-written anywhere; the user moves it into `.env`.
11
- * 2. **Voice selection.** `sttProvider`/`ttsProvider` are registry ids of
12
- * `kind: stt|tts` entries; the endpoint and the voice parameters live on
13
- * the entry, and `sttLanguage`/`ttsVoice` are per-request overrides of two
14
- * of those parameters. Resolving all of that used to be three apps'
15
- * worth of hardcoded endpoints and voice ids.
16
- */
17
-
18
- import { existsSync, readFileSync, writeFileSync } from "node:fs";
19
- import type {
20
- AIProviderVoiceEntry,
21
- AIProviderVoiceKind,
22
- AIProviderVoiceParams,
23
- AIProviderRegistryView,
24
- } from "../shared/provider-registry";
25
- import {
26
- entriesOfKind,
27
- selectVoiceEntryId,
28
- VOICE_OVERRIDE_PARAM,
29
- } from "../shared/provider-registry";
30
- import type { SkaileSettings } from "../shared/settings-types";
31
-
32
- /** What {@link migrateLegacyProviderSettings} did to one settings file. */
33
- export interface LegacySettingsMigration {
34
- /** The file that was inspected. */
35
- path: string;
36
- /** Whether the file was rewritten. */
37
- changed: boolean;
38
- /** The selector `provider` + `model` collapsed into, when that happened. */
39
- model?: string;
40
- /** Everything dropped or left untouched, and why. Secret-free. */
41
- warnings: string[];
42
- }
43
-
44
- /**
45
- * Migrate one legacy `settings.json` in place. Idempotent: a file with no
46
- * legacy `provider` and no `apiKeys` is left byte-identical.
47
- *
48
- * An `apiKeys` map is removed from the file and never stored anywhere else:
49
- * the registry holds references only, and a plaintext key has no safe
50
- * automatic destination. Each non-empty entry is reported through `warnings`
51
- * (without the key) so the user can move it into `.env`.
52
- *
53
- * @param settingsPath - Absolute path to a `settings.json`.
54
- */
55
- export function migrateLegacyProviderSettings(settingsPath: string): LegacySettingsMigration {
56
- const result: LegacySettingsMigration = {
57
- path: settingsPath,
58
- changed: false,
59
- warnings: [],
60
- };
61
- if (!existsSync(settingsPath)) return result;
62
-
63
- let settings: Record<string, unknown>;
64
- try {
65
- settings = JSON.parse(readFileSync(settingsPath, "utf8")) as Record<string, unknown>;
66
- } catch (err) {
67
- result.warnings.push(`${settingsPath} is not valid JSON (${(err as Error).message})`);
68
- return result;
69
- }
70
- if (typeof settings !== "object" || settings === null || Array.isArray(settings)) {
71
- result.warnings.push(`${settingsPath} does not contain a settings object`);
72
- return result;
73
- }
74
-
75
- // ── provider + free-text model → one selector ───────────────────────────
76
- const legacyProvider = typeof settings.provider === "string" ? settings.provider.trim() : "";
77
- if (legacyProvider) {
78
- const model = typeof settings.model === "string" ? settings.model.trim() : "";
79
- if (model && !model.includes("/")) {
80
- settings.model = `${legacyProvider}/${model}`;
81
- result.model = settings.model as string;
82
- } else if (!model) {
83
- result.warnings.push(
84
- `provider '${legacyProvider}' had no model to namespace — the session now resolves ` +
85
- "its model through the registry's own order",
86
- );
87
- }
88
- delete settings.provider;
89
- result.changed = true;
90
- }
91
-
92
- // ── apiKeys map → dropped, never stored ─────────────────────────────────
93
- if ("apiKeys" in settings) {
94
- const apiKeys = settings.apiKeys;
95
- const hasSecret =
96
- typeof apiKeys === "object" &&
97
- apiKeys !== null &&
98
- Object.values(apiKeys).some((value) => typeof value === "string" && value !== "");
99
- if (hasSecret) {
100
- result.warnings.push(
101
- "apiKeys found in settings: move each key into .env and reference it from providers.yml (skaile providers add)",
102
- );
103
- }
104
- delete settings.apiKeys;
105
- result.changed = true;
106
- }
107
-
108
- if (result.changed) {
109
- writeFileSync(settingsPath, `${JSON.stringify(settings, null, 2)}\n`, "utf8");
110
- }
111
- return result;
112
- }
113
-
114
- // ── Voice selection ────────────────────────────────────────────────────────
115
-
116
- /** Settings fields that name a voice entry and override its parameters. */
117
- export type VoiceSettings = Pick<
118
- SkaileSettings,
119
- "sttProvider" | "ttsProvider" | "sttLanguage" | "ttsVoice"
120
- >;
121
-
122
- /** The `kind: stt|tts` entry a request runs against, with its parameters resolved. */
123
- export interface VoiceProviderSelection {
124
- /** Registry id of the entry. */
125
- providerId: string;
126
- entry: AIProviderVoiceEntry;
127
- /** Endpoint from the entry; absent when the entry inherits its preset's default. */
128
- baseUrl?: string;
129
- /**
130
- * The entry's `params` with overrides applied — Deepgram `{model, language}`,
131
- * ElevenLabs `{voice_id, output_format}`. This is the only place a forge app
132
- * should read voice defaults from.
133
- */
134
- params: AIProviderVoiceParams;
135
- /** True when settings named no usable entry and registry order decided. */
136
- fallback: boolean;
137
- /** Non-fatal notes, e.g. settings naming an entry the registry does not have. */
138
- warnings: string[];
139
- }
140
-
141
- /**
142
- * Resolve which registry entry serves speech-to-text or text-to-speech.
143
- *
144
- * Selection is `settings.sttProvider`/`settings.ttsProvider`, falling back to
145
- * the first enabled entry of that kind in registry order when the setting is
146
- * absent or names an entry that is gone — a missing setting must not silence an
147
- * app that has a perfectly good entry configured.
148
- *
149
- * Parameter precedence, lowest first: the entry's `params`, then the settings
150
- * overrides (`sttLanguage` → `language`, `ttsVoice` → `voice_id`), then
151
- * `overrides` from the request. The per-request override is the contract
152
- * forge-concept already honoured and the other apps silently dropped.
153
- *
154
- * @returns The selection, or `undefined` when the registry has no enabled entry
155
- * of that kind.
156
- */
157
- export function resolveVoiceProvider(
158
- registry: AIProviderRegistryView,
159
- kind: AIProviderVoiceKind,
160
- settings: VoiceSettings = {},
161
- overrides: AIProviderVoiceParams = {},
162
- ): VoiceProviderSelection | undefined {
163
- const candidates = entriesOfKind(registry, kind);
164
- if (candidates.length === 0) return undefined;
165
-
166
- const warnings: string[] = [];
167
- const wanted = (kind === "stt" ? settings.sttProvider : settings.ttsProvider)?.trim();
168
- const ids = candidates.map(([id]) => id);
169
- // `candidates` is non-empty, so the shared rule always names one of these ids.
170
- const providerId = selectVoiceEntryId(ids, wanted) as string;
171
- const entry = candidates[ids.indexOf(providerId)][1];
172
- if (wanted && wanted !== providerId) {
173
- warnings.push(
174
- `settings name '${wanted}' for ${kind}, which is not an enabled ${kind} entry — ` +
175
- `using '${providerId}' instead`,
176
- );
177
- }
178
- const fallback = wanted !== providerId;
179
-
180
- const settingsOverride: AIProviderVoiceParams = {};
181
- const override = (kind === "stt" ? settings.sttLanguage : settings.ttsVoice)?.trim();
182
- if (override) settingsOverride[VOICE_OVERRIDE_PARAM[kind]] = override;
183
-
184
- return {
185
- providerId,
186
- entry,
187
- ...(entry.base_url ? { baseUrl: entry.base_url } : {}),
188
- params: { ...entry.params, ...settingsOverride, ...overrides },
189
- fallback,
190
- warnings,
191
- };
192
- }