dsh-plugin-shop 0.8.4-beta.0 → 0.8.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -195,7 +195,7 @@ export const TYPERT = {
195
195
  schema: dsh_plugin_shop_shop_catalog_result$schema,
196
196
  create: () => dsh_plugin_shop_shop_catalog_result$schema,
197
197
  },
198
- sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":1134,"column":9},
198
+ sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":1253,"column":9},
199
199
  },
200
200
  {
201
201
  id: 'dsh-plugin-shop#shop/installed',
@@ -211,7 +211,7 @@ export const TYPERT = {
211
211
  schema: dsh_plugin_shop_shop_installed_result$schema,
212
212
  create: () => dsh_plugin_shop_shop_installed_result$schema,
213
213
  },
214
- sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":1494,"column":9},
214
+ sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":1720,"column":9},
215
215
  },
216
216
  {
217
217
  id: 'dsh-plugin-shop#shop/installedSpecs',
@@ -227,7 +227,7 @@ export const TYPERT = {
227
227
  schema: dsh_plugin_shop_shop_installedSpecs_result$schema,
228
228
  create: () => dsh_plugin_shop_shop_installedSpecs_result$schema,
229
229
  },
230
- sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":1485,"column":9},
230
+ sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":1711,"column":9},
231
231
  },
232
232
  {
233
233
  id: 'dsh-plugin-shop#shop/installStart',
@@ -255,7 +255,7 @@ export const TYPERT = {
255
255
  schema: dsh_plugin_shop_shop_installStart_result$schema,
256
256
  create: () => dsh_plugin_shop_shop_installStart_result$schema,
257
257
  },
258
- sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":1260,"column":9},
258
+ sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":1379,"column":9},
259
259
  },
260
260
  {
261
261
  id: 'dsh-plugin-shop#shop/installStatus',
@@ -282,7 +282,7 @@ export const TYPERT = {
282
282
  schema: dsh_plugin_shop_shop_installStatus_result$schema,
283
283
  create: () => dsh_plugin_shop_shop_installStatus_result$schema,
284
284
  },
285
- sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":1461,"column":3},
285
+ sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":1687,"column":3},
286
286
  },
287
287
  {
288
288
  id: 'dsh-plugin-shop#shop/restart',
@@ -298,7 +298,7 @@ export const TYPERT = {
298
298
  schema: dsh_plugin_shop_shop_restart_result$schema,
299
299
  create: () => dsh_plugin_shop_shop_restart_result$schema,
300
300
  },
301
- sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":1680,"column":9},
301
+ sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":1916,"column":9},
302
302
  },
303
303
  {
304
304
  id: 'dsh-plugin-shop#shop/setEnabled',
@@ -325,7 +325,7 @@ export const TYPERT = {
325
325
  schema: dsh_plugin_shop_shop_setEnabled_result$schema,
326
326
  create: () => dsh_plugin_shop_shop_setEnabled_result$schema,
327
327
  },
328
- sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":887,"column":9},
328
+ sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":924,"column":9},
329
329
  },
330
330
  {
331
331
  id: 'dsh-plugin-shop#shop/uninstallStart',
@@ -353,7 +353,7 @@ export const TYPERT = {
353
353
  schema: dsh_plugin_shop_shop_uninstallStart_result$schema,
354
354
  create: () => dsh_plugin_shop_shop_uninstallStart_result$schema,
355
355
  },
356
- sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":1601,"column":9},
356
+ sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":1827,"column":9},
357
357
  },
358
358
  {
359
359
  id: 'dsh-plugin-shop#shop/updateStart',
@@ -380,7 +380,7 @@ export const TYPERT = {
380
380
  schema: dsh_plugin_shop_shop_updateStart_result$schema,
381
381
  create: () => dsh_plugin_shop_shop_updateStart_result$schema,
382
382
  },
383
- sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":1777,"column":9},
383
+ sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":2020,"column":9},
384
384
  },
