@frockbot/plugin-settings 0.1.3 → 0.2.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.
- package/frockbot.json +1 -0
- package/package.json +9 -9
- package/src/client/BotSettingsSurface.vue +22 -575
- package/src/client/ConnectionsSurface.vue +59 -10
- package/src/client/{PluginsTrigger.vue → ConnectorsTrigger.vue} +2 -2
- package/src/client/ModelsSurface.vue +26 -364
- package/src/client/PackageAccounts.vue +2 -25
- package/src/client/PackageCatalogSurface.vue +3 -3
- package/src/client/PackageSettingsForm.vue +6 -1
- package/src/client/PluginsSurface.vue +1 -1
- package/src/client/UserProfileTrigger.vue +2 -2
- package/src/client/UserSettingsSurface.vue +4 -18
- package/src/client/bot-settings.test.ts +71 -160
- package/src/client/bot-settings.ts +5 -42
- package/src/client/index.test.ts +7 -41
- package/src/client/index.ts +15 -5
- package/src/client/package-surfaces.test.ts +29 -3
- package/src/client/package-surfaces.ts +12 -3
- package/src/user.test.ts +425 -316
- package/src/user.ts +133 -346
- package/src/client/assignment-operations.ts +0 -22
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
4
|
-
*
|
|
3
|
+
* Connectors: the surface for accounts and services a User authorizes for all
|
|
4
|
+
* of their Bots — Composio, Gmail, a remote MCP server.
|
|
5
5
|
*
|
|
6
|
-
* It holds authorization and
|
|
7
|
-
* providing a connector is enabled at all is Plugins' question
|
|
8
|
-
* provider's account belongs to Models even though it is a Connection too.
|
|
6
|
+
* It holds Connection authorization and state only. Whether the Package
|
|
7
|
+
* providing a connector is enabled at all is Plugins' question.
|
|
9
8
|
*/
|
|
10
9
|
import { clientSurfaceRegistryKey } from "@frockbot/client-core";
|
|
11
10
|
import { UiAnchor, UiButton } from "@frockbot/client-ui";
|
|
@@ -50,6 +49,7 @@ const pendingAuthorizations = computed(() =>
|
|
|
50
49
|
),
|
|
51
50
|
);
|
|
52
51
|
const reconnectingConnectionId = ref<string>();
|
|
52
|
+
const togglingConnectionTypeId = ref<string>();
|
|
53
53
|
|
|
54
54
|
const apiKeyPackageId = ref<string>();
|
|
55
55
|
const apiKeyConnectionTypeId = ref<string>();
|
|
@@ -80,7 +80,7 @@ const mcpScope = ref("");
|
|
|
80
80
|
const mcpClientId = ref("");
|
|
81
81
|
|
|
82
82
|
/**
|
|
83
|
-
* The MCP lifecycle panel: the
|
|
83
|
+
* The MCP lifecycle panel: the server records the User Durable Object
|
|
84
84
|
* owns, which the Connection rows cannot show — a server's `needs-auth` or
|
|
85
85
|
* `error` state, when it last handshook, what its instructions are, and the
|
|
86
86
|
* refusals this build recorded rather than performed.
|
|
@@ -157,6 +157,43 @@ function connectionCount(packageId: string): number {
|
|
|
157
157
|
).length;
|
|
158
158
|
}
|
|
159
159
|
|
|
160
|
+
function credentiallessConnection(item: PluginCatalogItem) {
|
|
161
|
+
const connectionType = item.connectionTypes[0];
|
|
162
|
+
if (connectionType?.authorizationKind !== "none") return undefined;
|
|
163
|
+
return (web.value.userSettings?.connections ?? []).find(
|
|
164
|
+
(connection) =>
|
|
165
|
+
connection.packageId === item.packageId &&
|
|
166
|
+
connection.connectionTypeId === connectionType.id &&
|
|
167
|
+
connection.state !== "revoked",
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
async function toggleCredentialless(item: PluginCatalogItem): Promise<void> {
|
|
172
|
+
const connectionType = item.connectionTypes[0];
|
|
173
|
+
if (connectionType?.authorizationKind !== "none") return;
|
|
174
|
+
togglingConnectionTypeId.value = connectionType.id;
|
|
175
|
+
try {
|
|
176
|
+
const connection = credentiallessConnection(item);
|
|
177
|
+
if (!connection) {
|
|
178
|
+
await web.value.createConnection({
|
|
179
|
+
packageId: item.packageId,
|
|
180
|
+
connectionTypeId: connectionType.id,
|
|
181
|
+
label: item.displayName,
|
|
182
|
+
});
|
|
183
|
+
} else {
|
|
184
|
+
await web.value.setConnectionEnabled(
|
|
185
|
+
connection.connectionId,
|
|
186
|
+
connection.state !== "ready",
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
} catch (error) {
|
|
190
|
+
web.value.settingsError =
|
|
191
|
+
error instanceof Error ? error.message : "Could not update Connection";
|
|
192
|
+
} finally {
|
|
193
|
+
togglingConnectionTypeId.value = undefined;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
160
197
|
function beginMcpConnection(): void {
|
|
161
198
|
mcpFormOpen.value = true;
|
|
162
199
|
mcpLabel.value = "";
|
|
@@ -286,13 +323,12 @@ async function connect(
|
|
|
286
323
|
<div class="connections-surface">
|
|
287
324
|
<UiAnchor
|
|
288
325
|
anchor="user-connections"
|
|
289
|
-
label="
|
|
326
|
+
label="Connectors"
|
|
290
327
|
:href="connectionsLink"
|
|
291
328
|
class="settings-row"
|
|
292
329
|
>
|
|
293
330
|
<p class="field-hint">
|
|
294
|
-
|
|
295
|
-
needs an explicit Assignment before it can use one.
|
|
331
|
+
Connect an account or service once and every Bot you own can use it.
|
|
296
332
|
</p>
|
|
297
333
|
|
|
298
334
|
<section
|
|
@@ -348,7 +384,20 @@ async function connect(
|
|
|
348
384
|
</small>
|
|
349
385
|
</span>
|
|
350
386
|
<UiButton
|
|
351
|
-
v-if="
|
|
387
|
+
v-if="item.connectionTypes[0]?.authorizationKind === 'none'"
|
|
388
|
+
:disabled="
|
|
389
|
+
togglingConnectionTypeId === item.connectionTypes[0]?.id
|
|
390
|
+
"
|
|
391
|
+
@click="toggleCredentialless(item)"
|
|
392
|
+
>
|
|
393
|
+
{{
|
|
394
|
+
credentiallessConnection(item)?.state === "ready"
|
|
395
|
+
? "Disable"
|
|
396
|
+
: "Enable"
|
|
397
|
+
}}
|
|
398
|
+
</UiButton>
|
|
399
|
+
<UiButton
|
|
400
|
+
v-else-if="
|
|
352
401
|
connectionCount(item.packageId) === 0 ||
|
|
353
402
|
item.connectionTypes[0]?.allowMultiple
|
|
354
403
|
"
|
|
@@ -15,10 +15,10 @@ if (!surfaces || !web)
|
|
|
15
15
|
v-if="web.connectionsAvailable"
|
|
16
16
|
class="settings-trigger"
|
|
17
17
|
type="button"
|
|
18
|
-
@click="surfaces.open('
|
|
18
|
+
@click="surfaces.open('connections')"
|
|
19
19
|
>
|
|
20
20
|
<span class="settings-trigger__icon"><UiIcon name="plugins" /></span>
|
|
21
|
-
|
|
21
|
+
Connectors
|
|
22
22
|
</button>
|
|
23
23
|
</template>
|
|
24
24
|
|
|
@@ -1,134 +1,17 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
*
|
|
5
|
-
* Enablement is not here — Plugins turns a provider on and off, and a disabled
|
|
6
|
-
* provider disappears from this surface with its accounts and its catalogs
|
|
7
|
-
* intact.
|
|
8
|
-
*/
|
|
9
|
-
import { clientSurfaceRegistryKey } from "@frockbot/client-core";
|
|
10
|
-
import { UiAnchor, UiButton, UiField } from "@frockbot/client-ui";
|
|
11
|
-
import {
|
|
12
|
-
frockBotWebDataKey,
|
|
13
|
-
type PluginCatalogItem,
|
|
14
|
-
} from "@frockbot/plugin-shell/shared";
|
|
2
|
+
import { UiAnchor } from "@frockbot/client-ui";
|
|
3
|
+
import { frockBotWebDataKey } from "@frockbot/plugin-shell/shared";
|
|
15
4
|
import { settingsLinkV1 } from "@frockbot/plugin-shell/settings-links";
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
18
|
-
decodeModelSelection,
|
|
19
|
-
eligibleModelConnections,
|
|
20
|
-
encodeModelSelection,
|
|
21
|
-
modelSelectOptions,
|
|
22
|
-
} from "./bot-settings.js";
|
|
23
|
-
import { configurablePackages } from "./package-surfaces.js";
|
|
24
|
-
import PackageAccounts from "./PackageAccounts.vue";
|
|
25
|
-
import PackageSettingsForm from "./PackageSettingsForm.vue";
|
|
5
|
+
import { inject, onMounted } from "vue";
|
|
26
6
|
|
|
27
|
-
const providedSurfaces = inject(clientSurfaceRegistryKey);
|
|
28
7
|
const providedWeb = inject(frockBotWebDataKey);
|
|
29
|
-
if (!
|
|
30
|
-
throw new Error("settings client services were not provided");
|
|
31
|
-
}
|
|
32
|
-
const surfaces = providedSurfaces;
|
|
8
|
+
if (!providedWeb) throw new Error("settings client services were not provided");
|
|
33
9
|
const web = providedWeb;
|
|
34
|
-
|
|
10
|
+
|
|
35
11
|
const defaultModelLink = settingsLinkV1({ anchor: "user-default-model" });
|
|
36
12
|
const providersLink = settingsLinkV1({ anchor: "user-model-providers" });
|
|
37
13
|
|
|
38
|
-
|
|
39
|
-
const savingModel = ref(false);
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* The endpoint root of an Ollama Cloud Connection, declared by that Connection
|
|
43
|
-
* Type as `api-base-url`. It is the only Connection Type setting the shared
|
|
44
|
-
* API-key form has a field for.
|
|
45
|
-
*/
|
|
46
|
-
const OLLAMA_PACKAGE_ID = "provider-ollama-cloud";
|
|
47
|
-
const apiKeyPackageId = ref<string>();
|
|
48
|
-
const apiKeyConnectionTypeId = ref<string>();
|
|
49
|
-
const apiKeyLabel = ref("");
|
|
50
|
-
const apiKey = ref("");
|
|
51
|
-
const apiKeyBaseUrl = ref("");
|
|
52
|
-
|
|
53
|
-
const providers = computed(() =>
|
|
54
|
-
configurablePackages({
|
|
55
|
-
catalog: web.value.pluginCatalog,
|
|
56
|
-
packages: web.value.userSettings?.packages ?? [],
|
|
57
|
-
home: "models",
|
|
58
|
-
}),
|
|
59
|
-
);
|
|
60
|
-
const readyConnections = computed(() =>
|
|
61
|
-
eligibleModelConnections({
|
|
62
|
-
connections: web.value.userSettings?.connections ?? [],
|
|
63
|
-
packages: web.value.userSettings?.packages ?? [],
|
|
64
|
-
catalog: web.value.pluginCatalog,
|
|
65
|
-
}),
|
|
66
|
-
);
|
|
67
|
-
const modelOptions = computed(() => modelSelectOptions(readyConnections.value));
|
|
68
|
-
|
|
69
|
-
onMounted(async () => {
|
|
70
|
-
await web.value.loadPluginCatalog();
|
|
71
|
-
defaultModel.value = encodeModelSelection(
|
|
72
|
-
web.value.userSettings?.newBotModelTemplate,
|
|
73
|
-
);
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
async function saveDefaultModel(): Promise<void> {
|
|
77
|
-
savingModel.value = true;
|
|
78
|
-
try {
|
|
79
|
-
await web.value.saveDefaultModel(decodeModelSelection(defaultModel.value));
|
|
80
|
-
} catch (error) {
|
|
81
|
-
web.value.settingsError =
|
|
82
|
-
error instanceof Error ? error.message : "Could not save the model";
|
|
83
|
-
} finally {
|
|
84
|
-
savingModel.value = false;
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
function connectionCount(packageId: string): number {
|
|
89
|
-
return (web.value.userSettings?.connections ?? []).filter(
|
|
90
|
-
(connection) =>
|
|
91
|
-
connection.packageId === packageId && connection.state !== "revoked",
|
|
92
|
-
).length;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
function beginConnect(item: PluginCatalogItem): void {
|
|
96
|
-
const connectionType = item.connectionTypes[0];
|
|
97
|
-
if (!connectionType) return;
|
|
98
|
-
apiKeyPackageId.value = item.packageId;
|
|
99
|
-
apiKeyConnectionTypeId.value = connectionType.id;
|
|
100
|
-
apiKeyLabel.value = item.displayName;
|
|
101
|
-
apiKey.value = "";
|
|
102
|
-
apiKeyBaseUrl.value = "";
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
function cancelConnect(): void {
|
|
106
|
-
apiKey.value = "";
|
|
107
|
-
apiKeyBaseUrl.value = "";
|
|
108
|
-
apiKeyPackageId.value = undefined;
|
|
109
|
-
apiKeyConnectionTypeId.value = undefined;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
async function connectApiKey(): Promise<void> {
|
|
113
|
-
if (!apiKeyPackageId.value || !apiKeyConnectionTypeId.value) return;
|
|
114
|
-
try {
|
|
115
|
-
// An empty endpoint field means the Package default, so the command
|
|
116
|
-
// carries no settings at all rather than an empty bag.
|
|
117
|
-
const apiBaseUrl = apiKeyBaseUrl.value.trim();
|
|
118
|
-
await web.value.createApiKeyConnection({
|
|
119
|
-
packageId: apiKeyPackageId.value,
|
|
120
|
-
connectionTypeId: apiKeyConnectionTypeId.value,
|
|
121
|
-
label: apiKeyLabel.value,
|
|
122
|
-
apiKey: apiKey.value,
|
|
123
|
-
...(apiBaseUrl ? { settings: { "api-base-url": apiBaseUrl } } : {}),
|
|
124
|
-
});
|
|
125
|
-
cancelConnect();
|
|
126
|
-
} catch (error) {
|
|
127
|
-
apiKey.value = "";
|
|
128
|
-
web.value.settingsError =
|
|
129
|
-
error instanceof Error ? error.message : "Could not create Connection";
|
|
130
|
-
}
|
|
131
|
-
}
|
|
14
|
+
onMounted(() => void web.value.loadPluginCatalog());
|
|
132
15
|
</script>
|
|
133
16
|
|
|
134
17
|
<template>
|
|
@@ -137,142 +20,21 @@ async function connectApiKey(): Promise<void> {
|
|
|
137
20
|
anchor="user-default-model"
|
|
138
21
|
label="Default model"
|
|
139
22
|
:href="defaultModelLink"
|
|
140
|
-
class="
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
hint="used by every Bot"
|
|
146
|
-
>
|
|
147
|
-
<select v-model="defaultModel">
|
|
148
|
-
<option value="">No default model</option>
|
|
149
|
-
<option
|
|
150
|
-
v-for="model in modelOptions"
|
|
151
|
-
:key="model.value"
|
|
152
|
-
:value="model.value"
|
|
153
|
-
>
|
|
154
|
-
{{ model.label }}
|
|
155
|
-
</option>
|
|
156
|
-
</select>
|
|
157
|
-
</UiField>
|
|
158
|
-
<p v-if="modelOptions.length > 0" class="field-hint">
|
|
159
|
-
Used by every Bot unless a Bot overrides it in its own settings.
|
|
160
|
-
</p>
|
|
161
|
-
<div v-else class="model-empty">
|
|
162
|
-
<p>
|
|
163
|
-
Connect a model provider below, then choose the model your Bots use.
|
|
164
|
-
</p>
|
|
165
|
-
</div>
|
|
166
|
-
<div v-if="modelOptions.length > 0" class="settings-actions">
|
|
167
|
-
<UiButton
|
|
168
|
-
type="button"
|
|
169
|
-
variant="primary"
|
|
170
|
-
:disabled="savingModel"
|
|
171
|
-
@click="saveDefaultModel"
|
|
172
|
-
>
|
|
173
|
-
{{ savingModel ? "Saving…" : "Save model" }}
|
|
174
|
-
</UiButton>
|
|
175
|
-
</div>
|
|
176
|
-
</UiAnchor>
|
|
177
|
-
|
|
23
|
+
class="models-anchor"
|
|
24
|
+
/>
|
|
25
|
+
<div class="models-sections">
|
|
26
|
+
<k-slot name="frockbot.models-sections" />
|
|
27
|
+
</div>
|
|
178
28
|
<UiAnchor
|
|
179
29
|
anchor="user-model-providers"
|
|
180
30
|
label="Model providers"
|
|
181
31
|
:href="providersLink"
|
|
182
|
-
class="
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
<article
|
|
189
|
-
v-for="item in providers"
|
|
190
|
-
:key="item.packageId"
|
|
191
|
-
class="provider-card"
|
|
192
|
-
>
|
|
193
|
-
<div class="provider-summary">
|
|
194
|
-
<span class="provider-logo" aria-hidden="true">
|
|
195
|
-
{{ item.displayName.slice(0, 1) }}
|
|
196
|
-
</span>
|
|
197
|
-
<span class="provider-copy">
|
|
198
|
-
<strong>{{ item.displayName }}</strong>
|
|
199
|
-
<small>
|
|
200
|
-
{{
|
|
201
|
-
connectionCount(item.packageId) === 0
|
|
202
|
-
? "No account connected"
|
|
203
|
-
: `${connectionCount(item.packageId)} account(s)`
|
|
204
|
-
}}
|
|
205
|
-
</small>
|
|
206
|
-
</span>
|
|
207
|
-
<UiButton
|
|
208
|
-
v-if="
|
|
209
|
-
connectionCount(item.packageId) === 0 ||
|
|
210
|
-
item.connectionTypes[0]?.allowMultiple
|
|
211
|
-
"
|
|
212
|
-
@click="beginConnect(item)"
|
|
213
|
-
>
|
|
214
|
-
{{
|
|
215
|
-
connectionCount(item.packageId) === 0
|
|
216
|
-
? "Connect"
|
|
217
|
-
: "Add another account"
|
|
218
|
-
}}
|
|
219
|
-
</UiButton>
|
|
220
|
-
</div>
|
|
221
|
-
|
|
222
|
-
<PackageAccounts :item="item" />
|
|
223
|
-
|
|
224
|
-
<form
|
|
225
|
-
v-if="apiKeyPackageId === item.packageId"
|
|
226
|
-
class="api-key-form"
|
|
227
|
-
@submit.prevent="connectApiKey"
|
|
228
|
-
>
|
|
229
|
-
<label>
|
|
230
|
-
<span>Connection label</span>
|
|
231
|
-
<input v-model="apiKeyLabel" maxlength="120" required />
|
|
232
|
-
</label>
|
|
233
|
-
<label>
|
|
234
|
-
<span>API key</span>
|
|
235
|
-
<input
|
|
236
|
-
v-model="apiKey"
|
|
237
|
-
type="password"
|
|
238
|
-
autocomplete="new-password"
|
|
239
|
-
required
|
|
240
|
-
/>
|
|
241
|
-
</label>
|
|
242
|
-
<label v-if="item.packageId === OLLAMA_PACKAGE_ID">
|
|
243
|
-
<span>API base URL</span>
|
|
244
|
-
<input
|
|
245
|
-
v-model="apiKeyBaseUrl"
|
|
246
|
-
type="url"
|
|
247
|
-
inputmode="url"
|
|
248
|
-
autocomplete="off"
|
|
249
|
-
maxlength="2048"
|
|
250
|
-
placeholder="https://ollama.com"
|
|
251
|
-
/>
|
|
252
|
-
</label>
|
|
253
|
-
<p v-if="item.packageId === OLLAMA_PACKAGE_ID" class="field-hint">
|
|
254
|
-
Leave empty for Ollama Cloud. Point this at a local Ollama server
|
|
255
|
-
(http://127.0.0.1:11434) or any Ollama-compatible endpoint.
|
|
256
|
-
</p>
|
|
257
|
-
<div class="api-key-actions">
|
|
258
|
-
<UiButton @click="cancelConnect">Cancel</UiButton>
|
|
259
|
-
<UiButton type="submit" variant="primary">
|
|
260
|
-
Connect account
|
|
261
|
-
</UiButton>
|
|
262
|
-
</div>
|
|
263
|
-
</form>
|
|
264
|
-
|
|
265
|
-
<PackageSettingsForm :item="item" />
|
|
266
|
-
</article>
|
|
267
|
-
</div>
|
|
268
|
-
<div v-if="providers.length === 0" class="model-empty">
|
|
269
|
-
<p>No model provider is enabled.</p>
|
|
270
|
-
<UiButton type="button" @click="surfaces.open('plugins')">
|
|
271
|
-
Open Plugins
|
|
272
|
-
</UiButton>
|
|
273
|
-
</div>
|
|
274
|
-
</UiAnchor>
|
|
275
|
-
|
|
32
|
+
class="models-anchor"
|
|
33
|
+
/>
|
|
34
|
+
<p class="models-empty">
|
|
35
|
+
FrockBot chooses a model for you. Enable Custom models in Plugins to
|
|
36
|
+
choose an account model or a model for one Bot.
|
|
37
|
+
</p>
|
|
276
38
|
<p v-if="web.settingsError" class="settings-error" role="alert">
|
|
277
39
|
{{ web.settingsError }}
|
|
278
40
|
</p>
|
|
@@ -287,128 +49,28 @@ async function connectApiKey(): Promise<void> {
|
|
|
287
49
|
padding: 16px;
|
|
288
50
|
}
|
|
289
51
|
|
|
290
|
-
.
|
|
291
|
-
|
|
292
|
-
flex-direction: column;
|
|
293
|
-
gap: 16px;
|
|
294
|
-
padding-right: var(--frock-control-sm);
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
.field-hint {
|
|
298
|
-
margin: 0;
|
|
299
|
-
color: var(--frock-text-muted);
|
|
300
|
-
font-size: var(--frock-text-sm);
|
|
52
|
+
.models-anchor {
|
|
53
|
+
min-height: 1px;
|
|
301
54
|
}
|
|
302
55
|
|
|
303
|
-
.
|
|
56
|
+
.models-sections {
|
|
304
57
|
display: flex;
|
|
305
58
|
flex-direction: column;
|
|
306
|
-
|
|
307
|
-
gap: 10px;
|
|
308
|
-
padding: 12px;
|
|
309
|
-
border: 1px solid var(--frock-border);
|
|
310
|
-
border-radius: var(--frock-radius-card);
|
|
311
|
-
background: var(--frock-surface-subtle);
|
|
59
|
+
gap: 16px;
|
|
312
60
|
}
|
|
313
61
|
|
|
314
|
-
.
|
|
62
|
+
.models-empty {
|
|
315
63
|
margin: 0;
|
|
316
|
-
|
|
317
|
-
font-size: var(--frock-text-sm);
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
.provider-grid {
|
|
321
|
-
display: grid;
|
|
322
|
-
gap: 12px;
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
.provider-card {
|
|
326
|
-
min-width: 0;
|
|
327
|
-
padding: 8px;
|
|
64
|
+
padding: 12px;
|
|
328
65
|
border: 1px solid var(--frock-border);
|
|
329
66
|
border-radius: var(--frock-radius-card);
|
|
330
|
-
background: var(--frock-surface-raised);
|
|
331
|
-
box-shadow: var(--frock-shadow-card);
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
.provider-summary {
|
|
335
|
-
display: grid;
|
|
336
|
-
width: 100%;
|
|
337
|
-
min-width: 0;
|
|
338
|
-
grid-template-columns: 44px minmax(0, 1fr) auto;
|
|
339
|
-
align-items: center;
|
|
340
|
-
gap: 12px;
|
|
341
|
-
padding: 8px;
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
.provider-logo {
|
|
345
|
-
display: grid;
|
|
346
|
-
width: 44px;
|
|
347
|
-
height: 44px;
|
|
348
|
-
place-items: center;
|
|
349
|
-
border-radius: 12px;
|
|
350
|
-
color: var(--frock-action-secondary-text);
|
|
351
|
-
background: var(--frock-surface-accent);
|
|
352
|
-
font-weight: 800;
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
.provider-copy {
|
|
356
|
-
min-width: 0;
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
.provider-copy strong,
|
|
360
|
-
.provider-copy small {
|
|
361
|
-
display: block;
|
|
362
|
-
overflow: hidden;
|
|
363
|
-
white-space: nowrap;
|
|
364
|
-
text-overflow: ellipsis;
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
.provider-copy strong {
|
|
368
|
-
font-size: var(--frock-text-md);
|
|
369
|
-
font-weight: 600;
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
.provider-copy small {
|
|
373
|
-
margin-top: 4px;
|
|
374
|
-
color: var(--frock-text-muted);
|
|
375
|
-
font-size: var(--frock-text-sm);
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
.api-key-form {
|
|
379
|
-
display: grid;
|
|
380
|
-
gap: 12px;
|
|
381
|
-
margin: 0 8px;
|
|
382
|
-
padding: 12px 0 8px;
|
|
383
|
-
border-top: 1px solid var(--frock-border);
|
|
384
|
-
animation: frock-rise-in var(--frock-motion-enter) both;
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
.api-key-form label {
|
|
388
|
-
display: grid;
|
|
389
|
-
gap: 6px;
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
.api-key-form span {
|
|
393
67
|
color: var(--frock-text-muted);
|
|
68
|
+
background: var(--frock-surface-subtle);
|
|
394
69
|
font-size: var(--frock-text-sm);
|
|
395
70
|
}
|
|
396
71
|
|
|
397
|
-
.
|
|
398
|
-
|
|
399
|
-
padding: 8px 11px;
|
|
400
|
-
border: 1px solid var(--frock-border);
|
|
401
|
-
border-radius: 9px;
|
|
402
|
-
background: var(--frock-surface-raised);
|
|
403
|
-
color: var(--frock-text);
|
|
404
|
-
font-size: var(--frock-text-base);
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
.api-key-actions,
|
|
408
|
-
.settings-actions {
|
|
409
|
-
display: flex;
|
|
410
|
-
justify-content: flex-end;
|
|
411
|
-
gap: 8px;
|
|
72
|
+
.models-sections:not(:empty) ~ .models-empty {
|
|
73
|
+
display: none;
|
|
412
74
|
}
|
|
413
75
|
|
|
414
76
|
.settings-error {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
/**
|
|
3
|
-
* The accounts of one Package: every Connection it owns
|
|
4
|
-
*
|
|
3
|
+
* The accounts of one Package: every Connection it owns and the actions each
|
|
4
|
+
* one offers.
|
|
5
5
|
*
|
|
6
6
|
* Shared by Models and Connections, which differ in what they add around it —
|
|
7
7
|
* a provider catalog and a model choice on one, an authorization handoff on
|
|
@@ -41,20 +41,6 @@ const connections = computed<ConnectionView[]>(() =>
|
|
|
41
41
|
|
|
42
42
|
type StatusTone = "ready" | "muted" | "attention";
|
|
43
43
|
|
|
44
|
-
/**
|
|
45
|
-
* The User's default model, when this account is the one serving it. Shown on
|
|
46
|
-
* the account it belongs to; changing it is the row at the top of Models.
|
|
47
|
-
*/
|
|
48
|
-
function defaultModelName(connection: ConnectionView): string | undefined {
|
|
49
|
-
const selected = web.value.userSettings?.newBotModelTemplate;
|
|
50
|
-
if (selected?.connectionId !== connection.connectionId) return undefined;
|
|
51
|
-
return (
|
|
52
|
-
connection.modelCatalog?.models.find(
|
|
53
|
-
(model) => model.providerModelId === selected.providerModelId,
|
|
54
|
-
)?.displayName ?? selected.providerModelId
|
|
55
|
-
);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
44
|
function connectionTone(connection: ConnectionView): StatusTone {
|
|
59
45
|
if (connection.state === "ready") return "ready";
|
|
60
46
|
if (connection.state === "failed") return "attention";
|
|
@@ -194,9 +180,6 @@ async function revoke(packageId: string, connectionId: string): Promise<void> {
|
|
|
194
180
|
Revoke
|
|
195
181
|
</UiButton>
|
|
196
182
|
</div>
|
|
197
|
-
<p v-if="defaultModelName(connection)" class="account-default">
|
|
198
|
-
Default model: {{ defaultModelName(connection) }}
|
|
199
|
-
</p>
|
|
200
183
|
<p v-if="connection.failure" class="connection-failure" role="alert">
|
|
201
184
|
{{ connection.failure }}
|
|
202
185
|
</p>
|
|
@@ -316,12 +299,6 @@ async function revoke(packageId: string, connectionId: string): Promise<void> {
|
|
|
316
299
|
font-size: var(--frock-text-base);
|
|
317
300
|
}
|
|
318
301
|
|
|
319
|
-
.account-default {
|
|
320
|
-
margin: 0;
|
|
321
|
-
color: var(--frock-text-muted);
|
|
322
|
-
font-size: var(--frock-text-sm);
|
|
323
|
-
}
|
|
324
|
-
|
|
325
302
|
.connection-failure {
|
|
326
303
|
margin: 0;
|
|
327
304
|
color: var(--frock-danger-text);
|
|
@@ -153,7 +153,7 @@ function isPackageInstalled(packageId: string): boolean {
|
|
|
153
153
|
<div class="package-catalog-surface">
|
|
154
154
|
<p class="catalog-intro">
|
|
155
155
|
Browse published Packages. Installing one makes it available to every Bot
|
|
156
|
-
you own
|
|
156
|
+
you own when the Package is enabled.
|
|
157
157
|
</p>
|
|
158
158
|
<label class="catalog-search">
|
|
159
159
|
<UiIcon name="search" />
|
|
@@ -239,8 +239,8 @@ function isPackageInstalled(packageId: string): boolean {
|
|
|
239
239
|
>
|
|
240
240
|
<p>
|
|
241
241
|
Uninstalling {{ entry.displayName }} removes it from every
|
|
242
|
-
Bot.
|
|
243
|
-
|
|
242
|
+
Bot. Connections and credentials are kept, and become usable
|
|
243
|
+
again if you reinstall and enable the Package.
|
|
244
244
|
</p>
|
|
245
245
|
<div class="catalog-actions">
|
|
246
246
|
<UiButton @click="uninstallingPackageId = undefined">
|
|
@@ -23,7 +23,12 @@ const providedWeb = inject(frockBotWebDataKey);
|
|
|
23
23
|
if (!providedWeb) throw new Error("shell client data was not provided");
|
|
24
24
|
const web = providedWeb;
|
|
25
25
|
|
|
26
|
-
const definitions = computed(() =>
|
|
26
|
+
const definitions = computed(() =>
|
|
27
|
+
(props.item.settings ?? []).filter(
|
|
28
|
+
(definition) =>
|
|
29
|
+
definition.role !== "model" && definition.scopes.includes("user"),
|
|
30
|
+
),
|
|
31
|
+
);
|
|
27
32
|
const draft = ref<Record<string, string | number | boolean>>({});
|
|
28
33
|
|
|
29
34
|
/** The stored values of this Package, as the User settings hold them. */
|
|
@@ -159,7 +159,7 @@ async function setEnabled(packageId: string, next: boolean): Promise<void> {
|
|
|
159
159
|
>
|
|
160
160
|
<p class="plugin-intro">
|
|
161
161
|
Turn Packages on and off for your Bots. Set one up where it belongs:
|
|
162
|
-
model providers in Models, accounts in
|
|
162
|
+
model providers in Models, accounts in Connectors.
|
|
163
163
|
</p>
|
|
164
164
|
<div class="plugin-catalog-link">
|
|
165
165
|
<UiButton type="button" @click="openPackageCatalog">
|
|
@@ -90,7 +90,7 @@ onBeforeUnmount(() => window.removeEventListener("pointerdown", closeMenu));
|
|
|
90
90
|
Settings
|
|
91
91
|
</button>
|
|
92
92
|
<!-- The three Package surfaces are reachable where the User already is,
|
|
93
|
-
and gated exactly as the sidebar's
|
|
93
|
+
and gated exactly as the sidebar's Connectors trigger is: a shell
|
|
94
94
|
without the Connection protocol shows none of them. -->
|
|
95
95
|
<template v-if="web.connectionsAvailable">
|
|
96
96
|
<button type="button" role="menuitem" @click="openSurface('models')">
|
|
@@ -104,7 +104,7 @@ onBeforeUnmount(() => window.removeEventListener("pointerdown", closeMenu));
|
|
|
104
104
|
role="menuitem"
|
|
105
105
|
@click="openSurface('connections')"
|
|
106
106
|
>
|
|
107
|
-
|
|
107
|
+
Connectors
|
|
108
108
|
</button>
|
|
109
109
|
</template>
|
|
110
110
|
<button v-if="developmentIdentity" type="button" role="menuitem" disabled>
|