























@@ -40,6 +40,7 @@ import {
4040createPluginInstallLogger,
4141decidePreferredClawHubFallback,
4242formatPluginInstallWithHookFallbackError,
43+parseNpmPrefixSpec,
4344} from "./plugins-command-helpers.js";
4445import { persistHookPackInstall, persistPluginInstall } from "./plugins-install-persist.js";
4546import type { ConfigSnapshotForInstallPersist } from "./plugins-install-persist.js";
@@ -263,6 +264,74 @@ async function tryInstallHookPackFromNpmSpec(params: {
263264return { ok: true };
264265}
265266267+async function tryInstallPluginOrHookPackFromNpmSpec(params: {
268+snapshot: ConfigSnapshotForInstallPersist;
269+installMode: "install" | "update";
270+spec: string;
271+pin?: boolean;
272+safetyOverrides: InstallSafetyOverrides;
273+allowBundledFallback: boolean;
274+}): Promise<{ ok: true } | { ok: false }> {
275+const result = await installPluginFromNpmSpec({
276+ ...params.safetyOverrides,
277+mode: params.installMode,
278+spec: params.spec,
279+logger: createPluginInstallLogger(),
280+});
281+if (!result.ok) {
282+if (isTerminalPluginInstallSecurityFailure(result.code)) {
283+defaultRuntime.error(result.error);
284+return { ok: false };
285+}
286+if (params.allowBundledFallback) {
287+const bundledFallbackPlan = resolveBundledInstallPlanForNpmFailure({
288+rawSpec: params.spec,
289+code: result.code,
290+findBundledSource: (lookup) => findBundledPluginSource({ lookup }),
291+});
292+if (bundledFallbackPlan) {
293+await installBundledPluginSource({
294+snapshot: params.snapshot,
295+rawSpec: params.spec,
296+bundledSource: bundledFallbackPlan.bundledSource,
297+warning: bundledFallbackPlan.warning,
298+});
299+return { ok: true };
300+}
301+}
302+const hookFallback = await tryInstallHookPackFromNpmSpec({
303+snapshot: params.snapshot,
304+installMode: params.installMode,
305+spec: params.spec,
306+pin: params.pin,
307+});
308+if (hookFallback.ok) {
309+return { ok: true };
310+}
311+defaultRuntime.error(
312+formatPluginInstallWithHookFallbackError(result.error, hookFallback.error),
313+);
314+return { ok: false };
315+}
316+317+clearPluginManifestRegistryCache();
318+const installRecord = resolvePinnedNpmInstallRecordForCli(
319+params.spec,
320+Boolean(params.pin),
321+result.targetDir,
322+result.version,
323+result.npmResolution,
324+defaultRuntime.log,
325+theme.warn,
326+);
327+await persistPluginInstall({
328+snapshot: params.snapshot,
329+pluginId: result.pluginId,
330+install: installRecord,
331+});
332+return { ok: true };
333+}
334+266335function isTerminalPluginInstallSecurityFailure(code?: string): boolean {
267336return (
268337code === PLUGIN_INSTALL_ERROR_CODE.SECURITY_SCAN_BLOCKED ||
@@ -534,6 +603,26 @@ export async function runPluginInstallCommand(params: {
534603return defaultRuntime.exit(1);
535604}
536605606+const npmPrefixSpec = parseNpmPrefixSpec(raw);
607+if (npmPrefixSpec !== null) {
608+if (!npmPrefixSpec) {
609+defaultRuntime.error("unsupported npm: spec: missing package");
610+return defaultRuntime.exit(1);
611+}
612+const npmPrefixResult = await tryInstallPluginOrHookPackFromNpmSpec({
613+ snapshot,
614+ installMode,
615+spec: npmPrefixSpec,
616+pin: opts.pin,
617+ safetyOverrides,
618+allowBundledFallback: false,
619+});
620+if (!npmPrefixResult.ok) {
621+return defaultRuntime.exit(1);
622+}
623+return;
624+}
625+537626if (
538627looksLikeLocalInstallSpec(raw, [
539628".ts",
@@ -637,60 +726,15 @@ export async function runPluginInstallCommand(params: {
637726}
638727}
639728640-const result = await installPluginFromNpmSpec({
641-...safetyOverrides,
642-mode: installMode,
729+const npmResult = await tryInstallPluginOrHookPackFromNpmSpec({
730+snapshot,
731+ installMode,
643732spec: raw,
644-logger: createPluginInstallLogger(),
733+pin: opts.pin,
734+ safetyOverrides,
735+allowBundledFallback: true,
645736});
646-if (!result.ok) {
647-if (isTerminalPluginInstallSecurityFailure(result.code)) {
648-defaultRuntime.error(result.error);
649-return defaultRuntime.exit(1);
650-}
651-const bundledFallbackPlan = resolveBundledInstallPlanForNpmFailure({
652-rawSpec: raw,
653-code: result.code,
654-findBundledSource: (lookup) => findBundledPluginSource({ lookup }),
655-});
656-if (!bundledFallbackPlan) {
657-const hookFallback = await tryInstallHookPackFromNpmSpec({
658- snapshot,
659- installMode,
660-spec: raw,
661-pin: opts.pin,
662-});
663-if (hookFallback.ok) {
664-return;
665-}
666-defaultRuntime.error(
667-formatPluginInstallWithHookFallbackError(result.error, hookFallback.error),
668-);
669-return defaultRuntime.exit(1);
670-}
671-672-await installBundledPluginSource({
673- snapshot,
674-rawSpec: raw,
675-bundledSource: bundledFallbackPlan.bundledSource,
676-warning: bundledFallbackPlan.warning,
677-});
678-return;
737+if (!npmResult.ok) {
738+return defaultRuntime.exit(1);
679739}
680-681-clearPluginManifestRegistryCache();
682-const installRecord = resolvePinnedNpmInstallRecordForCli(
683-raw,
684-Boolean(opts.pin),
685-result.targetDir,
686-result.version,
687-result.npmResolution,
688-defaultRuntime.log,
689-theme.warn,
690-);
691-await persistPluginInstall({
692- snapshot,
693-pluginId: result.pluginId,
694-install: installRecord,
695-});
696740}
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。