385
385
  {
386
386
  id: 'dsh-plugin-shop#shop/version',
@@ -396,7 +396,7 @@ export const TYPERT = {
396
396
  schema: dsh_plugin_shop_shop_version_result$schema,
397
397
  create: () => dsh_plugin_shop_shop_version_result$schema,
398
398
  },
399
- sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":1735,"column":9},
399
+ sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":1978,"column":9},
400
400
  },
401
401
  ],
402
402
  model: {
@@ -192,7 +192,7 @@ export const TYPERT_REMOTE = {
192
192
  schema: dsh_plugin_shop_shop_catalog_result$schema,
193
193
  create: () => dsh_plugin_shop_shop_catalog_result$schema,
194
194
  },
195
- sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":1134,"column":9},
195
+ sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":1253,"column":9},
196
196
  },
197
197
  {
198
198
  id: 'dsh-plugin-shop#shop/installed',
@@ -208,7 +208,7 @@ export const TYPERT_REMOTE = {
208
208
  schema: dsh_plugin_shop_shop_installed_result$schema,
209
209
  create: () => dsh_plugin_shop_shop_installed_result$schema,
210
210
  },
211
- sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":1494,"column":9},
211
+ sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":1720,"column":9},
212
212
  },
213
213
  {
214
214
  id: 'dsh-plugin-shop#shop/installedSpecs',
@@ -224,7 +224,7 @@ export const TYPERT_REMOTE = {
224
224
  schema: dsh_plugin_shop_shop_installedSpecs_result$schema,
225
225
  create: () => dsh_plugin_shop_shop_installedSpecs_result$schema,
226
226
  },
227
- sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":1485,"column":9},
227
+ sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":1711,"column":9},
228
228
  },
229
229
  {
230
230
  id: 'dsh-plugin-shop#shop/installStart',
@@ -252,7 +252,7 @@ export const TYPERT_REMOTE = {
252
252
  schema: dsh_plugin_shop_shop_installStart_result$schema,
253
253
  create: () => dsh_plugin_shop_shop_installStart_result$schema,
254
254
  },
255
- sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":1260,"column":9},
255
+ sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":1379,"column":9},
256
256
  },
257
257
  {
258
258
  id: 'dsh-plugin-shop#shop/installStatus',
@@ -279,7 +279,7 @@ export const TYPERT_REMOTE = {
279
279
  schema: dsh_plugin_shop_shop_installStatus_result$schema,
280
280
  create: () => dsh_plugin_shop_shop_installStatus_result$schema,
281
281
  },
282
- sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":1461,"column":3},
282
+ sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":1687,"column":3},
283
283
  },
284
284
  {
285
285
  id: 'dsh-plugin-shop#shop/restart',
@@ -295,7 +295,7 @@ export const TYPERT_REMOTE = {
295
295
  schema: dsh_plugin_shop_shop_restart_result$schema,
296
296
  create: () => dsh_plugin_shop_shop_restart_result$schema,
297
297
  },
298
- sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":1680,"column":9},
298
+ sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":1916,"column":9},
299
299
  },
300
300
  {
301
301
  id: 'dsh-plugin-shop#shop/setEnabled',
@@ -322,7 +322,7 @@ export const TYPERT_REMOTE = {
322
322
  schema: dsh_plugin_shop_shop_setEnabled_result$schema,
323
323
  create: () => dsh_plugin_shop_shop_setEnabled_result$schema,
324
324
  },
325
- sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":887,"column":9},
325
+ sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":924,"column":9},
326
326
  },
327
327
  {
328
328
  id: 'dsh-plugin-shop#shop/uninstallStart',
@@ -350,7 +350,7 @@ export const TYPERT_REMOTE = {
350
350
  schema: dsh_plugin_shop_shop_uninstallStart_result$schema,
351
351
  create: () => dsh_plugin_shop_shop_uninstallStart_result$schema,
352
352
  },
353
- sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":1601,"column":9},
353
+ sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":1827,"column":9},
354
354
  },
