@frockbot/plugin-settings 0.1.4 → 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/package.json +9 -9
- package/src/user.test.ts +155 -0
- package/src/user.ts +21 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@frockbot/plugin-settings",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
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.
|
|
25
|
-
"@frockbot/client-core": "0.
|
|
26
|
-
"@frockbot/client-ui": "0.
|
|
27
|
-
"@frockbot/configuration-core": "0.
|
|
28
|
-
"@frockbot/connection-core": "0.
|
|
29
|
-
"@frockbot/kernel-composition": "0.
|
|
30
|
-
"@frockbot/plugin-auth": "0.
|
|
31
|
-
"@frockbot/plugin-shell": "0.
|
|
24
|
+
"@frockbot/catalog-core": "0.2.0",
|
|
25
|
+
"@frockbot/client-core": "0.2.0",
|
|
26
|
+
"@frockbot/client-ui": "0.2.0",
|
|
27
|
+
"@frockbot/configuration-core": "0.2.0",
|
|
28
|
+
"@frockbot/connection-core": "0.2.0",
|
|
29
|
+
"@frockbot/kernel-composition": "0.2.0",
|
|
30
|
+
"@frockbot/plugin-auth": "0.2.0",
|
|
31
|
+
"@frockbot/plugin-shell": "0.2.0",
|
|
32
32
|
"cordis": "4.0.0-rc.8",
|
|
33
33
|
"vue": "3.5.41"
|
|
34
34
|
},
|
package/src/user.test.ts
CHANGED
|
@@ -82,6 +82,75 @@ async function installOllama(
|
|
|
82
82
|
}
|
|
83
83
|
|
|
84
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
|
+
|
|
85
154
|
test("bootstraps first-party Packages once and preserves a later uninstall", async () => {
|
|
86
155
|
const storage = new MemoryStorage();
|
|
87
156
|
const settings = createUserSettingsBackendContribution({
|
|
@@ -657,6 +726,9 @@ class FakeCatalog implements UserPackageCatalogHost {
|
|
|
657
726
|
version: entry.version,
|
|
658
727
|
manifestHash: entry.manifestHash,
|
|
659
728
|
kind: entry.kind,
|
|
729
|
+
...(entry.bundle === undefined
|
|
730
|
+
? {}
|
|
731
|
+
: { contentHash: entry.bundle.contentHash }),
|
|
660
732
|
})),
|
|
661
733
|
},
|
|
662
734
|
});
|
|
@@ -752,6 +824,89 @@ describe("remote Package Catalog", () => {
|
|
|
752
824
|
});
|
|
753
825
|
});
|
|
754
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
|
+
|
|
755
910
|
test("refuses an install off the pinned generation", async () => {
|
|
756
911
|
const { catalog, settings } = catalogContribution();
|
|
757
912
|
await settings.readConfiguration({ schemaVersion: 1, userId: "user-1" });
|
package/src/user.ts
CHANGED
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
decodeUserConfigurationExecuteRpcV1,
|
|
10
10
|
decodeUserConfigurationReadRpcV1,
|
|
11
11
|
decodeUserSettingsViewV1,
|
|
12
|
+
migrateStoredUserSettingsV1,
|
|
12
13
|
MAX_USER_CONNECTIONS_V1,
|
|
13
14
|
USER_PROFILE_PLACEHOLDER_NAME_V1,
|
|
14
15
|
type ConnectionView,
|
|
@@ -302,6 +303,9 @@ function applyUserCommand(
|
|
|
302
303
|
: {
|
|
303
304
|
catalogId: command.catalogId,
|
|
304
305
|
catalogGeneration: command.catalogGeneration,
|
|
306
|
+
...(command.contentHash === undefined
|
|
307
|
+
? {}
|
|
308
|
+
: { contentHash: command.contentHash }),
|
|
305
309
|
provenance: "catalog" as const,
|
|
306
310
|
}),
|
|
307
311
|
...(Object.keys(values).length === 0 ? {} : { values }),
|
|
@@ -615,6 +619,7 @@ export class UserSettingsBackendContribution {
|
|
|
615
619
|
version: string;
|
|
616
620
|
catalogId: string;
|
|
617
621
|
catalogGeneration: string;
|
|
622
|
+
contentHash?: string;
|
|
618
623
|
}): Promise<CatalogEntryV1> {
|
|
619
624
|
const catalog = this.host.catalog;
|
|
620
625
|
if (!catalog) {
|
|
@@ -639,6 +644,17 @@ export class UserSettingsBackendContribution {
|
|
|
639
644
|
`Catalog entry "${command.catalogId}" does not offer Package "${command.packageId}" at version "${command.version}"`,
|
|
640
645
|
);
|
|
641
646
|
}
|
|
647
|
+
if (entry.bundle) {
|
|
648
|
+
if (command.contentHash !== entry.bundle.contentHash) {
|
|
649
|
+
throw new Error(
|
|
650
|
+
`Catalog entry "${command.catalogId}" requires bundle hash "${entry.bundle.contentHash}"`,
|
|
651
|
+
);
|
|
652
|
+
}
|
|
653
|
+
} else if (command.contentHash !== undefined) {
|
|
654
|
+
throw new Error(
|
|
655
|
+
`Catalog entry "${command.catalogId}" does not carry a Package bundle`,
|
|
656
|
+
);
|
|
657
|
+
}
|
|
642
658
|
return entry;
|
|
643
659
|
}
|
|
644
660
|
|
|
@@ -656,6 +672,9 @@ export class UserSettingsBackendContribution {
|
|
|
656
672
|
version: command.version,
|
|
657
673
|
catalogId: command.catalogId,
|
|
658
674
|
catalogGeneration: command.catalogGeneration,
|
|
675
|
+
...(command.contentHash === undefined
|
|
676
|
+
? {}
|
|
677
|
+
: { contentHash: command.contentHash }),
|
|
659
678
|
})
|
|
660
679
|
: undefined;
|
|
661
680
|
return this.host.storage.transaction((storage) =>
|
|
@@ -731,7 +750,7 @@ export class UserSettingsBackendContribution {
|
|
|
731
750
|
const current =
|
|
732
751
|
storedSettings === undefined
|
|
733
752
|
? initialState()
|
|
734
|
-
: decodeUserSettingsViewV1(storedSettings);
|
|
753
|
+
: decodeUserSettingsViewV1(migrateStoredUserSettingsV1(storedSettings));
|
|
735
754
|
if (command.type === "user/set-package-enabled" && command.enabled) {
|
|
736
755
|
const installed = current.packages.find(
|
|
737
756
|
(pkg) => pkg.packageId === command.packageId,
|
|
@@ -800,7 +819,7 @@ export class UserSettingsBackendContribution {
|
|
|
800
819
|
const stored = await storage.get<unknown>(STATE_KEY);
|
|
801
820
|
return stored === undefined
|
|
802
821
|
? initialState()
|
|
803
|
-
: decodeUserSettingsViewV1(stored);
|
|
822
|
+
: decodeUserSettingsViewV1(migrateStoredUserSettingsV1(stored));
|
|
804
823
|
}
|
|
805
824
|
|
|
806
825
|
async read(
|