dshmarket 1.66.21 → 1.66.23
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/client/client.js +102 -19
- package/lib/check.js +36 -1
- package/lib/dsh-cli.js +12 -1
- package/lib/install.js +31 -5
- package/lib/net.js +30 -0
- package/lib/official-desktop.js +5 -1
- package/lib/profile.js +14 -9
- package/lib/trial.js +11 -2
- package/lib/types/check.d.ts +21 -0
- package/lib/types/net.d.ts +25 -0
- package/lib/types/official-desktop.d.ts +1 -0
- package/package.json +1 -1
- package/src/check.ts +36 -1
- package/src/client/Market.module.css +16 -4
- package/src/client/MarketSection.tsx +28 -3
- package/src/client/locales.ts +2 -0
- package/src/client/market-data.ts +71 -13
- package/src/dsh-cli.ts +12 -1
- package/src/install.ts +29 -5
- package/src/net.ts +34 -0
- package/src/official-desktop.ts +6 -2
- package/src/profile.ts +14 -8
- package/src/trial.ts +9 -2
package/src/net.ts
CHANGED
|
@@ -32,6 +32,40 @@
|
|
|
32
32
|
|
|
33
33
|
import { Agent, EnvHttpProxyAgent, fetch as undiciFetch } from 'undici'
|
|
34
34
|
|
|
35
|
+
/**
|
|
36
|
+
* Hand the host's own undici the dispatcher our import took (#861).
|
|
37
|
+
*
|
|
38
|
+
* undici's `lib/global.js` runs at import time, and when its symbol is unset
|
|
39
|
+
* it installs an Agent on BOTH global dispatcher symbols: `.1`, which Node's
|
|
40
|
+
* built-in fetch reads, and `.2`, which undici 8's `getGlobalDispatcher()`
|
|
41
|
+
* reads. `setGlobalDispatcher` defines them non-configurable, so the value
|
|
42
|
+
* cannot be taken back — but it is writable, and undici 8 only ever READS
|
|
43
|
+
* `.2`, so hiding ours there is enough for it to install its own dispatcher
|
|
44
|
+
* and wrap it onto `.1` for the legacy consumers.
|
|
45
|
+
*
|
|
46
|
+
* Measured on Node 24.20 and 25.6 (undici 7.29 as the market's copy, 8.12 as
|
|
47
|
+
* the host's): with this, a host importing undici 8 after the market gets
|
|
48
|
+
* `getGlobalDispatcher() instanceof its own Agent` — without it, it is handed
|
|
49
|
+
* the market's undici 7 Agent (`instanceof` its own Agent: false) and never
|
|
50
|
+
* installs one, which is the state #861 reports as the host's own LLM and
|
|
51
|
+
* model-list requests failing once the market is loaded.
|
|
52
|
+
*
|
|
53
|
+
* `.1` is deliberately left alone: Node's built-in fetch reads it and asserts
|
|
54
|
+
* it is set, so clearing it breaks global fetch outright (measured: the same
|
|
55
|
+
* probe that makes `.2` undefined fails `assert(dispatcher)` when `.1` is
|
|
56
|
+
* cleared too). Our own requests are unaffected either way — every one of
|
|
57
|
+
* them carries a dispatcher this module created.
|
|
58
|
+
*/
|
|
59
|
+
export function releaseHostUndiciDispatcher(
|
|
60
|
+
global: Record<symbol, unknown> = globalThis as unknown as Record<symbol, unknown>,
|
|
61
|
+
ownAgent: abstract new (...args: never[]) => unknown = Agent,
|
|
62
|
+
): void {
|
|
63
|
+
const symbol = Symbol.for('undici.globalDispatcher.2')
|
|
64
|
+
if (global[symbol] instanceof ownAgent) global[symbol] = undefined
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
releaseHostUndiciDispatcher()
|
|
68
|
+
|
|
35
69
|
/**
|
|
36
70
|
* The proxy this process would use for the catalog, if any.
|
|
37
71
|
*
|
package/src/official-desktop.ts
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { randomUUID } from 'node:crypto'
|
|
4
4
|
import { progress, TARGET_RE, type DesktopPluginRuntime, type InstallResult } from './dsh-cli.ts'
|
|
5
|
+
import { activeRegion, DEFAULT_NPM_REGISTRY, routesFor } from './regions.ts'
|
|
5
6
|
|
|
6
7
|
interface ManagedResult {
|
|
7
8
|
application: string
|
|
@@ -10,7 +11,7 @@ interface ManagedResult {
|
|
|
10
11
|
}
|
|
11
12
|
|
|
12
13
|
export interface OfficialPluginManagerLike {
|
|
13
|
-
installBundle(spec: string, options?: { requestId?: string }): Promise<ManagedResult>
|
|
14
|
+
installBundle(spec: string, options?: { requestId?: string; registry?: string }): Promise<ManagedResult>
|
|
14
15
|
removeBundle(name: string): Promise<ManagedResult>
|
|
15
16
|
cancelInstall(requestId: string): Promise<unknown>
|
|
16
17
|
}
|
|
@@ -103,9 +104,12 @@ export function createOfficialDesktopRuntime(
|
|
|
103
104
|
progress.cancelling = false
|
|
104
105
|
let operation: Promise<ManagedResult>
|
|
105
106
|
try {
|
|
107
|
+
// Read at each operation so region switches apply without a restart.
|
|
108
|
+
// The default route leaves the host's own npm configuration in charge.
|
|
109
|
+
const registry = routesFor(activeRegion()).npmRegistry
|
|
106
110
|
operation = command === 'remove'
|
|
107
111
|
? manager.removeBundle(target)
|
|
108
|
-
: manager.installBundle(spec, { requestId })
|
|
112
|
+
: manager.installBundle(spec, { requestId, ...(registry === DEFAULT_NPM_REGISTRY ? {} : { registry }) })
|
|
109
113
|
} catch (error) {
|
|
110
114
|
progress.active = false
|
|
111
115
|
return Promise.resolve(failure(detail(error), 127))
|
package/src/profile.ts
CHANGED
|
@@ -894,7 +894,8 @@ export function parsePatchRows(text: string): { names: string[]; ids: string[];
|
|
|
894
894
|
// `insert:` indentation closes the block — those target OTHER plugins.
|
|
895
895
|
let insertIndent: number | null = null
|
|
896
896
|
// Indent of a `config:` key whose value is payload, not more rows.
|
|
897
|
-
// Everything deeper than it
|
|
897
|
+
// Everything deeper than it — and a `- ` at its own column, a sequence
|
|
898
|
+
// YAML lets sit unindented under its key — is data, and its `id:` keys live in another
|
|
898
899
|
// namespace (#841): a preset row writes `config.plugins[].id` for the
|
|
899
900
|
// entries it will instantiate, so two bundles that instantiate the same
|
|
900
901
|
// official plugins read as two bundles inserting the same entry ids —
|
|
@@ -947,17 +948,22 @@ export function parsePatchRows(text: string): { names: string[]; ids: string[];
|
|
|
947
948
|
}
|
|
948
949
|
return false
|
|
949
950
|
}
|
|
950
|
-
/**
|
|
951
|
+
/**
|
|
952
|
+
* Whether the key at `keyIndent` holds a block sequence. YAML lets that
|
|
953
|
+
* sequence sit at the key's own column (`config:` then `- id:` without
|
|
954
|
+
* further indent), so a `- ` there is the value, not a sibling.
|
|
955
|
+
*/
|
|
951
956
|
const valueIsList = (from: number, keyIndent: number): boolean => {
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
}
|
|
956
|
-
return false
|
|
957
|
+
const next = from + 1
|
|
958
|
+
if (next >= rows.length || rows[next].indent < keyIndent) return false
|
|
959
|
+
return isDash(next)
|
|
957
960
|
}
|
|
958
961
|
for (let i = 0; i < rows.length; i++) {
|
|
959
962
|
const { line, trimmed, indent } = rows[i]
|
|
960
|
-
|
|
963
|
+
// A `- ` at the payload key's own column is still that key's value.
|
|
964
|
+
if (payloadIndent !== null && indent <= payloadIndent && !(indent === payloadIndent && isDash(i))) {
|
|
965
|
+
payloadIndent = null
|
|
966
|
+
}
|
|
961
967
|
if (insertIndent !== null && indent <= insertIndent && !/^\s*-?\s*(id|name|config):/u.test(line)) {
|
|
962
968
|
insertIndent = null
|
|
963
969
|
}
|
package/src/trial.ts
CHANGED
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
import { existsSync, readFileSync } from 'node:fs'
|
|
28
28
|
import { join } from 'node:path'
|
|
29
29
|
import {
|
|
30
|
-
buildBundleLayers, composeLayers, findDshInstallDir, parsePatchFile,
|
|
30
|
+
buildBundleLayers, composeLayers, duplicateEntryIdsAreFatal, findDshInstallDir, parsePatchFile,
|
|
31
31
|
type DuplicateId, type LayerInput, type OrphanRow, type OverrideRow,
|
|
32
32
|
} from './check.ts'
|
|
33
33
|
import { resolveDshHome } from './home-paths.ts'
|
|
@@ -126,8 +126,15 @@ export function trialValidate(
|
|
|
126
126
|
if (bundle.error !== null) errors.push({ layer: bundle.name, message: bundle.error })
|
|
127
127
|
if (bundle.parseError !== null) errors.push({ layer: bundle.name, message: bundle.parseError })
|
|
128
128
|
}
|
|
129
|
+
// Same version gate as the check report (#853): on a loader that folds one
|
|
130
|
+
// id into one entry (1.0.4+) this is an override, not a refusal. Making it
|
|
131
|
+
// fatal here is what rolled back every update for a profile that already
|
|
132
|
+
// had a package row and a user-patch row on one id.
|
|
133
|
+
const duplicateFatal = duplicateEntryIdsAreFatal(profileDir)
|
|
129
134
|
for (const dup of composed.duplicates) {
|
|
130
|
-
|
|
135
|
+
const message = `duplicate loader entry id ${JSON.stringify(dup.id)} (${dup.count} rows) / 重复的 loader 条目 id ${JSON.stringify(dup.id)}`
|
|
136
|
+
if (duplicateFatal) errors.push({ layer: dup.layers.join(' / '), message })
|
|
137
|
+
else warnings.push({ layer: dup.layers.join(' / '), message: `${message} — 当前宿主会让后一层覆盖前一层,启动不受影响 / this host lets the later layer win, so the boot is unaffected` })
|
|
131
138
|
}
|
|
132
139
|
for (const orphan of composed.orphans) {
|
|
133
140
|
warnings.push({ layer: orphan.layer, message: `${orphan.id}: ${orphan.reason}` })
|