dsh-plugin-shop 0.8.7 → 0.9.0-beta.2

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.
@@ -36,9 +36,9 @@ export declare function installPhaseKey(phase: 'downloading' | 'installing'): Sh
36
36
  /** Why the shop cannot restart dsh, as a locale key.
37
37
  *
38
38
  * A sibling of `activationNoticeKey` and exhaustive for the same reason: the
39
- * switch has no default, so a fourth reason added to `RestartBlockedReason` is
40
- * a type error here rather than a card that silently keeps the third one's
41
- * copy. That is the whole point of the type — the single boolean this replaced
39
+ * switch has no default, so a reason added to `RestartBlockedReason` is a type
40
+ * error here rather than a card that silently keeps a neighbour's copy. That
41
+ * is the whole point of the type — the single boolean this replaced
42
42
  * had ONE string for three causes, and it named systemd, so a Windows reader
43
43
  * was told to restart a service they do not run and to set an override that
44
44
  * could not have helped. */
@@ -146,10 +146,12 @@ export declare function refusesInstall(blockers: readonly {
146
146
  kind: BlockerKind;
147
147
  }[]): boolean;
148
148
  /**
149
- * The refused peers as the refusal's copy names them: each peer and the range
150
- * the package declared, in the order dsh reported them. A blank range — which
151
- * dsh refuses — is quoted, or the sentence would name the peer and nothing
152
- * it asked for.
149
+ * The refused requirements as the refusal's copy names them: each one and the
150
+ * range the package declared, in the order dsh reported them. A peer is named
151
+ * by its package. `engines.dsh` is a manifest path, not a name anyone asked
152
+ * for, so it reads as `dsh` and its range, the way the sentence says "it
153
+ * requires dsh >=0.3.0". A blank range — which dsh refuses — is quoted, or the
154
+ * sentence would name the requirement and nothing it asked for.
153
155
  */
154
156
  export declare function refusedPeersText(refused: Readonly<Record<string, string>>): string;
