dshmarket 1.29.0 → 1.29.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/dsh-cli.js +37 -4
- package/lib/types/dsh-cli.d.ts +9 -1
- package/lib/types/verify.d.ts +0 -5
- package/lib/verify.js +30 -3
- package/package.json +1 -1
- package/src/dsh-cli.ts +44 -4
- package/src/verify.ts +31 -3
package/lib/dsh-cli.js
CHANGED
|
@@ -406,15 +406,41 @@ export function cancelActive() {
|
|
|
406
406
|
}
|
|
407
407
|
/** Whether `pnpm` resolves on PATH; success is cached, absence is re-probed. */
|
|
408
408
|
let pnpmReady = false;
|
|
409
|
+
/**
|
|
410
|
+
* Why the last probe said no.
|
|
411
|
+
*
|
|
412
|
+
* `missing` and `failed` are different problems with different fixes, and
|
|
413
|
+
* collapsing both into `false` made the market give one answer to both: it
|
|
414
|
+
* told a user whose pnpm ran perfectly from their shell to go set PNPM_HOME
|
|
415
|
+
* (#228). A binary that IS on the path and exits non-zero — a corepack shim
|
|
416
|
+
* that cannot reach the network to fetch pnpm itself is the common one —
|
|
417
|
+
* needs its own output shown, not a path to fix that is already right.
|
|
418
|
+
*/
|
|
419
|
+
let pnpmProbeFailure = null;
|
|
420
|
+
/** Why `pnpm --version` last failed, or null when it has not failed. */
|
|
421
|
+
export function lastPnpmProbeFailure() {
|
|
422
|
+
return pnpmProbeFailure;
|
|
423
|
+
}
|
|
409
424
|
/** Probe `pnpm --version` on PATH. */
|
|
410
425
|
export function probePnpm() {
|
|
411
426
|
if (pnpmReady)
|
|
412
427
|
return Promise.resolve(true);
|
|
413
428
|
return new Promise((resolvePromise) => {
|
|
414
|
-
|
|
415
|
-
|
|
429
|
+
// Piped, not ignored: the output of a pnpm that exists but will not run
|
|
430
|
+
// IS the explanation, and throwing it away is what left #228 with a
|
|
431
|
+
// failure nobody could act on.
|
|
432
|
+
const child = spawnShim('pnpm', ['--version'], { stdio: ['ignore', 'pipe', 'pipe'], viaShell: winCmdShim, env: spawnEnv() });
|
|
433
|
+
let output = '';
|
|
434
|
+
const collect = (chunk) => { output = (output + chunk.toString()).slice(-2000); };
|
|
435
|
+
child.stdout?.on('data', collect);
|
|
436
|
+
child.stderr?.on('data', collect);
|
|
437
|
+
child.on('error', (error) => {
|
|
438
|
+
pnpmProbeFailure = { kind: 'missing', output: error.message };
|
|
439
|
+
resolvePromise(false);
|
|
440
|
+
});
|
|
416
441
|
child.on('close', (code) => {
|
|
417
442
|
pnpmReady = code === 0;
|
|
443
|
+
pnpmProbeFailure = pnpmReady ? null : { kind: 'failed', output: output.trim() };
|
|
418
444
|
resolvePromise(pnpmReady);
|
|
419
445
|
});
|
|
420
446
|
});
|
|
@@ -468,7 +494,7 @@ export async function provisionPnpm() {
|
|
|
468
494
|
const npmFound = toolOnPath('npm');
|
|
469
495
|
if (!npmFound)
|
|
470
496
|
logEvent('warn', 'setup-pnpm', `npm is not on any searched path (node lives in ${nodeBinDir})`);
|
|
471
|
-
return { ok: false, hint: provisionHint(corepack.output, npm.output, npmFound) };
|
|
497
|
+
return { ok: false, hint: provisionHint(corepack.output, npm.output, npmFound, lastPnpmProbeFailure()) };
|
|
472
498
|
}
|
|
473
499
|
/** Executable suffixes a bare command name can carry on this platform. */
|
|
474
500
|
const EXECUTABLE_SUFFIXES = process.platform === 'win32'
|
|
@@ -508,7 +534,7 @@ export function toolOnPath(name) {
|
|
|
508
534
|
* a GUI launch with no Node on PATH at all).
|
|
509
535
|
* @returns a bilingual, actionable hint, or undefined when unrecognized.
|
|
510
536
|
*/
|
|
511
|
-
export function provisionHint(corepackOutput, npmOutput, npmFound = true) {
|
|
537
|
+
export function provisionHint(corepackOutput, npmOutput, npmFound = true, probeFailure = null) {
|
|
512
538
|
// Node itself unreachable: pointing the user back at this same button
|
|
513
539
|
// would be a dead end (#32). `npmFound` answers this from disk, so it
|
|
514
540
|
// holds on a Windows console that reports the same thing in a codepage we
|
|
@@ -543,6 +569,13 @@ export function provisionHint(corepackOutput, npmOutput, npmFound = true) {
|
|
|
543
569
|
// they can see succeeded, and their complaint was exactly that — "又不告诉
|
|
544
570
|
// 我怎么手动配置". Whatever the cause, the actionable question is the same
|
|
545
571
|
// one, so ask it: where is pnpm, and is that anywhere this process looks?
|
|
572
|
+
// pnpm IS on the path and exits non-zero. Telling this user to fix PNPM_HOME
|
|
573
|
+
// would be advice for the opposite problem — theirs runs fine from a shell,
|
|
574
|
+
// which is exactly what #228 reported. Its own output is the explanation.
|
|
575
|
+
if (probeFailure?.kind === 'failed') {
|
|
576
|
+
const detail = probeFailure.output === '' ? '' : `\n\n${probeFailure.output}`;
|
|
577
|
+
return `找到 pnpm 了,但运行 \`pnpm --version\` 失败——所以问题不在路径上,设 PNPM_HOME 没有用。最常见的原因是 corepack 的 shim 需要联网下载 pnpm 本体,而这台机器下不到。请在终端执行一次 \`pnpm --version\`:如果同样失败,按它的提示修(受限网络可用 \`brew install pnpm\` 或 \`npm i -g pnpm --registry <你的镜像>\` 装一个完整的 pnpm,绕开 shim);如果在终端里正常,说明 dsh 进程的环境和你的终端不同,请从该终端启动 dsh。pnpm 的原始输出:${detail} / pnpm was found, but \`pnpm --version\` fails — so this is not a path problem and PNPM_HOME will not help. The usual cause is a corepack shim that has to download pnpm itself and cannot reach the network. Run \`pnpm --version\` in a terminal: if it fails the same way, follow what it says (on a restricted network install a real pnpm with \`brew install pnpm\` or \`npm i -g pnpm --registry <your mirror>\` to bypass the shim); if it works there, the dsh process has a different environment than your shell — start dsh from that terminal. pnpm's own output:${detail}`;
|
|
578
|
+
}
|
|
546
579
|
const searched = toolSearchDirs().join(process.platform === 'win32' ? ' ; ' : ' : ');
|
|
547
580
|
const locate = process.platform === 'win32' ? 'where pnpm' : 'which pnpm';
|
|
548
581
|
return `pnpm 装好了,但这个 dsh 进程仍然启动不了它——安装步骤都成功,只是装到的位置不在它搜索的范围内。已找过:${searched}。请在终端执行 \`${locate}\` 看 pnpm 实际在哪:如果它不在上面这些目录里,把该目录设为 PNPM_HOME 后重启 dsh(\`export PNPM_HOME=<那个目录>\`),或者干脆从一个能直接运行 pnpm 的终端里启动 dsh。注意必须重启——正在运行的进程读不到新设的环境变量 / pnpm is installed but this dsh process still cannot start it: every step succeeded, the binary just landed somewhere this process does not look. Searched: ${searched}. Run \`${locate}\` in a terminal to see where pnpm actually is; if that directory is not in the list above, set PNPM_HOME to it and restart dsh (\`export PNPM_HOME=<that directory>\`), or simply start dsh from a terminal where \`pnpm\` already runs. The restart matters — a running process cannot see a newly set variable`;
|
package/lib/types/dsh-cli.d.ts
CHANGED
|
@@ -219,6 +219,11 @@ export declare function killChild(child: ChildProcess): void;
|
|
|
219
219
|
* @returns true when there was one to cancel.
|
|
220
220
|
*/
|
|
221
221
|
export declare function cancelActive(): boolean;
|
|
222
|
+
/** Why `pnpm --version` last failed, or null when it has not failed. */
|
|
223
|
+
export declare function lastPnpmProbeFailure(): {
|
|
224
|
+
kind: 'missing' | 'failed';
|
|
225
|
+
output: string;
|
|
226
|
+
} | null;
|
|
222
227
|
/** Probe `pnpm --version` on PATH. */
|
|
223
228
|
export declare function probePnpm(): Promise<boolean>;
|
|
224
229
|
/**
|
|
@@ -253,7 +258,10 @@ export declare function toolOnPath(name: string): boolean;
|
|
|
253
258
|
* a GUI launch with no Node on PATH at all).
|
|
254
259
|
* @returns a bilingual, actionable hint, or undefined when unrecognized.
|
|
255
260
|
*/
|
|
256
|
-
export declare function provisionHint(corepackOutput: string, npmOutput: string, npmFound?: boolean
|
|
261
|
+
export declare function provisionHint(corepackOutput: string, npmOutput: string, npmFound?: boolean, probeFailure?: {
|
|
262
|
+
kind: 'missing' | 'failed';
|
|
263
|
+
output: string;
|
|
264
|
+
} | null): string | undefined;
|
|
257
265
|
/** Live progress of the running plugin command, for the status route. */
|
|
258
266
|
export interface InstallProgress {
|
|
259
267
|
active: boolean;
|
package/lib/types/verify.d.ts
CHANGED
|
@@ -33,11 +33,6 @@ export interface ActivationResult {
|
|
|
33
33
|
/** True when the package is live in the running composition. */
|
|
34
34
|
hot: boolean;
|
|
35
35
|
}
|
|
36
|
-
/**
|
|
37
|
-
* Verify the activation state of one installed package.
|
|
38
|
-
* @param live - names live in the current composition; defaults to the
|
|
39
|
-
* market's hot-mount table (injectable for tests).
|
|
40
|
-
*/
|
|
41
36
|
export declare function verifyActivation(profile: string, name: string, live?: ReadonlySet<string>, explicitDir?: string, isDisabled?: boolean): ActivationResult;
|
|
42
37
|
/**
|
|
43
38
|
* Correct a post-UPDATE verdict for a plugin that was already running.
|
package/lib/verify.js
CHANGED
|
@@ -27,6 +27,7 @@ import { readFileSync } from 'node:fs';
|
|
|
27
27
|
import { Script } from 'node:vm';
|
|
28
28
|
import { join } from 'node:path';
|
|
29
29
|
import { listHotMounts, parseSimplePatch } from './hot.js';
|
|
30
|
+
import { userPatchPackageReferences } from './patch.js';
|
|
30
31
|
import { bundlePatchInsertedIds, hasDshManifest, hasLoadableEntry, profileDir, readInstalled } from './profile.js';
|
|
31
32
|
/** The profile manifest's `dsh.profile.bundles` — what the CLI reconciled. */
|
|
32
33
|
function readBundles(profile, explicitDir) {
|
|
@@ -103,6 +104,24 @@ function patchTextOf(profile, name, explicitDir) {
|
|
|
103
104
|
* @param live - names live in the current composition; defaults to the
|
|
104
105
|
* market's hot-mount table (injectable for tests).
|
|
105
106
|
*/
|
|
107
|
+
/**
|
|
108
|
+
* Whether the profile's OWN `cordis.patch.yml` inserts this package by name.
|
|
109
|
+
*
|
|
110
|
+
* A third evidence source beside the loader inventory and the package's own
|
|
111
|
+
* manifest, and the one that was missing (#165): a plugin the user wired up
|
|
112
|
+
* themselves declares nothing, is not hot-mounted until the next boot, and so
|
|
113
|
+
* fell through to `broken` — the market told them the install had failed
|
|
114
|
+
* verification while the plugin was, in fact, working.
|
|
115
|
+
*
|
|
116
|
+
* Read with the same parser the uninstall guard uses. Unreadable returns
|
|
117
|
+
* null, which is treated here as NO evidence rather than as evidence: this
|
|
118
|
+
* only ever upgrades a verdict away from `broken`, so being unsure has to
|
|
119
|
+
* leave the stricter answer standing.
|
|
120
|
+
*/
|
|
121
|
+
function patchLoads(activeProfileDir, name) {
|
|
122
|
+
const references = userPatchPackageReferences(join(activeProfileDir, 'cordis.patch.yml'), name);
|
|
123
|
+
return references !== null && references.length > 0;
|
|
124
|
+
}
|
|
106
125
|
export function verifyActivation(profile, name, live = new Set(listHotMounts()), explicitDir, isDisabled = false) {
|
|
107
126
|
const activeProfileDir = profileDir(profile, explicitDir);
|
|
108
127
|
const bundles = readBundles(profile, activeProfileDir);
|
|
@@ -142,12 +161,20 @@ export function verifyActivation(profile, name, live = new Set(listHotMounts()),
|
|
|
142
161
|
// Not live and no dsh surface: for a package the profile lists as a
|
|
143
162
|
// BUNDLE this is a real defect; for a plain dependency it is normal —
|
|
144
163
|
// most dependencies are libraries, not plugins (#135).
|
|
145
|
-
|
|
146
|
-
|
|
164
|
+
if (inBundles && !patchLoads(activeProfileDir, name)) {
|
|
165
|
+
return {
|
|
147
166
|
state: 'broken',
|
|
148
167
|
reasons: ['已列入 profile bundle 层但未声明 dsh 元数据,加载会失败 / listed in the profile bundle layer but declares no dsh metadata — loading it fails'],
|
|
149
168
|
bundle: true,
|
|
150
169
|
hot: false,
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
return inBundles
|
|
173
|
+
? {
|
|
174
|
+
state: 'restart',
|
|
175
|
+
reasons: ['由你自己的 cordis.patch.yml 按名加载,重启后生效 / loaded by name from your own cordis.patch.yml — live after a restart'],
|
|
176
|
+
bundle: true,
|
|
177
|
+
hot: false,
|
|
151
178
|
}
|
|
152
179
|
: {
|
|
153
180
|
state: 'inert',
|
|
@@ -159,7 +186,7 @@ export function verifyActivation(profile, name, live = new Set(listHotMounts()),
|
|
|
159
186
|
// Carrier bundles (#103) ship no entry of their own — what they mount is
|
|
160
187
|
// the point — so judge by "is anything loadable", not by this package's
|
|
161
188
|
// own artifact.
|
|
162
|
-
if (!loaderLive && !hasLoadableEntry(activeProfileDir, name)) {
|
|
189
|
+
if (!loaderLive && !hasLoadableEntry(activeProfileDir, name) && !patchLoads(activeProfileDir, name)) {
|
|
163
190
|
return {
|
|
164
191
|
state: 'broken',
|
|
165
192
|
reasons: [
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dshmarket",
|
|
3
3
|
"description": "Visual plugin market inside DeepSeek Harness — browse, search, and one-click install community plugins. · DSH 可视化插件市场:逛一逛,点一下,装好。",
|
|
4
|
-
"version": "1.29.
|
|
4
|
+
"version": "1.29.2",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
7
7
|
"types": "lib/types/index.d.ts",
|
package/src/dsh-cli.ts
CHANGED
|
@@ -526,14 +526,42 @@ export function cancelActive(): boolean {
|
|
|
526
526
|
/** Whether `pnpm` resolves on PATH; success is cached, absence is re-probed. */
|
|
527
527
|
let pnpmReady = false
|
|
528
528
|
|
|
529
|
+
/**
|
|
530
|
+
* Why the last probe said no.
|
|
531
|
+
*
|
|
532
|
+
* `missing` and `failed` are different problems with different fixes, and
|
|
533
|
+
* collapsing both into `false` made the market give one answer to both: it
|
|
534
|
+
* told a user whose pnpm ran perfectly from their shell to go set PNPM_HOME
|
|
535
|
+
* (#228). A binary that IS on the path and exits non-zero — a corepack shim
|
|
536
|
+
* that cannot reach the network to fetch pnpm itself is the common one —
|
|
537
|
+
* needs its own output shown, not a path to fix that is already right.
|
|
538
|
+
*/
|
|
539
|
+
let pnpmProbeFailure: { kind: 'missing' | 'failed'; output: string } | null = null
|
|
540
|
+
|
|
541
|
+
/** Why `pnpm --version` last failed, or null when it has not failed. */
|
|
542
|
+
export function lastPnpmProbeFailure(): { kind: 'missing' | 'failed'; output: string } | null {
|
|
543
|
+
return pnpmProbeFailure
|
|
544
|
+
}
|
|
545
|
+
|
|
529
546
|
/** Probe `pnpm --version` on PATH. */
|
|
530
547
|
export function probePnpm(): Promise<boolean> {
|
|
531
548
|
if (pnpmReady) return Promise.resolve(true)
|
|
532
549
|
return new Promise((resolvePromise) => {
|
|
533
|
-
|
|
534
|
-
|
|
550
|
+
// Piped, not ignored: the output of a pnpm that exists but will not run
|
|
551
|
+
// IS the explanation, and throwing it away is what left #228 with a
|
|
552
|
+
// failure nobody could act on.
|
|
553
|
+
const child = spawnShim('pnpm', ['--version'], { stdio: ['ignore', 'pipe', 'pipe'], viaShell: winCmdShim, env: spawnEnv() })
|
|
554
|
+
let output = ''
|
|
555
|
+
const collect = (chunk: Buffer): void => { output = (output + chunk.toString()).slice(-2000) }
|
|
556
|
+
child.stdout?.on('data', collect)
|
|
557
|
+
child.stderr?.on('data', collect)
|
|
558
|
+
child.on('error', (error) => {
|
|
559
|
+
pnpmProbeFailure = { kind: 'missing', output: error.message }
|
|
560
|
+
resolvePromise(false)
|
|
561
|
+
})
|
|
535
562
|
child.on('close', (code) => {
|
|
536
563
|
pnpmReady = code === 0
|
|
564
|
+
pnpmProbeFailure = pnpmReady ? null : { kind: 'failed', output: output.trim() }
|
|
537
565
|
resolvePromise(pnpmReady)
|
|
538
566
|
})
|
|
539
567
|
})
|
|
@@ -585,7 +613,7 @@ export async function provisionPnpm(): Promise<{ ok: boolean; hint?: string }> {
|
|
|
585
613
|
}
|
|
586
614
|
const npmFound = toolOnPath('npm')
|
|
587
615
|
if (!npmFound) logEvent('warn', 'setup-pnpm', `npm is not on any searched path (node lives in ${nodeBinDir})`)
|
|
588
|
-
return { ok: false, hint: provisionHint(corepack.output, npm.output, npmFound) }
|
|
616
|
+
return { ok: false, hint: provisionHint(corepack.output, npm.output, npmFound, lastPnpmProbeFailure()) }
|
|
589
617
|
}
|
|
590
618
|
|
|
591
619
|
/** Executable suffixes a bare command name can carry on this platform. */
|
|
@@ -626,7 +654,12 @@ export function toolOnPath(name: string): boolean {
|
|
|
626
654
|
* a GUI launch with no Node on PATH at all).
|
|
627
655
|
* @returns a bilingual, actionable hint, or undefined when unrecognized.
|
|
628
656
|
*/
|
|
629
|
-
export function provisionHint(
|
|
657
|
+
export function provisionHint(
|
|
658
|
+
corepackOutput: string,
|
|
659
|
+
npmOutput: string,
|
|
660
|
+
npmFound = true,
|
|
661
|
+
probeFailure: { kind: 'missing' | 'failed'; output: string } | null = null,
|
|
662
|
+
): string | undefined {
|
|
630
663
|
// Node itself unreachable: pointing the user back at this same button
|
|
631
664
|
// would be a dead end (#32). `npmFound` answers this from disk, so it
|
|
632
665
|
// holds on a Windows console that reports the same thing in a codepage we
|
|
@@ -661,6 +694,13 @@ export function provisionHint(corepackOutput: string, npmOutput: string, npmFoun
|
|
|
661
694
|
// they can see succeeded, and their complaint was exactly that — "又不告诉
|
|
662
695
|
// 我怎么手动配置". Whatever the cause, the actionable question is the same
|
|
663
696
|
// one, so ask it: where is pnpm, and is that anywhere this process looks?
|
|
697
|
+
// pnpm IS on the path and exits non-zero. Telling this user to fix PNPM_HOME
|
|
698
|
+
// would be advice for the opposite problem — theirs runs fine from a shell,
|
|
699
|
+
// which is exactly what #228 reported. Its own output is the explanation.
|
|
700
|
+
if (probeFailure?.kind === 'failed') {
|
|
701
|
+
const detail = probeFailure.output === '' ? '' : `\n\n${probeFailure.output}`
|
|
702
|
+
return `找到 pnpm 了,但运行 \`pnpm --version\` 失败——所以问题不在路径上,设 PNPM_HOME 没有用。最常见的原因是 corepack 的 shim 需要联网下载 pnpm 本体,而这台机器下不到。请在终端执行一次 \`pnpm --version\`:如果同样失败,按它的提示修(受限网络可用 \`brew install pnpm\` 或 \`npm i -g pnpm --registry <你的镜像>\` 装一个完整的 pnpm,绕开 shim);如果在终端里正常,说明 dsh 进程的环境和你的终端不同,请从该终端启动 dsh。pnpm 的原始输出:${detail} / pnpm was found, but \`pnpm --version\` fails — so this is not a path problem and PNPM_HOME will not help. The usual cause is a corepack shim that has to download pnpm itself and cannot reach the network. Run \`pnpm --version\` in a terminal: if it fails the same way, follow what it says (on a restricted network install a real pnpm with \`brew install pnpm\` or \`npm i -g pnpm --registry <your mirror>\` to bypass the shim); if it works there, the dsh process has a different environment than your shell — start dsh from that terminal. pnpm's own output:${detail}`
|
|
703
|
+
}
|
|
664
704
|
const searched = toolSearchDirs().join(process.platform === 'win32' ? ' ; ' : ' : ')
|
|
665
705
|
const locate = process.platform === 'win32' ? 'where pnpm' : 'which pnpm'
|
|
666
706
|
return `pnpm 装好了,但这个 dsh 进程仍然启动不了它——安装步骤都成功,只是装到的位置不在它搜索的范围内。已找过:${searched}。请在终端执行 \`${locate}\` 看 pnpm 实际在哪:如果它不在上面这些目录里,把该目录设为 PNPM_HOME 后重启 dsh(\`export PNPM_HOME=<那个目录>\`),或者干脆从一个能直接运行 pnpm 的终端里启动 dsh。注意必须重启——正在运行的进程读不到新设的环境变量 / pnpm is installed but this dsh process still cannot start it: every step succeeded, the binary just landed somewhere this process does not look. Searched: ${searched}. Run \`${locate}\` in a terminal to see where pnpm actually is; if that directory is not in the list above, set PNPM_HOME to it and restart dsh (\`export PNPM_HOME=<that directory>\`), or simply start dsh from a terminal where \`pnpm\` already runs. The restart matters — a running process cannot see a newly set variable`
|
package/src/verify.ts
CHANGED
|
@@ -28,6 +28,7 @@ import { readFileSync } from 'node:fs'
|
|
|
28
28
|
import { Script } from 'node:vm'
|
|
29
29
|
import { join } from 'node:path'
|
|
30
30
|
import { listHotMounts, parseSimplePatch } from './hot.ts'
|
|
31
|
+
import { userPatchPackageReferences } from './patch.ts'
|
|
31
32
|
import { bundlePatchInsertedIds, hasDshManifest, hasLoadableEntry, profileDir, readInstalled } from './profile.ts'
|
|
32
33
|
|
|
33
34
|
export type ActivationState = 'live' | 'restart' | 'inert' | 'broken' | 'missing' | 'disabled'
|
|
@@ -124,6 +125,25 @@ function patchTextOf(profile: string, name: string, explicitDir?: string): strin
|
|
|
124
125
|
* @param live - names live in the current composition; defaults to the
|
|
125
126
|
* market's hot-mount table (injectable for tests).
|
|
126
127
|
*/
|
|
128
|
+
/**
|
|
129
|
+
* Whether the profile's OWN `cordis.patch.yml` inserts this package by name.
|
|
130
|
+
*
|
|
131
|
+
* A third evidence source beside the loader inventory and the package's own
|
|
132
|
+
* manifest, and the one that was missing (#165): a plugin the user wired up
|
|
133
|
+
* themselves declares nothing, is not hot-mounted until the next boot, and so
|
|
134
|
+
* fell through to `broken` — the market told them the install had failed
|
|
135
|
+
* verification while the plugin was, in fact, working.
|
|
136
|
+
*
|
|
137
|
+
* Read with the same parser the uninstall guard uses. Unreadable returns
|
|
138
|
+
* null, which is treated here as NO evidence rather than as evidence: this
|
|
139
|
+
* only ever upgrades a verdict away from `broken`, so being unsure has to
|
|
140
|
+
* leave the stricter answer standing.
|
|
141
|
+
*/
|
|
142
|
+
function patchLoads(activeProfileDir: string, name: string): boolean {
|
|
143
|
+
const references = userPatchPackageReferences(join(activeProfileDir, 'cordis.patch.yml'), name)
|
|
144
|
+
return references !== null && references.length > 0
|
|
145
|
+
}
|
|
146
|
+
|
|
127
147
|
export function verifyActivation(
|
|
128
148
|
profile: string,
|
|
129
149
|
name: string,
|
|
@@ -172,10 +192,18 @@ export function verifyActivation(
|
|
|
172
192
|
// Not live and no dsh surface: for a package the profile lists as a
|
|
173
193
|
// BUNDLE this is a real defect; for a plain dependency it is normal —
|
|
174
194
|
// most dependencies are libraries, not plugins (#135).
|
|
195
|
+
if (inBundles && !patchLoads(activeProfileDir, name)) {
|
|
196
|
+
return {
|
|
197
|
+
state: 'broken',
|
|
198
|
+
reasons: ['已列入 profile bundle 层但未声明 dsh 元数据,加载会失败 / listed in the profile bundle layer but declares no dsh metadata — loading it fails'],
|
|
199
|
+
bundle: true,
|
|
200
|
+
hot: false,
|
|
201
|
+
}
|
|
202
|
+
}
|
|
175
203
|
return inBundles
|
|
176
204
|
? {
|
|
177
|
-
state: '
|
|
178
|
-
reasons: ['
|
|
205
|
+
state: 'restart',
|
|
206
|
+
reasons: ['由你自己的 cordis.patch.yml 按名加载,重启后生效 / loaded by name from your own cordis.patch.yml — live after a restart'],
|
|
179
207
|
bundle: true,
|
|
180
208
|
hot: false,
|
|
181
209
|
}
|
|
@@ -189,7 +217,7 @@ export function verifyActivation(
|
|
|
189
217
|
// Carrier bundles (#103) ship no entry of their own — what they mount is
|
|
190
218
|
// the point — so judge by "is anything loadable", not by this package's
|
|
191
219
|
// own artifact.
|
|
192
|
-
if (!loaderLive && !hasLoadableEntry(activeProfileDir, name)) {
|
|
220
|
+
if (!loaderLive && !hasLoadableEntry(activeProfileDir, name) && !patchLoads(activeProfileDir, name)) {
|
|
193
221
|
return {
|
|
194
222
|
state: 'broken',
|
|
195
223
|
reasons: [
|