dsh-plugin-shop 0.4.8 → 0.4.10
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/README.md +11 -7
- package/lib/client.js +179 -109
- package/lib/index.js +88 -10
- package/lib/typert.host.js +16 -13
- package/lib/typert.remote-client.js +14 -11
- package/lib/types/client/locales.d.ts +6 -0
- package/lib/types/client/present.d.ts +5 -0
- package/lib/types/host/catalog.d.ts +3 -2
- package/lib/types/host/index.d.ts +10 -0
- package/lib/types/host/install.d.ts +1 -1
- package/lib/types/host/repo-pins.d.ts +22 -0
- package/lib/types/host/types.d.ts +6 -1
- package/package.json +1 -1
package/lib/index.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { Remote, TypertRemoteService } from "@deepseek-ai/dsh-typert-protocol";
|
|
2
2
|
import { loadOptionalPatches, readProfileManifest, resolveProfileDir } from "@deepseek-ai/dsh-app-boot";
|
|
3
3
|
import { lt, minVersion, valid } from "semver";
|
|
4
|
+
import { closeSync, existsSync, mkdirSync, openSync, readFileSync, realpathSync, renameSync, writeFileSync } from "node:fs";
|
|
5
|
+
import { spawn, spawnSync } from "node:child_process";
|
|
4
6
|
import { fileURLToPath } from "node:url";
|
|
5
7
|
import { basename, dirname, join } from "node:path";
|
|
6
|
-
import { closeSync, existsSync, mkdirSync, openSync, readFileSync, realpathSync, renameSync, writeFileSync } from "node:fs";
|
|
7
8
|
import { createHash, randomUUID } from "node:crypto";
|
|
8
9
|
import { z } from "zod";
|
|
9
|
-
import { spawn } from "node:child_process";
|
|
10
10
|
import { dump } from "js-yaml";
|
|
11
11
|
//#region src/own-version.ts
|
|
12
12
|
/** The shop's own published version, read from the package.json that ships
|
|
@@ -37,7 +37,8 @@ const entrySchema = z.object({
|
|
|
37
37
|
]),
|
|
38
38
|
metadata: z.enum(["declared", "derived"]),
|
|
39
39
|
review: z.object({
|
|
40
|
-
reviewedVersion: z.string(),
|
|
40
|
+
reviewedVersion: z.string().optional(),
|
|
41
|
+
reviewedCommit: z.string().optional(),
|
|
41
42
|
reviewer: z.string(),
|
|
42
43
|
reviewCommit: z.string(),
|
|
43
44
|
notes: z.string()
|
|
@@ -56,7 +57,9 @@ const entrySchema = z.object({
|
|
|
56
57
|
zh: z.string().optional()
|
|
57
58
|
}),
|
|
58
59
|
capabilities: z.array(z.string())
|
|
59
|
-
}).optional()
|
|
60
|
+
}).optional(),
|
|
61
|
+
source: z.enum(["npm", "github"]).default("npm"),
|
|
62
|
+
repo: z.string().optional()
|
|
60
63
|
});
|
|
61
64
|
const dataSchema = z.object({
|
|
62
65
|
schemaVersion: z.number(),
|
|
@@ -140,13 +143,13 @@ async function loadCatalog(options) {
|
|
|
140
143
|
const readCached = () => {
|
|
141
144
|
try {
|
|
142
145
|
const pointer = pointerSchema.parse(JSON.parse(fsImpl.read(indexPath)));
|
|
143
|
-
if (pointer.schemaVersion >
|
|
146
|
+
if (pointer.schemaVersion > 3) throw new Error(`catalog schemaVersion ${pointer.schemaVersion} is newer than this build supports (3)`);
|
|
144
147
|
const dataPath = join(cacheDir, basename(pointer.plugins.url));
|
|
145
148
|
const dataText = fsImpl.read(dataPath);
|
|
146
149
|
const actual = createHash("sha256").update(dataText).digest("hex");
|
|
147
150
|
if (actual !== pointer.plugins.sha256) throw new Error(`cached catalog data failed integrity check: expected ${pointer.plugins.sha256}, got ${actual}`);
|
|
148
151
|
const data = dataSchema.parse(JSON.parse(dataText));
|
|
149
|
-
if (data.schemaVersion >
|
|
152
|
+
if (data.schemaVersion > 3) throw new Error(`catalog schemaVersion ${data.schemaVersion} is newer than this build supports (3)`);
|
|
150
153
|
let stars = {};
|
|
151
154
|
if (pointer.stars !== void 0) try {
|
|
152
155
|
const starsText = fsImpl.read(join(cacheDir, basename(pointer.stars.url)));
|
|
@@ -187,7 +190,7 @@ async function loadCatalog(options) {
|
|
|
187
190
|
throw error;
|
|
188
191
|
}
|
|
189
192
|
const pointer = pointerSchema.parse(JSON.parse(pointerText));
|
|
190
|
-
if (pointer.schemaVersion >
|
|
193
|
+
if (pointer.schemaVersion > 3) throw new Error(`catalog schemaVersion ${pointer.schemaVersion} is newer than this build supports (3)`);
|
|
191
194
|
const dataUrl = resolveDataUrl(baseUrl, pointer.plugins.url);
|
|
192
195
|
let dataText;
|
|
193
196
|
try {
|
|
@@ -205,7 +208,7 @@ async function loadCatalog(options) {
|
|
|
205
208
|
const actual = createHash("sha256").update(dataText).digest("hex");
|
|
206
209
|
if (actual !== pointer.plugins.sha256) throw new Error(`catalog data failed integrity check: expected ${pointer.plugins.sha256}, got ${actual}`);
|
|
207
210
|
const data = dataSchema.parse(JSON.parse(dataText));
|
|
208
|
-
if (data.schemaVersion >
|
|
211
|
+
if (data.schemaVersion > 3) throw new Error(`catalog schemaVersion ${data.schemaVersion} is newer than this build supports (3)`);
|
|
209
212
|
let stars = {};
|
|
210
213
|
if (pointer.stars !== void 0) try {
|
|
211
214
|
const starsResponse = await fetchImpl(resolveDataUrl(baseUrl, pointer.stars.url));
|
|
@@ -497,6 +500,25 @@ async function fetchLatestVersion(fetchFn = fetch) {
|
|
|
497
500
|
}
|
|
498
501
|
}
|
|
499
502
|
//#endregion
|
|
503
|
+
//#region src/host/repo-pins.ts
|
|
504
|
+
/** Read the pins file; any irregularity degrades to an empty record. */
|
|
505
|
+
function readRepoPins(fs, path) {
|
|
506
|
+
if (!fs.exists(path)) return {};
|
|
507
|
+
try {
|
|
508
|
+
const parsed = JSON.parse(fs.read(path));
|
|
509
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) return {};
|
|
510
|
+
const out = {};
|
|
511
|
+
for (const [name, commit] of Object.entries(parsed)) if (typeof commit === "string" && /^[0-9a-f]{40}$/.test(commit)) out[name] = commit;
|
|
512
|
+
return out;
|
|
513
|
+
} catch {
|
|
514
|
+
return {};
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
/** Write the pins file atomically enough for its purpose (a cache). */
|
|
518
|
+
function writeRepoPins(fs, path, pins) {
|
|
519
|
+
fs.write(path, `${JSON.stringify(pins, null, 2)}\n`);
|
|
520
|
+
}
|
|
521
|
+
//#endregion
|
|
500
522
|
//#region src/host/profile.ts
|
|
501
523
|
/** Profile directory discovery and user-layer writes (§8: hot enable/disable). */
|
|
502
524
|
/**
|
|
@@ -759,6 +781,8 @@ let ShopGateway = (() => {
|
|
|
759
781
|
restartExitDelayMs;
|
|
760
782
|
restartParentPid;
|
|
761
783
|
latestVersion;
|
|
784
|
+
hasGit;
|
|
785
|
+
pinFs;
|
|
762
786
|
/** The install gate runs against the last loaded snapshot, never a fresh
|
|
763
787
|
* fetch per request (§7.2: the Host's cached snapshot is the truth). */
|
|
764
788
|
/** Finished install records retained, so a poll sees the true terminal
|
|
@@ -786,6 +810,19 @@ let ShopGateway = (() => {
|
|
|
786
810
|
this.restartExitDelayMs = options.restartExitDelayMs ?? ShopGateway.RESTART_EXIT_DELAY_MS;
|
|
787
811
|
this.restartParentPid = options.restartParentPid ?? process.pid;
|
|
788
812
|
this.latestVersion = options.fetchLatestVersion ?? (() => fetchLatestVersion());
|
|
813
|
+
this.hasGit = options.hasGit ?? (() => spawnSync("git", ["--version"], { stdio: "ignore" }).status === 0);
|
|
814
|
+
this.pinFs = options.pinFs ?? {
|
|
815
|
+
exists: (path) => existsSync(path),
|
|
816
|
+
read: (path) => readFileSync(path, "utf8"),
|
|
817
|
+
write: (path, data) => {
|
|
818
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
819
|
+
writeFileSync(path, data);
|
|
820
|
+
}
|
|
821
|
+
};
|
|
822
|
+
}
|
|
823
|
+
/** The pins file lives in the shop's own cache, next to the catalog cache. */
|
|
824
|
+
pinsPath() {
|
|
825
|
+
return join(this.rowConfig().cacheDir, "github-pins.json");
|
|
789
826
|
}
|
|
790
827
|
/** The boot's Loader root directory (the active profile's `cordis.yml`
|
|
791
828
|
* directory, carried on `ctx.baseUrl`), when present. A `link:` install
|
|
@@ -882,12 +919,39 @@ let ShopGateway = (() => {
|
|
|
882
919
|
code: verdict.code,
|
|
883
920
|
detail: verdict.detail
|
|
884
921
|
};
|
|
922
|
+
const entry = this.lastSnapshot.entries.find((e) => e.name === args.name);
|
|
923
|
+
if (entry === void 0) return {
|
|
924
|
+
ok: false,
|
|
925
|
+
code: "not-in-catalog",
|
|
926
|
+
detail: `dsh-plugin-shop: ${args.name} is not in the catalog`
|
|
927
|
+
};
|
|
928
|
+
let spec;
|
|
929
|
+
if (entry.source === "github") {
|
|
930
|
+
if (entry.repo === void 0 || !/^[0-9a-f]{40}$/.test(args.version)) return {
|
|
931
|
+
ok: false,
|
|
932
|
+
code: "version-mismatch",
|
|
933
|
+
detail: `dsh-plugin-shop: ${args.name} has no installable commit`
|
|
934
|
+
};
|
|
935
|
+
if (!this.hasGit()) return {
|
|
936
|
+
ok: false,
|
|
937
|
+
code: "git-missing",
|
|
938
|
+
detail: "dsh-plugin-shop: git is not on PATH, which github installs require; install git and retry"
|
|
939
|
+
};
|
|
940
|
+
spec = `github:${entry.repo}#${args.version}`;
|
|
941
|
+
} else spec = `${args.name}@${args.version}`;
|
|
885
942
|
const running = startInstall({
|
|
886
943
|
profile: this.profile,
|
|
887
|
-
spec
|
|
944
|
+
spec,
|
|
888
945
|
dshBin: this.dshBin,
|
|
889
946
|
expectedName: args.name
|
|
890
947
|
});
|
|
948
|
+
if (entry.source === "github") {
|
|
949
|
+
const pins = readRepoPins(this.pinFs, this.pinsPath());
|
|
950
|
+
writeRepoPins(this.pinFs, this.pinsPath(), {
|
|
951
|
+
...pins,
|
|
952
|
+
[args.name]: args.version
|
|
953
|
+
});
|
|
954
|
+
}
|
|
891
955
|
this.installs.set(running.installId, running);
|
|
892
956
|
this.installOrder.push(running.installId);
|
|
893
957
|
this.evictFinishedInstalls();
|
|
@@ -936,11 +1000,20 @@ let ShopGateway = (() => {
|
|
|
936
1000
|
this.lastSnapshot = snapshot;
|
|
937
1001
|
}
|
|
938
1002
|
const dependencies = readProfileManifest("dsh-plugin-shop", this.profileDirResolved()).dependencies ?? {};
|
|
1003
|
+
const pins = readRepoPins(this.pinFs, this.pinsPath());
|
|
939
1004
|
const installed = [];
|
|
940
1005
|
for (const entry of this.lastSnapshot.entries) {
|
|
941
1006
|
const spec = dependencies[entry.name];
|
|
942
1007
|
if (spec === void 0) continue;
|
|
943
|
-
|
|
1008
|
+
if (entry.source === "github") {
|
|
1009
|
+
const pin = pins[entry.name];
|
|
1010
|
+
installed.push({
|
|
1011
|
+
name: entry.name,
|
|
1012
|
+
installed: pin ?? spec,
|
|
1013
|
+
latest: entry.version,
|
|
1014
|
+
outdated: pin !== void 0 && pin !== entry.version
|
|
1015
|
+
});
|
|
1016
|
+
} else installed.push({
|
|
944
1017
|
name: entry.name,
|
|
945
1018
|
installed: spec,
|
|
946
1019
|
latest: entry.version,
|
|
@@ -992,6 +1065,11 @@ let ShopGateway = (() => {
|
|
|
992
1065
|
dshBin: this.dshBin,
|
|
993
1066
|
expectedName: args.name
|
|
994
1067
|
});
|
|
1068
|
+
const pins = readRepoPins(this.pinFs, this.pinsPath());
|
|
1069
|
+
if (pins[args.name] !== void 0) {
|
|
1070
|
+
delete pins[args.name];
|
|
1071
|
+
writeRepoPins(this.pinFs, this.pinsPath(), pins);
|
|
1072
|
+
}
|
|
995
1073
|
this.installs.set(running.installId, running);
|
|
996
1074
|
this.installOrder.push(running.installId);
|
|
997
1075
|
this.evictFinishedInstalls();
|
package/lib/typert.host.js
CHANGED
|
@@ -18,7 +18,8 @@ const dsh_plugin_shop_shop_catalog_result$schema = z.object({
|
|
|
18
18
|
'tier': z.union([z.literal("verified"), z.literal("verified-stale"), z.literal("community")]),
|
|
19
19
|
'metadata': z.union([z.literal("declared"), z.literal("derived")]),
|
|
20
20
|
'review': z.union([z.undefined(), z.object({
|
|
21
|
-
'reviewedVersion': z.string(),
|
|
21
|
+
'reviewedVersion': z.union([z.undefined(), z.string()]).optional(),
|
|
22
|
+
'reviewedCommit': z.union([z.undefined(), z.string()]).optional(),
|
|
22
23
|
'reviewer': z.string(),
|
|
23
24
|
'reviewCommit': z.string(),
|
|
24
25
|
'notes': z.string(),
|
|
@@ -31,6 +32,8 @@ const dsh_plugin_shop_shop_catalog_result$schema = z.object({
|
|
|
31
32
|
}),
|
|
32
33
|
'capabilities': z.array(z.string()),
|
|
33
34
|
})]).optional(),
|
|
35
|
+
'source': z.union([z.literal("npm"), z.literal("github")]),
|
|
36
|
+
'repo': z.union([z.undefined(), z.string()]).optional(),
|
|
34
37
|
})),
|
|
35
38
|
'denied': z.array(z.object({
|
|
36
39
|
'name': z.string(),
|
|
@@ -54,7 +57,7 @@ const dsh_plugin_shop_shop_installStart_result$schema = z.union([z.object({
|
|
|
54
57
|
'installId': z.string(),
|
|
55
58
|
}), z.object({
|
|
56
59
|
'ok': z.literal(false),
|
|
57
|
-
'code': z.union([z.literal("not-in-catalog"), z.literal("denied"), z.literal("version-mismatch"), z.literal("needs-acknowledgement")]),
|
|
60
|
+
'code': z.union([z.literal("not-in-catalog"), z.literal("denied"), z.literal("version-mismatch"), z.literal("needs-acknowledgement"), z.literal("git-missing")]),
|
|
58
61
|
'detail': z.string(),
|
|
59
62
|
})])
|
|
60
63
|
const dsh_plugin_shop_shop_installStatus_parameter_0$schema = z.object({
|
|
@@ -137,7 +140,7 @@ export const TYPERT = {
|
|
|
137
140
|
typeSymbol: 'dsh-plugin-shop/types#ShopCatalogResult',
|
|
138
141
|
schema: dsh_plugin_shop_shop_catalog_result$schema,
|
|
139
142
|
},
|
|
140
|
-
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":
|
|
143
|
+
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":270,"column":9},
|
|
141
144
|
},
|
|
142
145
|
{
|
|
143
146
|
id: 'dsh-plugin-shop#shop/installed',
|
|
@@ -152,7 +155,7 @@ export const TYPERT = {
|
|
|
152
155
|
typeSymbol: 'dsh-plugin-shop#shop/installed:result',
|
|
153
156
|
schema: dsh_plugin_shop_shop_installed_result$schema,
|
|
154
157
|
},
|
|
155
|
-
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":
|
|
158
|
+
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":374,"column":9},
|
|
156
159
|
},
|
|
157
160
|
{
|
|
158
161
|
id: 'dsh-plugin-shop#shop/installStart',
|
|
@@ -178,7 +181,7 @@ export const TYPERT = {
|
|
|
178
181
|
typeSymbol: 'dsh-plugin-shop/types#ShopInstallResult',
|
|
179
182
|
schema: dsh_plugin_shop_shop_installStart_result$schema,
|
|
180
183
|
},
|
|
181
|
-
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":
|
|
184
|
+
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":296,"column":9},
|
|
182
185
|
},
|
|
183
186
|
{
|
|
184
187
|
id: 'dsh-plugin-shop#shop/installStatus',
|
|
@@ -203,7 +206,7 @@ export const TYPERT = {
|
|
|
203
206
|
typeSymbol: 'dsh-plugin-shop/types#ShopInstallStatusResult',
|
|
204
207
|
schema: dsh_plugin_shop_shop_installStatus_result$schema,
|
|
205
208
|
},
|
|
206
|
-
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":
|
|
209
|
+
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":363,"column":3},
|
|
207
210
|
},
|
|
208
211
|
{
|
|
209
212
|
id: 'dsh-plugin-shop#shop/restart',
|
|
@@ -218,7 +221,7 @@ export const TYPERT = {
|
|
|
218
221
|
typeSymbol: 'dsh-plugin-shop/types#ShopRestartResult',
|
|
219
222
|
schema: dsh_plugin_shop_shop_restart_result$schema,
|
|
220
223
|
},
|
|
221
|
-
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":
|
|
224
|
+
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":469,"column":9},
|
|
222
225
|
},
|
|
223
226
|
{
|
|
224
227
|
id: 'dsh-plugin-shop#shop/setEnabled',
|
|
@@ -243,7 +246,7 @@ export const TYPERT = {
|
|
|
243
246
|
typeSymbol: 'dsh-plugin-shop/types#ShopSetEnabledResult',
|
|
244
247
|
schema: dsh_plugin_shop_shop_setEnabled_result$schema,
|
|
245
248
|
},
|
|
246
|
-
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":
|
|
249
|
+
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":239,"column":3},
|
|
247
250
|
},
|
|
248
251
|
{
|
|
249
252
|
id: 'dsh-plugin-shop#shop/uninstallStart',
|
|
@@ -269,7 +272,7 @@ export const TYPERT = {
|
|
|
269
272
|
typeSymbol: 'dsh-plugin-shop/types#ShopUninstallResult',
|
|
270
273
|
schema: dsh_plugin_shop_shop_uninstallStart_result$schema,
|
|
271
274
|
},
|
|
272
|
-
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":
|
|
275
|
+
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":429,"column":9},
|
|
273
276
|
},
|
|
274
277
|
{
|
|
275
278
|
id: 'dsh-plugin-shop#shop/updateStart',
|
|
@@ -294,7 +297,7 @@ export const TYPERT = {
|
|
|
294
297
|
typeSymbol: 'dsh-plugin-shop/types#ShopUpdateResult',
|
|
295
298
|
schema: dsh_plugin_shop_shop_updateStart_result$schema,
|
|
296
299
|
},
|
|
297
|
-
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":
|
|
300
|
+
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":518,"column":9},
|
|
298
301
|
},
|
|
299
302
|
{
|
|
300
303
|
id: 'dsh-plugin-shop#shop/version',
|
|
@@ -309,7 +312,7 @@ export const TYPERT = {
|
|
|
309
312
|
typeSymbol: 'dsh-plugin-shop/types#ShopVersionResult',
|
|
310
313
|
schema: dsh_plugin_shop_shop_version_result$schema,
|
|
311
314
|
},
|
|
312
|
-
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":
|
|
315
|
+
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":503,"column":9},
|
|
313
316
|
},
|
|
314
317
|
],
|
|
315
318
|
model: {
|
|
@@ -395,7 +398,7 @@ export const TYPERT = {
|
|
|
395
398
|
"types": [
|
|
396
399
|
{
|
|
397
400
|
"name": "CatalogEntry",
|
|
398
|
-
"declaration": "export interface CatalogEntry {\n name: string;\n version: string;\n integrity: string | null;\n publishedAt: string | null;\n repository: string | null;\n license: string | null;\n tier: 'verified' | 'verified-stale' | 'community';\n metadata: 'declared' | 'derived';\n review?: { reviewedVersion
|
|
401
|
+
"declaration": "export interface CatalogEntry {\n name: string;\n version: string;\n integrity: string | null;\n publishedAt: string | null;\n repository: string | null;\n license: string | null;\n tier: 'verified' | 'verified-stale' | 'community';\n metadata: 'declared' | 'derived';\n review?: { reviewedVersion?: string; reviewedCommit?: string; reviewer: string; reviewCommit: string; notes: string; };\n catalog?: CatalogSection;\n source: 'npm' | 'github';\n repo?: string;\n}"
|
|
399
402
|
},
|
|
400
403
|
{
|
|
401
404
|
"name": "CatalogSection",
|
|
@@ -415,7 +418,7 @@ export const TYPERT = {
|
|
|
415
418
|
},
|
|
416
419
|
{
|
|
417
420
|
"name": "InstallRejectionCode",
|
|
418
|
-
"declaration": "export type InstallRejectionCode = 'not-in-catalog' | 'denied' | 'version-mismatch' | 'needs-acknowledgement';"
|
|
421
|
+
"declaration": "export type InstallRejectionCode = 'not-in-catalog' | 'denied' | 'version-mismatch' | 'needs-acknowledgement' | 'git-missing';"
|
|
419
422
|
},
|
|
420
423
|
{
|
|
421
424
|
"name": "InstallState",
|
|
@@ -18,7 +18,8 @@ const dsh_plugin_shop_shop_catalog_result$schema = z.object({
|
|
|
18
18
|
'tier': z.union([z.literal("verified"), z.literal("verified-stale"), z.literal("community")]),
|
|
19
19
|
'metadata': z.union([z.literal("declared"), z.literal("derived")]),
|
|
20
20
|
'review': z.union([z.undefined(), z.object({
|
|
21
|
-
'reviewedVersion': z.string(),
|
|
21
|
+
'reviewedVersion': z.union([z.undefined(), z.string()]).optional(),
|
|
22
|
+
'reviewedCommit': z.union([z.undefined(), z.string()]).optional(),
|
|
22
23
|
'reviewer': z.string(),
|
|
23
24
|
'reviewCommit': z.string(),
|
|
24
25
|
'notes': z.string(),
|
|
@@ -31,6 +32,8 @@ const dsh_plugin_shop_shop_catalog_result$schema = z.object({
|
|
|
31
32
|
}),
|
|
32
33
|
'capabilities': z.array(z.string()),
|
|
33
34
|
})]).optional(),
|
|
35
|
+
'source': z.union([z.literal("npm"), z.literal("github")]),
|
|
36
|
+
'repo': z.union([z.undefined(), z.string()]).optional(),
|
|
34
37
|
})),
|
|
35
38
|
'denied': z.array(z.object({
|
|
36
39
|
'name': z.string(),
|
|
@@ -54,7 +57,7 @@ const dsh_plugin_shop_shop_installStart_result$schema = z.union([z.object({
|
|
|
54
57
|
'installId': z.string(),
|
|
55
58
|
}), z.object({
|
|
56
59
|
'ok': z.literal(false),
|
|
57
|
-
'code': z.union([z.literal("not-in-catalog"), z.literal("denied"), z.literal("version-mismatch"), z.literal("needs-acknowledgement")]),
|
|
60
|
+
'code': z.union([z.literal("not-in-catalog"), z.literal("denied"), z.literal("version-mismatch"), z.literal("needs-acknowledgement"), z.literal("git-missing")]),
|
|
58
61
|
'detail': z.string(),
|
|
59
62
|
})])
|
|
60
63
|
const dsh_plugin_shop_shop_installStatus_parameter_0$schema = z.object({
|
|
@@ -134,7 +137,7 @@ export const TYPERT_REMOTE = {
|
|
|
134
137
|
typeSymbol: 'dsh-plugin-shop/types#ShopCatalogResult',
|
|
135
138
|
schema: dsh_plugin_shop_shop_catalog_result$schema,
|
|
136
139
|
},
|
|
137
|
-
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":
|
|
140
|
+
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":270,"column":9},
|
|
138
141
|
},
|
|
139
142
|
{
|
|
140
143
|
id: 'dsh-plugin-shop#shop/installed',
|
|
@@ -149,7 +152,7 @@ export const TYPERT_REMOTE = {
|
|
|
149
152
|
typeSymbol: 'dsh-plugin-shop#shop/installed:result',
|
|
150
153
|
schema: dsh_plugin_shop_shop_installed_result$schema,
|
|
151
154
|
},
|
|
152
|
-
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":
|
|
155
|
+
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":374,"column":9},
|
|
153
156
|
},
|
|
154
157
|
{
|
|
155
158
|
id: 'dsh-plugin-shop#shop/installStart',
|
|
@@ -175,7 +178,7 @@ export const TYPERT_REMOTE = {
|
|
|
175
178
|
typeSymbol: 'dsh-plugin-shop/types#ShopInstallResult',
|
|
176
179
|
schema: dsh_plugin_shop_shop_installStart_result$schema,
|
|
177
180
|
},
|
|
178
|
-
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":
|
|
181
|
+
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":296,"column":9},
|
|
179
182
|
},
|
|
180
183
|
{
|
|
181
184
|
id: 'dsh-plugin-shop#shop/installStatus',
|
|
@@ -200,7 +203,7 @@ export const TYPERT_REMOTE = {
|
|
|
200
203
|
typeSymbol: 'dsh-plugin-shop/types#ShopInstallStatusResult',
|
|
201
204
|
schema: dsh_plugin_shop_shop_installStatus_result$schema,
|
|
202
205
|
},
|
|
203
|
-
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":
|
|
206
|
+
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":363,"column":3},
|
|
204
207
|
},
|
|
205
208
|
{
|
|
206
209
|
id: 'dsh-plugin-shop#shop/restart',
|
|
@@ -215,7 +218,7 @@ export const TYPERT_REMOTE = {
|
|
|
215
218
|
typeSymbol: 'dsh-plugin-shop/types#ShopRestartResult',
|
|
216
219
|
schema: dsh_plugin_shop_shop_restart_result$schema,
|
|
217
220
|
},
|
|
218
|
-
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":
|
|
221
|
+
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":469,"column":9},
|
|
219
222
|
},
|
|
220
223
|
{
|
|
221
224
|
id: 'dsh-plugin-shop#shop/setEnabled',
|
|
@@ -240,7 +243,7 @@ export const TYPERT_REMOTE = {
|
|
|
240
243
|
typeSymbol: 'dsh-plugin-shop/types#ShopSetEnabledResult',
|
|
241
244
|
schema: dsh_plugin_shop_shop_setEnabled_result$schema,
|
|
242
245
|
},
|
|
243
|
-
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":
|
|
246
|
+
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":239,"column":3},
|
|
244
247
|
},
|
|
245
248
|
{
|
|
246
249
|
id: 'dsh-plugin-shop#shop/uninstallStart',
|
|
@@ -266,7 +269,7 @@ export const TYPERT_REMOTE = {
|
|
|
266
269
|
typeSymbol: 'dsh-plugin-shop/types#ShopUninstallResult',
|
|
267
270
|
schema: dsh_plugin_shop_shop_uninstallStart_result$schema,
|
|
268
271
|
},
|
|
269
|
-
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":
|
|
272
|
+
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":429,"column":9},
|
|
270
273
|
},
|
|
271
274
|
{
|
|
272
275
|
id: 'dsh-plugin-shop#shop/updateStart',
|
|
@@ -291,7 +294,7 @@ export const TYPERT_REMOTE = {
|
|
|
291
294
|
typeSymbol: 'dsh-plugin-shop/types#ShopUpdateResult',
|
|
292
295
|
schema: dsh_plugin_shop_shop_updateStart_result$schema,
|
|
293
296
|
},
|
|
294
|
-
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":
|
|
297
|
+
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":518,"column":9},
|
|
295
298
|
},
|
|
296
299
|
{
|
|
297
300
|
id: 'dsh-plugin-shop#shop/version',
|
|
@@ -306,7 +309,7 @@ export const TYPERT_REMOTE = {
|
|
|
306
309
|
typeSymbol: 'dsh-plugin-shop/types#ShopVersionResult',
|
|
307
310
|
schema: dsh_plugin_shop_shop_version_result$schema,
|
|
308
311
|
},
|
|
309
|
-
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":
|
|
312
|
+
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":503,"column":9},
|
|
310
313
|
},
|
|
311
314
|
],
|
|
312
315
|
}
|
|
@@ -44,6 +44,8 @@ export declare const zh: {
|
|
|
44
44
|
notInCatalogCode: string;
|
|
45
45
|
versionMismatchCode: string;
|
|
46
46
|
needsAcknowledgementCode: string;
|
|
47
|
+
gitMissingCode: string;
|
|
48
|
+
githubSource: string;
|
|
47
49
|
installedSection: string;
|
|
48
50
|
installedVersion: string;
|
|
49
51
|
latestVersion: string;
|
|
@@ -65,6 +67,7 @@ export declare const zh: {
|
|
|
65
67
|
updateTransportFailed: string;
|
|
66
68
|
checkUpdate: string;
|
|
67
69
|
upToDate: string;
|
|
70
|
+
github: string;
|
|
68
71
|
enabledSwitch: string;
|
|
69
72
|
toggleFailed: string;
|
|
70
73
|
hotApplyNote: string;
|
|
@@ -116,6 +119,8 @@ export declare const en: {
|
|
|
116
119
|
notInCatalogCode: string;
|
|
117
120
|
versionMismatchCode: string;
|
|
118
121
|
needsAcknowledgementCode: string;
|
|
122
|
+
gitMissingCode: string;
|
|
123
|
+
githubSource: string;
|
|
119
124
|
installedSection: string;
|
|
120
125
|
installedVersion: string;
|
|
121
126
|
latestVersion: string;
|
|
@@ -137,6 +142,7 @@ export declare const en: {
|
|
|
137
142
|
updateTransportFailed: string;
|
|
138
143
|
checkUpdate: string;
|
|
139
144
|
upToDate: string;
|
|
145
|
+
github: string;
|
|
140
146
|
enabledSwitch: string;
|
|
141
147
|
toggleFailed: string;
|
|
142
148
|
hotApplyNote: string;
|
|
@@ -17,6 +17,11 @@ export declare const ACKNOWLEDGEMENT_ZH = "\u5B89\u88C5\u540E\uFF0C\u6B64\u63D2\
|
|
|
17
17
|
/** Install rejection code → locale key, for the rejected-state code label
|
|
18
18
|
* (§7.2): every rejection the host can return has a human label here. */
|
|
19
19
|
export declare function rejectionCodeKey(code: InstallRejectionCode): ShopLocaleKey;
|
|
20
|
+
/** The version a card displays: full for npm, the short commit for github. */
|
|
21
|
+
export declare function displayVersion(entry: {
|
|
22
|
+
source: 'npm' | 'github';
|
|
23
|
+
version: string;
|
|
24
|
+
}): string;
|
|
20
25
|
/** One polled install status (§7.3 wire data), structural. */
|
|
21
26
|
export interface InstallStatusShape {
|
|
22
27
|
found: boolean;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/** Catalog fetch, verification, and disk cache — the Host's only network path. */
|
|
2
2
|
import type { CatalogEntry, DeniedEntry } from './types.ts';
|
|
3
|
-
/** Highest schemaVersion this build understands; a higher one is refused (§10).
|
|
4
|
-
|
|
3
|
+
/** Highest schemaVersion this build understands; a higher one is refused (§10).
|
|
4
|
+
* 3 adds `source` and repo entries (github install channel). */
|
|
5
|
+
export declare const SUPPORTED_SCHEMA_VERSION = 3;
|
|
5
6
|
export interface CatalogSnapshot {
|
|
6
7
|
schemaVersion: number;
|
|
7
8
|
builtAt: string;
|
|
@@ -6,6 +6,7 @@ import type { CatalogEntry, DeniedEntry } from './types.ts';
|
|
|
6
6
|
import { type InstallArgs, type InstallRejectionCode } from './install.ts';
|
|
7
7
|
import { type InstallStatus } from './executor.ts';
|
|
8
8
|
import { type RestartOutcome } from './restart.ts';
|
|
9
|
+
import { type RepoPinFs } from './repo-pins.ts';
|
|
9
10
|
export type { InstallArgs, InstallRejectionCode } from './install.ts';
|
|
10
11
|
export type { CatalogEntry } from './types.ts';
|
|
11
12
|
/** One Loader inventory entry, structurally — the shop never depends on
|
|
@@ -46,6 +47,11 @@ export interface ShopGatewayOptions {
|
|
|
46
47
|
/** How long the gateway waits after a successful restart response before
|
|
47
48
|
* exiting the old process; test-only shortening, production uses 2s. */
|
|
48
49
|
restartExitDelayMs?: number;
|
|
50
|
+
/** Whether `git` is on PATH — a github entry cannot install without it;
|
|
51
|
+
* test-only injection, production probes the real binary. */
|
|
52
|
+
hasGit?: () => boolean;
|
|
53
|
+
/** Test-only injection: the pins file's filesystem; production uses node:fs. */
|
|
54
|
+
pinFs?: RepoPinFs;
|
|
49
55
|
}
|
|
50
56
|
/** `shop/installStart` result (§7.3): rejections are typed wire values with an
|
|
51
57
|
* author-readable `detail`, not thrown RPC errors. */
|
|
@@ -138,6 +144,8 @@ export declare class ShopGateway extends TypertRemoteService {
|
|
|
138
144
|
private readonly restartExitDelayMs;
|
|
139
145
|
private readonly restartParentPid;
|
|
140
146
|
private readonly latestVersion;
|
|
147
|
+
private readonly hasGit;
|
|
148
|
+
private readonly pinFs;
|
|
141
149
|
/** The install gate runs against the last loaded snapshot, never a fresh
|
|
142
150
|
* fetch per request (§7.2: the Host's cached snapshot is the truth). */
|
|
143
151
|
/** Finished install records retained, so a poll sees the true terminal
|
|
@@ -154,6 +162,8 @@ export declare class ShopGateway extends TypertRemoteService {
|
|
|
154
162
|
/** Every install id in insertion order, oldest first; finished-record eviction walks this from the front. */
|
|
155
163
|
private readonly installOrder;
|
|
156
164
|
constructor(ctx: Context, options?: ShopGatewayOptions);
|
|
165
|
+
/** The pins file lives in the shop's own cache, next to the catalog cache. */
|
|
166
|
+
private pinsPath;
|
|
157
167
|
/** The boot's Loader root directory (the active profile's `cordis.yml`
|
|
158
168
|
* directory, carried on `ctx.baseUrl`), when present. A `link:` install
|
|
159
169
|
* keeps this package at its source location, so the walk-up from
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/** Install gate: the four rejection paths of §7.2, as a pure function. */
|
|
2
2
|
import type { CatalogSnapshot } from './catalog.ts';
|
|
3
|
-
export type InstallRejectionCode = 'not-in-catalog' | 'denied' | 'version-mismatch' | 'needs-acknowledgement';
|
|
3
|
+
export type InstallRejectionCode = 'not-in-catalog' | 'denied' | 'version-mismatch' | 'needs-acknowledgement' | 'git-missing';
|
|
4
4
|
export interface InstallArgs {
|
|
5
5
|
name: string;
|
|
6
6
|
version: string;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Host's memory of which commit it installed for each github entry.
|
|
3
|
+
*
|
|
4
|
+
* pnpm writes the manifest dependency as `github:owner/slug` — no commit —
|
|
5
|
+
* and the resolved commit lives only in the lockfile. The shop records the
|
|
6
|
+
* commit it pinned at install time in its own cache so `installed()` can
|
|
7
|
+
* report `outdated` honestly without parsing pnpm's lockfile. The profile
|
|
8
|
+
* manifest remains the source of truth for *presence*: a pin whose bundle
|
|
9
|
+
* is not a manifest dependency is a leftover from a failed install and is
|
|
10
|
+
* never reported.
|
|
11
|
+
*/
|
|
12
|
+
export interface RepoPinFs {
|
|
13
|
+
exists: (path: string) => boolean;
|
|
14
|
+
read: (path: string) => string;
|
|
15
|
+
write: (path: string, data: string) => void;
|
|
16
|
+
}
|
|
17
|
+
/** Bundle name to the pinned commit. */
|
|
18
|
+
export type RepoPins = Record<string, string>;
|
|
19
|
+
/** Read the pins file; any irregularity degrades to an empty record. */
|
|
20
|
+
export declare function readRepoPins(fs: RepoPinFs, path: string): RepoPins;
|
|
21
|
+
/** Write the pins file atomically enough for its purpose (a cache). */
|
|
22
|
+
export declare function writeRepoPins(fs: RepoPinFs, path: string, pins: RepoPins): void;
|
|
@@ -18,12 +18,17 @@ export interface CatalogEntry {
|
|
|
18
18
|
tier: 'verified' | 'verified-stale' | 'community';
|
|
19
19
|
metadata: 'declared' | 'derived';
|
|
20
20
|
review?: {
|
|
21
|
-
reviewedVersion
|
|
21
|
+
reviewedVersion?: string;
|
|
22
|
+
reviewedCommit?: string;
|
|
22
23
|
reviewer: string;
|
|
23
24
|
reviewCommit: string;
|
|
24
25
|
notes: string;
|
|
25
26
|
};
|
|
26
27
|
catalog?: CatalogSection;
|
|
28
|
+
/** Where the entry installs from. */
|
|
29
|
+
source: 'npm' | 'github';
|
|
30
|
+
/** `owner/slug`; present exactly when `source` is github. */
|
|
31
|
+
repo?: string;
|
|
27
32
|
}
|
|
28
33
|
export interface DeniedEntry {
|
|
29
34
|
name: string;
|
package/package.json
CHANGED