dsh-plugin-shop 0.7.4 → 0.8.0-beta.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/lib/client.js +251 -123
- package/lib/index.js +648 -142
- package/lib/typert.host.js +19 -13
- package/lib/typert.remote-client.js +16 -10
- package/lib/types/client/index.d.ts +3 -0
- package/lib/types/client/locales.d.ts +2 -0
- package/lib/types/client/present.d.ts +4 -2
- package/lib/types/client/useInstall.d.ts +18 -0
- package/lib/types/host/executor.d.ts +18 -0
- package/lib/types/host/hot.d.ts +5 -0
- package/lib/types/host/index.d.ts +44 -10
- package/lib/types/host/install.d.ts +9 -6
- package/lib/types/host/npm-origin.d.ts +4 -0
- package/lib/types/host/origin.d.ts +7 -1
- package/lib/types/host/peers.d.ts +75 -4
- package/lib/types/host/repo-pins.d.ts +1 -1
- package/lib/types/host/restart.d.ts +22 -2
- package/lib/types/host/self-update.d.ts +12 -4
- package/lib/types/own-version.d.ts +10 -0
- package/lib/types/shared/identity.d.ts +27 -0
- package/package.json +9 -9
package/lib/typert.host.js
CHANGED
|
@@ -55,6 +55,9 @@ const dsh_plugin_shop_shop_catalog_result$schema = z.object({
|
|
|
55
55
|
})
|
|
56
56
|
const dsh_plugin_shop_shop_installed_result$schema = z.array(z.object({
|
|
57
57
|
'name': z.string(),
|
|
58
|
+
'source': z.union([z.literal("npm"), z.literal("github")]),
|
|
59
|
+
'repo': z.union([z.undefined(), z.string()]).optional(),
|
|
60
|
+
'subdir': z.union([z.undefined(), z.string()]).optional(),
|
|
58
61
|
'installed': z.string(),
|
|
59
62
|
'latest': z.string(),
|
|
60
63
|
'outdated': z.boolean(),
|
|
@@ -64,13 +67,16 @@ const dsh_plugin_shop_shop_installStart_parameter_0$schema = z.object({
|
|
|
64
67
|
'name': z.string(),
|
|
65
68
|
'version': z.string(),
|
|
66
69
|
'acknowledged': z.union([z.undefined(), z.literal(false), z.literal(true)]).optional(),
|
|
70
|
+
'source': z.union([z.undefined(), z.literal("npm"), z.literal("github")]).optional(),
|
|
71
|
+
'repo': z.union([z.undefined(), z.string()]).optional(),
|
|
72
|
+
'subdir': z.union([z.undefined(), z.string()]).optional(),
|
|
67
73
|
})
|
|
68
74
|
const dsh_plugin_shop_shop_installStart_result$schema = z.union([z.object({
|
|
69
75
|
'ok': z.literal(true),
|
|
70
76
|
'installId': z.string(),
|
|
71
77
|
}), z.object({
|
|
72
78
|
'ok': z.literal(false),
|
|
73
|
-
'code': z.union([z.literal("not-in-catalog"), z.literal("denied"), z.literal("version-mismatch"), z.literal("needs-acknowledgement"), z.literal("tarball-integrity")]),
|
|
79
|
+
'code': z.union([z.literal("not-in-catalog"), z.literal("denied"), z.literal("version-mismatch"), z.literal("needs-acknowledgement"), z.literal("tarball-integrity"), z.literal("ambiguous-identity")]),
|
|
74
80
|
'detail': z.string(),
|
|
75
81
|
})])
|
|
76
82
|
const dsh_plugin_shop_shop_installStatus_parameter_0$schema = z.object({
|
|
@@ -155,7 +161,7 @@ export const TYPERT = {
|
|
|
155
161
|
typeSymbol: 'dsh-plugin-shop/types#ShopCatalogResult',
|
|
156
162
|
schema: dsh_plugin_shop_shop_catalog_result$schema,
|
|
157
163
|
},
|
|
158
|
-
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":
|
|
164
|
+
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":678,"column":9},
|
|
159
165
|
},
|
|
160
166
|
{
|
|
161
167
|
id: 'dsh-plugin-shop#shop/installed',
|
|
@@ -170,7 +176,7 @@ export const TYPERT = {
|
|
|
170
176
|
typeSymbol: 'dsh-plugin-shop#shop/installed:result',
|
|
171
177
|
schema: dsh_plugin_shop_shop_installed_result$schema,
|
|
172
178
|
},
|
|
173
|
-
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":
|
|
179
|
+
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":863,"column":9},
|
|
174
180
|
},
|
|
175
181
|
{
|
|
176
182
|
id: 'dsh-plugin-shop#shop/installStart',
|
|
@@ -196,7 +202,7 @@ export const TYPERT = {
|
|
|
196
202
|
typeSymbol: 'dsh-plugin-shop/types#ShopInstallResult',
|
|
197
203
|
schema: dsh_plugin_shop_shop_installStart_result$schema,
|
|
198
204
|
},
|
|
199
|
-
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":
|
|
205
|
+
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":726,"column":9},
|
|
200
206
|
},
|
|
201
207
|
{
|
|
202
208
|
id: 'dsh-plugin-shop#shop/installStatus',
|
|
@@ -221,7 +227,7 @@ export const TYPERT = {
|
|
|
221
227
|
typeSymbol: 'dsh-plugin-shop/types#ShopInstallStatusResult',
|
|
222
228
|
schema: dsh_plugin_shop_shop_installStatus_result$schema,
|
|
223
229
|
},
|
|
224
|
-
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":
|
|
230
|
+
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":852,"column":3},
|
|
225
231
|
},
|
|
226
232
|
{
|
|
227
233
|
id: 'dsh-plugin-shop#shop/restart',
|
|
@@ -236,7 +242,7 @@ export const TYPERT = {
|
|
|
236
242
|
typeSymbol: 'dsh-plugin-shop/types#ShopRestartResult',
|
|
237
243
|
schema: dsh_plugin_shop_shop_restart_result$schema,
|
|
238
244
|
},
|
|
239
|
-
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":
|
|
245
|
+
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":1021,"column":9},
|
|
240
246
|
},
|
|
241
247
|
{
|
|
242
248
|
id: 'dsh-plugin-shop#shop/setEnabled',
|
|
@@ -261,7 +267,7 @@ export const TYPERT = {
|
|
|
261
267
|
typeSymbol: 'dsh-plugin-shop/types#ShopSetEnabledResult',
|
|
262
268
|
schema: dsh_plugin_shop_shop_setEnabled_result$schema,
|
|
263
269
|
},
|
|
264
|
-
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":
|
|
270
|
+
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":535,"column":9},
|
|
265
271
|
},
|
|
266
272
|
{
|
|
267
273
|
id: 'dsh-plugin-shop#shop/uninstallStart',
|
|
@@ -287,7 +293,7 @@ export const TYPERT = {
|
|
|
287
293
|
typeSymbol: 'dsh-plugin-shop/types#ShopUninstallResult',
|
|
288
294
|
schema: dsh_plugin_shop_shop_uninstallStart_result$schema,
|
|
289
295
|
},
|
|
290
|
-
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":
|
|
296
|
+
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":956,"column":9},
|
|
291
297
|
},
|
|
292
298
|
{
|
|
293
299
|
id: 'dsh-plugin-shop#shop/updateStart',
|
|
@@ -312,7 +318,7 @@ export const TYPERT = {
|
|
|
312
318
|
typeSymbol: 'dsh-plugin-shop/types#ShopUpdateResult',
|
|
313
319
|
schema: dsh_plugin_shop_shop_updateStart_result$schema,
|
|
314
320
|
},
|
|
315
|
-
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":
|
|
321
|
+
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":1106,"column":9},
|
|
316
322
|
},
|
|
317
323
|
{
|
|
318
324
|
id: 'dsh-plugin-shop#shop/version',
|
|
@@ -327,7 +333,7 @@ export const TYPERT = {
|
|
|
327
333
|
typeSymbol: 'dsh-plugin-shop/types#ShopVersionResult',
|
|
328
334
|
schema: dsh_plugin_shop_shop_version_result$schema,
|
|
329
335
|
},
|
|
330
|
-
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":
|
|
336
|
+
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":1089,"column":9},
|
|
331
337
|
},
|
|
332
338
|
],
|
|
333
339
|
model: {
|
|
@@ -433,11 +439,11 @@ export const TYPERT = {
|
|
|
433
439
|
},
|
|
434
440
|
{
|
|
435
441
|
"name": "InstallArgs",
|
|
436
|
-
"declaration": "export interface InstallArgs {\n name: string;\n version: string;\n acknowledged?: boolean;\n}"
|
|
442
|
+
"declaration": "export interface InstallArgs {\n name: string;\n version: string;\n acknowledged?: boolean;\n source?: 'npm' | 'github';\n repo?: string;\n subdir?: string;\n}"
|
|
437
443
|
},
|
|
438
444
|
{
|
|
439
445
|
"name": "InstallRejectionCode",
|
|
440
|
-
"declaration": "export type InstallRejectionCode = 'not-in-catalog' | 'denied' | 'version-mismatch' | 'needs-acknowledgement' | 'tarball-integrity';"
|
|
446
|
+
"declaration": "export type InstallRejectionCode = 'not-in-catalog' | 'denied' | 'version-mismatch' | 'needs-acknowledgement' | 'tarball-integrity' | 'ambiguous-identity';"
|
|
441
447
|
},
|
|
442
448
|
{
|
|
443
449
|
"name": "InstallState",
|
|
@@ -457,7 +463,7 @@ export const TYPERT = {
|
|
|
457
463
|
},
|
|
458
464
|
{
|
|
459
465
|
"name": "ShopInstalledEntry",
|
|
460
|
-
"declaration": "export interface ShopInstalledEntry {\n name: string;\n installed: string;\n latest: string;\n outdated: boolean;\n enabled: boolean;\n}"
|
|
466
|
+
"declaration": "export interface ShopInstalledEntry {\n name: string;\n source: 'npm' | 'github';\n repo?: string;\n subdir?: string;\n installed: string;\n latest: string;\n outdated: boolean;\n enabled: boolean;\n}"
|
|
461
467
|
},
|
|
462
468
|
{
|
|
463
469
|
"name": "ShopInstallResult",
|
|
@@ -55,6 +55,9 @@ const dsh_plugin_shop_shop_catalog_result$schema = z.object({
|
|
|
55
55
|
})
|
|
56
56
|
const dsh_plugin_shop_shop_installed_result$schema = z.array(z.object({
|
|
57
57
|
'name': z.string(),
|
|
58
|
+
'source': z.union([z.literal("npm"), z.literal("github")]),
|
|
59
|
+
'repo': z.union([z.undefined(), z.string()]).optional(),
|
|
60
|
+
'subdir': z.union([z.undefined(), z.string()]).optional(),
|
|
58
61
|
'installed': z.string(),
|
|
59
62
|
'latest': z.string(),
|
|
60
63
|
'outdated': z.boolean(),
|
|
@@ -64,13 +67,16 @@ const dsh_plugin_shop_shop_installStart_parameter_0$schema = z.object({
|
|
|
64
67
|
'name': z.string(),
|
|
65
68
|
'version': z.string(),
|
|
66
69
|
'acknowledged': z.union([z.undefined(), z.literal(false), z.literal(true)]).optional(),
|
|
70
|
+
'source': z.union([z.undefined(), z.literal("npm"), z.literal("github")]).optional(),
|
|
71
|
+
'repo': z.union([z.undefined(), z.string()]).optional(),
|
|
72
|
+
'subdir': z.union([z.undefined(), z.string()]).optional(),
|
|
67
73
|
})
|
|
68
74
|
const dsh_plugin_shop_shop_installStart_result$schema = z.union([z.object({
|
|
69
75
|
'ok': z.literal(true),
|
|
70
76
|
'installId': z.string(),
|
|
71
77
|
}), z.object({
|
|
72
78
|
'ok': z.literal(false),
|
|
73
|
-
'code': z.union([z.literal("not-in-catalog"), z.literal("denied"), z.literal("version-mismatch"), z.literal("needs-acknowledgement"), z.literal("tarball-integrity")]),
|
|
79
|
+
'code': z.union([z.literal("not-in-catalog"), z.literal("denied"), z.literal("version-mismatch"), z.literal("needs-acknowledgement"), z.literal("tarball-integrity"), z.literal("ambiguous-identity")]),
|
|
74
80
|
'detail': z.string(),
|
|
75
81
|
})])
|
|
76
82
|
const dsh_plugin_shop_shop_installStatus_parameter_0$schema = z.object({
|
|
@@ -152,7 +158,7 @@ export const TYPERT_REMOTE = {
|
|
|
152
158
|
typeSymbol: 'dsh-plugin-shop/types#ShopCatalogResult',
|
|
153
159
|
schema: dsh_plugin_shop_shop_catalog_result$schema,
|
|
154
160
|
},
|
|
155
|
-
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":
|
|
161
|
+
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":678,"column":9},
|
|
156
162
|
},
|
|
157
163
|
{
|
|
158
164
|
id: 'dsh-plugin-shop#shop/installed',
|
|
@@ -167,7 +173,7 @@ export const TYPERT_REMOTE = {
|
|
|
167
173
|
typeSymbol: 'dsh-plugin-shop#shop/installed:result',
|
|
168
174
|
schema: dsh_plugin_shop_shop_installed_result$schema,
|
|
169
175
|
},
|
|
170
|
-
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":
|
|
176
|
+
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":863,"column":9},
|
|
171
177
|
},
|
|
172
178
|
{
|
|
173
179
|
id: 'dsh-plugin-shop#shop/installStart',
|
|
@@ -193,7 +199,7 @@ export const TYPERT_REMOTE = {
|
|
|
193
199
|
typeSymbol: 'dsh-plugin-shop/types#ShopInstallResult',
|
|
194
200
|
schema: dsh_plugin_shop_shop_installStart_result$schema,
|
|
195
201
|
},
|
|
196
|
-
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":
|
|
202
|
+
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":726,"column":9},
|
|
197
203
|
},
|
|
198
204
|
{
|
|
199
205
|
id: 'dsh-plugin-shop#shop/installStatus',
|
|
@@ -218,7 +224,7 @@ export const TYPERT_REMOTE = {
|
|
|
218
224
|
typeSymbol: 'dsh-plugin-shop/types#ShopInstallStatusResult',
|
|
219
225
|
schema: dsh_plugin_shop_shop_installStatus_result$schema,
|
|
220
226
|
},
|
|
221
|
-
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":
|
|
227
|
+
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":852,"column":3},
|
|
222
228
|
},
|
|
223
229
|
{
|
|
224
230
|
id: 'dsh-plugin-shop#shop/restart',
|
|
@@ -233,7 +239,7 @@ export const TYPERT_REMOTE = {
|
|
|
233
239
|
typeSymbol: 'dsh-plugin-shop/types#ShopRestartResult',
|
|
234
240
|
schema: dsh_plugin_shop_shop_restart_result$schema,
|
|
235
241
|
},
|
|
236
|
-
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":
|
|
242
|
+
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":1021,"column":9},
|
|
237
243
|
},
|
|
238
244
|
{
|
|
239
245
|
id: 'dsh-plugin-shop#shop/setEnabled',
|
|
@@ -258,7 +264,7 @@ export const TYPERT_REMOTE = {
|
|
|
258
264
|
typeSymbol: 'dsh-plugin-shop/types#ShopSetEnabledResult',
|
|
259
265
|
schema: dsh_plugin_shop_shop_setEnabled_result$schema,
|
|
260
266
|
},
|
|
261
|
-
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":
|
|
267
|
+
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":535,"column":9},
|
|
262
268
|
},
|
|
263
269
|
{
|
|
264
270
|
id: 'dsh-plugin-shop#shop/uninstallStart',
|
|
@@ -284,7 +290,7 @@ export const TYPERT_REMOTE = {
|
|
|
284
290
|
typeSymbol: 'dsh-plugin-shop/types#ShopUninstallResult',
|
|
285
291
|
schema: dsh_plugin_shop_shop_uninstallStart_result$schema,
|
|
286
292
|
},
|
|
287
|
-
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":
|
|
293
|
+
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":956,"column":9},
|
|
288
294
|
},
|
|
289
295
|
{
|
|
290
296
|
id: 'dsh-plugin-shop#shop/updateStart',
|
|
@@ -309,7 +315,7 @@ export const TYPERT_REMOTE = {
|
|
|
309
315
|
typeSymbol: 'dsh-plugin-shop/types#ShopUpdateResult',
|
|
310
316
|
schema: dsh_plugin_shop_shop_updateStart_result$schema,
|
|
311
317
|
},
|
|
312
|
-
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":
|
|
318
|
+
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":1106,"column":9},
|
|
313
319
|
},
|
|
314
320
|
{
|
|
315
321
|
id: 'dsh-plugin-shop#shop/version',
|
|
@@ -324,7 +330,7 @@ export const TYPERT_REMOTE = {
|
|
|
324
330
|
typeSymbol: 'dsh-plugin-shop/types#ShopVersionResult',
|
|
325
331
|
schema: dsh_plugin_shop_shop_version_result$schema,
|
|
326
332
|
},
|
|
327
|
-
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":
|
|
333
|
+
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":1089,"column":9},
|
|
328
334
|
},
|
|
329
335
|
],
|
|
330
336
|
}
|
|
@@ -15,6 +15,9 @@ declare module '@deepseek-ai/dsh-client-ui-slots' {
|
|
|
15
15
|
}
|
|
16
16
|
/** Dictionary namespace owned by this plugin. */
|
|
17
17
|
export declare const NS = "settings.shop";
|
|
18
|
+
/** How long a stashed catalog may satisfy a plain open. The host applies the
|
|
19
|
+
* same five-minute freshness window, so the client stash must not outlive it. */
|
|
20
|
+
export declare const WARM_TTL_MS: number;
|
|
18
21
|
/** Services required by the tab registration and the Remote mount.
|
|
19
22
|
*
|
|
20
23
|
* `remote.shop` is deliberately ABSENT: this package both mounts its own
|
|
@@ -57,6 +57,7 @@ export declare const zh: {
|
|
|
57
57
|
versionMismatchCode: string;
|
|
58
58
|
needsAcknowledgementCode: string;
|
|
59
59
|
tarballIntegrityCode: string;
|
|
60
|
+
ambiguousIdentityCode: string;
|
|
60
61
|
npmSource: string;
|
|
61
62
|
githubSource: string;
|
|
62
63
|
installedSection: string;
|
|
@@ -148,6 +149,7 @@ export declare const en: {
|
|
|
148
149
|
versionMismatchCode: string;
|
|
149
150
|
needsAcknowledgementCode: string;
|
|
150
151
|
tarballIntegrityCode: string;
|
|
152
|
+
ambiguousIdentityCode: string;
|
|
151
153
|
npmSource: string;
|
|
152
154
|
githubSource: string;
|
|
153
155
|
installedSection: string;
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* timers, no I/O: every function here maps a value to a value, so fixtures
|
|
3
3
|
* drive all of it. */
|
|
4
4
|
export { isShopLike } from '../shared/shop-like.ts';
|
|
5
|
+
export { identityKey, installedSpecMatches, parseRepoSpec, type EntryIdentity } from '../shared/identity.ts';
|
|
5
6
|
import type { ShopLocaleKey } from './locales.ts';
|
|
6
7
|
import type { CatalogEntry, HotRestartReason, InstallRejectionCode } from '../host/index.ts';
|
|
7
8
|
/** Hot-mount reason code → locale key, so the notice reads in the language
|
|
@@ -12,8 +13,9 @@ export declare function restartReasonKey(reason: HotRestartReason | undefined):
|
|
|
12
13
|
export declare function tierKey(tier: CatalogEntry['tier']): ShopLocaleKey;
|
|
13
14
|
/** The peers the Host said this installation does not provide, or none when it
|
|
14
15
|
* said nothing — a plugin that runs here and one the Host could not judge are
|
|
15
|
-
* both rendered as no warning at all.
|
|
16
|
-
|
|
16
|
+
* both rendered as no warning at all. The map is keyed by install identity,
|
|
17
|
+
* never by name, because two entries can share a name. */
|
|
18
|
+
export declare function missingPeersOf(incompatible: Record<string, string[]>, key: string): string[];
|
|
17
19
|
/** Spec §9.3 verbatim — the community-tier acknowledgement. The zh dictionary
|
|
18
20
|
* states the same facts in its own register; never soften this wording. */
|
|
19
21
|
export declare const ACKNOWLEDGEMENT_EN = "Once installed, this plugin holds the same privileges as a built-in one: reading and writing your files, running shell commands, and reading and modifying the requests sent to the model. It has not been reviewed.";
|
|
@@ -21,3 +21,21 @@ export declare function usePollStatus(view: InstallView, setView: Dispatch<SetSt
|
|
|
21
21
|
export declare function useInstall(install: (args: InstallArgs) => Promise<ShopInstallResult>, installStatus: (args: {
|
|
22
22
|
installId: string;
|
|
23
23
|
}) => Promise<ShopInstallStatusResult>): UseInstallResult;
|
|
24
|
+
/** One entry's install flow, as the tab hands it to a panel. */
|
|
25
|
+
export interface InstallFlow {
|
|
26
|
+
view: InstallView;
|
|
27
|
+
start: (args: InstallArgs) => Promise<void>;
|
|
28
|
+
reset: () => void;
|
|
29
|
+
}
|
|
30
|
+
export interface UseInstallFlows {
|
|
31
|
+
/** Two panels asking for the same install identity receive one flow. */
|
|
32
|
+
flowFor: (key: string) => InstallFlow;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Install flows owned by the tab and keyed by install identity. Keeping the
|
|
36
|
+
* state above individual cards preserves a running operation when filtering
|
|
37
|
+
* unmounts its card, and makes the shelf and Outdated panels agree.
|
|
38
|
+
*/
|
|
39
|
+
export declare function useInstallFlows(install: (args: InstallArgs) => Promise<ShopInstallResult>, installStatus: (args: {
|
|
40
|
+
installId: string;
|
|
41
|
+
}) => Promise<ShopInstallStatusResult>, onSettled?: (key: string) => void): UseInstallFlows;
|
|
@@ -45,6 +45,22 @@ export declare function installFailureDetail(profile: string, log: readonly stri
|
|
|
45
45
|
* 2026-09-02 as "Update failed / dsh not found on PATH" on a working install.
|
|
46
46
|
*/
|
|
47
47
|
export declare function spawnFailureDetail(code: string | undefined, message: string, dshBin: string, platform: NodeJS.Platform): string;
|
|
48
|
+
/** The failure detail for a command stopped by the shop's deadline. */
|
|
49
|
+
export declare function installTimeoutDetail(profile: string, timeoutMs: number): string;
|
|
50
|
+
/** Kill primitives injected by tests so both platform branches are testable. */
|
|
51
|
+
export interface KillFns {
|
|
52
|
+
killGroup: (pid: number) => void;
|
|
53
|
+
killPid: (pid: number) => void;
|
|
54
|
+
taskkill: (pid: number) => void;
|
|
55
|
+
}
|
|
56
|
+
/** Kill a child and its descendants (process group on POSIX, taskkill tree on Windows). */
|
|
57
|
+
export declare function killTree(pid: number | undefined, platform: NodeJS.Platform, kills?: KillFns): void;
|
|
58
|
+
/** One stream's line assembler, retaining partial UTF-8 and text lines. */
|
|
59
|
+
export interface LineSink {
|
|
60
|
+
write: (chunk: Buffer) => void;
|
|
61
|
+
flush: () => void;
|
|
62
|
+
}
|
|
63
|
+
export declare function lineSink(emit: (line: string) => void): LineSink;
|
|
48
64
|
/**
|
|
49
65
|
* Run one `dsh plugin --profile <profile> add <spec>` and track it.
|
|
50
66
|
* When `expectedName` is given, a zero exit is confirmed against the profile
|
|
@@ -62,6 +78,7 @@ export declare function startInstall(options: {
|
|
|
62
78
|
restartReason?: HotRestartReason;
|
|
63
79
|
} | void>;
|
|
64
80
|
onStatus?: (status: InstallStatus) => void;
|
|
81
|
+
timeoutMs?: number;
|
|
65
82
|
}): RunningInstall;
|
|
66
83
|
/**
|
|
67
84
|
* Run one `dsh plugin --profile <profile> remove <name>` and track it.
|
|
@@ -81,5 +98,6 @@ export declare function startUninstall(options: {
|
|
|
81
98
|
restartReason?: HotRestartReason;
|
|
82
99
|
} | void>;
|
|
83
100
|
onStatus?: (status: InstallStatus) => void;
|
|
101
|
+
timeoutMs?: number;
|
|
84
102
|
}): RunningInstall;
|
|
85
103
|
export {};
|
package/lib/types/host/hot.d.ts
CHANGED
|
@@ -91,6 +91,11 @@ export interface HotFs {
|
|
|
91
91
|
* nothing in the patch is evaluated. Pure: string in, rows out.
|
|
92
92
|
*/
|
|
93
93
|
export declare function parseSimplePatch(patchText: string): HotRow[] | null;
|
|
94
|
+
/** Render the accepted rows with prefixed ids — the exact input file the
|
|
95
|
+
* Include tree mounts. Only values the line scan accepted are emitted, and
|
|
96
|
+
* the output is itself a simple patch (round-trips through
|
|
97
|
+
* parseSimplePatch). */
|
|
98
|
+
export declare function renderRows(rows: HotRow[], prefix: string): string;
|
|
94
99
|
/**
|
|
95
100
|
* Mount one installed package into the running composition through an
|
|
96
101
|
* Include subtree: read its bundle patch, replicate the simple rows under
|
|
@@ -8,7 +8,7 @@ import { type InstallStatus } from './executor.ts';
|
|
|
8
8
|
import { hotMount, hotUnmount } from './hot.ts';
|
|
9
9
|
import { type RestartOutcome } from './restart.ts';
|
|
10
10
|
import { type RepoPinFs } from './repo-pins.ts';
|
|
11
|
-
import { type PeerResolver } from './peers.ts';
|
|
11
|
+
import { type PeerResolver, type PeerVersionResolver } from './peers.ts';
|
|
12
12
|
export type { InstallArgs, InstallRejectionCode } from './install.ts';
|
|
13
13
|
export type { HotRestartReason } from './hot.ts';
|
|
14
14
|
export type { CatalogEntry } from './types.ts';
|
|
@@ -66,6 +66,9 @@ export interface ShopGatewayOptions {
|
|
|
66
66
|
/** The dsh argv this process was launched with, for `shop/restart`;
|
|
67
67
|
* defaults to the real `process.argv` minus node and the script path. */
|
|
68
68
|
restartArgv?: string[];
|
|
69
|
+
/** The JS entry `shop/restart` re-runs; defaults to `process.argv[1]`, the
|
|
70
|
+
* script this dsh was started with. */
|
|
71
|
+
restartScript?: string;
|
|
69
72
|
/** Test-only injection: the exit the restart calls after the response is
|
|
70
73
|
* delivered. Production uses `process.exit`. */
|
|
71
74
|
exit?: (code?: number) => void;
|
|
@@ -97,6 +100,12 @@ export interface ShopGatewayOptions {
|
|
|
97
100
|
/** Test-only injection: answers whether a peer resolves. Production builds
|
|
98
101
|
* one from the profile anchor. */
|
|
99
102
|
resolvePeer?: PeerResolver;
|
|
103
|
+
/** Test-only injection: answers which version a peer resolves at, for the
|
|
104
|
+
* load-time self-check. Production builds one from the profile anchor. */
|
|
105
|
+
resolvePeerVersion?: PeerVersionResolver;
|
|
106
|
+
/** Test-only injection: the peer ranges the self-check judges against;
|
|
107
|
+
* production reads them from the shipped package.json. */
|
|
108
|
+
peerRanges?: Record<string, string>;
|
|
100
109
|
}
|
|
101
110
|
/** `shop/installStart` result (§7.3): rejections are typed wire values with an
|
|
102
111
|
* author-readable `detail`, not thrown RPC errors. */
|
|
@@ -152,14 +161,15 @@ export type ShopUpdateResult = {
|
|
|
152
161
|
ok: false;
|
|
153
162
|
detail: string;
|
|
154
163
|
};
|
|
155
|
-
/** `shop/installed` entry (§7.3): one installed catalog plugin.
|
|
156
|
-
*
|
|
157
|
-
*
|
|
158
|
-
*
|
|
159
|
-
* unknown; `outdated` is the Host's verdict that the installed version sits
|
|
160
|
-
* behind the catalog's — the client never does version math. */
|
|
164
|
+
/** `shop/installed` entry (§7.3): one installed catalog plugin. The identity
|
|
165
|
+
* fields distinguish same-named npm and GitHub rows; `installed` remains the
|
|
166
|
+
* manifest spec or the recorded GitHub pin, and the client never does version
|
|
167
|
+
* math. */
|
|
161
168
|
export interface ShopInstalledEntry {
|
|
162
169
|
name: string;
|
|
170
|
+
source: 'npm' | 'github';
|
|
171
|
+
repo?: string;
|
|
172
|
+
subdir?: string;
|
|
163
173
|
installed: string;
|
|
164
174
|
latest: string;
|
|
165
175
|
outdated: boolean;
|
|
@@ -192,9 +202,10 @@ export interface ShopCatalogResult {
|
|
|
192
202
|
/** GitHub star counts by package name; {} when the pointer names no sidecar
|
|
193
203
|
* or the sidecar could not be fetched/verified (§5). */
|
|
194
204
|
stars: Record<string, number>;
|
|
195
|
-
/**
|
|
196
|
-
*
|
|
197
|
-
* no verdict could be formed;
|
|
205
|
+
/** Install identity (`npm:<name>` / `github:<repo>#<subdir>`) → the declared
|
|
206
|
+
* peers this installation does not provide. A key is absent when the plugin
|
|
207
|
+
* runs here or when no verdict could be formed; same-named entries stay
|
|
208
|
+
* independent. */
|
|
198
209
|
incompatible: Record<string, string[]>;
|
|
199
210
|
}
|
|
200
211
|
/** Remote-only service exposing the shop Remote methods of §7.3.
|
|
@@ -213,6 +224,8 @@ export declare class ShopGateway extends TypertRemoteService {
|
|
|
213
224
|
/** The argv `shop/restart` re-spawns: the real process argv minus node and
|
|
214
225
|
* the CLI script path, or a test-provided substitute. */
|
|
215
226
|
private readonly restartArgv;
|
|
227
|
+
/** The script the restart re-runs — this process's own entry. */
|
|
228
|
+
private readonly restartScript;
|
|
216
229
|
/** The exit the restart calls once the response is out; `process.exit` in
|
|
217
230
|
* production, a spy in tests. */
|
|
218
231
|
private readonly exit;
|
|
@@ -242,9 +255,14 @@ export declare class ShopGateway extends TypertRemoteService {
|
|
|
242
255
|
/** The install gate runs against the last loaded snapshot, never a fresh
|
|
243
256
|
* fetch per request (§7.2: the Host's cached snapshot is the truth). */
|
|
244
257
|
private lastSnapshot;
|
|
258
|
+
/** A cold-cache catalog load shared by concurrent RPC calls. */
|
|
259
|
+
private inFlightLoad;
|
|
245
260
|
/** The origin list built for the last-seen `catalogUrl`, memoised so the
|
|
246
261
|
* user's npmrc is read at most once per gateway (see `originsFor`). */
|
|
247
262
|
private originCache;
|
|
263
|
+
/** The user's own `registry=` from `~/.npmrc`, read at most once per
|
|
264
|
+
* gateway. A wrapper distinguishes a genuine `null` from an unread value. */
|
|
265
|
+
private npmRegistryCache;
|
|
248
266
|
/** The incompatibility map already computed for `lastSnapshot`, keyed by
|
|
249
267
|
* that snapshot's own object identity. Design §3 asks for the verdict
|
|
250
268
|
* once per loaded snapshot, not once per RPC call: `loadCatalog` serves
|
|
@@ -260,6 +278,13 @@ export declare class ShopGateway extends TypertRemoteService {
|
|
|
260
278
|
/** Every install id in insertion order, oldest first; finished-record eviction walks this from the front. */
|
|
261
279
|
private readonly installOrder;
|
|
262
280
|
constructor(ctx: Context, options?: ShopGatewayOptions);
|
|
281
|
+
/**
|
|
282
|
+
* Warn once, at load, about any declared peer the harness provides outside
|
|
283
|
+
* its declared range. Advisory by construction: it cannot throw, and it
|
|
284
|
+
* never refuses the load — losing the whole shop is a worse outcome than
|
|
285
|
+
* running against a harness that moved.
|
|
286
|
+
*/
|
|
287
|
+
private checkPeerVersions;
|
|
263
288
|
/** The pins file lives in the shop's own cache, next to the catalog cache. */
|
|
264
289
|
private pinsPath;
|
|
265
290
|
/** The boot's Loader root directory (the active profile's `cordis.yml`
|
|
@@ -301,10 +326,17 @@ export declare class ShopGateway extends TypertRemoteService {
|
|
|
301
326
|
enabled: boolean;
|
|
302
327
|
}): Promise<ShopSetEnabledResult>;
|
|
303
328
|
private rowConfig;
|
|
329
|
+
/** The user's own registry, read once. The self-update check and catalog
|
|
330
|
+
* race share this preference so neither path repeatedly touches ~/.npmrc. */
|
|
331
|
+
private npmRegistry;
|
|
304
332
|
/** The origins to race for this row's catalog. Read once per gateway: the
|
|
305
333
|
* user's npmrc does not change under a running dsh, and re-reading it on
|
|
306
334
|
* every catalog call would put a filesystem read on the hot path. */
|
|
307
335
|
private originsFor;
|
|
336
|
+
/** Join an in-flight load; refresh requests always start a new load. */
|
|
337
|
+
private loadCatalogOnce;
|
|
338
|
+
/** Return the current snapshot, loading it once when needed. */
|
|
339
|
+
private snapshotNow;
|
|
308
340
|
/** The explicit restart override. Only the row's `config:` sub-object is
|
|
309
341
|
* passed to a plugin — a top-level `allowRestart:` beside `name:` would be
|
|
310
342
|
* silently ignored by the loader (dsh-market README, #227). */
|
|
@@ -326,6 +358,8 @@ export declare class ShopGateway extends TypertRemoteService {
|
|
|
326
358
|
* oldest finished ones (insertion order, oldest first). Running records
|
|
327
359
|
* are never evicted; an id absent from the map reports `found: false`. */
|
|
328
360
|
private evictFinishedInstalls;
|
|
361
|
+
/** Whether any command this gateway started is still running. */
|
|
362
|
+
private hasRunningCommand;
|
|
329
363
|
/** Poll one install's progress (§7.2); unknown ids report `found: false`. */
|
|
330
364
|
installStatus(args: {
|
|
331
365
|
installId: string;
|
|
@@ -1,21 +1,24 @@
|
|
|
1
1
|
/** Install gate: the gate rejection paths of §7.2, as a pure function. */
|
|
2
2
|
import type { CatalogSnapshot } from './catalog.ts';
|
|
3
|
-
|
|
3
|
+
import type { CatalogEntry } from './types.ts';
|
|
4
|
+
export type InstallRejectionCode = 'not-in-catalog' | 'denied' | 'version-mismatch' | 'needs-acknowledgement' | 'tarball-integrity' | 'ambiguous-identity';
|
|
4
5
|
export interface InstallArgs {
|
|
5
6
|
name: string;
|
|
6
7
|
version: string;
|
|
7
8
|
acknowledged?: boolean;
|
|
9
|
+
/** Optional identity fields keep old clients readable while refusing an
|
|
10
|
+
* ambiguous name-only request when the catalog has duplicate names. */
|
|
11
|
+
source?: 'npm' | 'github';
|
|
12
|
+
repo?: string;
|
|
13
|
+
subdir?: string;
|
|
8
14
|
}
|
|
9
15
|
export type ValidateResult = {
|
|
10
16
|
ok: true;
|
|
17
|
+
entry: CatalogEntry;
|
|
11
18
|
} | {
|
|
12
19
|
ok: false;
|
|
13
20
|
code: InstallRejectionCode;
|
|
14
21
|
detail: string;
|
|
15
22
|
};
|
|
16
|
-
/**
|
|
17
|
-
* Decide whether one install request may proceed, against the Host's own
|
|
18
|
-
* snapshot (§5.3). The browser sends a name; nothing the browser says about
|
|
19
|
-
* the package is trusted.
|
|
20
|
-
*/
|
|
23
|
+
/** Decide whether an install request may proceed and return the resolved row. */
|
|
21
24
|
export declare function validateInstall(snapshot: CatalogSnapshot, args: InstallArgs): ValidateResult;
|
|
@@ -5,6 +5,10 @@
|
|
|
5
5
|
* bytes reach a China-side machine at 12.53 MB/s from npmmirror against
|
|
6
6
|
* 0.03 MB/s from GitHub Pages. */
|
|
7
7
|
import { type CatalogOrigin } from './origin.ts';
|
|
8
|
+
/** Maximum compressed package bytes read from a registry. */
|
|
9
|
+
export declare const MAX_PACKAGE_BYTES: number;
|
|
10
|
+
/** Maximum bytes allowed after gzip inflation. */
|
|
11
|
+
export declare const MAX_INFLATED_BYTES: number;
|
|
8
12
|
/** Normalise to a trailing slash so relative `URL` resolution against a
|
|
9
13
|
* registry that carries a path — every corporate registry, e.g.
|
|
10
14
|
* `https://artifactory.corp/api/npm/npm-repo` — keeps that path instead of
|
|
@@ -20,7 +20,7 @@ export interface OriginHandle {
|
|
|
20
20
|
/** One file named by the pointer, by the pointer's own raw url string.
|
|
21
21
|
* Callers pass that string verbatim — never a basename, which would strip
|
|
22
22
|
* a hostile absolute url into a fetchable relative one. */
|
|
23
|
-
file: (url: string) => Promise<string>;
|
|
23
|
+
file: (url: string, signal?: AbortSignal) => Promise<string>;
|
|
24
24
|
}
|
|
25
25
|
export interface CatalogOrigin {
|
|
26
26
|
readonly id: string;
|
|
@@ -36,5 +36,11 @@ export interface CatalogOrigin {
|
|
|
36
36
|
* before the string could be inspected, so only comparing the resolved URL's
|
|
37
37
|
* origin to the base's closes every spelling class. */
|
|
38
38
|
export declare function resolveDataUrl(baseUrl: string, url: string): string;
|
|
39
|
+
/** The largest catalog body this host reads into memory. */
|
|
40
|
+
export declare const MAX_BODY_BYTES: number;
|
|
41
|
+
/** Read a response body through a byte cap, converting transport failures. */
|
|
42
|
+
export declare function readCappedBytes(response: Response, label: string, maxBytes?: number): Promise<Buffer>;
|
|
43
|
+
/** Read a capped response body as UTF-8 text. */
|
|
44
|
+
export declare function readCappedText(response: Response, label: string, maxBytes?: number): Promise<string>;
|
|
39
45
|
/** The transport this project has always used: a static `v1/` tree. */
|
|
40
46
|
export declare function httpOrigin(baseUrl: string, fetchImpl: typeof fetch): CatalogOrigin;
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
/** Harness compatibility: which declared peers the running installation does
|
|
2
|
-
* not provide (design 2026-09-01-harness-compatibility)
|
|
2
|
+
* not provide (design 2026-09-01-harness-compatibility), and — for the
|
|
3
|
+
* shop's OWN declared peers — which ones it provides at a version outside
|
|
4
|
+
* the declared range. */
|
|
5
|
+
import { type EntryIdentity } from '../shared/identity.ts';
|
|
3
6
|
/** Answers "can this installation provide `spec`?" — injected so fixtures
|
|
4
7
|
* drive every verdict and exactly one call site touches the filesystem. */
|
|
5
8
|
export type PeerResolver = (spec: string) => boolean;
|
|
@@ -19,7 +22,75 @@ export declare function nodeResolver(baseUrl: string): PeerResolver;
|
|
|
19
22
|
* never read as an accusation, because one false warning teaches a reader to
|
|
20
23
|
* ignore every warning.
|
|
21
24
|
*/
|
|
22
|
-
export declare function incompatibilityMap(entries: readonly {
|
|
23
|
-
name: string;
|
|
25
|
+
export declare function incompatibilityMap(entries: readonly (EntryIdentity & {
|
|
24
26
|
peers?: string[];
|
|
25
|
-
}[], resolve: PeerResolver): Record<string, string[]>;
|
|
27
|
+
})[], resolve: PeerResolver): Record<string, string[]>;
|
|
28
|
+
/**
|
|
29
|
+
* The same question one step further on: which VERSION does this
|
|
30
|
+
* installation provide for `spec`? `null` is the no-verdict signal — the
|
|
31
|
+
* peer is absent, or its manifest could not be read. Injected exactly like
|
|
32
|
+
* `PeerResolver`, so fixtures drive every verdict and only one call site
|
|
33
|
+
* touches the filesystem.
|
|
34
|
+
*/
|
|
35
|
+
export type PeerVersionResolver = (spec: string) => string | null;
|
|
36
|
+
/** One declared peer whose provided version is outside its declared range. */
|
|
37
|
+
export interface PeerVersionMismatch {
|
|
38
|
+
spec: string;
|
|
39
|
+
/** The range this build declares in `peerDependencies`. */
|
|
40
|
+
range: string;
|
|
41
|
+
/** The version the installation actually provides. */
|
|
42
|
+
found: string;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* The production resolver: `nodeResolver`'s own resolution, kept this time
|
|
46
|
+
* instead of collapsed to a boolean. Anything that leaves no version to read
|
|
47
|
+
* answers null — a peer that is absent, one that restricts `./package.json`
|
|
48
|
+
* in its exports, an unreadable or malformed manifest, a manifest with no
|
|
49
|
+
* `version`. Absence is deliberately NOT reported as a version violation:
|
|
50
|
+
* `incompatibilityMap` is what covers a missing peer.
|
|
51
|
+
*/
|
|
52
|
+
export declare function nodeVersionResolver(baseUrl: string): PeerVersionResolver;
|
|
53
|
+
/**
|
|
54
|
+
* The declared peers whose provided version is outside the declared range,
|
|
55
|
+
* ordered by peer name. Pure: the module graph arrives through `resolve`.
|
|
56
|
+
*
|
|
57
|
+
* `includePrerelease` is load-bearing, not a convenience. The harness ships
|
|
58
|
+
* nothing but `-rc` versions, so under strict semver `^0.1.1-rc.2` excludes
|
|
59
|
+
* `0.1.2-rc.1` — the version that is installed and works — and every future
|
|
60
|
+
* rc bump would raise a false alarm. With it on, the range still excludes an
|
|
61
|
+
* older prerelease (`0.1.1-rc.1`) and a minor- or major-line move
|
|
62
|
+
* (`0.2.0-rc.1`, `1.0.0`), which are the moves that actually break a plugin
|
|
63
|
+
* path. Discrimination on both sides is the whole point: one false warning
|
|
64
|
+
* teaches a reader to ignore every warning.
|
|
65
|
+
*
|
|
66
|
+
* Anything unreadable yields NO verdict for that peer and never a violation:
|
|
67
|
+
* a resolver that answers null or throws, a found version that is not
|
|
68
|
+
* semver, a declared range semver cannot parse. The other peers are still
|
|
69
|
+
* judged.
|
|
70
|
+
*/
|
|
71
|
+
export declare function peerVersionMismatches(ranges: Readonly<Record<string, string>>, resolve: PeerVersionResolver): PeerVersionMismatch[];
|
|
72
|
+
/**
|
|
73
|
+
* The load-time message for a set of mismatches, or null when there is
|
|
74
|
+
* nothing to say. Every mismatch is named with its declared range and the
|
|
75
|
+
* version found, in ONE message: the reader needs the whole picture in the
|
|
76
|
+
* line they will scroll past, and the message says the shop still loads
|
|
77
|
+
* because refusing to load would cost them the shop over a diagnostic.
|
|
78
|
+
*/
|
|
79
|
+
export declare function peerVersionWarning(mismatches: readonly PeerVersionMismatch[]): string | null;
|
|
80
|
+
/** What a load-time check needs: the ranges this build declares, a way to
|
|
81
|
+
* read what the installation provides, and somewhere to say it once. */
|
|
82
|
+
export interface PeerVersionCheckDeps {
|
|
83
|
+
ranges: Readonly<Record<string, string>>;
|
|
84
|
+
resolve: PeerVersionResolver;
|
|
85
|
+
warn: (message: string) => void;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* A self-check that warns AT MOST ONCE, however many times it is called.
|
|
89
|
+
* The guard is what keeps a diagnostic a diagnostic: a message repeated per
|
|
90
|
+
* call is noise a reader learns to skip, and the shop's load path is not the
|
|
91
|
+
* only place that could reasonably ask this question.
|
|
92
|
+
*
|
|
93
|
+
* It never throws. A check that could fail a load would be worse than the
|
|
94
|
+
* mismatch it reports — the user would lose the whole shop over a warning.
|
|
95
|
+
*/
|
|
96
|
+
export declare function createPeerVersionCheck(deps: PeerVersionCheckDeps): () => void;
|