@frockbot/plugin-settings 0.2.2 → 0.2.4
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/package.json +9 -9
- package/src/user.test.ts +189 -2
- package/src/user.ts +80 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@frockbot/plugin-settings",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.4",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -21,14 +21,14 @@
|
|
|
21
21
|
"typecheck": "vue-tsc --noEmit -p tsconfig.json"
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@frockbot/catalog-core": "0.2.
|
|
25
|
-
"@frockbot/client-core": "0.2.
|
|
26
|
-
"@frockbot/client-ui": "0.2.
|
|
27
|
-
"@frockbot/configuration-core": "0.2.
|
|
28
|
-
"@frockbot/connection-core": "0.2.
|
|
29
|
-
"@frockbot/kernel-composition": "0.2.
|
|
30
|
-
"@frockbot/plugin-auth": "0.2.
|
|
31
|
-
"@frockbot/plugin-shell": "0.2.
|
|
24
|
+
"@frockbot/catalog-core": "0.2.4",
|
|
25
|
+
"@frockbot/client-core": "0.2.4",
|
|
26
|
+
"@frockbot/client-ui": "0.2.4",
|
|
27
|
+
"@frockbot/configuration-core": "0.2.4",
|
|
28
|
+
"@frockbot/connection-core": "0.2.4",
|
|
29
|
+
"@frockbot/kernel-composition": "0.2.4",
|
|
30
|
+
"@frockbot/plugin-auth": "0.2.4",
|
|
31
|
+
"@frockbot/plugin-shell": "0.2.4",
|
|
32
32
|
"cordis": "4.0.0-rc.8",
|
|
33
33
|
"vue": "3.5.41"
|
|
34
34
|
},
|
package/src/user.test.ts
CHANGED
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
} from "@frockbot/configuration-core";
|
|
7
7
|
import {
|
|
8
8
|
createUserSettingsBackendContribution,
|
|
9
|
+
type AvailableUserPackage,
|
|
9
10
|
type UserPackageCatalogHost,
|
|
10
11
|
type UserSettingsStorage,
|
|
11
12
|
} from "./user.js";
|
|
@@ -246,6 +247,166 @@ describe("User settings backend Contribution", () => {
|
|
|
246
247
|
).toEqual(first);
|
|
247
248
|
});
|
|
248
249
|
|
|
250
|
+
test("repairs a pre-default-enablement account before Package enablement", async () => {
|
|
251
|
+
const storage = new MemoryStorage();
|
|
252
|
+
const availablePackages: AvailableUserPackage[] = [
|
|
253
|
+
{
|
|
254
|
+
packageId: "custom-models",
|
|
255
|
+
version: "0.0.1",
|
|
256
|
+
installByDefault: true,
|
|
257
|
+
defaultEnablement: "disabled",
|
|
258
|
+
dependencies: {
|
|
259
|
+
settings: ">=0.0.1",
|
|
260
|
+
shell: ">=0.0.1",
|
|
261
|
+
"ui-theme": ">=0.0.1",
|
|
262
|
+
},
|
|
263
|
+
},
|
|
264
|
+
{
|
|
265
|
+
packageId: "provider-ollama-cloud",
|
|
266
|
+
version: "0.0.1",
|
|
267
|
+
installByDefault: true,
|
|
268
|
+
defaultEnablement: "disabled",
|
|
269
|
+
dependencies: {
|
|
270
|
+
"custom-models": ">=0.0.1",
|
|
271
|
+
credentials: ">=0.0.1",
|
|
272
|
+
settings: ">=0.0.1",
|
|
273
|
+
web: ">=0.0.1",
|
|
274
|
+
},
|
|
275
|
+
},
|
|
276
|
+
{ packageId: "credentials", version: "0.0.1", installByDefault: true },
|
|
277
|
+
{ packageId: "settings", version: "0.0.1", installByDefault: true },
|
|
278
|
+
{ packageId: "shell", version: "0.0.1", installByDefault: true },
|
|
279
|
+
{ packageId: "ui-theme", version: "0.0.1", installByDefault: true },
|
|
280
|
+
{ packageId: "web", version: "0.0.1", installByDefault: true },
|
|
281
|
+
];
|
|
282
|
+
await storage.put("user-id", "legacy-user");
|
|
283
|
+
await storage.put("user-default-packages-bootstrap:v1", {
|
|
284
|
+
schemaVersion: 1,
|
|
285
|
+
});
|
|
286
|
+
await storage.put("user-configuration", {
|
|
287
|
+
schemaVersion: 1,
|
|
288
|
+
revision: 12,
|
|
289
|
+
profile: { name: "Legacy User" },
|
|
290
|
+
packages: [
|
|
291
|
+
{
|
|
292
|
+
packageId: "provider-workers-ai",
|
|
293
|
+
version: "0.0.1",
|
|
294
|
+
state: "installed",
|
|
295
|
+
},
|
|
296
|
+
{
|
|
297
|
+
packageId: "provider-ollama-cloud",
|
|
298
|
+
version: "0.0.1",
|
|
299
|
+
state: "installed",
|
|
300
|
+
},
|
|
301
|
+
],
|
|
302
|
+
connections: [
|
|
303
|
+
{
|
|
304
|
+
connectionId: "workers-ai-ambient",
|
|
305
|
+
packageId: "provider-workers-ai",
|
|
306
|
+
connectionTypeId: "workers-ai-account",
|
|
307
|
+
displayName: "Cloudflare Workers AI",
|
|
308
|
+
state: "ready",
|
|
309
|
+
generation: "workers-ai-ambient-v1",
|
|
310
|
+
providerType: "workers-ai",
|
|
311
|
+
safeMetadata: { catalog: "static" },
|
|
312
|
+
},
|
|
313
|
+
{
|
|
314
|
+
connectionId: "ollama-legacy",
|
|
315
|
+
packageId: "provider-ollama-cloud",
|
|
316
|
+
connectionTypeId: "ollama-cloud-account",
|
|
317
|
+
displayName: "Ollama",
|
|
318
|
+
state: "ready",
|
|
319
|
+
generation: "ollama-generation-1",
|
|
320
|
+
providerType: "ollama-cloud",
|
|
321
|
+
safeMetadata: {
|
|
322
|
+
dependentAssignments: [
|
|
323
|
+
{
|
|
324
|
+
botId: "primary",
|
|
325
|
+
generation: "assignment-1",
|
|
326
|
+
packageId: "provider-ollama-cloud",
|
|
327
|
+
capabilityId: "ollama-cloud-models",
|
|
328
|
+
claimOrder: 1,
|
|
329
|
+
status: "acknowledged",
|
|
330
|
+
},
|
|
331
|
+
],
|
|
332
|
+
},
|
|
333
|
+
},
|
|
334
|
+
],
|
|
335
|
+
platformModel: {
|
|
336
|
+
connectionId: "workers-ai-ambient",
|
|
337
|
+
providerModelId: "@cf/deepseek-ai/deepseek-v4-flash-0731",
|
|
338
|
+
},
|
|
339
|
+
newBotModelTemplate: {
|
|
340
|
+
connectionId: "ollama-legacy",
|
|
341
|
+
providerModelId: "glm-5.3-flash:cloud",
|
|
342
|
+
},
|
|
343
|
+
newBotModelTemplateSource: "user",
|
|
344
|
+
});
|
|
345
|
+
const settings = createUserSettingsBackendContribution({
|
|
346
|
+
storage,
|
|
347
|
+
availablePackages,
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
const migrated = await settings.readConfiguration({
|
|
351
|
+
schemaVersion: 1,
|
|
352
|
+
userId: "legacy-user",
|
|
353
|
+
});
|
|
354
|
+
expect(migrated.packages).toEqual([
|
|
355
|
+
{
|
|
356
|
+
packageId: "provider-ollama-cloud",
|
|
357
|
+
version: "0.0.1",
|
|
358
|
+
state: "disabled",
|
|
359
|
+
},
|
|
360
|
+
{
|
|
361
|
+
packageId: "custom-models",
|
|
362
|
+
version: "0.0.1",
|
|
363
|
+
state: "disabled",
|
|
364
|
+
provenance: "first-party",
|
|
365
|
+
},
|
|
366
|
+
...availablePackages.slice(2).map(({ packageId, version }) => ({
|
|
367
|
+
packageId,
|
|
368
|
+
version,
|
|
369
|
+
state: "installed" as const,
|
|
370
|
+
provenance: "first-party" as const,
|
|
371
|
+
})),
|
|
372
|
+
]);
|
|
373
|
+
expect(migrated.connections).toEqual([
|
|
374
|
+
expect.objectContaining({
|
|
375
|
+
connectionId: "ollama-legacy",
|
|
376
|
+
safeMetadata: {},
|
|
377
|
+
}),
|
|
378
|
+
]);
|
|
379
|
+
expect(migrated.platformModel).toBeUndefined();
|
|
380
|
+
|
|
381
|
+
const customModels = await settings.executeConfiguration({
|
|
382
|
+
schemaVersion: 1,
|
|
383
|
+
userId: "legacy-user",
|
|
384
|
+
command: {
|
|
385
|
+
schemaVersion: 1,
|
|
386
|
+
type: "user/set-package-enabled",
|
|
387
|
+
commandId: "enable-custom-models-after-migration",
|
|
388
|
+
expectedRevision: migrated.revision,
|
|
389
|
+
packageId: "custom-models",
|
|
390
|
+
enabled: true,
|
|
391
|
+
},
|
|
392
|
+
});
|
|
393
|
+
expect(customModels).toMatchObject({ status: "applied" });
|
|
394
|
+
await expect(
|
|
395
|
+
settings.executeConfiguration({
|
|
396
|
+
schemaVersion: 1,
|
|
397
|
+
userId: "legacy-user",
|
|
398
|
+
command: {
|
|
399
|
+
schemaVersion: 1,
|
|
400
|
+
type: "user/set-package-enabled",
|
|
401
|
+
commandId: "enable-ollama-after-migration",
|
|
402
|
+
expectedRevision: customModels.revision,
|
|
403
|
+
packageId: "provider-ollama-cloud",
|
|
404
|
+
enabled: true,
|
|
405
|
+
},
|
|
406
|
+
}),
|
|
407
|
+
).resolves.toMatchObject({ status: "applied" });
|
|
408
|
+
});
|
|
409
|
+
|
|
249
410
|
test("installs a Package disabled when explicitly requested", async () => {
|
|
250
411
|
const settings = contribution();
|
|
251
412
|
const receipt = await settings.executeConfiguration({
|
|
@@ -417,6 +578,32 @@ describe("User settings backend Contribution", () => {
|
|
|
417
578
|
const storage = new MemoryStorage();
|
|
418
579
|
const settings = contribution(storage);
|
|
419
580
|
|
|
581
|
+
await settings.executeConfigurationCommand(
|
|
582
|
+
"user-1",
|
|
583
|
+
{
|
|
584
|
+
schemaVersion: 1,
|
|
585
|
+
type: "user/install-package",
|
|
586
|
+
commandId: "bootstrap-platform-package",
|
|
587
|
+
expectedRevision: 0,
|
|
588
|
+
packageId: "flock",
|
|
589
|
+
version: "0.0.1",
|
|
590
|
+
},
|
|
591
|
+
storage,
|
|
592
|
+
);
|
|
593
|
+
await settings.createConnection(
|
|
594
|
+
"user-1",
|
|
595
|
+
{
|
|
596
|
+
connectionId: "flock-default",
|
|
597
|
+
packageId: "flock",
|
|
598
|
+
connectionTypeId: "flock-account",
|
|
599
|
+
displayName: "Flock",
|
|
600
|
+
state: "ready",
|
|
601
|
+
providerType: "flock",
|
|
602
|
+
safeMetadata: {},
|
|
603
|
+
},
|
|
604
|
+
storage,
|
|
605
|
+
);
|
|
606
|
+
|
|
420
607
|
await expect(
|
|
421
608
|
settings.executeConfigurationCommand(
|
|
422
609
|
"user-1",
|
|
@@ -424,7 +611,7 @@ describe("User settings backend Contribution", () => {
|
|
|
424
611
|
schemaVersion: 1,
|
|
425
612
|
type: "user/set-platform-model",
|
|
426
613
|
commandId: "bootstrap-platform-model",
|
|
427
|
-
expectedRevision:
|
|
614
|
+
expectedRevision: 2,
|
|
428
615
|
model: {
|
|
429
616
|
connectionId: "flock-default",
|
|
430
617
|
providerModelId: "@flock/auto",
|
|
@@ -432,7 +619,7 @@ describe("User settings backend Contribution", () => {
|
|
|
432
619
|
},
|
|
433
620
|
storage,
|
|
434
621
|
),
|
|
435
|
-
).resolves.toMatchObject({ status: "applied", revision:
|
|
622
|
+
).resolves.toMatchObject({ status: "applied", revision: 3 });
|
|
436
623
|
expect((await settings.read("user-1")).platformModel).toEqual({
|
|
437
624
|
connectionId: "flock-default",
|
|
438
625
|
providerModelId: "@flock/auto",
|
package/src/user.ts
CHANGED
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
type PackageSettingValueV1,
|
|
18
18
|
type OperationReceiptV1,
|
|
19
19
|
type PackageInstallationView,
|
|
20
|
+
type StoredUserSettingsPackageV1,
|
|
20
21
|
type UserConfigurationCommandV1,
|
|
21
22
|
type UserSettingsViewV1,
|
|
22
23
|
} from "@frockbot/configuration-core";
|
|
@@ -33,6 +34,7 @@ import type { Plugin } from "cordis";
|
|
|
33
34
|
|
|
34
35
|
const STATE_KEY = "user-configuration";
|
|
35
36
|
const DEFAULT_PACKAGES_BOOTSTRAP_KEY = "user-default-packages-bootstrap:v1";
|
|
37
|
+
const DEFAULT_PACKAGES_BOOTSTRAP_VERSION = 2;
|
|
36
38
|
/**
|
|
37
39
|
* The pinned Catalog generation lives beside the settings view rather than in
|
|
38
40
|
* it, so pinning on a read never bumps the settings revision a client is
|
|
@@ -397,6 +399,9 @@ function applyUserCommand(
|
|
|
397
399
|
export class UserSettingsBackendContribution {
|
|
398
400
|
private readonly availablePackages: ReadonlySet<string>;
|
|
399
401
|
|
|
402
|
+
/** Catalog-relative facts supplied to the raw stored-settings migration. */
|
|
403
|
+
private readonly storedSettingsPackages: readonly StoredUserSettingsPackageV1[];
|
|
404
|
+
|
|
400
405
|
/** Declared Package dependencies, by Package id and version. */
|
|
401
406
|
private readonly packageDependencies: ReadonlyMap<
|
|
402
407
|
string,
|
|
@@ -406,6 +411,13 @@ export class UserSettingsBackendContribution {
|
|
|
406
411
|
/** The immutable first-party installation rows written on first read. */
|
|
407
412
|
private readonly defaultPackages: readonly PackageInstallationView[];
|
|
408
413
|
|
|
414
|
+
/**
|
|
415
|
+
* Packages introduced by the default-enablement rollout, plus their
|
|
416
|
+
* dependency closure. A v1 marker predates those rows, so this is the one
|
|
417
|
+
* bounded set that marker migration may add without replaying every default.
|
|
418
|
+
*/
|
|
419
|
+
private readonly enablementRolloutPackages: readonly PackageInstallationView[];
|
|
420
|
+
|
|
409
421
|
/** Declared User-level settings, by Package id and version. */
|
|
410
422
|
private readonly packageSettingDefinitions: ReadonlyMap<
|
|
411
423
|
string,
|
|
@@ -420,6 +432,11 @@ export class UserSettingsBackendContribution {
|
|
|
420
432
|
>();
|
|
421
433
|
|
|
422
434
|
constructor(private readonly host: UserSettingsBackendHost) {
|
|
435
|
+
this.storedSettingsPackages = host.availablePackages.map((pkg) => ({
|
|
436
|
+
packageId: pkg.packageId,
|
|
437
|
+
version: pkg.version,
|
|
438
|
+
...(pkg.dependencies ? { dependencies: pkg.dependencies } : {}),
|
|
439
|
+
}));
|
|
423
440
|
this.availablePackages = new Set(
|
|
424
441
|
host.availablePackages.map(
|
|
425
442
|
({ packageId, version }) => `${packageId}\u0000${version}`,
|
|
@@ -452,6 +469,26 @@ export class UserSettingsBackendContribution {
|
|
|
452
469
|
]
|
|
453
470
|
: [],
|
|
454
471
|
);
|
|
472
|
+
const byPackageId = new Map(
|
|
473
|
+
host.availablePackages.map((pkg) => [pkg.packageId, pkg]),
|
|
474
|
+
);
|
|
475
|
+
const rolloutPackageIds = new Set(
|
|
476
|
+
host.availablePackages
|
|
477
|
+
.filter(
|
|
478
|
+
(pkg) => pkg.installByDefault && pkg.defaultEnablement !== undefined,
|
|
479
|
+
)
|
|
480
|
+
.map((pkg) => pkg.packageId),
|
|
481
|
+
);
|
|
482
|
+
for (const packageId of rolloutPackageIds) {
|
|
483
|
+
for (const dependencyId of Object.keys(
|
|
484
|
+
byPackageId.get(packageId)?.dependencies ?? {},
|
|
485
|
+
)) {
|
|
486
|
+
if (byPackageId.has(dependencyId)) rolloutPackageIds.add(dependencyId);
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
this.enablementRolloutPackages = this.defaultPackages.filter((pkg) =>
|
|
490
|
+
rolloutPackageIds.has(pkg.packageId),
|
|
491
|
+
);
|
|
455
492
|
}
|
|
456
493
|
|
|
457
494
|
/**
|
|
@@ -480,11 +517,49 @@ export class UserSettingsBackendContribution {
|
|
|
480
517
|
typeof marker !== "object" ||
|
|
481
518
|
Array.isArray(marker) ||
|
|
482
519
|
Object.keys(marker).length !== 1 ||
|
|
483
|
-
(marker as { schemaVersion?: unknown }).schemaVersion !== 1
|
|
520
|
+
((marker as { schemaVersion?: unknown }).schemaVersion !== 1 &&
|
|
521
|
+
(marker as { schemaVersion?: unknown }).schemaVersion !==
|
|
522
|
+
DEFAULT_PACKAGES_BOOTSTRAP_VERSION)
|
|
484
523
|
) {
|
|
485
524
|
throw new Error("Stored default Package bootstrap is invalid");
|
|
486
525
|
}
|
|
487
|
-
|
|
526
|
+
if (
|
|
527
|
+
(marker as { schemaVersion: number }).schemaVersion ===
|
|
528
|
+
DEFAULT_PACKAGES_BOOTSTRAP_VERSION
|
|
529
|
+
) {
|
|
530
|
+
return this.readSnapshot(transaction);
|
|
531
|
+
}
|
|
532
|
+
const stored = await transaction.get<unknown>(STATE_KEY);
|
|
533
|
+
const current =
|
|
534
|
+
stored === undefined
|
|
535
|
+
? initialState()
|
|
536
|
+
: decodeUserSettingsViewV1(
|
|
537
|
+
migrateStoredUserSettingsV1(
|
|
538
|
+
stored,
|
|
539
|
+
this.storedSettingsPackages,
|
|
540
|
+
),
|
|
541
|
+
);
|
|
542
|
+
const installedPackageIds = new Set(
|
|
543
|
+
current.packages.map((pkg) => pkg.packageId),
|
|
544
|
+
);
|
|
545
|
+
const additions = this.enablementRolloutPackages.filter(
|
|
546
|
+
(pkg) => !installedPackageIds.has(pkg.packageId),
|
|
547
|
+
);
|
|
548
|
+
const next = {
|
|
549
|
+
...current,
|
|
550
|
+
revision: current.revision + 1,
|
|
551
|
+
packages: [
|
|
552
|
+
...current.packages,
|
|
553
|
+
...additions.map((pkg) => structuredClone(pkg)),
|
|
554
|
+
],
|
|
555
|
+
} satisfies UserSettingsViewV1;
|
|
556
|
+
await transaction.put({
|
|
557
|
+
[STATE_KEY]: next,
|
|
558
|
+
[DEFAULT_PACKAGES_BOOTSTRAP_KEY]: {
|
|
559
|
+
schemaVersion: DEFAULT_PACKAGES_BOOTSTRAP_VERSION,
|
|
560
|
+
},
|
|
561
|
+
});
|
|
562
|
+
return structuredClone(next);
|
|
488
563
|
}
|
|
489
564
|
const current = await this.readSnapshot(transaction);
|
|
490
565
|
const installedPackageIds = new Set(
|
|
@@ -503,7 +578,9 @@ export class UserSettingsBackendContribution {
|
|
|
503
578
|
} satisfies UserSettingsViewV1;
|
|
504
579
|
await transaction.put({
|
|
505
580
|
[STATE_KEY]: next,
|
|
506
|
-
[DEFAULT_PACKAGES_BOOTSTRAP_KEY]: {
|
|
581
|
+
[DEFAULT_PACKAGES_BOOTSTRAP_KEY]: {
|
|
582
|
+
schemaVersion: DEFAULT_PACKAGES_BOOTSTRAP_VERSION,
|
|
583
|
+
},
|
|
507
584
|
});
|
|
508
585
|
return structuredClone(next);
|
|
509
586
|
};
|