155
157
  /**
@@ -233,8 +235,9 @@ export interface InstallStatusShape {
233
235
  /** The install view state machine (§7.2). `phase` distinguishes the download
234
236
  * that runs ahead of the per-profile mutex from the install that holds it.
235
237
  * It rides the `running` kind rather than adding a kind of its own: every
236
- * `view.kind === 'running'` site — `useInstall.ts`'s poll collection among
237
- * them — must keep treating a downloading install as live. */
238
+ * `view.kind === 'running'` site — the flow registry's poll collection
239
+ * (flow-registry.ts) among them — must keep treating a downloading install
240
+ * as live. */
238
241
  export type InstallView = {
239
242
  kind: 'idle';
240
243
  } | {
@@ -328,12 +331,23 @@ export declare function restartMonitorVerdict(input: {
328
331
  * a host older than the field, the one that matters most.
329
332
  */
330
333
  export declare function answeredByAnotherProcess(previous: ShopVersionResult, next: ShopVersionResult): boolean;
334
+ /**
335
+ * Whether an update of the shop has landed and waits for a restart: the
336
+ * profile holds a version other than the one running. A host older than the
337
+ * field sends none, and that reads as waiting: a client newer than its host
338
+ * exists only because the disk holds a newer shop than the process running
339
+ * (design §7.3, 2026-09-27 amendment). The tab's restart offer and the
340
+ * hand-over after a hot swap (design 2026-10-16-shop-sidebar-page §6) read
341
+ * it alike.
342
+ */
343
+ export declare function updateAwaitsRestart(version: ShopVersionResult): boolean;
331
344
  /**
332
345
  * Whether dsh restarting under the page settles this receipt: a change that
333
- * landed and was waiting for exactly that. The tab clears these once the
334
- * answers come from another process, and nothing else — a reload is still
335
- * owed by a page nobody reloaded, a failure still happened, and a flow still
336
- * running settles by its own poll.
346
+ * landed and was waiting for exactly that. The page store clears these
347
+ * (page-store.ts, `noteRestarted`) once the answers come from another
348
+ * process, and nothing else — a reload is still owed by a page nobody
349
+ * reloaded, a failure still happened, and a flow still running settles by
350
+ * its own poll.
337
351
  */
338
352
  export declare function settledByRestart(view: InstallView): boolean;
339
353
  /** How long the check-update button reports "up to date" after a re-check
@@ -456,6 +470,14 @@ export declare function npmPageUrl(entry: CatalogEntry): string | null;
456
470
  * This mirrors the registry's identity verbatim; the two must not drift.
457
471
  */
458
472
  export declare function entryKey(entry: CatalogEntry): string;
473
+ /**
474
+ * Whether the shelf lists `entry`: a name the catalog's `notAShop` clears,
475
+ * or one whose name and repository `isShopLike` leaves alone. One rule for
476
+ * the shelf and for the Plugins page's "View in Plugin shop" (design
477
+ * 2026-10-16-shop-sidebar-page §7), so that button never sends a reader to a
478
+ * shelf that hides the plugin.
479
+ */
480
+ export declare function listedOnShelf(entry: CatalogEntry, cleared: ReadonlySet<string>): boolean;
459
481
  /**
460
482
  * One entry's star count from the sidecar, or undefined when it has none.
461
483
  *
@@ -72,6 +72,17 @@ export type RestartRequestOutcome = {
72
72
  } | {
73
73
  kind: 'unreachable';
74
74
  };
75
+ /**
76
+ * Whether a shop instance on this page has registered its restart path:
77
+ * every `apply` since 0.8.4 does so before anything can ask for a restart.
78
+ * Read before this instance's own `apply` registers one, a store here names
79
+ * a predecessor — a 0.8.x shop when no page store sits beside it
80
+ * (page-store.ts `readPredecessor`; design 2026-10-16-shop-sidebar-page §6).
81
+ * The store outlives a carrier's withdrawal: a predecessor's teardown
82
+ * withdraws its carrier and leaves the store, and the store is what names it.
83
+ * Only a read: unlike `monitorStore`, it never creates the store.
84
+ */
85
+ export declare function hasRestartMonitorStore(): boolean;
75
86
  /** The page's restart, or null when it has committed none. The same object
76
87
  * until the state changes, as `useSyncExternalStore` requires. */
77
88
  export declare function readRestartMonitor(): RestartMonitorState | null;
@@ -1,5 +1,6 @@
1
1
  /**
2
- * One keyed registry of install-shaped flows: start, poll to terminal, reset.
2
+ * React's view of a keyed registry of install-shaped flows (flow-registry.ts):
3
+ * start, poll to terminal, reset.
3
4
  *
4
5
  * Install and uninstall are the same machine. Both fold a
5
6
  * `ShopInstallStatusResult` through `reduceInstall` into an `InstallView`,
@@ -12,16 +13,22 @@
12
13
  * install refusal is a `rejected` view carrying the gate's code; an uninstall
13
14
  * refusal is a `failed` view carrying the host's published detail, because
14
15
  * `ShopUninstallResult`'s failure variant has no code to populate and those
15
- * codes belong to the install gate. That difference is the `begin` parameter,
16
- * and it is the whole of it.
16
+ * codes belong to the install gate. That difference is the registry's
17
+ * `begin`, and it is the whole of it.
17
18
  *
18
19
  * The two used to be hand-synced copies — 71 of 84 lines identical. The cost
19
20
  * was not theoretical: a single review round had to make the same edit twice
20
21
  * (the settle signature, and its call), and the `pending` set below was added
21
22
  * to one of them and not the other before this module existed.
23
+ *
24
+ * Since 2026-10-16 the registry is its own module, framework-free, and the
25
+ * polling belongs to it: a component's effect stopped the moment the shop left
26
+ * the screen (design 2026-10-16-shop-sidebar-page §5). This module only
27
+ * renders one, through `useRegistryFlows`.
22
28
  */
23
- import type { ShopInstallStatusResult } from '../host/index.ts';
24
- import { type InstallView } from './present.ts';
29
+ import type { InstallArgs } from '../host/index.ts';
30
+ import { type FlowRegistry } from './flow-registry.ts';
31
+ import type { InstallView } from './present.ts';
25
32
  /** One identity's flow, as the tab hands it to a panel. */
26
33
  export interface KeyedFlow<TArgs> {
27
34
  view: InstallView;
@@ -39,16 +46,13 @@ export interface UseKeyedFlows<TArgs> {
39
46
  * moves. The accessor's identity still tracks `views`; the result's does
40
47
  * not, and the result is what a card should be given. */
41
48
  flowFor: (key: string) => KeyedFlow<TArgs>;
42
- /** Return one identity to `idle` without building a flow for it. Stable
43
- * across renders (unlike `flowFor`, whose identity tracks `views`), so one
44
- * registry's settle callback can supersede the other's receipt for the same
45
- * key without taking a dependency on that registry's output. */
46
- resetFlow: (key: string) => void;
47
49
  /** Return every identity whose view `predicate` selects to `idle`, and
48
- * nothing else. Stable across renders like `resetFlow`; a call selecting
50
+ * nothing else. Stable across renders, unlike `flowFor`; a call selecting
49
51
  * nothing leaves the registry — and so every flow and `pending` — exactly
50
- * as it was. The tab clears the receipts dsh restarting under it settled
51
- * with this (design §8, 2026-10-06 amendment). */
52
+ * as it was. The receipts dsh restarting under the page settled are
53
+ * cleared the same way at the store, through each registry's own
54
+ * `resetWhere` (page-store.ts `noteRestarted`; design §8, 2026-10-06
55
+ * amendment). */
52
56
  resetWhere: (predicate: (view: InstallView) => boolean) => void;
53
57
  /**
54
58
  * The identities whose flow is not `idle` — asked by the shelf, which has
@@ -65,16 +69,20 @@ export interface UseKeyedFlows<TArgs> {
65
69
  */
66
70
  pending: ReadonlySet<string>;
67
71
  }
68
- /**
69
- * @param begin the starting RPC and its answer, mapped to the first view.
70
- * Never rejects: a transport throw is the caller's to fold into a view,
71
- * because only the caller knows which localized line stands in for a detail
72
- * too private to render.
73
- * @param onSettled called once per identity that reaches a terminal state,
74
- * with WHICH terminal state. The outcome is load-bearing: a failed flow
75
- * changed nothing on the server, so a caller acting on "settled" alone
76
- * discards outcomes that are still true.
77
- */
78
- export declare function useKeyedFlows<TArgs>(begin: (args: TArgs) => Promise<InstallView>, installStatus: (args: {
79
- installId: string;
80
- }) => Promise<ShopInstallStatusResult>, onSettled?: (key: string, outcome: 'done' | 'failed') => void): UseKeyedFlows<TArgs>;
72
+ /** One entry's install flow, as the tab hands it to a panel: the page
73
+ * store's install registry (page-store.ts), begun through flow-registry.ts's
74
+ * `beginInstall`. */
75
+ export type InstallFlow = KeyedFlow<InstallArgs>;
76
+ /** One entry's uninstall flow, as the tab hands it to a panel: the page
77
+ * store's uninstall registry, begun through `beginUninstall`. */
78
+ export type UninstallFlow = KeyedFlow<{
79
+ name: string;
80
+ }>;
81
+ /** The uninstall flows as the tab hands them to the shadowed rows. */
82
+ export type UseUninstallFlows = UseKeyedFlows<{
83
+ name: string;
84
+ }>;
85
+ /** The keyed-flow face over any registry: one of the page store's, which
86
+ * outlives the component, or one of the store a tab builds for itself when
87
+ * none is handed over (ShopTab). */
88
+ export declare function useRegistryFlows<TArgs>(registry: FlowRegistry<TArgs>): UseKeyedFlows<TArgs>;
@@ -1,25 +1,28 @@
1
- /** Self-update driving hook for the shop itself: start, poll to terminal.
2
- * Shares the install view machine and poll loop; the start mapping is the
3
- * uninstall one — a business refusal (a non-semver version) lands in the
4
- * `failed` view with the host's detail, never in the install `rejected`
5
- * codes. */
6
- import type { ShopInstallStatusResult, ShopUpdateResult } from '../host/index.ts';
7
- import { type InstallView } from './present.ts';
1
+ /** The shop's own update as the tab renders it: the face over the registry
2
+ * that holds it, a page store's `self` (page-store.ts). It shares the install
3
+ * view machine and the registry's polling; its start mapping is
4
+ * flow-registry.ts's `beginUpdate`, which shares the uninstall's refusal
5
+ * rule — a business refusal (a non-semver version) lands in the `failed`
6
+ * view with the host's detail, never in the install `rejected` codes. */
7
+ import type { FlowRegistry } from './flow-registry.ts';
8
+ import type { InstallView } from './present.ts';
8
9
  export interface UseUpdateSelfResult {
9
10
  view: InstallView;
10
11
  start: (args: {
11
12
  version: string;
12
13
  }) => Promise<void>;
13
14
  /** Return the view to `idle` when `predicate` selects it; stable across
14
- * renders. The keyed registries' `resetWhere`, for the one self-update:
15
- * dsh restarting under the page settles an update that was waiting for
16
- * exactly that (design §8, 2026-10-06 amendment). */
15
+ * renders. The failed panel's Dismiss uses it, so the version row decides
16
+ * again; an update waiting for a restart is settled at the store instead,
17
+ * when dsh restarts under the page (page-store.ts `noteRestarted`; design
18
+ * §8, 2026-10-06 amendment). */
17
19
  resetWhere: (predicate: (view: InstallView) => boolean) => void;
18
20
  }
19
- /** Drive one self-update: the host's business union, the shared polling
20
- * loop, and terminal states. The `rejected` install state never occurs. */
21
- export declare function useUpdateSelf(updateStart: (args: {
21
+ /** The one key the self-update registry holds: the shop has one update. */
22
+ export declare const SELF_UPDATE_KEY = "self";
23
+ /** The self-update face over a registry: the keyed-flow face
24
+ * (`useRegistryFlows`) at its one key, so the subscription and the flow's
25
+ * identity are that hook's and nothing here can drift from it. */
26
+ export declare function useRegistrySelf(registry: FlowRegistry<{
22
27
  version: string;
23
- }) => Promise<ShopUpdateResult>, installStatus: (args: {
24
- installId: string;
25
- }) => Promise<ShopInstallStatusResult>): UseUpdateSelfResult;
28
+ }>): UseUpdateSelfResult;
@@ -28,13 +28,19 @@
28
28
  */
29
29
  import { type ProfileTemplates } from './compatibility.ts';
30
30
  /** One package's harness peers as the running dsh judges them: app-boot's
31
- * `evaluatePluginCompatibility` result, the shape 0.1.7 returns. */
31
+ * `evaluatePluginCompatibility` result, in the shape 0.1.7 returns, whichever
32
+ * name the running dsh gives its list (`peerCheckOf`). */
32
33
  export interface PeerIssue {
33
34
  name: string;
34
35
  version: string;
35
36
  /** The dsh version the check judged against — app-boot's own. */
36
37
  runtimeVersion: string;
37
- /** The refused peers, each with the range the package declared. */
38
+ /** What the package asked of dsh and the running one does not meet, each
39
+ * with the range the package declared. A key is a dsh peer's package name,
40
+ * or the literal `engines.dsh`. dsh names this list `requirements` from
41
+ * 0.2.1 and `peers` before it (design 2026-10-19-dsh-021-readiness, section
42
+ * 4); `refusedRequirements` reads either into this field, so every consumer
43
+ * keeps reading the one name. */
38
44
  peers: Record<string, string>;
39
45
  /** Whether the profile holds dsh's exact-version exemption for this
40
46
  * package on this dsh, in which case dsh accepts the install anyway. */
@@ -84,6 +90,24 @@ export interface RunningHarness {
84
90
  * not be read — no verdict either way. */
85
91
  peerCheck: PeerCheck | null;
86
92
  }
93
+ /**
94
+ * The list a refusal names, read off one answer of dsh's compatibility check
95
+ * or of its plugin manager, under whichever name the dsh that gave the answer
96
+ * uses: `requirements` from 0.2.1, `peers` from 0.1.7 to 0.2.0. app-boot's
97
+ * `evaluatePluginCompatibility` and dsh-plugin-manager's `IncompatiblePlugin`
98
+ * were renamed together (design 2026-10-19-dsh-021-readiness, section 4, read
99
+ * off 0.2.1-rc.2's own types and code on 2026-10-19). A key is a dsh peer's
100
+ * package name or the literal `engines.dsh`; a value is the range the package
101
+ * declared.
102
+ *
103
+ * `requirements` is read first, and only when it holds an object: an answer
104
+ * whose `requirements` is anything else is not in the newer shape, so its
105
+ * `peers` is tried. Only the entries whose range is a string are kept, as
106
+ * `incompatibleList` always kept them, so one malformed range costs its own
107
+ * entry and not the rest of the list. An answer naming neither reads as an
108
+ * empty list, never as a throw.
109
+ */
110
+ export declare function refusedRequirements(answer: Readonly<Record<string, unknown>>): Record<string, string>;
87
111
  /**
88
112
  * The harness this process runs, identified from `script` — the gateway's
89
113
  * `restartScript`, `process.argv[1]` in production — or nothing, never a
@@ -211,7 +211,7 @@ export type ShopRestartResult = RestartOutcome;
211
211
  * restart was impossible after it had become possible. Static here, dynamic
212
212
  * there; the split is the whole reason this is a separate predicate rather
213
213
  * than a cache of `restart()`'s answer. */
214
- export type RestartBlockedReason = 'desktop' | 'windows' | 'systemd' | 'port-zero';
214
+ export type RestartBlockedReason = 'desktop' | 'from-default-profile' | 'windows' | 'systemd' | 'port-zero';
215
215
  /** `shop/version` result (§7.3): the RUNNING shop version (from the shipped
216
216
  * package.json, not the manifest's range), the npm latest when the check
217
217
  * could answer (`null` = no answer — advisory, never an error), and the
@@ -618,14 +618,32 @@ export declare class ShopGateway extends TypertRemoteService {
618
618
  *
619
619
  * The order is load-bearing for the copy a reader sees. The desktop
620
620
  * profile first: dsh itself refuses to launch it, so no platform,
621
- * supervisor or port could make a restart possible there. Then Windows,
622
- * because the platform check has no override and reporting the systemd one
623
- * there sends a Windows user to set `allowRestart: true`, which this gate
624
- * would still refuse.
621
+ * supervisor or port could make a restart possible there, and the remedy
622
+ * is the app. Then `from-default-profile` and Windows, both ahead of
623
+ * systemd, because neither has an override and reporting the systemd one
624
+ * there sends the reader to set `allowRestart: true`, which this gate would
625
+ * still refuse. And `from-default-profile` ahead of Windows, because every
626
+ * remedy after `desktop` is "restart dsh manually", which a reader launched
627
+ * with the flag would do with the flag, and on 0.2.1 that recreates the
628
+ * profile: its copy is the one that says "without that flag", and a
629
+ * Windows, systemd or `--port 0` reader needs it as much as any other.
625
630
  *
626
631
  * - `desktop`: dsh's CLI refuses `--profile desktop` ("managed exclusively
627
632
  * by the Electron application"), so the relaunch would exit at once and
628
633
  * leave nothing on the port; the app restarts it.
634
+ * - `from-default-profile`: from dsh 0.2.1, `--from-default-profile <name>`
635
+ * replaces an existing custom profile with a fresh copy of the shipped
636
+ * template on every launch, and the relaunch re-runs this argv verbatim,
637
+ * so it would delete every plugin in the profile, this shop included.
638
+ * Measured 2026-10-19 in throwaway homes: 0.2.1-rc.2 booted a relaunched
639
+ * profile that had held the shop with no dependencies, while 0.2.0-rc.2
640
+ * refused to boot an existing profile at all, so there the restarted dsh
641
+ * failed to come back. Both spellings are the flag:
642
+ * `--from-default-profile <name>` and `--from-default-profile=<name>`. It
643
+ * is left in the argv rather than stripped: the restarted process would no
644
+ * longer run the command the reader chose, and their next launch with the
645
+ * flag recreates the profile anyway. No override (design
646
+ * 2026-10-19-dsh-021-readiness §3).
629
647
  * - `windows`: the handoff helper is a POSIX shell one-liner (restart.ts)
630
648
  * and there is no `sh` on Windows. That spawn fails ASYNCHRONOUSLY, so
631
649
  * committing would answer `ok: true`, exit this process, and leave nothing
@@ -633,7 +651,7 @@ export declare class ShopGateway extends TypertRemoteService {
633
651
  * - `systemd`: under a unit the two-phase handoff kills itself, because the
634
652
  * main process exiting also kills the unit's cgroup and takes the detached
635
653
  * helper with it; the service never comes back. Overridable, and the only
636
- * one of the three that is.
654
+ * one that is.
637
655
  * - `port-zero`: the OS hands the NEW process a fresh port the browser
638
656
  * cannot know, so a restart would strand the client on a dead origin. */
639
657
  private staticRestartBlock;
@@ -668,6 +686,15 @@ export declare class ShopGateway extends TypertRemoteService {
668
686
  * passing request reaches the executor.
669
687
  */
670
688
  install(args: InstallArgs): Promise<ShopInstallResult>;
689
+ /**
690
+ * One install of `name` through dsh's CLI, `dsh plugin --profile <p> add
691
+ * <spec>` (§7.2): the path every install takes when dsh offers no plugin
692
+ * manager, and the one that continues an update the manager refuses in
693
+ * place (design 2026-10-19-dsh-021-readiness §2). `alreadyImported` is
694
+ * whether this process may already hold the package's module, which rules
695
+ * out a hot mount. The caller tracks the record and records any pin, once.
696
+ */
697
+ private startCliInstall;
671
698
  /**
672
699
  * What would stop the profile from starting now that `name`'s files are on
673
700
  * disk, or null: an entry id another installed package already declares,
@@ -790,5 +817,11 @@ export declare class ShopGateway extends TypertRemoteService {
790
817
  updateStart(args: {
791
818
  version: string;
792
819
  }): Promise<ShopUpdateResult>;
820
+ /** The shop's own update through dsh's CLI: the path when dsh offers no
821
+ * plugin manager, and the one that continues a self-update the manager
822
+ * refuses in place (design 2026-10-19-dsh-021-readiness §2). It passes no
823
+ * `afterDone`, so it reports a restart: a host half cannot swap itself live
824
+ * (§8). The caller tracks the record. */
825
+ private startCliSelfUpdate;
793
826
  }
794
827
  export default ShopGateway;
@@ -49,3 +49,27 @@ export interface ManagerOperationOptions {
49
49
  * can throw, and a null-prototype object has no conversion to a string. */
50
50
  export declare const messageOf: (error: unknown) => string;
51
51
  export declare function startManagerOperation(options: ManagerOperationOptions): RunningInstall;
52
+ /**
53
+ * `first`, continued by the record `next` starts when `first` ends refused,
54
+ * as one record under `first`'s id: design 2026-10-19-dsh-021-readiness,
55
+ * section 2. dsh-plugin-manager 0.2.1-rc.2 refuses an update in place as
56
+ * `already-installed`, and the gateway continues such an update through
57
+ * dsh's CLI. One record, so the client keeps polling the id it was given,
58
+ * and the restart gate and eviction, which read every tracked record, see
59
+ * the command that runs.
60
+ *
61
+ * While `first` runs, this is `first`. Whether it continues is decided once,
62
+ * when `first` ends: the first `status()` read or `first.finished` settling
63
+ * that sees `first` terminal asks `refused`, and that answer is kept. So a
64
+ * caller may set its flag while reading the answer that ends `first`, and a
65
+ * flag flipped after that changes neither `status()` nor `finished`, which
66
+ * answer from that one decision. Not refused, this stays `first`. Refused,
67
+ * `next` runs, exactly once, at that same read or finish, so no read between
68
+ * the refusal and the next command reports the refusal. From then on the
69
+ * record is `next`'s, its state, detail and activation, with `first`'s log,
70
+ * then `bridge`, then `next`'s log. That log joins two bounded logs and the
71
+ * bridge, so a continued record can carry about twice `MAX_LOG_LINES` and
72
+ * `MAX_LOG_BYTES`, where one record of `startManagerOperation` carries at most
73
+ * one line over them.
74
+ */
75
+ export declare function continueAfter(first: RunningInstall, refused: () => boolean, next: () => RunningInstall, bridge: string): RunningInstall;
@@ -24,6 +24,11 @@ export interface PluginManagerLike {
24
24
  * All or nothing: half a service would take an install and fail its
25
25
  * uninstall. */
26
26
  export declare function asPluginManager(service: unknown): PluginManagerLike | null;
27
+ /** One entry of dsh's `incompatible` list. `peers` is what the package
28
+ * declared and the running dsh does not meet, read under whichever name that
29
+ * dsh gives it, `requirements` from 0.2.1 and `peers` before
30
+ * (`refusedRequirements`): a key is a dsh peer's package name or
31
+ * `engines.dsh`. */
27
32
  export interface ManagerIncompatible {
28
33
  name: string;
29
34
  version: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-plugin-shop",
3
- "version": "0.8.7",
3
+ "version": "0.9.0-beta.2",
4
4
  "description": "The DeepSeek Harness plugin shop: browse, install, enable, and update dsh plugins from a git-auditable catalog.",
5
5
  "icon": "./assets/icon.png",
6
6
  "keywords": [
@@ -1,10 +0,0 @@
1
- /**
2
- * The shop's Settings section (design §5.1, amended 2026-10-15): its own
3
- * entry in the Settings nav, directly after Built-in plugins, where it used
4
- * to be a tab inside that section. The page carries the title and the one
5
- * intro line the built-in Models and Built-in plugins pages carry, above the
6
- * shop body, which is `ShopTab` unchanged.
7
- */
8
- import type { ReactNode } from 'react';
9
- import { type ShopTabProps } from './ShopTab.tsx';
10
- export declare function ShopSection(props: ShopTabProps): ReactNode;
@@ -1,31 +0,0 @@
1
- /** Install driving hooks: the shared poll loop, and the tab's keyed registry. */
2
- import { type Dispatch, type SetStateAction } from 'react';
3
- import { type InstallView } from './present.ts';
4
- import { type KeyedFlow, type UseKeyedFlows } from './useFlows.ts';
5
- import type { InstallArgs, ShopInstallResult, ShopInstallStatusResult } from '../host/index.ts';
6
- /** The single-view poll loop: while the view is `running`, poll once per
7
- * second and fold each status through the reducer. A poll failure is
8
- * transient — the host retains the record, so the next tick finds it. The
9
- * rejection handler must be present: an unhandled rejection here would
10
- * escape the poll loop.
11
- *
12
- * Kept for the self-update (`useUpdateSelf`), which drives ONE flow with no
13
- * identity to key it by. Everything per-plugin goes through `useKeyedFlows`,
14
- * whose own loop polls every running identity from one interval. */
15
- export declare function usePollStatus(view: InstallView, setView: Dispatch<SetStateAction<InstallView>>, installStatus: (args: {
16
- installId: string;
17
- }) => Promise<ShopInstallStatusResult>): void;
18
- /** One entry's install flow, as the tab hands it to a panel. */
19
- export type InstallFlow = KeyedFlow<InstallArgs>;
20
- export type UseInstallFlows = UseKeyedFlows<InstallArgs>;
21
- /**
22
- * Install flows owned by the tab and keyed by install identity. Keeping the
23
- * state above individual cards preserves a running operation when filtering
24
- * unmounts its card, and makes the shelf and Outdated panels agree.
25
- *
26
- * The registry itself is `useKeyedFlows`; this supplies the one thing that is
27
- * install-specific — what the starting RPC's answer means.
28
- */
29
- export declare function useInstallFlows(install: (args: InstallArgs) => Promise<ShopInstallResult>, installStatus: (args: {
30
- installId: string;
31
- }) => Promise<ShopInstallStatusResult>, onSettled?: (key: string, outcome: 'done' | 'failed') => void): UseInstallFlows;
@@ -1,28 +0,0 @@
1
- /** Uninstall driving hook: the tab's keyed registry, one start mapping. */
2
- import type { ShopInstallStatusResult, ShopUninstallResult } from '../host/index.ts';
3
- import { type KeyedFlow, type UseKeyedFlows } from './useFlows.ts';
4
- /** One entry's uninstall flow, as the tab hands it to a panel. */
5
- export type UninstallFlow = KeyedFlow<{
6
- name: string;
7
- }>;
8
- export type UseUninstallFlows = UseKeyedFlows<{
9
- name: string;
10
- }>;
11
- /**
12
- * Uninstall flows owned by the tab and keyed by install identity. Lifting the
13
- * state above individual cards is what lets a completed uninstall's outcome
14
- * survive the installed-projection refresh that follows it — the row backing
15
- * the card disappears, but the flow keyed by identity does not.
16
- *
17
- * The registry itself is `useKeyedFlows`; this supplies the one thing that is
18
- * uninstall-specific — what the starting RPC's answer means. An uninstall
19
- * business failure (not in the catalog / not installed) lands in the `failed`
20
- * view with the host's published detail, never in the install `rejected`
21
- * codes: those belong to the install gate, and `ShopUninstallResult`'s
22
- * failure variant has no `code` field to populate.
23
- */
24
- export declare function useUninstallFlows(uninstall: (args: {
25
- name: string;
26
- }) => Promise<ShopUninstallResult>, installStatus: (args: {
27
- installId: string;
28
- }) => Promise<ShopInstallStatusResult>, onSettled?: (key: string, outcome: 'done' | 'failed') => void): UseUninstallFlows;