355
355
  {
356
356
  id: 'dsh-plugin-shop#shop/updateStart',
@@ -377,7 +377,7 @@ export const TYPERT_REMOTE = {
377
377
  schema: dsh_plugin_shop_shop_updateStart_result$schema,
378
378
  create: () => dsh_plugin_shop_shop_updateStart_result$schema,
379
379
  },
380
- sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":1777,"column":9},
380
+ sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":2020,"column":9},
381
381
  },
382
382
  {
383
383
  id: 'dsh-plugin-shop#shop/version',
@@ -393,7 +393,7 @@ export const TYPERT_REMOTE = {
393
393
  schema: dsh_plugin_shop_shop_version_result$schema,
394
394
  create: () => dsh_plugin_shop_shop_version_result$schema,
395
395
  },
396
- sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":1735,"column":9},
396
+ sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":1978,"column":9},
397
397
  },
398
398
  ],
399
399
  }
@@ -146,6 +146,7 @@ export declare const zh: {
146
146
  enabledSwitch: string;
147
147
  toggleFailed: string;
148
148
  hotApplyNote: string;
149
+ toggleRestartNote: string;
149
150
  };
150
151
  /** Shop locale key union. */
151
152
  export type ShopLocaleKey = keyof typeof zh;
@@ -264,4 +265,5 @@ export declare const en: {
264
265
  enabledSwitch: string;
265
266
  toggleFailed: string;
266
267
  hotApplyNote: string;
268
+ toggleRestartNote: string;
267
269
  };
