@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
package/src/user.test.ts
CHANGED
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
import { describe, expect, test } from "bun:test";
|
|
2
2
|
import type { CatalogEntryV1, CatalogIndexV1 } from "@frockbot/catalog-core";
|
|
3
3
|
import {
|
|
4
|
-
capabilityAssignmentFailureV1,
|
|
5
|
-
initializeBotSettingsV1,
|
|
6
|
-
resolveBotExecutionPlanV1,
|
|
7
4
|
USER_PROFILE_PLACEHOLDER_NAME_V1,
|
|
8
5
|
type UserSettingsViewV1,
|
|
9
6
|
} from "@frockbot/configuration-core";
|
|
@@ -85,6 +82,75 @@ async function installOllama(
|
|
|
85
82
|
}
|
|
86
83
|
|
|
87
84
|
describe("User settings backend Contribution", () => {
|
|
85
|
+
test("reads old settings purely and writes the migrated shape on the next command", async () => {
|
|
86
|
+
const storage = new MemoryStorage();
|
|
87
|
+
// Literal durable shape from eb0283edcce5daea976a21a9f6a6414bedc6e2bc,
|
|
88
|
+
// the first parent of PR #134's merge commit.
|
|
89
|
+
const historical = {
|
|
90
|
+
schemaVersion: 1,
|
|
91
|
+
revision: 7,
|
|
92
|
+
profile: { name: "Existing User" },
|
|
93
|
+
packages: [],
|
|
94
|
+
connections: [
|
|
95
|
+
{
|
|
96
|
+
connectionId: "ollama-1",
|
|
97
|
+
packageId: "provider-ollama-cloud",
|
|
98
|
+
connectionTypeId: "ollama-cloud-account",
|
|
99
|
+
displayName: "Work",
|
|
100
|
+
state: "ready",
|
|
101
|
+
safeMetadata: {
|
|
102
|
+
dependentAssignments: [
|
|
103
|
+
{
|
|
104
|
+
botId: "primary",
|
|
105
|
+
generation: "assignment-1",
|
|
106
|
+
packageId: "provider-ollama-cloud",
|
|
107
|
+
capabilityId: "ollama-cloud-models",
|
|
108
|
+
claimOrder: 0,
|
|
109
|
+
status: "acknowledged",
|
|
110
|
+
},
|
|
111
|
+
],
|
|
112
|
+
},
|
|
113
|
+
},
|
|
114
|
+
],
|
|
115
|
+
newBotModelTemplate: {
|
|
116
|
+
connectionId: "ollama-1",
|
|
117
|
+
providerModelId: "glm-5.3-flash:cloud",
|
|
118
|
+
},
|
|
119
|
+
newBotModelTemplateSource: "user",
|
|
120
|
+
};
|
|
121
|
+
storage.values.set("user-configuration", structuredClone(historical));
|
|
122
|
+
const settings = contribution(storage);
|
|
123
|
+
|
|
124
|
+
await expect(settings.readSnapshot()).resolves.toMatchObject({
|
|
125
|
+
revision: 7,
|
|
126
|
+
connections: [{ safeMetadata: {} }],
|
|
127
|
+
});
|
|
128
|
+
expect(await storage.get<unknown>("user-configuration")).toEqual(
|
|
129
|
+
historical,
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
await settings.executeConfiguration({
|
|
133
|
+
schemaVersion: 1,
|
|
134
|
+
userId: "user-1",
|
|
135
|
+
command: {
|
|
136
|
+
schemaVersion: 1,
|
|
137
|
+
type: "user/update-profile",
|
|
138
|
+
commandId: "migrate-user-settings",
|
|
139
|
+
expectedRevision: 7,
|
|
140
|
+
profile: { name: "Migrated User" },
|
|
141
|
+
},
|
|
142
|
+
});
|
|
143
|
+
const written =
|
|
144
|
+
await storage.get<Record<string, unknown>>("user-configuration");
|
|
145
|
+
expect(written).toMatchObject({
|
|
146
|
+
revision: 8,
|
|
147
|
+
profile: { name: "Migrated User" },
|
|
148
|
+
connections: [{ safeMetadata: {} }],
|
|
149
|
+
});
|
|
150
|
+
expect(written).not.toHaveProperty("newBotModelTemplate");
|
|
151
|
+
expect(written).not.toHaveProperty("newBotModelTemplateSource");
|
|
152
|
+
});
|
|
153
|
+
|
|
88
154
|
test("bootstraps first-party Packages once and preserves a later uninstall", async () => {
|
|
89
155
|
const storage = new MemoryStorage();
|
|
90
156
|
const settings = createUserSettingsBackendContribution({
|
|
@@ -150,6 +216,229 @@ describe("User settings backend Contribution", () => {
|
|
|
150
216
|
]);
|
|
151
217
|
});
|
|
152
218
|
|
|
219
|
+
test("bootstraps a declared default-disabled Package without re-enabling it", async () => {
|
|
220
|
+
const settings = createUserSettingsBackendContribution({
|
|
221
|
+
storage: new MemoryStorage(),
|
|
222
|
+
availablePackages: [
|
|
223
|
+
{
|
|
224
|
+
packageId: "custom-models",
|
|
225
|
+
version: "0.0.1",
|
|
226
|
+
installByDefault: true,
|
|
227
|
+
defaultEnablement: "disabled",
|
|
228
|
+
},
|
|
229
|
+
],
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
const first = await settings.readConfiguration({
|
|
233
|
+
schemaVersion: 1,
|
|
234
|
+
userId: "user-1",
|
|
235
|
+
});
|
|
236
|
+
expect(first.packages).toEqual([
|
|
237
|
+
{
|
|
238
|
+
packageId: "custom-models",
|
|
239
|
+
version: "0.0.1",
|
|
240
|
+
state: "disabled",
|
|
241
|
+
provenance: "first-party",
|
|
242
|
+
},
|
|
243
|
+
]);
|
|
244
|
+
expect(
|
|
245
|
+
await settings.readConfiguration({ schemaVersion: 1, userId: "user-1" }),
|
|
246
|
+
).toEqual(first);
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
test("installs a Package disabled when explicitly requested", async () => {
|
|
250
|
+
const settings = contribution();
|
|
251
|
+
const receipt = await settings.executeConfiguration({
|
|
252
|
+
schemaVersion: 1,
|
|
253
|
+
userId: "user-1",
|
|
254
|
+
command: {
|
|
255
|
+
schemaVersion: 1,
|
|
256
|
+
type: "user/install-package",
|
|
257
|
+
commandId: "install-disabled",
|
|
258
|
+
expectedRevision: 0,
|
|
259
|
+
packageId: "provider-ollama-cloud",
|
|
260
|
+
version: "0.0.1",
|
|
261
|
+
enabled: false,
|
|
262
|
+
},
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
expect(receipt).toMatchObject({ status: "applied", revision: 1 });
|
|
266
|
+
expect((await settings.read("user-1")).packages).toMatchObject([
|
|
267
|
+
{ packageId: "provider-ollama-cloud", state: "disabled" },
|
|
268
|
+
]);
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
test("does not clear an existing failed installation", async () => {
|
|
272
|
+
const storage = new MemoryStorage();
|
|
273
|
+
const settings = contribution(storage);
|
|
274
|
+
await storage.put("user-id", "user-1");
|
|
275
|
+
await storage.put("user-configuration", {
|
|
276
|
+
schemaVersion: 1,
|
|
277
|
+
revision: 0,
|
|
278
|
+
profile: { name: "User" },
|
|
279
|
+
packages: [
|
|
280
|
+
{
|
|
281
|
+
packageId: "provider-ollama-cloud",
|
|
282
|
+
version: "0.0.1",
|
|
283
|
+
state: "failed",
|
|
284
|
+
failure: "activation failed",
|
|
285
|
+
},
|
|
286
|
+
],
|
|
287
|
+
connections: [],
|
|
288
|
+
} satisfies UserSettingsViewV1);
|
|
289
|
+
|
|
290
|
+
await settings.executeConfiguration({
|
|
291
|
+
schemaVersion: 1,
|
|
292
|
+
userId: "user-1",
|
|
293
|
+
command: {
|
|
294
|
+
schemaVersion: 1,
|
|
295
|
+
type: "user/install-package",
|
|
296
|
+
commandId: "retry-failed-disabled",
|
|
297
|
+
expectedRevision: 0,
|
|
298
|
+
packageId: "provider-ollama-cloud",
|
|
299
|
+
version: "0.0.1",
|
|
300
|
+
enabled: false,
|
|
301
|
+
},
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
expect((await settings.read("user-1")).packages).toMatchObject([
|
|
305
|
+
{
|
|
306
|
+
packageId: "provider-ollama-cloud",
|
|
307
|
+
state: "failed",
|
|
308
|
+
failure: "activation failed",
|
|
309
|
+
},
|
|
310
|
+
]);
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
test("refuses enabling until every declared dependency is enabled", async () => {
|
|
314
|
+
const settings = createUserSettingsBackendContribution({
|
|
315
|
+
storage: new MemoryStorage(),
|
|
316
|
+
availablePackages: [
|
|
317
|
+
{ packageId: "custom-models", version: "0.0.1" },
|
|
318
|
+
{
|
|
319
|
+
packageId: "provider-ollama-cloud",
|
|
320
|
+
version: "0.0.1",
|
|
321
|
+
dependencies: { "custom-models": ">=0.0.1" },
|
|
322
|
+
},
|
|
323
|
+
],
|
|
324
|
+
});
|
|
325
|
+
await settings.executeConfiguration({
|
|
326
|
+
schemaVersion: 1,
|
|
327
|
+
userId: "user-1",
|
|
328
|
+
command: {
|
|
329
|
+
schemaVersion: 1,
|
|
330
|
+
type: "user/install-package",
|
|
331
|
+
commandId: "install-custom-models-disabled",
|
|
332
|
+
expectedRevision: 0,
|
|
333
|
+
packageId: "custom-models",
|
|
334
|
+
version: "0.0.1",
|
|
335
|
+
enabled: false,
|
|
336
|
+
},
|
|
337
|
+
});
|
|
338
|
+
const refusedInstall = await settings.executeConfiguration({
|
|
339
|
+
schemaVersion: 1,
|
|
340
|
+
userId: "user-1",
|
|
341
|
+
command: {
|
|
342
|
+
schemaVersion: 1,
|
|
343
|
+
type: "user/install-package",
|
|
344
|
+
commandId: "install-ollama-enabled-too-soon",
|
|
345
|
+
expectedRevision: 1,
|
|
346
|
+
packageId: "provider-ollama-cloud",
|
|
347
|
+
version: "0.0.1",
|
|
348
|
+
},
|
|
349
|
+
});
|
|
350
|
+
expect(refusedInstall).toMatchObject({
|
|
351
|
+
status: "rejected",
|
|
352
|
+
revision: 1,
|
|
353
|
+
failure: expect.stringContaining("custom-models"),
|
|
354
|
+
});
|
|
355
|
+
await settings.executeConfiguration({
|
|
356
|
+
schemaVersion: 1,
|
|
357
|
+
userId: "user-1",
|
|
358
|
+
command: {
|
|
359
|
+
schemaVersion: 1,
|
|
360
|
+
type: "user/install-package",
|
|
361
|
+
commandId: "install-ollama-disabled",
|
|
362
|
+
expectedRevision: 1,
|
|
363
|
+
packageId: "provider-ollama-cloud",
|
|
364
|
+
version: "0.0.1",
|
|
365
|
+
enabled: false,
|
|
366
|
+
},
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
const refused = await settings.executeConfiguration({
|
|
370
|
+
schemaVersion: 1,
|
|
371
|
+
userId: "user-1",
|
|
372
|
+
command: {
|
|
373
|
+
schemaVersion: 1,
|
|
374
|
+
type: "user/set-package-enabled",
|
|
375
|
+
commandId: "enable-ollama-too-soon",
|
|
376
|
+
expectedRevision: 2,
|
|
377
|
+
packageId: "provider-ollama-cloud",
|
|
378
|
+
enabled: true,
|
|
379
|
+
},
|
|
380
|
+
});
|
|
381
|
+
expect(refused).toMatchObject({
|
|
382
|
+
status: "rejected",
|
|
383
|
+
revision: 2,
|
|
384
|
+
failure: expect.stringContaining("custom-models"),
|
|
385
|
+
});
|
|
386
|
+
expect((await settings.read("user-1")).packages[1]?.state).toBe("disabled");
|
|
387
|
+
|
|
388
|
+
await settings.executeConfiguration({
|
|
389
|
+
schemaVersion: 1,
|
|
390
|
+
userId: "user-1",
|
|
391
|
+
command: {
|
|
392
|
+
schemaVersion: 1,
|
|
393
|
+
type: "user/set-package-enabled",
|
|
394
|
+
commandId: "enable-custom-models",
|
|
395
|
+
expectedRevision: 2,
|
|
396
|
+
packageId: "custom-models",
|
|
397
|
+
enabled: true,
|
|
398
|
+
},
|
|
399
|
+
});
|
|
400
|
+
await expect(
|
|
401
|
+
settings.executeConfiguration({
|
|
402
|
+
schemaVersion: 1,
|
|
403
|
+
userId: "user-1",
|
|
404
|
+
command: {
|
|
405
|
+
schemaVersion: 1,
|
|
406
|
+
type: "user/set-package-enabled",
|
|
407
|
+
commandId: "enable-ollama",
|
|
408
|
+
expectedRevision: 3,
|
|
409
|
+
packageId: "provider-ollama-cloud",
|
|
410
|
+
enabled: true,
|
|
411
|
+
},
|
|
412
|
+
}),
|
|
413
|
+
).resolves.toMatchObject({ status: "applied", revision: 4 });
|
|
414
|
+
});
|
|
415
|
+
|
|
416
|
+
test("applies the platform model through the backend Contribution", async () => {
|
|
417
|
+
const storage = new MemoryStorage();
|
|
418
|
+
const settings = contribution(storage);
|
|
419
|
+
|
|
420
|
+
await expect(
|
|
421
|
+
settings.executeConfigurationCommand(
|
|
422
|
+
"user-1",
|
|
423
|
+
{
|
|
424
|
+
schemaVersion: 1,
|
|
425
|
+
type: "user/set-platform-model",
|
|
426
|
+
commandId: "bootstrap-platform-model",
|
|
427
|
+
expectedRevision: 0,
|
|
428
|
+
model: {
|
|
429
|
+
connectionId: "flock-default",
|
|
430
|
+
providerModelId: "@flock/auto",
|
|
431
|
+
},
|
|
432
|
+
},
|
|
433
|
+
storage,
|
|
434
|
+
),
|
|
435
|
+
).resolves.toMatchObject({ status: "applied", revision: 1 });
|
|
436
|
+
expect((await settings.read("user-1")).platformModel).toEqual({
|
|
437
|
+
connectionId: "flock-default",
|
|
438
|
+
providerModelId: "@flock/auto",
|
|
439
|
+
});
|
|
440
|
+
});
|
|
441
|
+
|
|
153
442
|
test("owns durable User configuration independently of providers", async () => {
|
|
154
443
|
const storage = new MemoryStorage();
|
|
155
444
|
const settings = contribution(storage);
|
|
@@ -223,233 +512,6 @@ describe("User settings backend Contribution", () => {
|
|
|
223
512
|
).rejects.toThrow("Package is not available in this application");
|
|
224
513
|
});
|
|
225
514
|
|
|
226
|
-
test("coordinates Connection dependencies in provider-neutral User state", async () => {
|
|
227
|
-
const settings = contribution();
|
|
228
|
-
await settings.executeConfiguration({
|
|
229
|
-
schemaVersion: 1,
|
|
230
|
-
userId: "user-1",
|
|
231
|
-
command: {
|
|
232
|
-
schemaVersion: 1,
|
|
233
|
-
type: "user/install-package",
|
|
234
|
-
commandId: "install-ollama",
|
|
235
|
-
expectedRevision: 0,
|
|
236
|
-
packageId: "provider-ollama-cloud",
|
|
237
|
-
version: "0.0.1",
|
|
238
|
-
},
|
|
239
|
-
});
|
|
240
|
-
await settings.createConnection("user-1", {
|
|
241
|
-
connectionId: "ollama-1",
|
|
242
|
-
packageId: "provider-ollama-cloud",
|
|
243
|
-
connectionTypeId: "ollama-cloud-account",
|
|
244
|
-
displayName: "Work",
|
|
245
|
-
state: "ready",
|
|
246
|
-
providerType: "ollama-cloud",
|
|
247
|
-
generation: "connection-generation",
|
|
248
|
-
safeMetadata: {},
|
|
249
|
-
});
|
|
250
|
-
const requirement = {
|
|
251
|
-
schemaVersion: 1 as const,
|
|
252
|
-
packageId: "provider-ollama-cloud",
|
|
253
|
-
packageVersion: "0.0.1",
|
|
254
|
-
capabilityId: "ollama-cloud-models",
|
|
255
|
-
connectionTypeIds: ["ollama-cloud-account"],
|
|
256
|
-
};
|
|
257
|
-
|
|
258
|
-
await expect(
|
|
259
|
-
settings.claimConnectionDependency(
|
|
260
|
-
"user-1",
|
|
261
|
-
"ollama-1",
|
|
262
|
-
"bot-1",
|
|
263
|
-
"assignment-1",
|
|
264
|
-
requirement,
|
|
265
|
-
),
|
|
266
|
-
).resolves.toBe(true);
|
|
267
|
-
await expect(
|
|
268
|
-
settings.acknowledgeConnectionDependency(
|
|
269
|
-
"user-1",
|
|
270
|
-
"ollama-1",
|
|
271
|
-
"bot-1",
|
|
272
|
-
"assignment-1",
|
|
273
|
-
),
|
|
274
|
-
).resolves.toBe(true);
|
|
275
|
-
expect(
|
|
276
|
-
(await settings.getConnection("user-1", "ollama-1"))?.safeMetadata,
|
|
277
|
-
).toMatchObject({
|
|
278
|
-
dependentAssignments: [
|
|
279
|
-
{
|
|
280
|
-
botId: "bot-1",
|
|
281
|
-
generation: "assignment-1",
|
|
282
|
-
packageId: "provider-ollama-cloud",
|
|
283
|
-
capabilityId: "ollama-cloud-models",
|
|
284
|
-
status: "acknowledged",
|
|
285
|
-
},
|
|
286
|
-
],
|
|
287
|
-
});
|
|
288
|
-
await expect(
|
|
289
|
-
settings.compensateConnectionDependency(
|
|
290
|
-
"user-1",
|
|
291
|
-
"ollama-1",
|
|
292
|
-
"bot-1",
|
|
293
|
-
"assignment-1",
|
|
294
|
-
),
|
|
295
|
-
).resolves.toBe(false);
|
|
296
|
-
|
|
297
|
-
await settings.createConnection("user-1", {
|
|
298
|
-
connectionId: "ollama-2",
|
|
299
|
-
packageId: "provider-ollama-cloud",
|
|
300
|
-
connectionTypeId: "ollama-cloud-account",
|
|
301
|
-
displayName: "Personal",
|
|
302
|
-
state: "ready",
|
|
303
|
-
providerType: "ollama-cloud",
|
|
304
|
-
generation: "connection-generation-2",
|
|
305
|
-
safeMetadata: {},
|
|
306
|
-
});
|
|
307
|
-
await settings.claimConnectionDependency(
|
|
308
|
-
"user-1",
|
|
309
|
-
"ollama-2",
|
|
310
|
-
"bot-1",
|
|
311
|
-
"assignment-2",
|
|
312
|
-
requirement,
|
|
313
|
-
);
|
|
314
|
-
await settings.acknowledgeConnectionDependency(
|
|
315
|
-
"user-1",
|
|
316
|
-
"ollama-2",
|
|
317
|
-
"bot-1",
|
|
318
|
-
"assignment-2",
|
|
319
|
-
);
|
|
320
|
-
|
|
321
|
-
expect(
|
|
322
|
-
(await settings.getConnection("user-1", "ollama-1"))?.safeMetadata,
|
|
323
|
-
).toMatchObject({ dependentAssignments: [] });
|
|
324
|
-
expect(
|
|
325
|
-
(await settings.getConnection("user-1", "ollama-2"))?.safeMetadata,
|
|
326
|
-
).toMatchObject({
|
|
327
|
-
dependentAssignments: [
|
|
328
|
-
{
|
|
329
|
-
botId: "bot-1",
|
|
330
|
-
generation: "assignment-2",
|
|
331
|
-
packageId: "provider-ollama-cloud",
|
|
332
|
-
capabilityId: "ollama-cloud-models",
|
|
333
|
-
status: "acknowledged",
|
|
334
|
-
},
|
|
335
|
-
],
|
|
336
|
-
});
|
|
337
|
-
await expect(
|
|
338
|
-
settings.releaseConnectionDependency(
|
|
339
|
-
"user-1",
|
|
340
|
-
"ollama-2",
|
|
341
|
-
"bot-1",
|
|
342
|
-
"assignment-2",
|
|
343
|
-
),
|
|
344
|
-
).resolves.toBe(true);
|
|
345
|
-
await expect(
|
|
346
|
-
settings.releaseConnectionDependency(
|
|
347
|
-
"user-1",
|
|
348
|
-
"ollama-2",
|
|
349
|
-
"bot-1",
|
|
350
|
-
"assignment-2",
|
|
351
|
-
),
|
|
352
|
-
).resolves.toBe(true);
|
|
353
|
-
expect(
|
|
354
|
-
(await settings.getConnection("user-1", "ollama-2"))?.safeMetadata,
|
|
355
|
-
).toMatchObject({ dependentAssignments: [] });
|
|
356
|
-
});
|
|
357
|
-
|
|
358
|
-
test("rejects acknowledgement from a superseded dependency claim", async () => {
|
|
359
|
-
const settings = contribution();
|
|
360
|
-
await settings.executeConfiguration({
|
|
361
|
-
schemaVersion: 1,
|
|
362
|
-
userId: "user-1",
|
|
363
|
-
command: {
|
|
364
|
-
schemaVersion: 1,
|
|
365
|
-
type: "user/install-package",
|
|
366
|
-
commandId: "install-fenced-package",
|
|
367
|
-
expectedRevision: 0,
|
|
368
|
-
packageId: "provider-ollama-cloud",
|
|
369
|
-
version: "0.0.1",
|
|
370
|
-
},
|
|
371
|
-
});
|
|
372
|
-
for (const connectionId of ["ollama-old", "ollama-new"]) {
|
|
373
|
-
await settings.createConnection("user-1", {
|
|
374
|
-
connectionId,
|
|
375
|
-
packageId: "provider-ollama-cloud",
|
|
376
|
-
connectionTypeId: "ollama-cloud-account",
|
|
377
|
-
displayName: connectionId,
|
|
378
|
-
state: "ready",
|
|
379
|
-
providerType: "ollama-cloud",
|
|
380
|
-
generation: `${connectionId}-generation`,
|
|
381
|
-
safeMetadata: {},
|
|
382
|
-
});
|
|
383
|
-
}
|
|
384
|
-
const requirement = {
|
|
385
|
-
schemaVersion: 1 as const,
|
|
386
|
-
packageId: "provider-ollama-cloud",
|
|
387
|
-
packageVersion: "0.0.1",
|
|
388
|
-
capabilityId: "ollama-cloud-models",
|
|
389
|
-
connectionTypeIds: ["ollama-cloud-account"],
|
|
390
|
-
};
|
|
391
|
-
await settings.claimConnectionDependency(
|
|
392
|
-
"user-1",
|
|
393
|
-
"ollama-old",
|
|
394
|
-
"bot-1",
|
|
395
|
-
"assignment-old",
|
|
396
|
-
requirement,
|
|
397
|
-
);
|
|
398
|
-
await settings.claimConnectionDependency(
|
|
399
|
-
"user-1",
|
|
400
|
-
"ollama-new",
|
|
401
|
-
"bot-1",
|
|
402
|
-
"assignment-new",
|
|
403
|
-
requirement,
|
|
404
|
-
);
|
|
405
|
-
await settings.claimConnectionDependency(
|
|
406
|
-
"user-1",
|
|
407
|
-
"ollama-old",
|
|
408
|
-
"bot-1",
|
|
409
|
-
"assignment-old",
|
|
410
|
-
requirement,
|
|
411
|
-
);
|
|
412
|
-
|
|
413
|
-
await expect(
|
|
414
|
-
settings.acknowledgeConnectionDependency(
|
|
415
|
-
"user-1",
|
|
416
|
-
"ollama-old",
|
|
417
|
-
"bot-1",
|
|
418
|
-
"assignment-old",
|
|
419
|
-
),
|
|
420
|
-
).resolves.toBe(false);
|
|
421
|
-
expect(
|
|
422
|
-
(await settings.getConnection("user-1", "ollama-new"))?.safeMetadata,
|
|
423
|
-
).toMatchObject({
|
|
424
|
-
dependentAssignments: [
|
|
425
|
-
{ generation: "assignment-new", status: "pending" },
|
|
426
|
-
],
|
|
427
|
-
});
|
|
428
|
-
await expect(
|
|
429
|
-
settings.compensateConnectionDependency(
|
|
430
|
-
"user-1",
|
|
431
|
-
"ollama-old",
|
|
432
|
-
"bot-1",
|
|
433
|
-
"assignment-old",
|
|
434
|
-
),
|
|
435
|
-
).resolves.toBe(true);
|
|
436
|
-
expect(
|
|
437
|
-
(await settings.getConnection("user-1", "ollama-old"))?.safeMetadata,
|
|
438
|
-
).toMatchObject({ dependentAssignments: [] });
|
|
439
|
-
|
|
440
|
-
await expect(
|
|
441
|
-
settings.acknowledgeConnectionDependency(
|
|
442
|
-
"user-1",
|
|
443
|
-
"ollama-new",
|
|
444
|
-
"bot-1",
|
|
445
|
-
"assignment-new",
|
|
446
|
-
),
|
|
447
|
-
).resolves.toBe(true);
|
|
448
|
-
expect(
|
|
449
|
-
(await settings.getConnection("user-1", "ollama-old"))?.safeMetadata,
|
|
450
|
-
).toMatchObject({ dependentAssignments: [] });
|
|
451
|
-
});
|
|
452
|
-
|
|
453
515
|
test("compacts revoked Connections and bounds active Connections", async () => {
|
|
454
516
|
const settings = contribution();
|
|
455
517
|
await settings.read("user-1");
|
|
@@ -664,6 +726,9 @@ class FakeCatalog implements UserPackageCatalogHost {
|
|
|
664
726
|
version: entry.version,
|
|
665
727
|
manifestHash: entry.manifestHash,
|
|
666
728
|
kind: entry.kind,
|
|
729
|
+
...(entry.bundle === undefined
|
|
730
|
+
? {}
|
|
731
|
+
: { contentHash: entry.bundle.contentHash }),
|
|
667
732
|
})),
|
|
668
733
|
},
|
|
669
734
|
});
|
|
@@ -759,6 +824,89 @@ describe("remote Package Catalog", () => {
|
|
|
759
824
|
});
|
|
760
825
|
});
|
|
761
826
|
|
|
827
|
+
test("admits only the exact hash of a code-carrying Catalog entry", async () => {
|
|
828
|
+
const contentHash = "b".repeat(64);
|
|
829
|
+
const catalog = new FakeCatalog();
|
|
830
|
+
catalog.entries = [
|
|
831
|
+
catalogEntry({
|
|
832
|
+
packageId: "parcel-tracking",
|
|
833
|
+
catalogId: "parcel-tracking",
|
|
834
|
+
displayName: "Parcel tracking",
|
|
835
|
+
manifestHash: "e".repeat(64),
|
|
836
|
+
bundle: {
|
|
837
|
+
contentHash,
|
|
838
|
+
size: 512,
|
|
839
|
+
mediaType: "application/javascript",
|
|
840
|
+
bundlerVersion: "catalog-test@1",
|
|
841
|
+
manifest: {
|
|
842
|
+
schemaVersion: 3,
|
|
843
|
+
id: "parcel-tracking",
|
|
844
|
+
displayName: "Parcel tracking",
|
|
845
|
+
version: "0.0.1",
|
|
846
|
+
compatibility: { frockbot: "*" },
|
|
847
|
+
dependencies: {},
|
|
848
|
+
contributions: {
|
|
849
|
+
runtime: { entry: "./package.js", host: "bot-isolate" },
|
|
850
|
+
},
|
|
851
|
+
tools: [],
|
|
852
|
+
permissions: [],
|
|
853
|
+
},
|
|
854
|
+
},
|
|
855
|
+
}),
|
|
856
|
+
];
|
|
857
|
+
const { settings, storage } = catalogContribution(
|
|
858
|
+
new MemoryStorage(),
|
|
859
|
+
catalog,
|
|
860
|
+
);
|
|
861
|
+
await settings.readConfiguration({ schemaVersion: 1, userId: "user-1" });
|
|
862
|
+
|
|
863
|
+
await expect(
|
|
864
|
+
settings.executeConfiguration({
|
|
865
|
+
schemaVersion: 1,
|
|
866
|
+
userId: "user-1",
|
|
867
|
+
command: {
|
|
868
|
+
schemaVersion: 1,
|
|
869
|
+
type: "user/install-package",
|
|
870
|
+
commandId: "install-parcel",
|
|
871
|
+
expectedRevision: 0,
|
|
872
|
+
packageId: "parcel-tracking",
|
|
873
|
+
version: "0.0.1",
|
|
874
|
+
catalogId: "parcel-tracking",
|
|
875
|
+
catalogGeneration: "gen-one",
|
|
876
|
+
contentHash,
|
|
877
|
+
},
|
|
878
|
+
}),
|
|
879
|
+
).resolves.toMatchObject({ status: "applied" });
|
|
880
|
+
expect(
|
|
881
|
+
(await storage.get<UserSettingsViewV1>("user-configuration"))?.packages,
|
|
882
|
+
).toEqual([
|
|
883
|
+
expect.objectContaining({ packageId: "parcel-tracking", contentHash }),
|
|
884
|
+
]);
|
|
885
|
+
|
|
886
|
+
const mismatch = catalogContribution(new MemoryStorage(), catalog);
|
|
887
|
+
await mismatch.settings.readConfiguration({
|
|
888
|
+
schemaVersion: 1,
|
|
889
|
+
userId: "user-1",
|
|
890
|
+
});
|
|
891
|
+
await expect(
|
|
892
|
+
mismatch.settings.executeConfiguration({
|
|
893
|
+
schemaVersion: 1,
|
|
894
|
+
userId: "user-1",
|
|
895
|
+
command: {
|
|
896
|
+
schemaVersion: 1,
|
|
897
|
+
type: "user/install-package",
|
|
898
|
+
commandId: "install-wrong-hash",
|
|
899
|
+
expectedRevision: 0,
|
|
900
|
+
packageId: "parcel-tracking",
|
|
901
|
+
version: "0.0.1",
|
|
902
|
+
catalogId: "parcel-tracking",
|
|
903
|
+
catalogGeneration: "gen-one",
|
|
904
|
+
contentHash: "c".repeat(64),
|
|
905
|
+
},
|
|
906
|
+
}),
|
|
907
|
+
).rejects.toThrow('requires bundle hash "bbbb');
|
|
908
|
+
});
|
|
909
|
+
|
|
762
910
|
test("refuses an install off the pinned generation", async () => {
|
|
763
911
|
const { catalog, settings } = catalogContribution();
|
|
764
912
|
await settings.readConfiguration({ schemaVersion: 1, userId: "user-1" });
|
|
@@ -935,92 +1083,6 @@ describe("uninstall", () => {
|
|
|
935
1083
|
]);
|
|
936
1084
|
});
|
|
937
1085
|
|
|
938
|
-
test("dependent Assignments become unavailable tombstones, not deletions", async () => {
|
|
939
|
-
const storage = new MemoryStorage();
|
|
940
|
-
const settings = contribution(storage);
|
|
941
|
-
await settings.executeConfiguration({
|
|
942
|
-
schemaVersion: 1,
|
|
943
|
-
userId: "user-1",
|
|
944
|
-
command: {
|
|
945
|
-
schemaVersion: 1,
|
|
946
|
-
type: "user/install-package",
|
|
947
|
-
commandId: "install-provider",
|
|
948
|
-
expectedRevision: 0,
|
|
949
|
-
packageId: "provider-ollama-cloud",
|
|
950
|
-
version: "0.0.1",
|
|
951
|
-
},
|
|
952
|
-
});
|
|
953
|
-
await settings.createConnection("user-1", {
|
|
954
|
-
connectionId: "connection-1",
|
|
955
|
-
packageId: "provider-ollama-cloud",
|
|
956
|
-
connectionTypeId: "ollama-cloud-account",
|
|
957
|
-
displayName: "Work",
|
|
958
|
-
state: "ready",
|
|
959
|
-
safeMetadata: {},
|
|
960
|
-
});
|
|
961
|
-
const assignment = {
|
|
962
|
-
assignmentId: "assignment-1",
|
|
963
|
-
packageId: "provider-ollama-cloud",
|
|
964
|
-
capabilityId: "ollama-cloud-models",
|
|
965
|
-
connectionId: "connection-1",
|
|
966
|
-
};
|
|
967
|
-
const packages = [
|
|
968
|
-
{
|
|
969
|
-
packageId: "provider-ollama-cloud",
|
|
970
|
-
version: "0.0.1",
|
|
971
|
-
capabilities: [
|
|
972
|
-
{
|
|
973
|
-
id: "ollama-cloud-models",
|
|
974
|
-
kind: "model" as const,
|
|
975
|
-
connectionTypes: ["ollama-cloud-account"],
|
|
976
|
-
},
|
|
977
|
-
],
|
|
978
|
-
connectionTypes: [
|
|
979
|
-
{
|
|
980
|
-
id: "ollama-cloud-account",
|
|
981
|
-
capabilities: ["ollama-cloud-models"],
|
|
982
|
-
},
|
|
983
|
-
],
|
|
984
|
-
},
|
|
985
|
-
];
|
|
986
|
-
|
|
987
|
-
const installed = await settings.readSnapshot();
|
|
988
|
-
expect(
|
|
989
|
-
capabilityAssignmentFailureV1({ assignment, user: installed, packages }),
|
|
990
|
-
).toBeUndefined();
|
|
991
|
-
|
|
992
|
-
await settings.executeConfiguration({
|
|
993
|
-
schemaVersion: 1,
|
|
994
|
-
userId: "user-1",
|
|
995
|
-
command: {
|
|
996
|
-
schemaVersion: 1,
|
|
997
|
-
type: "user/uninstall-package",
|
|
998
|
-
commandId: "uninstall-provider",
|
|
999
|
-
expectedRevision: installed.revision,
|
|
1000
|
-
packageId: "provider-ollama-cloud",
|
|
1001
|
-
},
|
|
1002
|
-
});
|
|
1003
|
-
|
|
1004
|
-
const uninstalled = await settings.readSnapshot();
|
|
1005
|
-
expect(
|
|
1006
|
-
capabilityAssignmentFailureV1({
|
|
1007
|
-
assignment,
|
|
1008
|
-
user: uninstalled,
|
|
1009
|
-
packages,
|
|
1010
|
-
}),
|
|
1011
|
-
).toContain("is not installed and enabled");
|
|
1012
|
-
expect(
|
|
1013
|
-
resolveBotExecutionPlanV1({
|
|
1014
|
-
bot: {
|
|
1015
|
-
...initializeBotSettingsV1("bot-1"),
|
|
1016
|
-
assignments: [{ ...assignment, state: "enabled" }],
|
|
1017
|
-
},
|
|
1018
|
-
user: uninstalled,
|
|
1019
|
-
packages,
|
|
1020
|
-
}).assignments,
|
|
1021
|
-
).toEqual([{ ...assignment, state: "unavailable" }]);
|
|
1022
|
-
});
|
|
1023
|
-
|
|
1024
1086
|
test("refuses to uninstall a Package that is not installed", async () => {
|
|
1025
1087
|
const settings = contribution();
|
|
1026
1088
|
|
|
@@ -1079,6 +1141,53 @@ describe("Package-level setting values", () => {
|
|
|
1079
1141
|
});
|
|
1080
1142
|
});
|
|
1081
1143
|
|
|
1144
|
+
test("removes only declared setting ids and drops an emptied value bag", async () => {
|
|
1145
|
+
const settings = contribution();
|
|
1146
|
+
await installOllama(settings, "user-1");
|
|
1147
|
+
await settings.executeConfiguration({
|
|
1148
|
+
schemaVersion: 1,
|
|
1149
|
+
userId: "user-1",
|
|
1150
|
+
command: {
|
|
1151
|
+
schemaVersion: 1,
|
|
1152
|
+
type: "user/set-package-settings",
|
|
1153
|
+
commandId: "set-before-unset",
|
|
1154
|
+
expectedRevision: 1,
|
|
1155
|
+
packageId: "provider-ollama-cloud",
|
|
1156
|
+
values: { "web-search-max-results": 4 },
|
|
1157
|
+
},
|
|
1158
|
+
});
|
|
1159
|
+
|
|
1160
|
+
await settings.executeConfiguration({
|
|
1161
|
+
schemaVersion: 1,
|
|
1162
|
+
userId: "user-1",
|
|
1163
|
+
command: {
|
|
1164
|
+
schemaVersion: 1,
|
|
1165
|
+
type: "user/set-package-settings",
|
|
1166
|
+
commandId: "unset-value",
|
|
1167
|
+
expectedRevision: 2,
|
|
1168
|
+
packageId: "provider-ollama-cloud",
|
|
1169
|
+
unset: ["web-search-max-results"],
|
|
1170
|
+
},
|
|
1171
|
+
});
|
|
1172
|
+
expect((await settings.read("user-1")).packages[0]?.values).toBeUndefined();
|
|
1173
|
+
|
|
1174
|
+
await expect(
|
|
1175
|
+
settings.executeConfiguration({
|
|
1176
|
+
schemaVersion: 1,
|
|
1177
|
+
userId: "user-1",
|
|
1178
|
+
command: {
|
|
1179
|
+
schemaVersion: 1,
|
|
1180
|
+
type: "user/set-package-settings",
|
|
1181
|
+
commandId: "unset-unknown",
|
|
1182
|
+
expectedRevision: 3,
|
|
1183
|
+
packageId: "provider-ollama-cloud",
|
|
1184
|
+
unset: ["unknown-setting"],
|
|
1185
|
+
},
|
|
1186
|
+
}),
|
|
1187
|
+
).rejects.toThrow(/not declared by this Package/);
|
|
1188
|
+
expect((await settings.read("user-1")).revision).toBe(3);
|
|
1189
|
+
});
|
|
1190
|
+
|
|
1082
1191
|
test("a replayed command id returns its receipt without applying twice", async () => {
|
|
1083
1192
|
const settings = contribution();
|
|
1084
1193
|
await installOllama(settings, "user-1");
|