@@ -744,6 +744,14 @@ type ValidatedPackagePlugin = {
744744 peerDependencies : Record < string , string > ;
745745} ;
746746
747+ function resolveOpenClawHostLinkDependencies ( manifest : PackageManifest ) : Record < string , string > {
748+ const spec =
749+ manifest . peerDependencies ?. openclaw ??
750+ manifest . dependencies ?. openclaw ??
751+ manifest . optionalDependencies ?. openclaw ;
752+ return spec ? { openclaw : spec } : { } ;
753+ }
754+
747755async function validatePackagePluginInstallSource ( params : {
748756 runtime : Awaited < ReturnType < typeof loadPluginInstallRuntime > > ;
749757 packageDir : string ;
@@ -901,7 +909,7 @@ async function validatePackagePluginInstallSource(params: {
901909 version : typeof manifest . version === "string" ? manifest . version : undefined ,
902910 extensions,
903911 hasRuntimeDependencies : hasPackageRuntimeDependencies ( manifest ) ,
904- peerDependencies : manifest . peerDependencies ?? { } ,
912+ peerDependencies : resolveOpenClawHostLinkDependencies ( manifest ) ,
905913 } ,
906914 } ;
907915}
@@ -913,7 +921,15 @@ async function scanAndLinkInstalledPackage(params: {
913921 pluginId : string ;
914922 peerDependencies : Record < string , string > ;
915923 logger : PluginInstallLogger ;
924+ linkOpenClawBeforeScan ?: boolean ;
916925} ) : Promise < Extract < InstallPluginResult , { ok : false } > | null > {
926+ if ( params . linkOpenClawBeforeScan ) {
927+ await linkOpenClawPeerDependencies ( {
928+ installedDir : params . installedDir ,
929+ peerDependencies : params . peerDependencies ,
930+ logger : params . logger ,
931+ } ) ;
932+ }
917933 const scanResult = await runInstallSourceScan ( {
918934 subject : `Plugin "${ params . pluginId } "` ,
919935 scan : async ( ) =>
@@ -929,11 +945,13 @@ async function scanAndLinkInstalledPackage(params: {
929945 if ( scanResult ) {
930946 return scanResult ;
931947 }
932- await linkOpenClawPeerDependencies ( {
933- installedDir : params . installedDir ,
934- peerDependencies : params . peerDependencies ,
935- logger : params . logger ,
936- } ) ;
948+ if ( ! params . linkOpenClawBeforeScan ) {
949+ await linkOpenClawPeerDependencies ( {
950+ installedDir : params . installedDir ,
951+ peerDependencies : params . peerDependencies ,
952+ logger : params . logger ,
953+ } ) ;
954+ }
937955 return null ;
938956}
939957
@@ -966,6 +984,7 @@ export async function installPluginFromInstalledPackageDir(
966984 pluginId : validated . plugin . pluginId ,
967985 peerDependencies : validated . plugin . peerDependencies ,
968986 logger,
987+ linkOpenClawBeforeScan : params . dependencyScanRootDir !== undefined ,
969988 } ) ;
970989 if ( postInstallError ) {
971990 return postInstallError ;
@@ -1216,6 +1235,40 @@ export async function installPluginFromFile(params: {
12161235 return buildFileInstallResult ( pluginId , preparedTarget . targetPath ) ;
12171236}
12181237
1238+ async function repairManagedNpmRootOpenClawPeerForInstall ( params : {
1239+ logger : PluginInstallLogger ;
1240+ npmRoot : string ;
1241+ phase : "before npm install" | "after npm install" ;
1242+ timeoutMs : number ;
1243+ trustedManagedNpmRoot ?: boolean ;
1244+ } ) : Promise < void > {
1245+ const repairedOpenClawPeer = await repairManagedNpmRootOpenClawPeer ( {
1246+ defaultNpmRoot : resolveDefaultPluginNpmDir ( ) ,
1247+ env : createSafeNpmInstallEnv ( process . env , { packageLock : true , quiet : true } ) ,
1248+ hostPackageRoot : resolveOpenClawPackageRootSync ( {
1249+ argv1 : process . argv [ 1 ] ,
1250+ moduleUrl : import . meta. url ,
1251+ cwd : process . cwd ( ) ,
1252+ } ) ,
1253+ npmRoot : params . npmRoot ,
1254+ runCommand : runCommandWithTimeout ,
1255+ timeoutMs : params . timeoutMs ,
1256+ trustedByInstallRecord : params . trustedManagedNpmRoot ,
1257+ } ) ;
1258+ for ( const warning of repairedOpenClawPeer . warnings ) {
1259+ params . logger . warn ?.( warning ) ;
1260+ }
1261+ if ( repairedOpenClawPeer . status === "repaired" ) {
1262+ params . logger . info ?.(
1263+ `Repaired stale openclaw peer dependency in ${ params . npmRoot } ${ params . phase } ` ,
1264+ ) ;
1265+ } else if ( repairedOpenClawPeer . status === "skipped" ) {
1266+ params . logger . warn ?.(
1267+ `Skipped stale openclaw peer repair in ${ params . npmRoot } ${ params . phase } : ${ repairedOpenClawPeer . reason ?? "unproven managed npm root" } ` ,
1268+ ) ;
1269+ }
1270+ }
1271+
12191272export async function installPluginFromNpmSpec (
12201273 params : InstallSafetyOverrides & {
12211274 spec : string ;
@@ -1340,29 +1393,13 @@ export async function installPluginFromNpmSpec(
13401393
13411394 logger . info ?.( `Installing ${ spec } into ${ npmRoot } …` ) ;
13421395 if ( parsedSpec . name !== "openclaw" ) {
1343- const repairedOpenClawPeer = await repairManagedNpmRootOpenClawPeer ( {
1344- defaultNpmRoot : resolveDefaultPluginNpmDir ( ) ,
1345- env : createSafeNpmInstallEnv ( process . env , { packageLock : true , quiet : true } ) ,
1346- hostPackageRoot : resolveOpenClawPackageRootSync ( {
1347- argv1 : process . argv [ 1 ] ,
1348- moduleUrl : import . meta. url ,
1349- cwd : process . cwd ( ) ,
1350- } ) ,
1396+ await repairManagedNpmRootOpenClawPeerForInstall ( {
1397+ logger,
13511398 npmRoot,
1352- runCommand : runCommandWithTimeout ,
1399+ phase : "before npm install" ,
13531400 timeoutMs,
1354- trustedByInstallRecord : params . trustedManagedNpmRoot ,
1401+ trustedManagedNpmRoot : params . trustedManagedNpmRoot ,
13551402 } ) ;
1356- for ( const warning of repairedOpenClawPeer . warnings ) {
1357- logger . warn ?.( warning ) ;
1358- }
1359- if ( repairedOpenClawPeer . status === "repaired" ) {
1360- logger . info ?.( `Repaired stale openclaw peer dependency in ${ npmRoot } ` ) ;
1361- } else if ( repairedOpenClawPeer . status === "skipped" ) {
1362- logger . warn ?.(
1363- `Skipped stale openclaw peer repair in ${ npmRoot } : ${ repairedOpenClawPeer . reason ?? "unproven managed npm root" } ` ,
1364- ) ;
1365- }
13661403 }
13671404 await upsertManagedNpmRootDependency ( {
13681405 npmRoot,
@@ -1377,6 +1414,7 @@ export async function installPluginFromNpmSpec(
13771414 "npm" ,
13781415 ...createSafeNpmInstallArgs ( {
13791416 omitDev : true ,
1417+ omitPeer : true ,
13801418 loglevel : "error" ,
13811419 noAudit : true ,
13821420 noFund : true ,
@@ -1401,6 +1439,16 @@ export async function installPluginFromNpmSpec(
14011439 } ;
14021440 }
14031441
1442+ if ( parsedSpec . name !== "openclaw" ) {
1443+ await repairManagedNpmRootOpenClawPeerForInstall ( {
1444+ logger,
1445+ npmRoot,
1446+ phase : "after npm install" ,
1447+ timeoutMs,
1448+ trustedManagedNpmRoot : params . trustedManagedNpmRoot ,
1449+ } ) ;
1450+ }
1451+
14041452 let installedDependency : ManagedNpmRootInstalledDependency | null ;
14051453 try {
14061454 installedDependency = await readManagedNpmRootInstalledDependency ( {
0 commit comments