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.
- package/lib/client.js +1561 -762
- package/lib/index.js +210 -34
- package/lib/typert.host.js +12 -12
- package/lib/typert.remote-client.js +11 -11
- package/lib/types/client/ShopDetailAction.d.ts +46 -0
- package/lib/types/client/ShopGlyph.d.ts +12 -0
- package/lib/types/client/ShopPage.d.ts +27 -0
- package/lib/types/client/ShopTab.d.ts +27 -10
- package/lib/types/client/flow-registry.d.ts +89 -0
- package/lib/types/client/index.d.ts +52 -7
- package/lib/types/client/listeners.d.ts +27 -0
- package/lib/types/client/locales.d.ts +17 -5
- package/lib/types/client/page-store.d.ts +255 -0
- package/lib/types/client/present.d.ts +35 -13
- package/lib/types/client/restart-monitor.d.ts +11 -0
- package/lib/types/client/useFlows.d.ts +34 -26
- package/lib/types/client/useUpdateSelf.d.ts +19 -16
- package/lib/types/host/harness.d.ts +26 -2
- package/lib/types/host/index.d.ts +39 -6
- package/lib/types/host/manager-runner.d.ts +24 -0
- package/lib/types/host/plugin-manager.d.ts +5 -0
- package/package.json +1 -1
- package/lib/types/client/ShopSection.d.ts +0 -10
- package/lib/types/client/useInstall.d.ts +0 -31
- package/lib/types/client/useUninstall.d.ts +0 -28
package/lib/index.js
CHANGED
|
@@ -3983,6 +3983,30 @@ async function appBootFacts(dshDir) {
|
|
|
3983
3983
|
peerCheck: peerCheckOf(exported, appBootDir)
|
|
3984
3984
|
};
|
|
3985
3985
|
}
|
|
3986
|
+
/** `value` when it is a non-null object, else null. */
|
|
3987
|
+
const objectOrNull = (value) => typeof value === "object" && value !== null ? value : null;
|
|
3988
|
+
/**
|
|
3989
|
+
* The list a refusal names, read off one answer of dsh's compatibility check
|
|
3990
|
+
* or of its plugin manager, under whichever name the dsh that gave the answer
|
|
3991
|
+
* uses: `requirements` from 0.2.1, `peers` from 0.1.7 to 0.2.0. app-boot's
|
|
3992
|
+
* `evaluatePluginCompatibility` and dsh-plugin-manager's `IncompatiblePlugin`
|
|
3993
|
+
* were renamed together (design 2026-10-19-dsh-021-readiness, section 4, read
|
|
3994
|
+
* off 0.2.1-rc.2's own types and code on 2026-10-19). A key is a dsh peer's
|
|
3995
|
+
* package name or the literal `engines.dsh`; a value is the range the package
|
|
3996
|
+
* declared.
|
|
3997
|
+
*
|
|
3998
|
+
* `requirements` is read first, and only when it holds an object: an answer
|
|
3999
|
+
* whose `requirements` is anything else is not in the newer shape, so its
|
|
4000
|
+
* `peers` is tried. Only the entries whose range is a string are kept, as
|
|
4001
|
+
* `incompatibleList` always kept them, so one malformed range costs its own
|
|
4002
|
+
* entry and not the rest of the list. An answer naming neither reads as an
|
|
4003
|
+
* empty list, never as a throw.
|
|
4004
|
+
*/
|
|
4005
|
+
function refusedRequirements(answer) {
|
|
4006
|
+
const list = objectOrNull(answer.requirements) ?? objectOrNull(answer.peers);
|
|
4007
|
+
if (list === null) return {};
|
|
4008
|
+
return Object.fromEntries(Object.entries(list).filter((pair) => typeof pair[1] === "string"));
|
|
4009
|
+
}
|
|
3986
4010
|
/**
|
|
3987
4011
|
* The peer check an app-boot exports, bound to its own version (see
|
|
3988
4012
|
* `PeerCheck.evaluate`), or null when it exports only part of one — a rule
|
|
@@ -3990,6 +4014,10 @@ async function appBootFacts(dshDir) {
|
|
|
3990
4014
|
* or when its manifest carries no version, which is the one `evaluate`
|
|
3991
4015
|
* judges by. Never throws, so a check that cannot be formed costs the
|
|
3992
4016
|
* templates and the patch-list answer nothing.
|
|
4017
|
+
*
|
|
4018
|
+
* What `evaluate` hands back is app-boot's answer with the shop's own `peers`
|
|
4019
|
+
* filled from whichever name the list came under (`refusedRequirements`), so
|
|
4020
|
+
* a dsh that renames it again costs this function and nothing past it.
|
|
3993
4021
|
*/
|
|
3994
4022
|
function peerCheckOf(exported, appBootDir) {
|
|
3995
4023
|
const { evaluatePluginCompatibility: evaluate, readProfileVersionExemptions: exemptions } = exported;
|
|
@@ -4004,7 +4032,14 @@ function peerCheckOf(exported, appBootDir) {
|
|
|
4004
4032
|
const runtime = version;
|
|
4005
4033
|
const judge = evaluate;
|
|
4006
4034
|
return {
|
|
4007
|
-
evaluate: (manifest, profileExemptions) =>
|
|
4035
|
+
evaluate: (manifest, profileExemptions) => {
|
|
4036
|
+
const answer = judge(manifest, profileExemptions, runtime);
|
|
4037
|
+
if (answer === void 0 || answer === null) return void 0;
|
|
4038
|
+
return {
|
|
4039
|
+
...answer,
|
|
4040
|
+
peers: refusedRequirements(answer)
|
|
4041
|
+
};
|
|
4042
|
+
},
|
|
4008
4043
|
exemptions
|
|
4009
4044
|
};
|
|
4010
4045
|
}
|
|
@@ -4092,7 +4127,7 @@ function incompatibleList(value) {
|
|
|
4092
4127
|
name,
|
|
4093
4128
|
version,
|
|
4094
4129
|
runtimeVersion,
|
|
4095
|
-
peers:
|
|
4130
|
+
peers: refusedRequirements(entry)
|
|
4096
4131
|
}];
|
|
4097
4132
|
});
|
|
4098
4133
|
}
|
|
@@ -4228,7 +4263,8 @@ const CODE_SENTENCE = {
|
|
|
4228
4263
|
"stale-approval": "a build approval names a package pnpm no longer holds",
|
|
4229
4264
|
"management-required": "the plugin belongs to dsh itself",
|
|
4230
4265
|
unaddressable: "the plugin is not a row of the profile's own patch, so dsh cannot address it",
|
|
4231
|
-
"operation-error": "dsh hit an unexpected error"
|
|
4266
|
+
"operation-error": "dsh hit an unexpected error",
|
|
4267
|
+
"already-installed": "this dsh updates no installed package in place: uninstall it, then install it again"
|
|
4232
4268
|
};
|
|
4233
4269
|
/** What each classified pnpm failure was, as dsh classifies it. */
|
|
4234
4270
|
const KIND_SENTENCE = {
|
|
@@ -4568,6 +4604,90 @@ function startManagerOperation(options) {
|
|
|
4568
4604
|
finished
|
|
4569
4605
|
};
|
|
4570
4606
|
}
|
|
4607
|
+
/**
|
|
4608
|
+
* `first`, continued by the record `next` starts when `first` ends refused,
|
|
4609
|
+
* as one record under `first`'s id: design 2026-10-19-dsh-021-readiness,
|
|
4610
|
+
* section 2. dsh-plugin-manager 0.2.1-rc.2 refuses an update in place as
|
|
4611
|
+
* `already-installed`, and the gateway continues such an update through
|
|
4612
|
+
* dsh's CLI. One record, so the client keeps polling the id it was given,
|
|
4613
|
+
* and the restart gate and eviction, which read every tracked record, see
|
|
4614
|
+
* the command that runs.
|
|
4615
|
+
*
|
|
4616
|
+
* While `first` runs, this is `first`. Whether it continues is decided once,
|
|
4617
|
+
* when `first` ends: the first `status()` read or `first.finished` settling
|
|
4618
|
+
* that sees `first` terminal asks `refused`, and that answer is kept. So a
|
|
4619
|
+
* caller may set its flag while reading the answer that ends `first`, and a
|
|
4620
|
+
* flag flipped after that changes neither `status()` nor `finished`, which
|
|
4621
|
+
* answer from that one decision. Not refused, this stays `first`. Refused,
|
|
4622
|
+
* `next` runs, exactly once, at that same read or finish, so no read between
|
|
4623
|
+
* the refusal and the next command reports the refusal. From then on the
|
|
4624
|
+
* record is `next`'s, its state, detail and activation, with `first`'s log,
|
|
4625
|
+
* then `bridge`, then `next`'s log. That log joins two bounded logs and the
|
|
4626
|
+
* bridge, so a continued record can carry about twice `MAX_LOG_LINES` and
|
|
4627
|
+
* `MAX_LOG_BYTES`, where one record of `startManagerOperation` carries at most
|
|
4628
|
+
* one line over them.
|
|
4629
|
+
*/
|
|
4630
|
+
function continueAfter(first, refused, next, bridge) {
|
|
4631
|
+
let begun = false;
|
|
4632
|
+
/** Whether `first` ended refused: `refused`'s answer, asked once, by the
|
|
4633
|
+
* first read or finish that sees `first` terminal. */
|
|
4634
|
+
let refusal;
|
|
4635
|
+
/** The log `first` ended on, and the record carrying on from it: what
|
|
4636
|
+
* `next` returned, or the failure it threw. */
|
|
4637
|
+
let continued;
|
|
4638
|
+
const behind = (head, tail) => ({
|
|
4639
|
+
...tail,
|
|
4640
|
+
log: [
|
|
4641
|
+
...head,
|
|
4642
|
+
bridge,
|
|
4643
|
+
...tail.log
|
|
4644
|
+
]
|
|
4645
|
+
});
|
|
4646
|
+
const status = () => {
|
|
4647
|
+
if (continued !== void 0) return behind(continued.head, continued.running.status());
|
|
4648
|
+
if (begun) return {
|
|
4649
|
+
state: "running",
|
|
4650
|
+
log: [...first.status().log, bridge]
|
|
4651
|
+
};
|
|
4652
|
+
const own = first.status();
|
|
4653
|
+
if (!isTerminalInstallState(own.state)) return own;
|
|
4654
|
+
refusal ??= refused();
|
|
4655
|
+
if (!refusal) return own;
|
|
4656
|
+
begun = true;
|
|
4657
|
+
let running;
|
|
4658
|
+
try {
|
|
4659
|
+
running = next();
|
|
4660
|
+
} catch (error) {
|
|
4661
|
+
const failed = {
|
|
4662
|
+
state: "failed",
|
|
4663
|
+
log: [],
|
|
4664
|
+
detail: `dsh-plugin-shop: ${messageOf(error)}`
|
|
4665
|
+
};
|
|
4666
|
+
running = {
|
|
4667
|
+
installId: first.installId,
|
|
4668
|
+
status: () => failed,
|
|
4669
|
+
finished: Promise.resolve(failed)
|
|
4670
|
+
};
|
|
4671
|
+
}
|
|
4672
|
+
continued = {
|
|
4673
|
+
head: own.log,
|
|
4674
|
+
running
|
|
4675
|
+
};
|
|
4676
|
+
return behind(own.log, running.status());
|
|
4677
|
+
};
|
|
4678
|
+
const finished = first.finished.then(async (own) => {
|
|
4679
|
+
status();
|
|
4680
|
+
const now = continued;
|
|
4681
|
+
if (now === void 0) return own;
|
|
4682
|
+
return behind(now.head, await now.running.finished);
|
|
4683
|
+
});
|
|
4684
|
+
finished.catch(() => {});
|
|
4685
|
+
return {
|
|
4686
|
+
installId: first.installId,
|
|
4687
|
+
status,
|
|
4688
|
+
finished
|
|
4689
|
+
};
|
|
4690
|
+
}
|
|
4571
4691
|
//#endregion
|
|
4572
4692
|
//#region src/host/index.ts
|
|
4573
4693
|
/** ShopGateway: the Host half of dsh-plugin-shop (§5.1). */
|
|
@@ -4618,10 +4738,11 @@ var __esDecorate = function(ctor, descriptorIn, decorators, contextIn, initializ
|
|
|
4618
4738
|
* WHY a restart is impossible. They did: `version()` reported a boolean that
|
|
4619
4739
|
* covered two of the refusals, the client had one string for that boolean, and
|
|
4620
4740
|
* that string named systemd — so a Windows user was told to restart a systemd
|
|
4621
|
-
* service and to set an override that the platform check, being
|
|
4622
|
-
* of the
|
|
4741
|
+
* service and to set an override that the platform check, being checked ahead
|
|
4742
|
+
* of the systemd one, could never reach. */
|
|
4623
4743
|
const RESTART_BLOCKED_DETAIL = {
|
|
4624
4744
|
desktop: "dsh-plugin-shop: the desktop profile is managed by the DeepSeek Harness desktop app, and dsh refuses to restart it from here; restart the app to apply the change",
|
|
4745
|
+
"from-default-profile": "dsh-plugin-shop: restart is not supported when dsh was launched with --from-default-profile; from dsh 0.2.1 a restart would recreate the profile from its template and delete every plugin installed in it, this shop included. Restart dsh manually without that flag to apply the change",
|
|
4625
4746
|
windows: "dsh-plugin-shop: restart is not supported on Windows yet; restart dsh manually to apply the change",
|
|
4626
4747
|
systemd: "dsh-plugin-shop: restart is disabled because this process is a systemd service — a restart would kill the takeover helper along with the unit, and the service would not come back. Set allowRestart: true in the shop row config to override.",
|
|
4627
4748
|
"port-zero": "dsh-plugin-shop: restart is not supported when dsh was launched with --port 0; restart dsh manually"
|
|
@@ -4640,6 +4761,14 @@ const DESKTOP_PROFILE_DETAIL = "dsh-plugin-shop: the desktop profile is managed
|
|
|
4640
4761
|
* CLI path's switch, and switching a deselected package off) is not
|
|
4641
4762
|
* refused. */
|
|
4642
4763
|
const switchWhileBusyDetail = (name) => `dsh-plugin-shop: an install, update or uninstall is still running in this profile, and dsh holds the profile until it ends; switch ${name} after it finishes.`;
|
|
4764
|
+
/** The line a record gains when dsh's plugin manager refuses an update in
|
|
4765
|
+
* place and the shop continues it through dsh's CLI (design
|
|
4766
|
+
* 2026-10-19-dsh-021-readiness §2): what dsh refused, and the command that
|
|
4767
|
+
* runs instead. It names a `dsh plugin` command, so it never reaches a
|
|
4768
|
+
* desktop reader: the continuation does not run in that profile. */
|
|
4769
|
+
function cliFallbackLine(profile, spec) {
|
|
4770
|
+
return `dsh-plugin-shop: dsh's plugin manager updates no installed package in place (already-installed); running \`dsh plugin --profile ${profile} add ${spec}\` instead`;
|
|
4771
|
+
}
|
|
4643
4772
|
/** How many bytes a release tarball may be at the integrity check. The
|
|
4644
4773
|
* registry already refuses to publish a tarball over 32 MiB, so 64 MiB is
|
|
4645
4774
|
* headroom, not a gate of its own. */
|
|
@@ -5531,14 +5660,32 @@ let ShopGateway = (() => {
|
|
|
5531
5660
|
*
|
|
5532
5661
|
* The order is load-bearing for the copy a reader sees. The desktop
|
|
5533
5662
|
* profile first: dsh itself refuses to launch it, so no platform,
|
|
5534
|
-
* supervisor or port could make a restart possible there
|
|
5535
|
-
*
|
|
5536
|
-
*
|
|
5537
|
-
*
|
|
5663
|
+
* supervisor or port could make a restart possible there, and the remedy
|
|
5664
|
+
* is the app. Then `from-default-profile` and Windows, both ahead of
|
|
5665
|
+
* systemd, because neither has an override and reporting the systemd one
|
|
5666
|
+
* there sends the reader to set `allowRestart: true`, which this gate would
|
|
5667
|
+
* still refuse. And `from-default-profile` ahead of Windows, because every
|
|
5668
|
+
* remedy after `desktop` is "restart dsh manually", which a reader launched
|
|
5669
|
+
* with the flag would do with the flag, and on 0.2.1 that recreates the
|
|
5670
|
+
* profile: its copy is the one that says "without that flag", and a
|
|
5671
|
+
* Windows, systemd or `--port 0` reader needs it as much as any other.
|
|
5538
5672
|
*
|
|
5539
5673
|
* - `desktop`: dsh's CLI refuses `--profile desktop` ("managed exclusively
|
|
5540
5674
|
* by the Electron application"), so the relaunch would exit at once and
|
|
5541
5675
|
* leave nothing on the port; the app restarts it.
|
|
5676
|
+
* - `from-default-profile`: from dsh 0.2.1, `--from-default-profile <name>`
|
|
5677
|
+
* replaces an existing custom profile with a fresh copy of the shipped
|
|
5678
|
+
* template on every launch, and the relaunch re-runs this argv verbatim,
|
|
5679
|
+
* so it would delete every plugin in the profile, this shop included.
|
|
5680
|
+
* Measured 2026-10-19 in throwaway homes: 0.2.1-rc.2 booted a relaunched
|
|
5681
|
+
* profile that had held the shop with no dependencies, while 0.2.0-rc.2
|
|
5682
|
+
* refused to boot an existing profile at all, so there the restarted dsh
|
|
5683
|
+
* failed to come back. Both spellings are the flag:
|
|
5684
|
+
* `--from-default-profile <name>` and `--from-default-profile=<name>`. It
|
|
5685
|
+
* is left in the argv rather than stripped: the restarted process would no
|
|
5686
|
+
* longer run the command the reader chose, and their next launch with the
|
|
5687
|
+
* flag recreates the profile anyway. No override (design
|
|
5688
|
+
* 2026-10-19-dsh-021-readiness §3).
|
|
5542
5689
|
* - `windows`: the handoff helper is a POSIX shell one-liner (restart.ts)
|
|
5543
5690
|
* and there is no `sh` on Windows. That spawn fails ASYNCHRONOUSLY, so
|
|
5544
5691
|
* committing would answer `ok: true`, exit this process, and leave nothing
|
|
@@ -5546,11 +5693,12 @@ let ShopGateway = (() => {
|
|
|
5546
5693
|
* - `systemd`: under a unit the two-phase handoff kills itself, because the
|
|
5547
5694
|
* main process exiting also kills the unit's cgroup and takes the detached
|
|
5548
5695
|
* helper with it; the service never comes back. Overridable, and the only
|
|
5549
|
-
* one
|
|
5696
|
+
* one that is.
|
|
5550
5697
|
* - `port-zero`: the OS hands the NEW process a fresh port the browser
|
|
5551
5698
|
* cannot know, so a restart would strand the client on a dead origin. */
|
|
5552
5699
|
staticRestartBlock() {
|
|
5553
5700
|
if (isDesktopProfile(this.profile)) return "desktop";
|
|
5701
|
+
if (this.restartArgv.some((arg) => arg === "--from-default-profile" || arg.startsWith("--from-default-profile="))) return "from-default-profile";
|
|
5554
5702
|
if (!this.restartPlatformSupported()) return "windows";
|
|
5555
5703
|
if (detectSupervisor(this.env, { ppid: this.ppid }) === "systemd" && !this.allowRestartConfigured()) return "systemd";
|
|
5556
5704
|
const portIndex = this.restartArgv.indexOf("--port");
|
|
@@ -5678,10 +5826,14 @@ let ShopGateway = (() => {
|
|
|
5678
5826
|
const alreadyImported = isUpdate || this.imported.has(args.name);
|
|
5679
5827
|
const harness = await this.runningHarness();
|
|
5680
5828
|
if (manager !== null) {
|
|
5681
|
-
const
|
|
5682
|
-
|
|
5829
|
+
const continues = isUpdate && !isDesktopProfile(this.profile);
|
|
5830
|
+
let refusedInPlace = false;
|
|
5831
|
+
const running = continueAfter(this.startManagerInstall(manager, spec, `install ${spec}`, (raw) => {
|
|
5832
|
+
const change = readChange(raw);
|
|
5833
|
+
if (!isUpdate && change.stage === "enable") this.imported.add(args.name);
|
|
5834
|
+
if (continues && change.errorCode === "already-installed") refusedInPlace = true;
|
|
5683
5835
|
return managerOutcome(raw, this.outcomeContext(args.name, isUpdate ? "update" : "install", alreadyImported, this.packageHasClientHalf(args.name)));
|
|
5684
|
-
}, () => this.postInstallHazard(args.name, harness, " It is on disk: uninstall it from the shop to undo this install."));
|
|
5836
|
+
}, () => this.postInstallHazard(args.name, harness, " It is on disk: uninstall it from the shop to undo this install.")), () => refusedInPlace, () => this.startCliInstall(args.name, spec, harness, true), cliFallbackLine(this.profile, spec));
|
|
5685
5837
|
this.track(running);
|
|
5686
5838
|
this.recordGithubPin(entry);
|
|
5687
5839
|
return {
|
|
@@ -5690,13 +5842,31 @@ let ShopGateway = (() => {
|
|
|
5690
5842
|
state: running.status().state
|
|
5691
5843
|
};
|
|
5692
5844
|
}
|
|
5693
|
-
const running =
|
|
5845
|
+
const running = this.startCliInstall(args.name, spec, harness, alreadyImported);
|
|
5846
|
+
this.recordGithubPin(entry);
|
|
5847
|
+
this.track(running);
|
|
5848
|
+
return {
|
|
5849
|
+
ok: true,
|
|
5850
|
+
installId: running.installId,
|
|
5851
|
+
state: running.status().state
|
|
5852
|
+
};
|
|
5853
|
+
}
|
|
5854
|
+
/**
|
|
5855
|
+
* One install of `name` through dsh's CLI, `dsh plugin --profile <p> add
|
|
5856
|
+
* <spec>` (§7.2): the path every install takes when dsh offers no plugin
|
|
5857
|
+
* manager, and the one that continues an update the manager refuses in
|
|
5858
|
+
* place (design 2026-10-19-dsh-021-readiness §2). `alreadyImported` is
|
|
5859
|
+
* whether this process may already hold the package's module, which rules
|
|
5860
|
+
* out a hot mount. The caller tracks the record and records any pin, once.
|
|
5861
|
+
*/
|
|
5862
|
+
startCliInstall(name, spec, harness, alreadyImported) {
|
|
5863
|
+
return startInstall({
|
|
5694
5864
|
profile: this.profile,
|
|
5695
5865
|
spec,
|
|
5696
5866
|
dshBin: this.dshBin,
|
|
5697
|
-
expectedName:
|
|
5867
|
+
expectedName: name,
|
|
5698
5868
|
prefetcher: this.prefetcher,
|
|
5699
|
-
alsoConfirm: () => this.postInstallHazard(
|
|
5869
|
+
alsoConfirm: () => this.postInstallHazard(name, harness, ` It is on disk: run \`dsh plugin --profile ${this.profile} remove ${name}\` to undo this install.`),
|
|
5700
5870
|
afterDone: async () => {
|
|
5701
5871
|
if (alreadyImported) return {
|
|
5702
5872
|
activation: "restart",
|
|
@@ -5706,8 +5876,8 @@ let ShopGateway = (() => {
|
|
|
5706
5876
|
mount: hotMount,
|
|
5707
5877
|
unmount: hotUnmount
|
|
5708
5878
|
};
|
|
5709
|
-
this.imported.add(
|
|
5710
|
-
const result = await hot.mount({ plugin: (plugin, config) => this.home.plugin(plugin, config) }, this.profileDirResolved(),
|
|
5879
|
+
this.imported.add(name);
|
|
5880
|
+
const result = await hot.mount({ plugin: (plugin, config) => this.home.plugin(plugin, config) }, this.profileDirResolved(), name);
|
|
5711
5881
|
if (!result.ok) return {
|
|
5712
5882
|
activation: "restart",
|
|
5713
5883
|
...result.reason !== null ? { restartReason: result.reason } : {}
|
|
@@ -5715,17 +5885,10 @@ let ShopGateway = (() => {
|
|
|
5715
5885
|
return { activation: activationOf({
|
|
5716
5886
|
hostLive: true,
|
|
5717
5887
|
clientLive: true,
|
|
5718
|
-
hasClientHalf: this.packageHasClientHalf(
|
|
5888
|
+
hasClientHalf: this.packageHasClientHalf(name)
|
|
5719
5889
|
}) };
|
|
5720
5890
|
}
|
|
5721
5891
|
});
|
|
5722
|
-
this.recordGithubPin(entry);
|
|
5723
|
-
this.track(running);
|
|
5724
|
-
return {
|
|
5725
|
-
ok: true,
|
|
5726
|
-
installId: running.installId,
|
|
5727
|
-
state: running.status().state
|
|
5728
|
-
};
|
|
5729
5892
|
}
|
|
5730
5893
|
/**
|
|
5731
5894
|
* What would stop the profile from starting now that `name`'s files are on
|
|
@@ -6157,7 +6320,12 @@ let ShopGateway = (() => {
|
|
|
6157
6320
|
};
|
|
6158
6321
|
const spec = `dsh-plugin-shop@${args.version}`;
|
|
6159
6322
|
if (manager !== null) {
|
|
6160
|
-
const
|
|
6323
|
+
const continues = !isDesktopProfile(this.profile);
|
|
6324
|
+
let refusedInPlace = false;
|
|
6325
|
+
const running = continueAfter(this.startManagerInstall(manager, spec, `update ${spec}`, (raw) => {
|
|
6326
|
+
if (continues && readChange(raw).errorCode === "already-installed") refusedInPlace = true;
|
|
6327
|
+
return managerOutcome(raw, this.outcomeContext("dsh-plugin-shop", "update", true, true));
|
|
6328
|
+
}), () => refusedInPlace, () => this.startCliSelfUpdate(spec), cliFallbackLine(this.profile, spec));
|
|
6161
6329
|
this.track(running);
|
|
6162
6330
|
return {
|
|
6163
6331
|
ok: true,
|
|
@@ -6165,13 +6333,7 @@ let ShopGateway = (() => {
|
|
|
6165
6333
|
state: running.status().state
|
|
6166
6334
|
};
|
|
6167
6335
|
}
|
|
6168
|
-
const running =
|
|
6169
|
-
profile: this.profile,
|
|
6170
|
-
spec,
|
|
6171
|
-
dshBin: this.dshBin,
|
|
6172
|
-
expectedName: "dsh-plugin-shop",
|
|
6173
|
-
prefetcher: this.prefetcher
|
|
6174
|
-
});
|
|
6336
|
+
const running = this.startCliSelfUpdate(spec);
|
|
6175
6337
|
this.track(running);
|
|
6176
6338
|
return {
|
|
6177
6339
|
ok: true,
|
|
@@ -6179,6 +6341,20 @@ let ShopGateway = (() => {
|
|
|
6179
6341
|
state: running.status().state
|
|
6180
6342
|
};
|
|
6181
6343
|
}
|
|
6344
|
+
/** The shop's own update through dsh's CLI: the path when dsh offers no
|
|
6345
|
+
* plugin manager, and the one that continues a self-update the manager
|
|
6346
|
+
* refuses in place (design 2026-10-19-dsh-021-readiness §2). It passes no
|
|
6347
|
+
* `afterDone`, so it reports a restart: a host half cannot swap itself live
|
|
6348
|
+
* (§8). The caller tracks the record. */
|
|
6349
|
+
startCliSelfUpdate(spec) {
|
|
6350
|
+
return startInstall({
|
|
6351
|
+
profile: this.profile,
|
|
6352
|
+
spec,
|
|
6353
|
+
dshBin: this.dshBin,
|
|
6354
|
+
expectedName: "dsh-plugin-shop",
|
|
6355
|
+
prefetcher: this.prefetcher
|
|
6356
|
+
});
|
|
6357
|
+
}
|
|
6182
6358
|
};
|
|
6183
6359
|
})();
|
|
6184
6360
|
//#endregion
|
package/lib/typert.host.js
CHANGED
|
@@ -165,7 +165,7 @@ const dsh_plugin_shop_shop_version_result$schema = z.object({
|
|
|
165
165
|
'installed': z.string(),
|
|
166
166
|
'latest': z.union([z.literal(null), z.string()]),
|
|
167
167
|
'outdated': z.boolean(),
|
|
168
|
-
'restartBlocked': z.union([z.literal(null), z.literal("desktop"), z.literal("windows"), z.literal("systemd"), z.literal("port-zero")]),
|
|
168
|
+
'restartBlocked': z.union([z.literal(null), z.literal("desktop"), z.literal("from-default-profile"), z.literal("windows"), z.literal("systemd"), z.literal("port-zero")]),
|
|
169
169
|
'pendingVersion': z.union([z.undefined(), z.literal(null), z.string()]).optional(),
|
|
170
170
|
'bootId': z.union([z.undefined(), z.string()]).optional(),
|
|
171
171
|
})
|
|
@@ -202,7 +202,7 @@ export const TYPERT = {
|
|
|
202
202
|
schema: dsh_plugin_shop_shop_catalog_result$schema,
|
|
203
203
|
create: () => dsh_plugin_shop_shop_catalog_result$schema,
|
|
204
204
|
},
|
|
205
|
-
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":
|
|
205
|
+
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":1301,"column":9},
|
|
206
206
|
},
|
|
207
207
|
{
|
|
208
208
|
id: 'dsh-plugin-shop#shop/installed',
|
|
@@ -218,7 +218,7 @@ export const TYPERT = {
|
|
|
218
218
|
schema: dsh_plugin_shop_shop_installed_result$schema,
|
|
219
219
|
create: () => dsh_plugin_shop_shop_installed_result$schema,
|
|
220
220
|
},
|
|
221
|
-
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":
|
|
221
|
+
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":1804,"column":9},
|
|
222
222
|
},
|
|
223
223
|
{
|
|
224
224
|
id: 'dsh-plugin-shop#shop/installedSpecs',
|
|
@@ -234,7 +234,7 @@ export const TYPERT = {
|
|
|
234
234
|
schema: dsh_plugin_shop_shop_installedSpecs_result$schema,
|
|
235
235
|
create: () => dsh_plugin_shop_shop_installedSpecs_result$schema,
|
|
236
236
|
},
|
|
237
|
-
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":
|
|
237
|
+
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":1795,"column":9},
|
|
238
238
|
},
|
|
239
239
|
{
|
|
240
240
|
id: 'dsh-plugin-shop#shop/installStart',
|
|
@@ -262,7 +262,7 @@ export const TYPERT = {
|
|
|
262
262
|
schema: dsh_plugin_shop_shop_installStart_result$schema,
|
|
263
263
|
create: () => dsh_plugin_shop_shop_installStart_result$schema,
|
|
264
264
|
},
|
|
265
|
-
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":
|
|
265
|
+
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":1427,"column":9},
|
|
266
266
|
},
|
|
267
267
|
{
|
|
268
268
|
id: 'dsh-plugin-shop#shop/installStatus',
|
|
@@ -289,7 +289,7 @@ export const TYPERT = {
|
|
|
289
289
|
schema: dsh_plugin_shop_shop_installStatus_result$schema,
|
|
290
290
|
create: () => dsh_plugin_shop_shop_installStatus_result$schema,
|
|
291
291
|
},
|
|
292
|
-
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":
|
|
292
|
+
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":1771,"column":3},
|
|
293
293
|
},
|
|
294
294
|
{
|
|
295
295
|
id: 'dsh-plugin-shop#shop/restart',
|
|
@@ -305,7 +305,7 @@ export const TYPERT = {
|
|
|
305
305
|
schema: dsh_plugin_shop_shop_restart_result$schema,
|
|
306
306
|
create: () => dsh_plugin_shop_shop_restart_result$schema,
|
|
307
307
|
},
|
|
308
|
-
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":
|
|
308
|
+
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":2055,"column":9},
|
|
309
309
|
},
|
|
310
310
|
{
|
|
311
311
|
id: 'dsh-plugin-shop#shop/setEnabled',
|
|
@@ -332,7 +332,7 @@ export const TYPERT = {
|
|
|
332
332
|
schema: dsh_plugin_shop_shop_setEnabled_result$schema,
|
|
333
333
|
create: () => dsh_plugin_shop_shop_setEnabled_result$schema,
|
|
334
334
|
},
|
|
335
|
-
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":
|
|
335
|
+
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":953,"column":9},
|
|
336
336
|
},
|
|
337
337
|
{
|
|
338
338
|
id: 'dsh-plugin-shop#shop/uninstallStart',
|
|
@@ -360,7 +360,7 @@ export const TYPERT = {
|
|
|
360
360
|
schema: dsh_plugin_shop_shop_uninstallStart_result$schema,
|
|
361
361
|
create: () => dsh_plugin_shop_shop_uninstallStart_result$schema,
|
|
362
362
|
},
|
|
363
|
-
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":
|
|
363
|
+
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":1949,"column":9},
|
|
364
364
|
},
|
|
365
365
|
{
|
|
366
366
|
id: 'dsh-plugin-shop#shop/updateStart',
|
|
@@ -387,7 +387,7 @@ export const TYPERT = {
|
|
|
387
387
|
schema: dsh_plugin_shop_shop_updateStart_result$schema,
|
|
388
388
|
create: () => dsh_plugin_shop_shop_updateStart_result$schema,
|
|
389
389
|
},
|
|
390
|
-
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":
|
|
390
|
+
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":2162,"column":9},
|
|
391
391
|
},
|
|
392
392
|
{
|
|
393
393
|
id: 'dsh-plugin-shop#shop/version',
|
|
@@ -403,7 +403,7 @@ export const TYPERT = {
|
|
|
403
403
|
schema: dsh_plugin_shop_shop_version_result$schema,
|
|
404
404
|
create: () => dsh_plugin_shop_shop_version_result$schema,
|
|
405
405
|
},
|
|
406
|
-
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":
|
|
406
|
+
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":2119,"column":9},
|
|
407
407
|
},
|
|
408
408
|
],
|
|
409
409
|
model: {
|
|
@@ -544,7 +544,7 @@ export const TYPERT = {
|
|
|
544
544
|
},
|
|
545
545
|
{
|
|
546
546
|
"name": "RestartBlockedReason",
|
|
547
|
-
"declaration": "export type RestartBlockedReason = 'desktop' | 'windows' | 'systemd' | 'port-zero';"
|
|
547
|
+
"declaration": "export type RestartBlockedReason = 'desktop' | 'from-default-profile' | 'windows' | 'systemd' | 'port-zero';"
|
|
548
548
|
},
|
|
549
549
|
{
|
|
550
550
|
"name": "RestartOutcome",
|
|
@@ -165,7 +165,7 @@ const dsh_plugin_shop_shop_version_result$schema = z.object({
|
|
|
165
165
|
'installed': z.string(),
|
|
166
166
|
'latest': z.union([z.literal(null), z.string()]),
|
|
167
167
|
'outdated': z.boolean(),
|
|
168
|
-
'restartBlocked': z.union([z.literal(null), z.literal("desktop"), z.literal("windows"), z.literal("systemd"), z.literal("port-zero")]),
|
|
168
|
+
'restartBlocked': z.union([z.literal(null), z.literal("desktop"), z.literal("from-default-profile"), z.literal("windows"), z.literal("systemd"), z.literal("port-zero")]),
|
|
169
169
|
'pendingVersion': z.union([z.undefined(), z.literal(null), z.string()]).optional(),
|
|
170
170
|
'bootId': z.union([z.undefined(), z.string()]).optional(),
|
|
171
171
|
})
|
|
@@ -199,7 +199,7 @@ export const TYPERT_REMOTE = {
|
|
|
199
199
|
schema: dsh_plugin_shop_shop_catalog_result$schema,
|
|
200
200
|
create: () => dsh_plugin_shop_shop_catalog_result$schema,
|
|
201
201
|
},
|
|
202
|
-
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":
|
|
202
|
+
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":1301,"column":9},
|
|
203
203
|
},
|
|
204
204
|
{
|
|
205
205
|
id: 'dsh-plugin-shop#shop/installed',
|
|
@@ -215,7 +215,7 @@ export const TYPERT_REMOTE = {
|
|
|
215
215
|
schema: dsh_plugin_shop_shop_installed_result$schema,
|
|
216
216
|
create: () => dsh_plugin_shop_shop_installed_result$schema,
|
|
217
217
|
},
|
|
218
|
-
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":
|
|
218
|
+
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":1804,"column":9},
|
|
219
219
|
},
|
|
220
220
|
{
|
|
221
221
|
id: 'dsh-plugin-shop#shop/installedSpecs',
|
|
@@ -231,7 +231,7 @@ export const TYPERT_REMOTE = {
|
|
|
231
231
|
schema: dsh_plugin_shop_shop_installedSpecs_result$schema,
|
|
232
232
|
create: () => dsh_plugin_shop_shop_installedSpecs_result$schema,
|
|
233
233
|
},
|
|
234
|
-
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":
|
|
234
|
+
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":1795,"column":9},
|
|
235
235
|
},
|
|
236
236
|
{
|
|
237
237
|
id: 'dsh-plugin-shop#shop/installStart',
|
|
@@ -259,7 +259,7 @@ export const TYPERT_REMOTE = {
|
|
|
259
259
|
schema: dsh_plugin_shop_shop_installStart_result$schema,
|
|
260
260
|
create: () => dsh_plugin_shop_shop_installStart_result$schema,
|
|
261
261
|
},
|
|
262
|
-
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":
|
|
262
|
+
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":1427,"column":9},
|
|
263
263
|
},
|
|
264
264
|
{
|
|
265
265
|
id: 'dsh-plugin-shop#shop/installStatus',
|
|
@@ -286,7 +286,7 @@ export const TYPERT_REMOTE = {
|
|
|
286
286
|
schema: dsh_plugin_shop_shop_installStatus_result$schema,
|
|
287
287
|
create: () => dsh_plugin_shop_shop_installStatus_result$schema,
|
|
288
288
|
},
|
|
289
|
-
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":
|
|
289
|
+
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":1771,"column":3},
|
|
290
290
|
},
|
|
291
291
|
{
|
|
292
292
|
id: 'dsh-plugin-shop#shop/restart',
|
|
@@ -302,7 +302,7 @@ export const TYPERT_REMOTE = {
|
|
|
302
302
|
schema: dsh_plugin_shop_shop_restart_result$schema,
|
|
303
303
|
create: () => dsh_plugin_shop_shop_restart_result$schema,
|
|
304
304
|
},
|
|
305
|
-
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":
|
|
305
|
+
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":2055,"column":9},
|
|
306
306
|
},
|
|
307
307
|
{
|
|
308
308
|
id: 'dsh-plugin-shop#shop/setEnabled',
|
|
@@ -329,7 +329,7 @@ export const TYPERT_REMOTE = {
|
|
|
329
329
|
schema: dsh_plugin_shop_shop_setEnabled_result$schema,
|
|
330
330
|
create: () => dsh_plugin_shop_shop_setEnabled_result$schema,
|
|
331
331
|
},
|
|
332
|
-
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":
|
|
332
|
+
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":953,"column":9},
|
|
333
333
|
},
|
|
334
334
|
{
|
|
335
335
|
id: 'dsh-plugin-shop#shop/uninstallStart',
|
|
@@ -357,7 +357,7 @@ export const TYPERT_REMOTE = {
|
|
|
357
357
|
schema: dsh_plugin_shop_shop_uninstallStart_result$schema,
|
|
358
358
|
create: () => dsh_plugin_shop_shop_uninstallStart_result$schema,
|
|
359
359
|
},
|
|
360
|
-
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":
|
|
360
|
+
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":1949,"column":9},
|
|
361
361
|
},
|
|
362
362
|
{
|
|
363
363
|
id: 'dsh-plugin-shop#shop/updateStart',
|
|
@@ -384,7 +384,7 @@ export const TYPERT_REMOTE = {
|
|
|
384
384
|
schema: dsh_plugin_shop_shop_updateStart_result$schema,
|
|
385
385
|
create: () => dsh_plugin_shop_shop_updateStart_result$schema,
|
|
386
386
|
},
|
|
387
|
-
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":
|
|
387
|
+
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":2162,"column":9},
|
|
388
388
|
},
|
|
389
389
|
{
|
|
390
390
|
id: 'dsh-plugin-shop#shop/version',
|
|
@@ -400,7 +400,7 @@ export const TYPERT_REMOTE = {
|
|
|
400
400
|
schema: dsh_plugin_shop_shop_version_result$schema,
|
|
401
401
|
create: () => dsh_plugin_shop_shop_version_result$schema,
|
|
402
402
|
},
|
|
403
|
-
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":
|
|
403
|
+
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":2119,"column":9},
|
|
404
404
|
},
|
|
405
405
|
],
|
|
406
406
|
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The shop's two buttons on dsh's Plugins page (design
|
|
3
|
+
* 2026-10-16-shop-sidebar-page §7), in the head of a detail page, before the
|
|
4
|
+
* page's own switch and uninstall (`plugins.detail.actions`, dsh 0.1.7-rc.2
|
|
5
|
+
* and later): "Open Plugin shop" on the shop's own bundle page, and "View in
|
|
6
|
+
* Plugin shop" on the page of any bundle the shelf lists. Any other subject
|
|
7
|
+
* renders nothing, as the slot's contract asks of an entry.
|
|
8
|
+
*
|
|
9
|
+
* It renders inside dsh's page, so it carries its own error boundary: a
|
|
10
|
+
* throw here costs the reader a button, never dsh's page.
|
|
11
|
+
*/
|
|
12
|
+
import { type ReactNode } from 'react';
|
|
13
|
+
import type { InjectFace, PropsLocale } from '@deepseek-ai/dsh-client-ui-slots';
|
|
14
|
+
import type { ShopCatalogResult } from '../host/index.ts';
|
|
15
|
+
import type { ShopPageStore } from './page-store.ts';
|
|
16
|
+
/** One bundle as a detail contribution sees it: the field this reads of the
|
|
17
|
+
* Plugins page's `PluginPackageRef` (`dsh-client-ui-plugin-manager`'s slot
|
|
18
|
+
* contract, identical in 0.1.7-rc.2 and 0.2.0-rc.2). */
|
|
19
|
+
export interface PluginPackageRef {
|
|
20
|
+
readonly name: string;
|
|
21
|
+
}
|
|
22
|
+
/** What a detail page is about, as the Plugins page hands it over. */
|
|
23
|
+
export type PluginsSubject = {
|
|
24
|
+
readonly kind: 'bundle';
|
|
25
|
+
readonly pkg: PluginPackageRef;
|
|
26
|
+
} | {
|
|
27
|
+
readonly kind: 'row';
|
|
28
|
+
readonly pkg: PluginPackageRef;
|
|
29
|
+
} | {
|
|
30
|
+
readonly kind: 'item';
|
|
31
|
+
readonly id: string;
|
|
32
|
+
};
|
|
33
|
+
/** The face index.ts hands the buttons. */
|
|
34
|
+
export interface ShopDetailActionInjected {
|
|
35
|
+
/** The catalog a listing question is asked of (index.ts `listing`). */
|
|
36
|
+
listing: () => Promise<ShopCatalogResult>;
|
|
37
|
+
/** Whether the page can be opened at all: false without a layout service
|
|
38
|
+
* that can select a panel. */
|
|
39
|
+
canOpen: () => boolean;
|
|
40
|
+
openShop: () => void;
|
|
41
|
+
page: ShopPageStore;
|
|
42
|
+
}
|
|
43
|
+
export type ShopDetailActionProps = {
|
|
44
|
+
subject: PluginsSubject;
|
|
45
|
+
} & PropsLocale<'settings.shop'> & InjectFace<ShopDetailActionInjected>;
|
|
46
|
+
export declare function ShopDetailAction(props: ShopDetailActionProps): ReactNode;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The shop's glyph in dsh's sidebar (design 2026-10-16-shop-sidebar-page
|
|
3
|
+
* §4.3): a bag with a whale tail, drawn the way dsh draws its own panel
|
|
4
|
+
* glyphs — a 16×16 view box, one-pixel `currentColor` strokes, no fill — so
|
|
5
|
+
* the row's colour and its selection treatment reach it unchanged. `active`
|
|
6
|
+
* is ignored, as dsh's own glyphs ignore it: the row draws the selection.
|
|
7
|
+
*/
|
|
8
|
+
import type { ReactNode } from 'react';
|
|
9
|
+
export declare function ShopGlyph({ size }: {
|
|
10
|
+
size: number;
|
|
11
|
+
active: boolean;
|
|
12
|
+
}): ReactNode;
|