@@ -222,6 +222,7 @@ export type InstallView = {
222
222
  activation: Activation;
223
223
  log: string[];
224
224
  restartReason?: HotRestartReason;
225
+ detail?: string;
225
226
  } | {
226
227
  kind: 'failed';
227
228
  detail: string;
@@ -17,17 +17,45 @@
17
17
  * swapped-in module instance shares with the one it replaced — and the probe
18
18
  * loop is plain timers owned by no fiber. Any tab reads the state, so the
19
19
  * replacement renders the restart under way instead of offering another.
20
+ *
21
+ * The REQUEST is the page's too, since later the same day. On dsh
22
+ * 0.1.5-rc.3 and 0.1.7-rc.2 alike a unary Remote call is an HTTP POST whose
23
+ * fetch rides the calling mount's abort signal, and an answer that arrives after the mount went inactive is
24
+ * rewritten to "no longer mounted" (dsh-api-gateway's client). A swap landing
25
+ * inside the restart's round trip therefore left the tab that pressed with no
26
+ * answer, whether or not the host had committed: measured with the request
27
+ * held, the host never restarted; with the response held, it did, and the page
28
+ * stayed on the old process showing the old version. So every module instance
29
+ * registers its own path to the host here (the client half's apply), a confirm
30
+ * records the request before anything is awaited, and a request whose path is
31
+ * withdrawn mid-call is asked again through a live one. The host answers a
32
+ * repeat with the restart it already committed.
20
33
  */
21
- /** A restart this page committed: still waiting for the new server, or
22
- * given up on it. `logFile` is where the host said the new process writes;
23
- * a host older than that field sends none. */
34
+ import type { ShopRestartResult } from '../host/index.ts';
35
+ /** What this page is doing about a restart: waiting for the host to answer
36
+ * the confirm, waiting for the new server once it committed, or given up on
37
+ * that server. `logFile` is where the host said the new process writes; a
38
+ * host older than that field sends none. */
24
39
  export type RestartMonitorState = {
40
+ kind: 'requesting';
41
+ } | {
25
42
  kind: 'restarting';
26
43
  logFile?: string;
27
44
  } | {
28
45
  kind: 'failed';
29
46
  logFile?: string;
30
47
  };
48
+ /** How one press ended, for the panel that made it: the page follows the
49
+ * restart from here, the host refused it, or it never reached a host that
50
+ * could answer. */
51
+ export type RestartRequestOutcome = {
52
+ kind: 'started';
53
+ } | {
54
+ kind: 'refused';
55
+ detail: string;
56
+ } | {
57
+ kind: 'unreachable';
58
+ };
31
59
  /** The page's restart, or null when it has committed none. The same object
32
60
  * until the state changes, as `useSyncExternalStore` requires. */
33
61
  export declare function readRestartMonitor(): RestartMonitorState | null;
@@ -52,7 +80,35 @@ export declare function startRestartMonitor(options: {
52
80
  reload: () => void;
53
81
  logFile?: string;
54
82
  }): void;
55
- /** Stop the probe loop and forget the page's restart. Nothing in the page
56
- * calls this — a committed restart ends in a reload or a failure — so its
57
- * caller is a test isolating one case from the next. */
83
+ /**
84
+ * Register one module instance's path to the host; the client half's apply
85
+ * does, with the `restart` it hands the tab. The returned function withdraws
86
+ * it, and has to run BEFORE the mount it rides is disposed: a request still in
87
+ * flight on it is then asked again through the newest live path — at once if
88
+ * one is registered, or as soon as one is — rather than waiting on a call the
89
+ * dispose is about to abort.
90
+ */
91
+ export declare function registerRestartCarrier(restart: () => Promise<ShopRestartResult>): () => void;
92
+ /**
93
+ * Ask the host to restart, for the page (design §8). The request is the
94
+ * page's from the confirm on, not from the answer: every offer renders it as
95
+ * under way, and a second press joins it rather than asking twice. It rides
96
+ * the newest path a module instance registered, or — where no client half
97
+ * registered one — the pressing tab's own `restart`.
98
+ *
99
+ * Resolves, never rejects, with what the press should show. A commit starts
100
+ * the monitor and is `started`. A refusal and an unreachable host leave
101
+ * nothing under way. A path withdrawn mid-call decides nothing but a commit;
102
+ * the ask on the path that replaced it decides the rest, and that ask
103
+ * finding no host is followed as a restart, since the first ask may have
104
+ * committed and taken its host down with it.
105
+ */
106
+ export declare function requestRestart(options: {
107
+ reload: () => void;
108
+ restart: () => Promise<ShopRestartResult>;
109
+ }): Promise<RestartRequestOutcome>;
110
+ /** Stop the probe loop and forget the page's restart, its request and every
111
+ * registered path. Nothing in the page calls this — a committed restart ends
112
+ * in a reload or a failure — so its caller is a test isolating one case from
113
+ * the next. */
58
114
  export declare function resetRestartMonitor(): void;
@@ -4,7 +4,7 @@
4
4
  * post-exit manifest confirmation. */
5
5
  import type { HotRestartReason } from './hot.ts';
6
6
  import type { Activation } from './activation.ts';
7
- import type { Prefetcher } from './prefetch.ts';
7
+ import type { Prefetcher, PrefetchRequest } from './prefetch.ts';
8
8
  import { type InstallState } from '../shared/install-state.ts';
9
9
  export type { InstallState } from '../shared/install-state.ts';
10
10
  export interface InstallStatus {
@@ -17,11 +17,28 @@ export interface InstallStatus {
17
17
  restartReason?: HotRestartReason;
18
18
  detail?: string;
19
19
  }
20
- interface RunningInstall {
20
+ /** Bound one dsh command so a stalled install cannot hold the profile queue
21
+ * forever. Tests can pass a shorter value; production stays generous. */
22
+ export declare const INSTALL_TIMEOUT_MS: number;
23
+ export interface RunningInstall {
21
24
  installId: string;
22
25
  status: () => InstallStatus;
23
26
  finished: Promise<InstallStatus>;
24
27
  }
28
+ /** Run `task` in `profile`'s queue, after every command already in it: the
29
+ * queue `spawnPluginCli` uses, shared with the plugin manager's runner so the
30
+ * two paths are never concurrent in one profile. `ahead` is how many commands
31
+ * were queued or running when the slot was taken. */
32
+ export declare function inProfileQueue<T>(profile: string, task: () => Promise<T>): {
33
+ ahead: number;
34
+ finished: Promise<T>;
35
+ };
36
+ /** An install record's log: the newest lines within MAX_LOG_LINES and
37
+ * MAX_LOG_BYTES, never dropping the newest one. */
38
+ export declare function createBoundedLog(): {
39
+ push(line: string): void;
40
+ lines(): string[];
41
+ };
25
42
  /**
26
43
  * Why a zero-exit install left `dsh.profile.bundles` without the entry.
27
44
  *
@@ -71,6 +88,11 @@ export declare function activationFailureDetail(args: {
71
88
  before: Readonly<Record<string, string>> | null;
72
89
  after: Readonly<Record<string, string>>;
73
90
  }): string;
91
+ /** The line dsh 0.1.7 opens a refused install with (dsh-plugin-manager's
92
+ * `rejected`), and the exemption command its CLI prints after it, one per
93
+ * refused package (`runPlugin`). The opener is exported for the plugin
94
+ * manager's answer, whose output carries the same block. */
95
+ export declare const REFUSAL_OPENER = "dsh: installation rejected: ";
74
96
  /**
75
97
  * The one log line worth putting in front of the user, plus the recovery hint.
76
98
  *
@@ -85,7 +107,7 @@ export declare function activationFailureDetail(args: {
85
107
  * Scanning from the end: a pnpm error code first, then any thrown error, then
86
108
  * the last line that is not noise.
87
109
  */
88
- export declare function installFailureDetail(profile: string, log: readonly string[]): string;
110
+ export declare function installFailureDetail(profile: string, log: readonly string[], hint?: string): string;
89
111
  /**
90
112
  * Why a spawn of the dsh CLI never started.
91
113
  *
@@ -121,6 +143,19 @@ export interface LineSink {
121
143
  flush: () => void;
122
144
  }
123
145
  export declare function lineSink(emit: (line: string) => void): LineSink;
146
+ /**
147
+ * The operand gate, one function for every caller: it refuses, by throwing,
148
+ * an operand the dsh CLI would parse as a flag, and one carrying the shell
149
+ * punctuation `UNSAFE_TARGET` names. A legitimate operand (a catalog name for
150
+ * a removal; a `name@version`, `github:` or tarball spec for an install)
151
+ * never begins with `-`, so the first refusal cannot reject a real install or
152
+ * uninstall, and failing loudly beats letting the CLI reinterpret an operand
153
+ * as an option. `spawnPluginCli` runs it before it spawns anything or takes a
154
+ * queue slot; the gateway's plugin manager path runs it before a spec reaches
155
+ * `installBundle` or the download phase, whose pump relies on it (see
156
+ * `requestDownloadPhase`). Returns the operand, known to be present.
157
+ */
158
+ export declare function gateOperand(target: string | undefined): string;
124
159
  /**
125
160
  * The operand as the downstream dsh must receive it for pnpm to see it whole.
126
161
  *
@@ -156,6 +191,18 @@ export declare function lineSink(emit: (line: string) => void): LineSink;
156
191
  * deepseek-ai/deepseek-harness discussion #5815.
157
192
  */
158
193
  export declare function shellSafeTarget(target: string, platform: NodeJS.Platform): string;
194
+ /**
195
+ * Ask the pump to warm `spec` while an operation waits its turn, and say in
196
+ * that operation's log why there is no download phase when there is none.
197
+ * Shared by the CLI executor and the plugin manager runner.
198
+ */
199
+ export declare function requestDownloadPhase(options: {
200
+ prefetcher: Prefetcher;
201
+ profile: string;
202
+ spec: string;
203
+ env?: NodeJS.ProcessEnv;
204
+ log: (line: string) => void;
205
+ }): PrefetchRequest | null;
159
206
  /**
160
207
  * Run one `dsh plugin --profile <profile> add <spec>` and track it.
161
208
  * When `expectedName` is given, a zero exit is confirmed against the profile
@@ -39,21 +39,6 @@ export interface LoaderEntryLike {
39
39
  disabled: boolean | null;
40
40
  }, create?: boolean, force?: boolean): Promise<void>;
41
41
  }
42
- /** The slice of dsh 0.1.7's `pluginManager` service this gateway calls,
43
- * structurally: the build compiles against the 0.1.1-rc.2 harness floor,
44
- * where the package does not exist. `setBundleEnabled` is what dsh's own
45
- * Plugins page calls for its bundle switch, and it answers with the saved
46
- * change and how it applied (design 2026-09-26-plugin-manager-delegation,
47
- * section 2). */
48
- export interface BundleSelector {
49
- setBundleEnabled(name: string, enabled: boolean): Promise<{
50
- application?: unknown;
51
- error?: {
52
- code?: unknown;
53
- diagnostic?: unknown;
54
- };
55
- }>;
56
- }
57
42
  /** Test-only injection points; production callers pass nothing. */
58
43
  export interface ShopGatewayOptions {
59
44
  catalogUrl?: string;
@@ -362,6 +347,11 @@ export declare class ShopGateway extends TypertRemoteService {
362
347
  private readonly exit;
363
348
  private readonly restartExitDelayMs;
364
349
  private readonly restartParentPid;
350
+ /** The restart this process committed, once it has: every later
351
+ * `shop/restart` answers with it. A page that lost the first answer to an
352
+ * HMR swap asks again (client/restart-monitor.ts), and inside the exit
353
+ * delay that ask reaches this same process. */
354
+ private committedRestart;
365
355
  private readonly latestVersion;
366
356
  private readonly pinFs;
367
357
  private readonly allowRestart?;
@@ -380,6 +370,9 @@ export declare class ShopGateway extends TypertRemoteService {
380
370
  private readonly prefetcher;
381
371
  /** What this process may already have imported (see `importedThisProcess`). */
382
372
  private readonly imported;
373
+ /** Routes the output dsh's `pluginManager` streams to the record whose
374
+ * request id it carries (see the constructor). */
375
+ private readonly managerLogs;
383
376
  /** The install gate runs against the last loaded snapshot, never a fresh
384
377
  * fetch per request (§7.2: the Host's cached snapshot is the truth). */
385
378
  /** Finished install records retained, so a poll sees the true terminal
@@ -404,7 +397,8 @@ export declare class ShopGateway extends TypertRemoteService {
404
397
  * promise, so concurrent first calls share one read; `readRunningHarness`
405
398
  * never rejects, so there is no failure to retry. */
406
399
  private harnessRead;
407
- /** Install records, running and finished; a poll finds one here or reports not found. */
400
+ /** Install records, running and finished, from the CLI and from dsh's
401
+ * `pluginManager` alike; a poll finds one here or reports not found. */
408
402
  private readonly installs;
409
403
  /** Every install id in insertion order, oldest first; finished-record eviction walks this from the front. */
410
404
  private readonly installOrder;
@@ -443,11 +437,13 @@ export declare class ShopGateway extends TypertRemoteService {
443
437
  * walk of the profile's disk can see (design
444
438
  * 2026-09-01-harness-compatibility §11). */
445
439
  private harnessPackages;
446
- /** dsh's `pluginManager` service, when the running harness provides one
447
- * (0.1.7 and later), else null. Read on each use, like `pluginPackages`.
448
- * It is the only way back for a bundle its Plugins page deselected: the
449
- * 0.1.7 CLI does not select an installed package again on `add`, while
450
- * 0.1.5's does (both measured 2026-09-27). */
440
+ /** dsh's `pluginManager` service, when the running harness provides all of
441
+ * it (0.1.7 and later), else null. Never cached across calls, like
442
+ * `pluginPackages`, but read once per RPC, at its start, and passed down:
443
+ * read again after an await, a service disposed in between would let a
444
+ * desktop mutation past the desktop gate and onto the CLI path, which dsh
445
+ * refuses for that profile. Design 2026-09-26-plugin-manager-delegation,
446
+ * section 3.1. */
451
447
  private pluginManager;
452
448
  /** "Does this installation provide `spec`?", asked from `anchor`: the
453
449
  * harness's own resolution in front of the walk where the harness has one,
@@ -505,6 +501,11 @@ export declare class ShopGateway extends TypertRemoteService {
505
501
  * refuse a hot mount to a package this process never imported.
506
502
  */
507
503
  private installedSpecOf;
504
+ /** Add every dependency the profile manifest holds to the `imported`
505
+ * record (see `importedThisProcess`): each is a package dsh's loader may
506
+ * have loaded in this process, at boot or by an install since. An
507
+ * unreadable manifest adds nothing. */
508
+ private recordDependenciesImported;
508
509
  private ownedEntryIdsOrNone;
509
510
  /** Whether an installed package declares `dsh.client`. Reads through the
510
511
  * `hotFs` option — `HotFs` is `hot.ts`'s type, but this gateway forwards
@@ -554,6 +555,26 @@ export declare class ShopGateway extends TypertRemoteService {
554
555
  * float a registry package to `latest`.
555
556
  */
556
557
  private setDeselectedEnabled;
558
+ /**
559
+ * dsh's answer to a switch or a bundle selection, read the one way both
560
+ * callers need it (R32): the change, scrubbed for a desktop reader, and the
561
+ * detail that fails the request, or null when dsh applied it. `act` is what
562
+ * was asked, as "switch <name>", `request` names the request itself, as
563
+ * "the switch for <name>", and `stays` is the state the plugin keeps when
564
+ * the request does not take effect. An application this shop does not know
565
+ * fails rather than reading as applied: a later harness may answer
566
+ * something new.
567
+ *
568
+ * `overridden` fails too (R39). dsh answers it when, after it wrote the row
569
+ * and reloaded, the live entry still differs from the request
570
+ * (dsh-plugin-manager 0.1.7-rc.2, setPluginEnabled), and its README names
571
+ * what outranks the profile's own patch: home and invocation patches keep
572
+ * their higher priority. The change is saved and the plugin runs as it
573
+ * did, so a success would flip the client's switch over a plugin that did
574
+ * not change, and that switch never re-reads the live state.
575
+ * setBundleEnabled never answers it; a selection reads it the same way.
576
+ */
577
+ private readManagerAnswer;
557
578
  private rowConfig;
558
579
  /** The user's own registry, read once. The self-update check and catalog
559
580
  * race share this preference so neither path repeatedly touches ~/.npmrc. */
@@ -628,6 +649,46 @@ export declare class ShopGateway extends TypertRemoteService {
628
649
  * passing request reaches the executor.
629
650
  */
630
651
  install(args: InstallArgs): Promise<ShopInstallResult>;
652
+ /**
653
+ * What would stop the profile from starting now that `name`'s files are on
654
+ * disk, or null: an entry id another installed package already declares,
655
+ * or a bundle patch declaration this dsh cannot read. Neither is knowable
656
+ * before the files land. The package stays on disk either way, so each
657
+ * detail ends with `undo`, the sentence that says how to take the install
658
+ * back on the path that made it.
659
+ */
660
+ private postInstallHazard;
661
+ /** Remember a github entry's pinned commit: the manifest records only
662
+ * `github:owner/slug`, so the pins file is how `installed()` reports
663
+ * outdated honestly. A failed install leaves a pin behind, but the
664
+ * manifest presence gate keeps it invisible. */
665
+ private recordGithubPin;
666
+ /** Forget an uninstalled package's pins: its own, and its identity pin when
667
+ * a catalog row matched the installed spec. Both uninstall paths call this
668
+ * once their operation has started, so a stale pin never outlives the
669
+ * uninstall in the shop's cache either way. The plugin manager path tracks
670
+ * its record first, so a write here that throws cannot hide an operation
671
+ * already queued. */
672
+ private forgetPins;
673
+ /** What `managerOutcome` reads dsh's answer against. The gateway's own
674
+ * facts are set here once, for every operation through the service: the
675
+ * profile, whether it is the desktop one, and the deadline a timeout
676
+ * detail names. */
677
+ private outcomeContext;
678
+ /**
679
+ * One install-shaped operation through dsh's `pluginManager` (design
680
+ * 2026-09-26-plugin-manager-delegation, section 6): `installBundle(spec)`
681
+ * under the record's own id as its request id, so the output dsh streams
682
+ * for it reaches this record, in the profile's queue with the download
683
+ * phase the CLI path has. At the shop's deadline dsh is asked to cancel it;
684
+ * a service that offers no `cancelInstall` gets no deadline, and its record
685
+ * settles when dsh answers. `install` and `updateStart` differ only in the
686
+ * mechanism line and in how they read the answer.
687
+ */
688
+ private startManagerInstall;
689
+ /** Keep a started record where `installStatus`, the restart gate and
690
+ * eviction find it, then bound the finished ones. */
691
+ private track;
631
692
  /** Bound retained finished records at MAX_FINISHED_INSTALLS, evicting the
632
693
  * oldest finished ones (insertion order, oldest first). Live records —
633
694
  * running AND queued — are never evicted; an id absent from the map reports
@@ -0,0 +1,51 @@
1
+ /**
2
+ * One package operation through dsh's `pluginManager` service, tracked as the
3
+ * same `RunningInstall` the CLI executor produces: design
4
+ * 2026-09-26-plugin-manager-delegation, section 6. It runs in the same
5
+ * per-profile queue as CLI commands, so the two paths are never concurrent in
6
+ * one profile.
7
+ */
8
+ import { type RunningInstall } from './executor.ts';
9
+ import { type ManagerOutcome } from './plugin-manager.ts';
10
+ import type { Prefetcher } from './prefetch.ts';
11
+ /** The first line of every record this runner writes. The shop's log panel
12
+ * shows it and the 0.1.7 e2e asserts it, so a silent fall back to the CLI
13
+ * fails a case instead of passing it. */
14
+ export declare const MECHANISM_PREFIX = "via dsh's plugin manager:";
15
+ /** Routes the service's `plugin-manager/install-log` chunks to the record
16
+ * whose request id they carry. A chunk for any other id is dropped. dsh
17
+ * tags each chunk with the stream it came from; a non-string stream reads
18
+ * as `stdout`, matching a harness that predates the tag. */
19
+ export declare class ManagerLogs {
20
+ private readonly sinks;
21
+ open(requestId: string, sink: (text: string, stream: string) => void): () => void;
22
+ chunk(requestId: unknown, text: unknown, stream?: unknown): void;
23
+ }
24
+ export interface ManagerOperationOptions {
25
+ profile: string;
26
+ /** The record's id, handed to the service as its request id. */
27
+ requestId: string;
28
+ /** What the mechanism line says after the prefix, e.g. `install dsh-a@1.0.0`. */
29
+ mechanism: string;
30
+ logs: ManagerLogs;
31
+ run: (requestId: string) => Promise<unknown>;
32
+ cancel?: (requestId: string) => Promise<unknown>;
33
+ outcome: (raw: unknown) => ManagerOutcome;
34
+ alsoConfirm?: () => string | null;
35
+ /** The download phase, as on the CLI path: `spec` is what it warms. */
36
+ prefetcher?: Prefetcher;
37
+ spec?: string;
38
+ env?: NodeJS.ProcessEnv;
39
+ timeoutMs?: number;
40
+ /** The desktop profile, whose readers can run no `dsh plugin` command: a
41
+ * message the service call throws is scrubbed as dsh's answer is. */
42
+ desktop?: boolean;
43
+ }
44
+ /** What a thrown value says about itself, and total: describing an error is
45
+ * never itself the reason a settle fails. An `Error` gives its `message`,
46
+ * anything else `String(value)`. A message that is not a string, or a
47
+ * description that throws anywhere, reads as one fixed string: a Proxy whose
48
+ * getPrototypeOf trap throws defeats `instanceof` itself, a `message` getter
49
+ * can throw, and a null-prototype object has no conversion to a string. */
50
+ export declare const messageOf: (error: unknown) => string;
51
+ export declare function startManagerOperation(options: ManagerOperationOptions): RunningInstall;