opkg 0.9.2 → 0.9.4
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/.claude/agents/code-reviewer.md +171 -0
- package/.claude/commands/commit-push-pr.md +20 -0
- package/.claude/commands/{specs/read.md → read-specs.md} +1 -1
- package/.claude/commands/{specs/update.md → update-specs.md} +4 -0
- package/.claude/skills/code-review-excellence/SKILL.md +538 -0
- package/README.md +2 -2
- package/package.json +3 -1
- package/packages/cli/dist/add-IJAPFHIX.js +624 -0
- package/packages/cli/dist/add-IJAPFHIX.js.map +7 -0
- package/packages/cli/dist/add-LLUNFLJI.js +624 -0
- package/packages/cli/dist/add-LLUNFLJI.js.map +7 -0
- package/packages/cli/dist/add-U44SL3OR.js +624 -0
- package/packages/cli/dist/add-U44SL3OR.js.map +7 -0
- package/packages/cli/dist/chunk-23VBP5L6.js +371 -0
- package/packages/cli/dist/chunk-23VBP5L6.js.map +7 -0
- package/packages/cli/dist/chunk-2SVHLF5C.js +1419 -0
- package/packages/cli/dist/chunk-2SVHLF5C.js.map +7 -0
- package/packages/cli/dist/chunk-37256POU.js +99 -0
- package/packages/cli/dist/chunk-37256POU.js.map +7 -0
- package/packages/cli/dist/chunk-3PZRVA6O.js +196 -0
- package/packages/cli/dist/chunk-3PZRVA6O.js.map +7 -0
- package/packages/cli/dist/chunk-427DCURL.js +155 -0
- package/packages/cli/dist/chunk-427DCURL.js.map +7 -0
- package/packages/cli/dist/chunk-4B5HJLP2.js +48 -0
- package/packages/cli/dist/chunk-4B5HJLP2.js.map +7 -0
- package/packages/cli/dist/chunk-4OWT3YEG.js +413 -0
- package/packages/cli/dist/chunk-4OWT3YEG.js.map +7 -0
- package/packages/cli/dist/chunk-4RIBTBXI.js +568 -0
- package/packages/cli/dist/chunk-4RIBTBXI.js.map +7 -0
- package/packages/cli/dist/chunk-4X2EJHJN.js +63 -0
- package/packages/cli/dist/chunk-4X2EJHJN.js.map +7 -0
- package/packages/cli/dist/chunk-6CYW66HD.js +1136 -0
- package/packages/cli/dist/chunk-6CYW66HD.js.map +7 -0
- package/packages/cli/dist/chunk-6DITYAFA.js +172 -0
- package/packages/cli/dist/chunk-6DITYAFA.js.map +7 -0
- package/packages/cli/dist/chunk-7KEAKEVZ.js +568 -0
- package/packages/cli/dist/chunk-7KEAKEVZ.js.map +7 -0
- package/packages/cli/dist/chunk-ABFUD25D.js +61 -0
- package/packages/cli/dist/chunk-ABFUD25D.js.map +7 -0
- package/packages/cli/dist/chunk-AR7GJCG6.js +274 -0
- package/packages/cli/dist/chunk-AR7GJCG6.js.map +7 -0
- package/packages/cli/dist/chunk-AYTGQCXH.js +86 -0
- package/packages/cli/dist/chunk-AYTGQCXH.js.map +7 -0
- package/packages/cli/dist/chunk-BROJ6OUT.js +631 -0
- package/packages/cli/dist/chunk-BROJ6OUT.js.map +7 -0
- package/packages/cli/dist/chunk-BVVSU7QD.js +23 -0
- package/packages/cli/dist/chunk-BVVSU7QD.js.map +7 -0
- package/packages/cli/dist/chunk-C6FY55UP.js +108 -0
- package/packages/cli/dist/chunk-C6FY55UP.js.map +7 -0
- package/packages/cli/dist/chunk-CVA64SXK.js +1136 -0
- package/packages/cli/dist/chunk-CVA64SXK.js.map +7 -0
- package/packages/cli/dist/chunk-D3O7LY2Q.js +1151 -0
- package/packages/cli/dist/chunk-D3O7LY2Q.js.map +7 -0
- package/packages/cli/dist/chunk-D6LEPODL.js +413 -0
- package/packages/cli/dist/chunk-D6LEPODL.js.map +7 -0
- package/packages/cli/dist/chunk-DEC24S7E.js +186 -0
- package/packages/cli/dist/chunk-DEC24S7E.js.map +7 -0
- package/packages/cli/dist/chunk-FMVVJH5M.js +371 -0
- package/packages/cli/dist/chunk-FMVVJH5M.js.map +7 -0
- package/packages/cli/dist/chunk-GDVFS3YP.js +130 -0
- package/packages/cli/dist/chunk-GDVFS3YP.js.map +7 -0
- package/packages/cli/dist/chunk-GEP2G5HF.js +31 -0
- package/packages/cli/dist/chunk-GEP2G5HF.js.map +7 -0
- package/packages/cli/dist/chunk-GSWHZBT2.js +62 -0
- package/packages/cli/dist/chunk-GSWHZBT2.js.map +7 -0
- package/packages/cli/dist/chunk-HTYHJA3B.js +61 -0
- package/packages/cli/dist/chunk-HTYHJA3B.js.map +7 -0
- package/packages/cli/dist/chunk-HYKYECAE.js +222 -0
- package/packages/cli/dist/chunk-HYKYECAE.js.map +7 -0
- package/packages/cli/dist/chunk-I7FEAHB4.js +100 -0
- package/packages/cli/dist/chunk-I7FEAHB4.js.map +7 -0
- package/packages/cli/dist/chunk-IHVZ5AUJ.js +107 -0
- package/packages/cli/dist/chunk-IHVZ5AUJ.js.map +7 -0
- package/packages/cli/dist/chunk-KI7FDU3H.js +99 -0
- package/packages/cli/dist/chunk-KI7FDU3H.js.map +7 -0
- package/packages/cli/dist/chunk-L5GRJQBS.js +32 -0
- package/packages/cli/dist/chunk-L5GRJQBS.js.map +7 -0
- package/packages/cli/dist/chunk-LHEAUDJL.js +302 -0
- package/packages/cli/dist/chunk-LHEAUDJL.js.map +7 -0
- package/packages/cli/dist/chunk-MIURCESJ.js +48 -0
- package/packages/cli/dist/chunk-MIURCESJ.js.map +7 -0
- package/packages/cli/dist/chunk-N43IXOND.js +732 -0
- package/packages/cli/dist/chunk-N43IXOND.js.map +7 -0
- package/packages/cli/dist/chunk-OUZRMGPV.js +274 -0
- package/packages/cli/dist/chunk-OUZRMGPV.js.map +7 -0
- package/packages/cli/dist/chunk-PSQXKAL4.js +371 -0
- package/packages/cli/dist/chunk-PSQXKAL4.js.map +7 -0
- package/packages/cli/dist/chunk-PUDRKDVZ.js +1419 -0
- package/packages/cli/dist/chunk-PUDRKDVZ.js.map +7 -0
- package/packages/cli/dist/chunk-RAKMX654.js +631 -0
- package/packages/cli/dist/chunk-RAKMX654.js.map +7 -0
- package/packages/cli/dist/chunk-RSFLK2TP.js +195 -0
- package/packages/cli/dist/chunk-RSFLK2TP.js.map +7 -0
- package/packages/cli/dist/chunk-S26PR2BS.js +99 -0
- package/packages/cli/dist/chunk-S26PR2BS.js.map +7 -0
- package/packages/cli/dist/chunk-U7FW7SXX.js +568 -0
- package/packages/cli/dist/chunk-U7FW7SXX.js.map +7 -0
- package/packages/cli/dist/chunk-VKM6K5TN.js +413 -0
- package/packages/cli/dist/chunk-VKM6K5TN.js.map +7 -0
- package/packages/cli/dist/chunk-VKNJG4JN.js +253 -0
- package/packages/cli/dist/chunk-VKNJG4JN.js.map +7 -0
- package/packages/cli/dist/chunk-VQ2KY6CK.js +113 -0
- package/packages/cli/dist/chunk-VQ2KY6CK.js.map +7 -0
- package/packages/cli/dist/chunk-VQDTXLOX.js +1312 -0
- package/packages/cli/dist/chunk-VQDTXLOX.js.map +7 -0
- package/packages/cli/dist/chunk-VXNS3X5O.js +60 -0
- package/packages/cli/dist/chunk-VXNS3X5O.js.map +7 -0
- package/packages/cli/dist/chunk-WF7H2YDU.js +376 -0
- package/packages/cli/dist/chunk-WF7H2YDU.js.map +7 -0
- package/packages/cli/dist/chunk-WNRXZLWW.js +266 -0
- package/packages/cli/dist/chunk-WNRXZLWW.js.map +7 -0
- package/packages/cli/dist/chunk-WT4VVCXM.js +1121 -0
- package/packages/cli/dist/chunk-WT4VVCXM.js.map +7 -0
- package/packages/cli/dist/configure-3AZUMDJZ.js +107 -0
- package/packages/cli/dist/configure-3AZUMDJZ.js.map +7 -0
- package/packages/cli/dist/configure-D722JQOD.js +107 -0
- package/packages/cli/dist/configure-D722JQOD.js.map +7 -0
- package/packages/cli/dist/configure-IU5H7XD6.js +107 -0
- package/packages/cli/dist/configure-IU5H7XD6.js.map +7 -0
- package/packages/cli/dist/file-format-detector-PXCIAKTK.js +22 -0
- package/packages/cli/dist/file-format-detector-PXCIAKTK.js.map +7 -0
- package/packages/cli/dist/index.js +17 -17
- package/packages/cli/dist/install-EZNWMLJR.js +7581 -0
- package/packages/cli/dist/install-EZNWMLJR.js.map +7 -0
- package/packages/cli/dist/install-F5ANFUBX.js +7577 -0
- package/packages/cli/dist/install-F5ANFUBX.js.map +7 -0
- package/packages/cli/dist/install-JSXEPPC2.js +7104 -0
- package/packages/cli/dist/install-JSXEPPC2.js.map +7 -0
- package/packages/cli/dist/install-QHEBX7JH.js +7101 -0
- package/packages/cli/dist/install-QHEBX7JH.js.map +7 -0
- package/packages/cli/dist/list-DMOUATYI.js +327 -0
- package/packages/cli/dist/list-DMOUATYI.js.map +7 -0
- package/packages/cli/dist/list-UESSCB7Y.js +327 -0
- package/packages/cli/dist/list-UESSCB7Y.js.map +7 -0
- package/packages/cli/dist/list-XR7RSJFS.js +327 -0
- package/packages/cli/dist/list-XR7RSJFS.js.map +7 -0
- package/packages/cli/dist/login-EYZ2SOYZ.js +150 -0
- package/packages/cli/dist/login-EYZ2SOYZ.js.map +7 -0
- package/packages/cli/dist/login-JWCSTAEU.js +150 -0
- package/packages/cli/dist/login-JWCSTAEU.js.map +7 -0
- package/packages/cli/dist/login-NRKHXZKM.js +150 -0
- package/packages/cli/dist/login-NRKHXZKM.js.map +7 -0
- package/packages/cli/dist/logout-HDMYRXIE.js +40 -0
- package/packages/cli/dist/logout-HDMYRXIE.js.map +7 -0
- package/packages/cli/dist/logout-SYHXCVCQ.js +40 -0
- package/packages/cli/dist/logout-SYHXCVCQ.js.map +7 -0
- package/packages/cli/dist/logout-X3XUUOH5.js +40 -0
- package/packages/cli/dist/logout-X3XUUOH5.js.map +7 -0
- package/packages/cli/dist/new-3LTFKDTQ.js +277 -0
- package/packages/cli/dist/new-3LTFKDTQ.js.map +7 -0
- package/packages/cli/dist/new-F46OSD72.js +277 -0
- package/packages/cli/dist/new-F46OSD72.js.map +7 -0
- package/packages/cli/dist/new-OPCCLNL2.js +277 -0
- package/packages/cli/dist/new-OPCCLNL2.js.map +7 -0
- package/packages/cli/dist/package-marker-detector-T5O5YD2E.js +80 -0
- package/packages/cli/dist/package-marker-detector-T5O5YD2E.js.map +7 -0
- package/packages/cli/dist/package-yml-QWZIJDYU.js +16 -0
- package/packages/cli/dist/package-yml-QWZIJDYU.js.map +7 -0
- package/packages/cli/dist/plugin-naming-YP2I4NPA.js +29 -0
- package/packages/cli/dist/plugin-naming-YP2I4NPA.js.map +7 -0
- package/packages/cli/dist/publish-4H43PCSG.js +619 -0
- package/packages/cli/dist/publish-4H43PCSG.js.map +7 -0
- package/packages/cli/dist/publish-RULKLNUX.js +619 -0
- package/packages/cli/dist/publish-RULKLNUX.js.map +7 -0
- package/packages/cli/dist/publish-URWY2P3E.js +619 -0
- package/packages/cli/dist/publish-URWY2P3E.js.map +7 -0
- package/packages/cli/dist/remove-BD52BHR2.js +542 -0
- package/packages/cli/dist/remove-BD52BHR2.js.map +7 -0
- package/packages/cli/dist/remove-G5NRC7LD.js +542 -0
- package/packages/cli/dist/remove-G5NRC7LD.js.map +7 -0
- package/packages/cli/dist/remove-TC3FQUYQ.js +542 -0
- package/packages/cli/dist/remove-TC3FQUYQ.js.map +7 -0
- package/packages/cli/dist/resource-discoverer-4X4RY43E.js +17 -0
- package/packages/cli/dist/resource-discoverer-4X4RY43E.js.map +7 -0
- package/packages/cli/dist/save-24TESYKI.js +1728 -0
- package/packages/cli/dist/save-24TESYKI.js.map +7 -0
- package/packages/cli/dist/save-N3QWF2WN.js +1728 -0
- package/packages/cli/dist/save-N3QWF2WN.js.map +7 -0
- package/packages/cli/dist/save-P2U67DTV.js +1728 -0
- package/packages/cli/dist/save-P2U67DTV.js.map +7 -0
- package/packages/cli/dist/search-ABROK3UO.js +157 -0
- package/packages/cli/dist/search-ABROK3UO.js.map +7 -0
- package/packages/cli/dist/search-WVFXFNAV.js +157 -0
- package/packages/cli/dist/search-WVFXFNAV.js.map +7 -0
- package/packages/cli/dist/search-YQN2Q2SO.js +157 -0
- package/packages/cli/dist/search-YQN2Q2SO.js.map +7 -0
- package/packages/cli/dist/set-DCWF73F6.js +251 -0
- package/packages/cli/dist/set-DCWF73F6.js.map +7 -0
- package/packages/cli/dist/set-GJEG2F6Y.js +251 -0
- package/packages/cli/dist/set-GJEG2F6Y.js.map +7 -0
- package/packages/cli/dist/set-NGM2FIKF.js +251 -0
- package/packages/cli/dist/set-NGM2FIKF.js.map +7 -0
- package/packages/cli/dist/uninstall-3CJQMTYH.js +539 -0
- package/packages/cli/dist/uninstall-3CJQMTYH.js.map +7 -0
- package/packages/cli/dist/uninstall-Q3CP4UN5.js +539 -0
- package/packages/cli/dist/uninstall-Q3CP4UN5.js.map +7 -0
- package/packages/cli/dist/uninstall-QU5OMEEC.js +539 -0
- package/packages/cli/dist/uninstall-QU5OMEEC.js.map +7 -0
- package/packages/cli/dist/unpublish-GHJQYC4S.js +245 -0
- package/packages/cli/dist/unpublish-GHJQYC4S.js.map +7 -0
- package/packages/cli/dist/unpublish-L2CYMK4B.js +245 -0
- package/packages/cli/dist/unpublish-L2CYMK4B.js.map +7 -0
- package/packages/cli/dist/unpublish-VBTNTMS5.js +245 -0
- package/packages/cli/dist/unpublish-VBTNTMS5.js.map +7 -0
- package/packages/cli/dist/view-MXRBMXOG.js +488 -0
- package/packages/cli/dist/view-MXRBMXOG.js.map +7 -0
- package/packages/cli/dist/view-NMND7SAW.js +488 -0
- package/packages/cli/dist/view-NMND7SAW.js.map +7 -0
- package/packages/cli/dist/view-RPQRDSYB.js +488 -0
- package/packages/cli/dist/view-RPQRDSYB.js.map +7 -0
- package/packages/cli/package.json +2 -0
- package/packages/core/dist/constants/index.d.ts +9 -0
- package/packages/core/dist/constants/index.d.ts.map +1 -1
- package/packages/core/dist/constants/index.js +12 -0
- package/packages/core/dist/constants/index.js.map +1 -1
- package/packages/core/dist/core/dependency-resolver/index.d.ts +2 -10
- package/packages/core/dist/core/dependency-resolver/index.d.ts.map +1 -1
- package/packages/core/dist/core/dependency-resolver/index.js +3 -14
- package/packages/core/dist/core/dependency-resolver/index.js.map +1 -1
- package/packages/core/dist/core/install/base-detector.d.ts +2 -1
- package/packages/core/dist/core/install/base-detector.d.ts.map +1 -1
- package/packages/core/dist/core/install/base-detector.js +54 -1
- package/packages/core/dist/core/install/base-detector.js.map +1 -1
- package/packages/core/dist/core/install/conflicts/file-conflict-resolver.d.ts +7 -5
- package/packages/core/dist/core/install/conflicts/file-conflict-resolver.d.ts.map +1 -1
- package/packages/core/dist/core/install/conflicts/file-conflict-resolver.js +25 -9
- package/packages/core/dist/core/install/conflicts/file-conflict-resolver.js.map +1 -1
- package/packages/core/dist/core/install/flow-index-installer.d.ts +2 -1
- package/packages/core/dist/core/install/flow-index-installer.d.ts.map +1 -1
- package/packages/core/dist/core/install/flow-index-installer.js +19 -4
- package/packages/core/dist/core/install/flow-index-installer.js.map +1 -1
- package/packages/core/dist/core/install/input-classifier-base.js +3 -3
- package/packages/core/dist/core/install/input-classifier-base.js.map +1 -1
- package/packages/core/dist/core/install/install-reporting.d.ts.map +1 -1
- package/packages/core/dist/core/install/install-reporting.js +7 -9
- package/packages/core/dist/core/install/install-reporting.js.map +1 -1
- package/packages/core/dist/core/install/list-handler.d.ts.map +1 -1
- package/packages/core/dist/core/install/list-handler.js +3 -0
- package/packages/core/dist/core/install/list-handler.js.map +1 -1
- package/packages/core/dist/core/install/marketplace-handler.d.ts.map +1 -1
- package/packages/core/dist/core/install/marketplace-handler.js.map +1 -1
- package/packages/core/dist/core/install/operations/conflict-handler.d.ts +2 -1
- package/packages/core/dist/core/install/operations/conflict-handler.d.ts.map +1 -1
- package/packages/core/dist/core/install/operations/conflict-handler.js +2 -2
- package/packages/core/dist/core/install/operations/conflict-handler.js.map +1 -1
- package/packages/core/dist/core/install/operations/installation-executor.d.ts +3 -0
- package/packages/core/dist/core/install/operations/installation-executor.d.ts.map +1 -1
- package/packages/core/dist/core/install/operations/installation-executor.js +39 -22
- package/packages/core/dist/core/install/operations/installation-executor.js.map +1 -1
- package/packages/core/dist/core/install/orchestrator/orchestrator.d.ts +7 -3
- package/packages/core/dist/core/install/orchestrator/orchestrator.d.ts.map +1 -1
- package/packages/core/dist/core/install/orchestrator/orchestrator.js +193 -93
- package/packages/core/dist/core/install/orchestrator/orchestrator.js.map +1 -1
- package/packages/core/dist/core/install/orchestrator/strategies/git-strategy.d.ts +1 -0
- package/packages/core/dist/core/install/orchestrator/strategies/git-strategy.d.ts.map +1 -1
- package/packages/core/dist/core/install/orchestrator/strategies/git-strategy.js +11 -24
- package/packages/core/dist/core/install/orchestrator/strategies/git-strategy.js.map +1 -1
- package/packages/core/dist/core/install/orchestrator/strategies/path-strategy.d.ts +2 -0
- package/packages/core/dist/core/install/orchestrator/strategies/path-strategy.d.ts.map +1 -1
- package/packages/core/dist/core/install/orchestrator/strategies/path-strategy.js +14 -14
- package/packages/core/dist/core/install/orchestrator/strategies/path-strategy.js.map +1 -1
- package/packages/core/dist/core/install/orchestrator/strategies/registry-strategy.d.ts +7 -0
- package/packages/core/dist/core/install/orchestrator/strategies/registry-strategy.d.ts.map +1 -1
- package/packages/core/dist/core/install/orchestrator/strategies/registry-strategy.js +28 -0
- package/packages/core/dist/core/install/orchestrator/strategies/registry-strategy.js.map +1 -1
- package/packages/core/dist/core/install/orchestrator/types.d.ts +2 -0
- package/packages/core/dist/core/install/orchestrator/types.d.ts.map +1 -1
- package/packages/core/dist/core/install/path-package-loader.d.ts.map +1 -1
- package/packages/core/dist/core/install/path-package-loader.js +20 -1
- package/packages/core/dist/core/install/path-package-loader.js.map +1 -1
- package/packages/core/dist/core/install/platform-resolution.d.ts +3 -0
- package/packages/core/dist/core/install/platform-resolution.d.ts.map +1 -1
- package/packages/core/dist/core/install/platform-resolution.js +5 -2
- package/packages/core/dist/core/install/platform-resolution.js.map +1 -1
- package/packages/core/dist/core/install/preprocessing/context-population.d.ts +18 -0
- package/packages/core/dist/core/install/preprocessing/context-population.d.ts.map +1 -0
- package/packages/core/dist/core/install/preprocessing/context-population.js +36 -0
- package/packages/core/dist/core/install/preprocessing/context-population.js.map +1 -0
- package/packages/core/dist/core/install/preprocessing/convenience-preprocessor.d.ts +23 -0
- package/packages/core/dist/core/install/preprocessing/convenience-preprocessor.d.ts.map +1 -1
- package/packages/core/dist/core/install/preprocessing/convenience-preprocessor.js +44 -0
- package/packages/core/dist/core/install/preprocessing/convenience-preprocessor.js.map +1 -1
- package/packages/core/dist/core/install/sources/git-source.d.ts.map +1 -1
- package/packages/core/dist/core/install/sources/git-source.js +67 -4
- package/packages/core/dist/core/install/sources/git-source.js.map +1 -1
- package/packages/core/dist/core/install/sources/path-source.d.ts.map +1 -1
- package/packages/core/dist/core/install/sources/path-source.js +8 -0
- package/packages/core/dist/core/install/sources/path-source.js.map +1 -1
- package/packages/core/dist/core/install/strategies/flow-based-strategy.d.ts.map +1 -1
- package/packages/core/dist/core/install/strategies/flow-based-strategy.js +12 -5
- package/packages/core/dist/core/install/strategies/flow-based-strategy.js.map +1 -1
- package/packages/core/dist/core/install/strategies/types.d.ts +11 -1
- package/packages/core/dist/core/install/strategies/types.d.ts.map +1 -1
- package/packages/core/dist/core/install/unified/context-builders.d.ts +5 -0
- package/packages/core/dist/core/install/unified/context-builders.d.ts.map +1 -1
- package/packages/core/dist/core/install/unified/context-builders.js +12 -0
- package/packages/core/dist/core/install/unified/context-builders.js.map +1 -1
- package/packages/core/dist/core/install/unified/context-helpers.d.ts +0 -4
- package/packages/core/dist/core/install/unified/context-helpers.d.ts.map +1 -1
- package/packages/core/dist/core/install/unified/context-helpers.js +0 -24
- package/packages/core/dist/core/install/unified/context-helpers.js.map +1 -1
- package/packages/core/dist/core/install/unified/index.d.ts +1 -1
- package/packages/core/dist/core/install/unified/index.d.ts.map +1 -1
- package/packages/core/dist/core/install/unified/index.js +1 -1
- package/packages/core/dist/core/install/unified/index.js.map +1 -1
- package/packages/core/dist/core/install/unified/multi-context-pipeline.d.ts +6 -0
- package/packages/core/dist/core/install/unified/multi-context-pipeline.d.ts.map +1 -1
- package/packages/core/dist/core/install/unified/multi-context-pipeline.js +11 -4
- package/packages/core/dist/core/install/unified/multi-context-pipeline.js.map +1 -1
- package/packages/core/dist/core/install/unified/phases/conflicts.d.ts.map +1 -1
- package/packages/core/dist/core/install/unified/phases/conflicts.js +1 -1
- package/packages/core/dist/core/install/unified/phases/conflicts.js.map +1 -1
- package/packages/core/dist/core/install/unified/phases/execute.d.ts.map +1 -1
- package/packages/core/dist/core/install/unified/phases/execute.js +5 -5
- package/packages/core/dist/core/install/unified/phases/execute.js.map +1 -1
- package/packages/core/dist/core/install/unified/phases/load-package.js +3 -3
- package/packages/core/dist/core/install/unified/phases/load-package.js.map +1 -1
- package/packages/core/dist/core/install/unified/phases/report.js +1 -1
- package/packages/core/dist/core/install/unified/phases/report.js.map +1 -1
- package/packages/core/dist/core/install/unified/pipeline.d.ts.map +1 -1
- package/packages/core/dist/core/install/unified/pipeline.js +7 -10
- package/packages/core/dist/core/install/unified/pipeline.js.map +1 -1
- package/packages/core/dist/core/install/wave-resolver/content-root-cache.d.ts +24 -0
- package/packages/core/dist/core/install/wave-resolver/content-root-cache.d.ts.map +1 -0
- package/packages/core/dist/core/install/wave-resolver/content-root-cache.js +71 -0
- package/packages/core/dist/core/install/wave-resolver/content-root-cache.js.map +1 -0
- package/packages/core/dist/core/install/wave-resolver/context-builder.d.ts +39 -0
- package/packages/core/dist/core/install/wave-resolver/context-builder.d.ts.map +1 -0
- package/packages/core/dist/core/install/wave-resolver/context-builder.js +148 -0
- package/packages/core/dist/core/install/wave-resolver/context-builder.js.map +1 -0
- package/packages/core/dist/core/install/wave-resolver/fetcher.d.ts +49 -0
- package/packages/core/dist/core/install/wave-resolver/fetcher.d.ts.map +1 -0
- package/packages/core/dist/core/install/wave-resolver/fetcher.js +221 -0
- package/packages/core/dist/core/install/wave-resolver/fetcher.js.map +1 -0
- package/packages/core/dist/core/install/wave-resolver/index-updater.d.ts +23 -0
- package/packages/core/dist/core/install/wave-resolver/index-updater.d.ts.map +1 -0
- package/packages/core/dist/core/install/wave-resolver/index-updater.js +87 -0
- package/packages/core/dist/core/install/wave-resolver/index-updater.js.map +1 -0
- package/packages/core/dist/core/install/wave-resolver/index-write-collector.d.ts +101 -0
- package/packages/core/dist/core/install/wave-resolver/index-write-collector.d.ts.map +1 -0
- package/packages/core/dist/core/install/wave-resolver/index-write-collector.js +194 -0
- package/packages/core/dist/core/install/wave-resolver/index-write-collector.js.map +1 -0
- package/packages/core/dist/core/install/wave-resolver/index.d.ts +17 -0
- package/packages/core/dist/core/install/wave-resolver/index.d.ts.map +1 -0
- package/packages/core/dist/core/install/wave-resolver/index.js +16 -0
- package/packages/core/dist/core/install/wave-resolver/index.js.map +1 -0
- package/packages/core/dist/core/install/wave-resolver/manifest-reader.d.ts +34 -0
- package/packages/core/dist/core/install/wave-resolver/manifest-reader.d.ts.map +1 -0
- package/packages/core/dist/core/install/wave-resolver/manifest-reader.js +112 -0
- package/packages/core/dist/core/install/wave-resolver/manifest-reader.js.map +1 -0
- package/packages/core/dist/core/install/wave-resolver/types.d.ts +210 -0
- package/packages/core/dist/core/install/wave-resolver/types.d.ts.map +1 -0
- package/packages/core/dist/core/install/wave-resolver/types.js +6 -0
- package/packages/core/dist/core/install/wave-resolver/types.js.map +1 -0
- package/packages/core/dist/core/install/wave-resolver/version-solver.d.ts +65 -0
- package/packages/core/dist/core/install/wave-resolver/version-solver.d.ts.map +1 -0
- package/packages/core/dist/core/install/wave-resolver/version-solver.js +166 -0
- package/packages/core/dist/core/install/wave-resolver/version-solver.js.map +1 -0
- package/packages/core/dist/core/install/wave-resolver/wave-engine.d.ts +16 -0
- package/packages/core/dist/core/install/wave-resolver/wave-engine.d.ts.map +1 -0
- package/packages/core/dist/core/install/wave-resolver/wave-engine.js +337 -0
- package/packages/core/dist/core/install/wave-resolver/wave-engine.js.map +1 -0
- package/packages/core/dist/core/install/wave-resolver/wave-installer.d.ts +50 -0
- package/packages/core/dist/core/install/wave-resolver/wave-installer.d.ts.map +1 -0
- package/packages/core/dist/core/install/wave-resolver/wave-installer.js +246 -0
- package/packages/core/dist/core/install/wave-resolver/wave-installer.js.map +1 -0
- package/packages/core/dist/core/ports/buffered-output.d.ts +36 -0
- package/packages/core/dist/core/ports/buffered-output.d.ts.map +1 -0
- package/packages/core/dist/core/ports/buffered-output.js +89 -0
- package/packages/core/dist/core/ports/buffered-output.js.map +1 -0
- package/packages/core/dist/core/ports/resolve.d.ts +0 -13
- package/packages/core/dist/core/ports/resolve.d.ts.map +1 -1
- package/packages/core/dist/core/ports/resolve.js +0 -28
- package/packages/core/dist/core/ports/resolve.js.map +1 -1
- package/packages/core/dist/core/remove/removal-confirmation.d.ts +4 -1
- package/packages/core/dist/core/remove/removal-confirmation.d.ts.map +1 -1
- package/packages/core/dist/core/remove/removal-confirmation.js +5 -4
- package/packages/core/dist/core/remove/removal-confirmation.js.map +1 -1
- package/packages/core/dist/core/remove/remove-from-source-pipeline.d.ts.map +1 -1
- package/packages/core/dist/core/remove/remove-from-source-pipeline.js +1 -10
- package/packages/core/dist/core/remove/remove-from-source-pipeline.js.map +1 -1
- package/packages/core/dist/core/uninstall/uninstall-executor.js +1 -1
- package/packages/core/dist/core/uninstall/uninstall-executor.js.map +1 -1
- package/packages/core/dist/core/uninstall/uninstall-reporter.d.ts +2 -2
- package/packages/core/dist/core/uninstall/uninstall-reporter.d.ts.map +1 -1
- package/packages/core/dist/core/uninstall/uninstall-reporter.js +4 -4
- package/packages/core/dist/core/uninstall/uninstall-reporter.js.map +1 -1
- package/packages/core/dist/index.d.ts +1 -1
- package/packages/core/dist/index.d.ts.map +1 -1
- package/packages/core/dist/types/execution-context.d.ts +40 -10
- package/packages/core/dist/types/execution-context.d.ts.map +1 -1
- package/packages/core/dist/utils/concurrency-pool.d.ts +34 -0
- package/packages/core/dist/utils/concurrency-pool.d.ts.map +1 -0
- package/packages/core/dist/utils/concurrency-pool.js +58 -0
- package/packages/core/dist/utils/concurrency-pool.js.map +1 -0
- package/packages/core/dist/utils/plugin-naming.d.ts +11 -3
- package/packages/core/dist/utils/plugin-naming.d.ts.map +1 -1
- package/packages/core/dist/utils/plugin-naming.js +27 -7
- package/packages/core/dist/utils/plugin-naming.js.map +1 -1
- package/plans/wave-resolver.md +254 -0
- package/.claude/agents/essentials/code-simplifier.md +0 -52
- package/.claude/commands/essentials/cleanup.md +0 -1
- package/.claude/commands/essentials/review.md +0 -8
- package/.claude/commands/git/commit.md +0 -5
|
@@ -0,0 +1,1728 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
assertMutableSourceOrThrow,
|
|
4
|
+
isRegistryPath
|
|
5
|
+
} from "./chunk-IBIYIU3K.js";
|
|
6
|
+
import {
|
|
7
|
+
calculateFileHash,
|
|
8
|
+
createFlowExecutor
|
|
9
|
+
} from "./chunk-ELCB4RC2.js";
|
|
10
|
+
import "./chunk-IXOEIRDT.js";
|
|
11
|
+
import "./chunk-QURLGVA4.js";
|
|
12
|
+
import {
|
|
13
|
+
getTargetPath
|
|
14
|
+
} from "./chunk-SHKYQQJJ.js";
|
|
15
|
+
import {
|
|
16
|
+
readWorkspaceIndex
|
|
17
|
+
} from "./chunk-GP5FJYSS.js";
|
|
18
|
+
import "./chunk-BCYZDID6.js";
|
|
19
|
+
import {
|
|
20
|
+
resolveDeclaredPath
|
|
21
|
+
} from "./chunk-FRYA3JAQ.js";
|
|
22
|
+
import {
|
|
23
|
+
createCliExecutionContext,
|
|
24
|
+
resolveOutput,
|
|
25
|
+
resolvePrompt
|
|
26
|
+
} from "./chunk-RAKMX654.js";
|
|
27
|
+
import "./chunk-XEPVYZO3.js";
|
|
28
|
+
import "./chunk-QTQYI4L5.js";
|
|
29
|
+
import {
|
|
30
|
+
arePackageNamesEquivalent,
|
|
31
|
+
normalizePackageName
|
|
32
|
+
} from "./chunk-VN22A7NW.js";
|
|
33
|
+
import "./chunk-UDAWJRKD.js";
|
|
34
|
+
import {
|
|
35
|
+
splitFrontmatter
|
|
36
|
+
} from "./chunk-KGBDKY5V.js";
|
|
37
|
+
import {
|
|
38
|
+
getGlobalExportFlows,
|
|
39
|
+
getGlobalImportFlows,
|
|
40
|
+
getPlatformDefinition,
|
|
41
|
+
inferPlatformFromWorkspaceFile
|
|
42
|
+
} from "./chunk-GKEHDSL4.js";
|
|
43
|
+
import {
|
|
44
|
+
normalizePathForProcessing
|
|
45
|
+
} from "./chunk-YMKK4XPN.js";
|
|
46
|
+
import {
|
|
47
|
+
FILE_PATTERNS,
|
|
48
|
+
MUTABILITY,
|
|
49
|
+
SOURCE_TYPES
|
|
50
|
+
} from "./chunk-J4IFFBLP.js";
|
|
51
|
+
import {
|
|
52
|
+
ensureDir,
|
|
53
|
+
exists,
|
|
54
|
+
getStats,
|
|
55
|
+
readTextFile,
|
|
56
|
+
walkFiles,
|
|
57
|
+
writeTextFile
|
|
58
|
+
} from "./chunk-S47F4OG4.js";
|
|
59
|
+
import "./chunk-ID4SVDQZ.js";
|
|
60
|
+
import {
|
|
61
|
+
logger
|
|
62
|
+
} from "./chunk-5EFWGD33.js";
|
|
63
|
+
|
|
64
|
+
// ../core/src/core/source-resolution/resolve-package-source.ts
|
|
65
|
+
import path from "path";
|
|
66
|
+
async function resolvePackageSource(workspaceRoot, packageName) {
|
|
67
|
+
let normalizedTarget = normalizePackageName(packageName), ws = await readWorkspaceIndex(workspaceRoot), entryKey = Object.keys(ws.index.packages ?? {}).find(
|
|
68
|
+
(k) => arePackageNamesEquivalent(k, normalizedTarget)
|
|
69
|
+
), entry = entryKey ? ws.index.packages?.[entryKey] : void 0;
|
|
70
|
+
if (!entry?.path)
|
|
71
|
+
throw new Error(
|
|
72
|
+
`Package '${packageName}' is not installed in this workspace.
|
|
73
|
+
Run 'opkg install ${packageName}' to install it first.`
|
|
74
|
+
);
|
|
75
|
+
let resolved = resolveDeclaredPath(entry.path, workspaceRoot), absolutePath = path.join(resolved.absolute, path.sep), mutability = isRegistryPath(absolutePath) ? MUTABILITY.IMMUTABLE : MUTABILITY.MUTABLE, sourceType = isRegistryPath(absolutePath) ? SOURCE_TYPES.REGISTRY : SOURCE_TYPES.PATH;
|
|
76
|
+
return {
|
|
77
|
+
packageName: normalizePackageName(entryKey ?? normalizedTarget),
|
|
78
|
+
absolutePath,
|
|
79
|
+
declaredPath: resolved.declared,
|
|
80
|
+
mutability,
|
|
81
|
+
version: entry.version,
|
|
82
|
+
sourceType
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// ../core/src/core/save/save-candidate-builder.ts
|
|
87
|
+
import { join, relative } from "path";
|
|
88
|
+
async function buildCandidates(options) {
|
|
89
|
+
let errors = [];
|
|
90
|
+
logger.debug("Building workspace candidates from workspace paths");
|
|
91
|
+
let { candidates: workspaceCandidates, errors: workspaceErrors } = await buildWorkspaceCandidates(
|
|
92
|
+
options.workspaceRoot,
|
|
93
|
+
options.packageRoot,
|
|
94
|
+
options.filesMapping
|
|
95
|
+
);
|
|
96
|
+
errors.push(...workspaceErrors), logger.debug("Building local source refs from package source (lazy mode)");
|
|
97
|
+
let localSourceRefs = await buildLocalSourceRefs(
|
|
98
|
+
options.packageRoot
|
|
99
|
+
);
|
|
100
|
+
return logger.debug(
|
|
101
|
+
`Built ${localSourceRefs.length} local source refs, ${workspaceCandidates.length} workspace candidates`
|
|
102
|
+
), {
|
|
103
|
+
localCandidates: [],
|
|
104
|
+
localSourceRefs,
|
|
105
|
+
workspaceCandidates,
|
|
106
|
+
errors
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
async function buildLocalSourceRefs(packageRoot) {
|
|
110
|
+
let refs = [];
|
|
111
|
+
for await (let absPath of walkFiles(packageRoot)) {
|
|
112
|
+
let relPath = relative(packageRoot, absPath), normalizedPath = normalizePathForProcessing(relPath);
|
|
113
|
+
normalizedPath && (normalizedPath.startsWith(".openpackage/") || normalizedPath === "openpackage.yml" || normalizedPath.startsWith(".") && !normalizedPath.match(/^\.(cursor|claude|opencode|windsurf|roo|factory|kilo|qwen|warp|codex|pi|kilocode|agent|augment)/) || (refs.push({ registryPath: normalizedPath, fullPath: absPath }), logger.debug(`Built local source ref: ${normalizedPath}`)));
|
|
114
|
+
}
|
|
115
|
+
return refs;
|
|
116
|
+
}
|
|
117
|
+
async function materializeLocalCandidate(ref, packageRoot) {
|
|
118
|
+
return buildCandidate("local", ref.fullPath, ref.registryPath, {
|
|
119
|
+
packageRoot,
|
|
120
|
+
workspaceRoot: packageRoot,
|
|
121
|
+
inferPlatform: !1,
|
|
122
|
+
parseMarkdown: !0
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
async function buildWorkspaceCandidates(workspaceRoot, packageRoot, filesMapping) {
|
|
126
|
+
let candidates = [], errors = [];
|
|
127
|
+
for (let [rawKey, targets] of Object.entries(filesMapping)) {
|
|
128
|
+
let registryKey = normalizePathForProcessing(rawKey);
|
|
129
|
+
if (!registryKey || !Array.isArray(targets)) continue;
|
|
130
|
+
let isDirectoryMapping = registryKey.endsWith("/");
|
|
131
|
+
for (let mapping of targets) {
|
|
132
|
+
let workspaceRel = getTargetPath(mapping), normalizedTargetPath = normalizePathForProcessing(workspaceRel);
|
|
133
|
+
if (!normalizedTargetPath) continue;
|
|
134
|
+
let absTargetPath = join(workspaceRoot, normalizedTargetPath), mergeMetadata = typeof mapping == "object" && mapping !== null ? { merge: mapping.merge, keys: mapping.keys } : void 0;
|
|
135
|
+
if (isDirectoryMapping) {
|
|
136
|
+
logger.debug(`Enumerating directory mapping: ${registryKey} -> ${normalizedTargetPath}`);
|
|
137
|
+
try {
|
|
138
|
+
let files = await collectFilesUnderDirectory(absTargetPath);
|
|
139
|
+
logger.debug(`Found ${files.length} files under directory ${normalizedTargetPath}`);
|
|
140
|
+
for (let relFile of files) {
|
|
141
|
+
let registryPath = normalizePathForProcessing(join(registryKey, relFile));
|
|
142
|
+
if (!registryPath) continue;
|
|
143
|
+
let absWorkspaceFile = join(absTargetPath, relFile), candidate = await buildCandidate("workspace", absWorkspaceFile, registryPath, {
|
|
144
|
+
packageRoot,
|
|
145
|
+
workspaceRoot,
|
|
146
|
+
inferPlatform: !0,
|
|
147
|
+
parseMarkdown: !0,
|
|
148
|
+
mergeStrategy: mergeMetadata?.merge,
|
|
149
|
+
mergeKeys: mergeMetadata?.keys
|
|
150
|
+
});
|
|
151
|
+
candidate && (candidates.push(candidate), logger.debug(`Built workspace candidate: ${registryPath} (from directory)`));
|
|
152
|
+
}
|
|
153
|
+
} catch (error) {
|
|
154
|
+
let errorMsg = error instanceof Error ? error.message : String(error);
|
|
155
|
+
errors.push({
|
|
156
|
+
path: absTargetPath,
|
|
157
|
+
registryPath: registryKey,
|
|
158
|
+
reason: `Failed to enumerate directory: ${errorMsg}`
|
|
159
|
+
}), logger.warn(`Failed to enumerate directory ${absTargetPath}: ${errorMsg}`);
|
|
160
|
+
}
|
|
161
|
+
} else {
|
|
162
|
+
if (!await exists(absTargetPath)) {
|
|
163
|
+
logger.debug(`Workspace file not found (skipping): ${normalizedTargetPath}`);
|
|
164
|
+
continue;
|
|
165
|
+
}
|
|
166
|
+
let candidate = await buildCandidate("workspace", absTargetPath, registryKey, {
|
|
167
|
+
packageRoot,
|
|
168
|
+
workspaceRoot,
|
|
169
|
+
inferPlatform: !0,
|
|
170
|
+
parseMarkdown: !0,
|
|
171
|
+
mergeStrategy: mergeMetadata?.merge,
|
|
172
|
+
mergeKeys: mergeMetadata?.keys
|
|
173
|
+
});
|
|
174
|
+
candidate && (candidates.push(candidate), logger.debug(`Built workspace candidate: ${registryKey}`));
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
return { candidates, errors };
|
|
179
|
+
}
|
|
180
|
+
async function buildCandidate(source, absPath, registryPath, options) {
|
|
181
|
+
try {
|
|
182
|
+
let content = await readTextFile(absPath), contentHash = await calculateFileHash(content), stats = await getStats(absPath), rootPath = source === "workspace" ? options.workspaceRoot : options.packageRoot, relPath = absPath.slice(rootPath.length + 1), displayPath = normalizePathForProcessing(relPath) || registryPath, platform;
|
|
183
|
+
if (options.inferPlatform && source === "workspace") {
|
|
184
|
+
let sourceDir = deriveSourceDir(displayPath);
|
|
185
|
+
platform = inferPlatformFromWorkspaceFile(
|
|
186
|
+
absPath,
|
|
187
|
+
sourceDir,
|
|
188
|
+
registryPath,
|
|
189
|
+
options.workspaceRoot
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
let frontmatter, rawFrontmatter, markdownBody, isMarkdown = !1;
|
|
193
|
+
if (options.parseMarkdown && (absPath.endsWith(".md") || absPath.endsWith(".markdown"))) {
|
|
194
|
+
isMarkdown = !0;
|
|
195
|
+
try {
|
|
196
|
+
let parsed = splitFrontmatter(content);
|
|
197
|
+
parsed.frontmatter && Object.keys(parsed.frontmatter).length > 0 && (frontmatter = parsed.frontmatter, rawFrontmatter = parsed.rawFrontmatter, markdownBody = parsed.body);
|
|
198
|
+
} catch (error) {
|
|
199
|
+
logger.debug(`Failed to parse frontmatter for ${absPath}: ${error}`);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
return {
|
|
203
|
+
source,
|
|
204
|
+
registryPath,
|
|
205
|
+
fullPath: absPath,
|
|
206
|
+
content,
|
|
207
|
+
contentHash,
|
|
208
|
+
mtime: stats.mtime.getTime(),
|
|
209
|
+
displayPath,
|
|
210
|
+
platform,
|
|
211
|
+
frontmatter,
|
|
212
|
+
rawFrontmatter,
|
|
213
|
+
markdownBody,
|
|
214
|
+
isMarkdown,
|
|
215
|
+
mergeStrategy: options.mergeStrategy,
|
|
216
|
+
mergeKeys: options.mergeKeys
|
|
217
|
+
};
|
|
218
|
+
} catch (error) {
|
|
219
|
+
let errorMsg = error instanceof Error ? error.message : String(error);
|
|
220
|
+
return logger.warn(`Failed to build candidate for ${absPath}: ${errorMsg}`), null;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
async function collectFilesUnderDirectory(absDir) {
|
|
224
|
+
let collected = [];
|
|
225
|
+
if (!await exists(absDir))
|
|
226
|
+
return collected;
|
|
227
|
+
for await (let absFile of walkFiles(absDir)) {
|
|
228
|
+
let relPath = absFile.slice(absDir.length + 1).replace(/\\/g, "/");
|
|
229
|
+
collected.push(relPath);
|
|
230
|
+
}
|
|
231
|
+
return collected;
|
|
232
|
+
}
|
|
233
|
+
function deriveSourceDir(relPath) {
|
|
234
|
+
return relPath && relPath.split("/")[0] || "";
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// ../core/src/core/save/save-group-builder.ts
|
|
238
|
+
import { minimatch } from "minimatch";
|
|
239
|
+
function buildCandidateGroups(localRefs, workspaceCandidates, workspaceRoot = process.cwd()) {
|
|
240
|
+
let map = /* @__PURE__ */ new Map();
|
|
241
|
+
logger.debug(`Building groups from ${localRefs.length} local refs and ${workspaceCandidates.length} workspace candidates`);
|
|
242
|
+
for (let ref of localRefs) {
|
|
243
|
+
let group = ensureGroup(map, ref.registryPath);
|
|
244
|
+
group.localRef = ref, logger.debug(`Added local ref: ${ref.registryPath}`);
|
|
245
|
+
}
|
|
246
|
+
for (let candidate of workspaceCandidates) {
|
|
247
|
+
logger.debug(
|
|
248
|
+
`Processing workspace candidate: ${candidate.displayPath} (registryPath: ${candidate.registryPath}, platform: ${candidate.platform || "none"})`
|
|
249
|
+
);
|
|
250
|
+
let group = map.get(candidate.registryPath);
|
|
251
|
+
if (group && logger.debug(` Found exact match by registryPath: ${candidate.registryPath}`), !group) {
|
|
252
|
+
let sourceRegistryPath = findSourceFileForWorkspace(
|
|
253
|
+
candidate,
|
|
254
|
+
localRefs,
|
|
255
|
+
workspaceRoot
|
|
256
|
+
);
|
|
257
|
+
sourceRegistryPath || (sourceRegistryPath = findSourceFileByFallback(
|
|
258
|
+
candidate.registryPath,
|
|
259
|
+
localRefs
|
|
260
|
+
)), sourceRegistryPath ? (logger.debug(
|
|
261
|
+
` Matched to source: workspace ${candidate.displayPath} \u2192 source ${sourceRegistryPath}`
|
|
262
|
+
), group = ensureGroup(map, sourceRegistryPath)) : logger.debug(" No source match found");
|
|
263
|
+
}
|
|
264
|
+
group || (logger.debug(` Creating new group with workspace registryPath: ${candidate.registryPath}`), group = ensureGroup(map, candidate.registryPath)), group.workspace.push(candidate);
|
|
265
|
+
}
|
|
266
|
+
let allGroups = Array.from(map.values()), activeCount = allGroups.filter((g) => g.workspace.length > 0).length;
|
|
267
|
+
return logger.debug(`Built ${map.size} candidate groups (${activeCount} with workspace candidates)`), allGroups;
|
|
268
|
+
}
|
|
269
|
+
function findSourceFileByFallback(registryPath, localRefs) {
|
|
270
|
+
let baseName = registryPath.replace(/\.[^.]+$/, ""), candidates = localRefs.filter((c) => c.registryPath.replace(/\.[^.]+$/, "") === baseName);
|
|
271
|
+
if (candidates.length === 1)
|
|
272
|
+
return logger.debug(` Fallback match: ${registryPath} \u2192 ${candidates[0].registryPath} (same basename)`), candidates[0].registryPath;
|
|
273
|
+
let fileNameBase = (registryPath.split("/").pop() || "").replace(/\.[^.]+$/, "");
|
|
274
|
+
if (fileNameBase) {
|
|
275
|
+
let fileNameCandidates = localRefs.filter((c) => (c.registryPath.split("/").pop() || "").replace(/\.[^.]+$/, "") === fileNameBase);
|
|
276
|
+
if (fileNameCandidates.length === 1)
|
|
277
|
+
return logger.debug(
|
|
278
|
+
` Fallback match: ${registryPath} \u2192 ${fileNameCandidates[0].registryPath} (same filename)`
|
|
279
|
+
), fileNameCandidates[0].registryPath;
|
|
280
|
+
}
|
|
281
|
+
return null;
|
|
282
|
+
}
|
|
283
|
+
function findSourceFileForWorkspace(workspaceCandidate, localRefs, workspaceRoot) {
|
|
284
|
+
let platform = workspaceCandidate.platform;
|
|
285
|
+
if (!platform || platform === "ai")
|
|
286
|
+
return null;
|
|
287
|
+
try {
|
|
288
|
+
let platformExportFlows = getPlatformDefinition(platform, workspaceRoot).export || [], allExportFlows = [...getGlobalExportFlows(workspaceRoot) || [], ...platformExportFlows];
|
|
289
|
+
logger.debug(` Checking ${allExportFlows.length} export flows for platform ${platform}`);
|
|
290
|
+
let workspacePath = workspaceCandidate.displayPath;
|
|
291
|
+
for (let flow of allExportFlows) {
|
|
292
|
+
let toPattern = Array.isArray(flow.to) ? flow.to[0] : flow.to;
|
|
293
|
+
if (typeof toPattern == "object" && "$switch" in toPattern || typeof toPattern != "string" || !minimatch(workspacePath, toPattern, { dot: !0 }))
|
|
294
|
+
continue;
|
|
295
|
+
logger.debug(` Workspace path ${workspacePath} matches 'to' pattern: ${toPattern}`);
|
|
296
|
+
let fromPatterns = Array.isArray(flow.from) ? flow.from : [flow.from];
|
|
297
|
+
for (let fromPattern of fromPatterns)
|
|
298
|
+
if (!(typeof fromPattern == "object" && "$switch" in fromPattern) && typeof fromPattern == "string") {
|
|
299
|
+
logger.debug(` Checking 'from' pattern: ${fromPattern}`);
|
|
300
|
+
for (let ref of localRefs)
|
|
301
|
+
if (minimatch(ref.registryPath, fromPattern, { dot: !0 }))
|
|
302
|
+
return logger.debug(` Found matching source: ${ref.registryPath}`), ref.registryPath;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
logger.debug(" No matching source file found");
|
|
306
|
+
} catch (error) {
|
|
307
|
+
logger.warn(`Failed to find source file for workspace candidate: ${error}`);
|
|
308
|
+
}
|
|
309
|
+
return null;
|
|
310
|
+
}
|
|
311
|
+
function filterGroupsWithWorkspace(groups) {
|
|
312
|
+
return groups.filter((group) => group.workspace.length > 0);
|
|
313
|
+
}
|
|
314
|
+
function ensureGroup(map, registryPath) {
|
|
315
|
+
let group = map.get(registryPath);
|
|
316
|
+
return group || (group = {
|
|
317
|
+
registryPath,
|
|
318
|
+
workspace: []
|
|
319
|
+
}, map.set(registryPath, group)), group;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// ../core/src/core/save/save-conversion-helper.ts
|
|
323
|
+
import { join as join2 } from "path";
|
|
324
|
+
import { tmpdir } from "os";
|
|
325
|
+
import { mkdtemp, rm } from "fs/promises";
|
|
326
|
+
import { minimatch as minimatch2 } from "minimatch";
|
|
327
|
+
|
|
328
|
+
// ../core/src/core/save/save-merge-extractor.ts
|
|
329
|
+
async function extractPackageContribution(candidate) {
|
|
330
|
+
if (candidate.source !== "workspace")
|
|
331
|
+
return {
|
|
332
|
+
success: !1,
|
|
333
|
+
error: "Extract only applies to workspace candidates"
|
|
334
|
+
};
|
|
335
|
+
if (!candidate.mergeStrategy || !candidate.mergeKeys || candidate.mergeKeys.length === 0)
|
|
336
|
+
return {
|
|
337
|
+
success: !1,
|
|
338
|
+
error: "No merge metadata present"
|
|
339
|
+
};
|
|
340
|
+
let { mergeStrategy, mergeKeys, content } = candidate;
|
|
341
|
+
try {
|
|
342
|
+
switch (mergeStrategy) {
|
|
343
|
+
case "deep":
|
|
344
|
+
case "shallow":
|
|
345
|
+
let normalizedKeys = normalizeKeysToParent(mergeKeys);
|
|
346
|
+
return await extractFromJsonMerge(content, normalizedKeys);
|
|
347
|
+
case "composite":
|
|
348
|
+
return {
|
|
349
|
+
success: !1,
|
|
350
|
+
error: "Composite merge extraction not yet implemented"
|
|
351
|
+
};
|
|
352
|
+
case "replace":
|
|
353
|
+
return {
|
|
354
|
+
success: !1,
|
|
355
|
+
error: "Replace strategy does not require extraction"
|
|
356
|
+
};
|
|
357
|
+
default:
|
|
358
|
+
return {
|
|
359
|
+
success: !1,
|
|
360
|
+
error: `Unknown merge strategy: ${mergeStrategy}`
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
} catch (error) {
|
|
364
|
+
return logger.debug(`Failed to extract package contribution: ${error}`), {
|
|
365
|
+
success: !1,
|
|
366
|
+
error: error instanceof Error ? error.message : String(error)
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
async function extractContentByKeys(content, mergeKeys) {
|
|
371
|
+
try {
|
|
372
|
+
let normalizedKeys = normalizeKeysToParent(mergeKeys);
|
|
373
|
+
return await extractFromJsonMerge(content, normalizedKeys);
|
|
374
|
+
} catch (error) {
|
|
375
|
+
return {
|
|
376
|
+
success: !1,
|
|
377
|
+
error: error instanceof Error ? error.message : String(error)
|
|
378
|
+
};
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
function normalizeKeysToParent(keys) {
|
|
382
|
+
if (keys.length === 0) return [];
|
|
383
|
+
if (keys.every((key) => key.split(".").length <= 2))
|
|
384
|
+
return logger.debug(
|
|
385
|
+
"Keys already at parent level - using as-is",
|
|
386
|
+
{ keys }
|
|
387
|
+
), keys;
|
|
388
|
+
if (keys.length === 1) {
|
|
389
|
+
let parts = keys[0].split(".");
|
|
390
|
+
return parts.length >= 2 ? [parts.slice(0, 2).join(".")] : keys;
|
|
391
|
+
}
|
|
392
|
+
let firstParts = keys[0].split("."), commonDepth = 0;
|
|
393
|
+
for (let depth = 0; depth < firstParts.length; depth++) {
|
|
394
|
+
let segment = firstParts[depth];
|
|
395
|
+
if (keys.every((key) => {
|
|
396
|
+
let parts = key.split(".");
|
|
397
|
+
return parts.length > depth && parts[depth] === segment;
|
|
398
|
+
}))
|
|
399
|
+
commonDepth = depth + 1;
|
|
400
|
+
else
|
|
401
|
+
break;
|
|
402
|
+
}
|
|
403
|
+
let normalized = /* @__PURE__ */ new Set();
|
|
404
|
+
for (let key of keys) {
|
|
405
|
+
let parts = key.split("."), targetDepth = Math.max(2, commonDepth + 1), extractDepth = Math.min(targetDepth, parts.length);
|
|
406
|
+
extractDepth >= 2 ? normalized.add(parts.slice(0, extractDepth).join(".")) : parts.length > 0 && normalized.add(key);
|
|
407
|
+
}
|
|
408
|
+
let result = Array.from(normalized);
|
|
409
|
+
return logger.debug(
|
|
410
|
+
`Normalized ${keys.length} tracked keys to ${result.length} extraction key(s)`,
|
|
411
|
+
{ original: keys, normalized: result }
|
|
412
|
+
), result;
|
|
413
|
+
}
|
|
414
|
+
async function extractFromJsonMerge(content, mergeKeys) {
|
|
415
|
+
try {
|
|
416
|
+
let merged = JSON.parse(content), extracted = {};
|
|
417
|
+
for (let keyPath of mergeKeys) {
|
|
418
|
+
let value = getNestedValue(merged, keyPath);
|
|
419
|
+
value !== void 0 && setNestedValue(extracted, keyPath, value);
|
|
420
|
+
}
|
|
421
|
+
let extractedContent = JSON.stringify(extracted, null, 2) + `
|
|
422
|
+
`, extractedHash = await calculateFileHash(extractedContent);
|
|
423
|
+
return logger.debug(
|
|
424
|
+
`Extracted ${mergeKeys.length} key(s) from merged file`,
|
|
425
|
+
{ keys: mergeKeys }
|
|
426
|
+
), {
|
|
427
|
+
success: !0,
|
|
428
|
+
extractedContent,
|
|
429
|
+
extractedHash
|
|
430
|
+
};
|
|
431
|
+
} catch (error) {
|
|
432
|
+
return {
|
|
433
|
+
success: !1,
|
|
434
|
+
error: error instanceof Error ? error.message : String(error)
|
|
435
|
+
};
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
function getNestedValue(obj, keyPath) {
|
|
439
|
+
let keys = keyPath.split("."), current = obj;
|
|
440
|
+
for (let key of keys) {
|
|
441
|
+
if (current == null || typeof current != "object")
|
|
442
|
+
return;
|
|
443
|
+
current = current[key];
|
|
444
|
+
}
|
|
445
|
+
return current;
|
|
446
|
+
}
|
|
447
|
+
function setNestedValue(obj, keyPath, value) {
|
|
448
|
+
let keys = keyPath.split("."), current = obj;
|
|
449
|
+
for (let i = 0; i < keys.length - 1; i++) {
|
|
450
|
+
let key = keys[i];
|
|
451
|
+
(!(key in current) || typeof current[key] != "object") && (current[key] = {}), current = current[key];
|
|
452
|
+
}
|
|
453
|
+
let lastKey = keys[keys.length - 1];
|
|
454
|
+
current[lastKey] = value;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
// ../core/src/core/save/save-conversion-helper.ts
|
|
458
|
+
var sharedTempDir = null, tempDirCounter = 0;
|
|
459
|
+
async function initSharedTempDir() {
|
|
460
|
+
sharedTempDir || (sharedTempDir = await mkdtemp(join2(tmpdir(), "opkg-save-")));
|
|
461
|
+
}
|
|
462
|
+
async function cleanupSharedTempDir() {
|
|
463
|
+
if (sharedTempDir) {
|
|
464
|
+
try {
|
|
465
|
+
await rm(sharedTempDir, { recursive: !0, force: !0 });
|
|
466
|
+
} catch (error) {
|
|
467
|
+
logger.debug("Failed to cleanup shared temp directory", { tempDir: sharedTempDir, error });
|
|
468
|
+
}
|
|
469
|
+
sharedTempDir = null, tempDirCounter = 0;
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
async function allocateTempSubdir() {
|
|
473
|
+
if (!sharedTempDir)
|
|
474
|
+
return await mkdtemp(join2(tmpdir(), "opkg-save-standalone-"));
|
|
475
|
+
let subDir = join2(sharedTempDir, `op-${tempDirCounter++}`);
|
|
476
|
+
return await ensureDir(subDir), subDir;
|
|
477
|
+
}
|
|
478
|
+
var conversionCache = /* @__PURE__ */ new Map();
|
|
479
|
+
function getCacheKey(candidate) {
|
|
480
|
+
return `${candidate.fullPath}:${candidate.contentHash}:${candidate.platform || "none"}`;
|
|
481
|
+
}
|
|
482
|
+
async function calculateConvertedHash(candidate, workspaceRoot) {
|
|
483
|
+
if (!candidate.platform || candidate.platform === "ai")
|
|
484
|
+
return candidate.contentHash;
|
|
485
|
+
let cacheKey = getCacheKey(candidate), cached = conversionCache.get(cacheKey);
|
|
486
|
+
if (cached)
|
|
487
|
+
return logger.debug(`Cache hit for converted hash: ${candidate.displayPath}`), cached;
|
|
488
|
+
let result = await convertWorkspaceToUniversal(
|
|
489
|
+
candidate.content,
|
|
490
|
+
candidate.platform,
|
|
491
|
+
candidate.registryPath,
|
|
492
|
+
workspaceRoot
|
|
493
|
+
);
|
|
494
|
+
return !result.success || !result.convertedHash ? (logger.debug(
|
|
495
|
+
`Conversion not applicable or failed for ${candidate.displayPath}, using raw hash`,
|
|
496
|
+
{ reason: result.error }
|
|
497
|
+
), candidate.contentHash) : (conversionCache.set(cacheKey, result.convertedHash), result.convertedHash);
|
|
498
|
+
}
|
|
499
|
+
async function convertWorkspaceToUniversal(workspaceContent, platform, registryPath, workspaceRoot) {
|
|
500
|
+
try {
|
|
501
|
+
let platformImportFlows = getPlatformDefinition(platform, workspaceRoot).import || [], allImportFlows = [...getGlobalImportFlows(workspaceRoot) || [], ...platformImportFlows];
|
|
502
|
+
if (allImportFlows.length === 0)
|
|
503
|
+
return logger.debug(`No import flows defined for platform ${platform}`), {
|
|
504
|
+
success: !1,
|
|
505
|
+
error: "No import flows defined for platform"
|
|
506
|
+
};
|
|
507
|
+
let matchingFlow = findMatchingImportFlow(
|
|
508
|
+
allImportFlows,
|
|
509
|
+
registryPath,
|
|
510
|
+
platform,
|
|
511
|
+
workspaceRoot
|
|
512
|
+
);
|
|
513
|
+
if (!matchingFlow)
|
|
514
|
+
return logger.debug(`No matching import flow for ${registryPath} on platform ${platform}`), {
|
|
515
|
+
success: !1,
|
|
516
|
+
error: "No matching import flow found"
|
|
517
|
+
};
|
|
518
|
+
let tempDir = await allocateTempSubdir(), inputDir = join2(tempDir, "in"), outputDir = join2(tempDir, "out");
|
|
519
|
+
await ensureDir(inputDir), await ensureDir(outputDir);
|
|
520
|
+
let workspaceSourcePath = inferWorkspaceSourcePath(
|
|
521
|
+
matchingFlow,
|
|
522
|
+
registryPath,
|
|
523
|
+
platform
|
|
524
|
+
), inputFilePath = join2(inputDir, workspaceSourcePath);
|
|
525
|
+
await ensureDir(join2(inputFilePath, "..")), await writeTextFile(inputFilePath, workspaceContent);
|
|
526
|
+
let flowContext = {
|
|
527
|
+
workspaceRoot: outputDir,
|
|
528
|
+
// Output goes to outputDir
|
|
529
|
+
packageRoot: inputDir,
|
|
530
|
+
// Input comes from inputDir
|
|
531
|
+
platform,
|
|
532
|
+
// Source platform
|
|
533
|
+
packageName: "temp",
|
|
534
|
+
direction: "install",
|
|
535
|
+
// Import flows are used during "install" direction
|
|
536
|
+
variables: {
|
|
537
|
+
name: "temp",
|
|
538
|
+
version: "0.0.0",
|
|
539
|
+
platform,
|
|
540
|
+
// For conditionals: $$platform
|
|
541
|
+
source: platform,
|
|
542
|
+
// For conditionals: $$source
|
|
543
|
+
sourcePlatform: platform,
|
|
544
|
+
targetPlatform: "openpackage"
|
|
545
|
+
},
|
|
546
|
+
dryRun: !1
|
|
547
|
+
}, executor = createFlowExecutor(), concreteFlow = {
|
|
548
|
+
...matchingFlow,
|
|
549
|
+
from: workspaceSourcePath
|
|
550
|
+
// Use concrete file path
|
|
551
|
+
}, flowResult = await executor.executeFlow(concreteFlow, flowContext);
|
|
552
|
+
if (!flowResult.success)
|
|
553
|
+
return {
|
|
554
|
+
success: !1,
|
|
555
|
+
error: `Flow execution failed: ${flowResult.error?.message}`
|
|
556
|
+
};
|
|
557
|
+
if (typeof flowResult.target != "string")
|
|
558
|
+
return {
|
|
559
|
+
success: !1,
|
|
560
|
+
error: "Flow did not produce target path"
|
|
561
|
+
};
|
|
562
|
+
let outputFilePath = flowResult.target;
|
|
563
|
+
if (!await exists(outputFilePath))
|
|
564
|
+
return {
|
|
565
|
+
success: !1,
|
|
566
|
+
error: "Flow did not produce output file"
|
|
567
|
+
};
|
|
568
|
+
let convertedContent = await readTextFile(outputFilePath), convertedHash = await calculateFileHash(convertedContent);
|
|
569
|
+
return logger.debug(
|
|
570
|
+
`Successfully converted ${registryPath} from ${platform} format to universal`,
|
|
571
|
+
{
|
|
572
|
+
originalHash: await calculateFileHash(workspaceContent),
|
|
573
|
+
convertedHash
|
|
574
|
+
}
|
|
575
|
+
), {
|
|
576
|
+
success: !0,
|
|
577
|
+
convertedContent,
|
|
578
|
+
convertedHash
|
|
579
|
+
};
|
|
580
|
+
} catch (error) {
|
|
581
|
+
return logger.warn(
|
|
582
|
+
`Conversion failed for ${registryPath} (platform: ${platform})`,
|
|
583
|
+
{ error }
|
|
584
|
+
), {
|
|
585
|
+
success: !1,
|
|
586
|
+
error: error instanceof Error ? error.message : String(error)
|
|
587
|
+
};
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
function findMatchingImportFlow(flows, registryPath, platform, workspaceRoot) {
|
|
591
|
+
for (let flow of flows) {
|
|
592
|
+
let toPattern = Array.isArray(flow.to) ? flow.to[0] : flow.to;
|
|
593
|
+
if (typeof toPattern == "object" && "$switch" in toPattern) {
|
|
594
|
+
logger.debug("Skipping flow with switch expression in to field");
|
|
595
|
+
continue;
|
|
596
|
+
}
|
|
597
|
+
if (typeof toPattern == "string" && minimatch2(registryPath, toPattern, { dot: !0 })) {
|
|
598
|
+
if (flow.when && !evaluateWhenCondition(flow.when, platform, workspaceRoot)) {
|
|
599
|
+
logger.debug(`Flow condition not met for ${registryPath}`, { when: flow.when });
|
|
600
|
+
continue;
|
|
601
|
+
}
|
|
602
|
+
return flow;
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
function evaluateWhenCondition(when, platform, workspaceRoot) {
|
|
607
|
+
if (when.$eq && Array.isArray(when.$eq) && when.$eq.length === 2) {
|
|
608
|
+
let left = resolveVariable(when.$eq[0], platform), right = resolveVariable(when.$eq[1], platform);
|
|
609
|
+
return left === right;
|
|
610
|
+
}
|
|
611
|
+
if (when.$ne && Array.isArray(when.$ne) && when.$ne.length === 2) {
|
|
612
|
+
let left = resolveVariable(when.$ne[0], platform), right = resolveVariable(when.$ne[1], platform);
|
|
613
|
+
return left !== right;
|
|
614
|
+
}
|
|
615
|
+
return when.exists ? !0 : (logger.debug("Unknown condition type in when clause", { when }), !1);
|
|
616
|
+
}
|
|
617
|
+
function resolveVariable(value, platform) {
|
|
618
|
+
return typeof value == "string" && (value === "$$platform" || value === "$$source") ? platform : value;
|
|
619
|
+
}
|
|
620
|
+
function inferWorkspaceSourcePath(flow, registryPath, platform) {
|
|
621
|
+
let fromPattern = Array.isArray(flow.from) ? flow.from[0] : flow.from;
|
|
622
|
+
if (typeof fromPattern == "object" && "$switch" in fromPattern)
|
|
623
|
+
return logger.debug("Cannot infer source path from switch expression, using registry path"), registryPath;
|
|
624
|
+
if (typeof fromPattern != "string")
|
|
625
|
+
return registryPath;
|
|
626
|
+
if (fromPattern.includes("**") || fromPattern.includes("*")) {
|
|
627
|
+
let baseDir = fromPattern.split("**")[0].replace(/\*+/g, ""), toPattern = Array.isArray(flow.to) ? flow.to[0] : flow.to;
|
|
628
|
+
if (typeof toPattern == "string" && toPattern.includes("**")) {
|
|
629
|
+
let toBase = toPattern.split("**")[0], subPath = registryPath.startsWith(toBase) ? registryPath.slice(toBase.length) : registryPath;
|
|
630
|
+
return `${baseDir}${subPath}`;
|
|
631
|
+
}
|
|
632
|
+
return registryPath;
|
|
633
|
+
}
|
|
634
|
+
return fromPattern;
|
|
635
|
+
}
|
|
636
|
+
async function convertSourceToWorkspace(sourceContent, platform, sourceRegistryPath, workspacePath, workspaceRoot) {
|
|
637
|
+
try {
|
|
638
|
+
let platformExportFlows = getPlatformDefinition(platform, workspaceRoot).export || [], allExportFlows = [...getGlobalExportFlows(workspaceRoot) || [], ...platformExportFlows];
|
|
639
|
+
if (allExportFlows.length === 0)
|
|
640
|
+
return {
|
|
641
|
+
success: !1,
|
|
642
|
+
error: "No export flows defined for platform"
|
|
643
|
+
};
|
|
644
|
+
let matchingFlow = findMatchingExportFlow(
|
|
645
|
+
allExportFlows,
|
|
646
|
+
sourceRegistryPath,
|
|
647
|
+
workspacePath,
|
|
648
|
+
platform,
|
|
649
|
+
workspaceRoot
|
|
650
|
+
);
|
|
651
|
+
if (!matchingFlow)
|
|
652
|
+
return {
|
|
653
|
+
success: !1,
|
|
654
|
+
error: "No matching export flow found"
|
|
655
|
+
};
|
|
656
|
+
let tempDir = await allocateTempSubdir(), inputDir = join2(tempDir, "in"), outputDir = join2(tempDir, "out");
|
|
657
|
+
await ensureDir(inputDir), await ensureDir(outputDir);
|
|
658
|
+
let inputFilePath = join2(inputDir, sourceRegistryPath);
|
|
659
|
+
await ensureDir(join2(inputFilePath, "..")), await writeTextFile(inputFilePath, sourceContent);
|
|
660
|
+
let flowContext = {
|
|
661
|
+
workspaceRoot: outputDir,
|
|
662
|
+
// Output goes to outputDir
|
|
663
|
+
packageRoot: inputDir,
|
|
664
|
+
// Input comes from inputDir
|
|
665
|
+
platform,
|
|
666
|
+
packageName: "temp",
|
|
667
|
+
direction: "install",
|
|
668
|
+
// Export flows are used during "install" direction
|
|
669
|
+
variables: {
|
|
670
|
+
name: "temp",
|
|
671
|
+
version: "0.0.0",
|
|
672
|
+
platform,
|
|
673
|
+
source: "openpackage",
|
|
674
|
+
sourcePlatform: "openpackage",
|
|
675
|
+
targetPlatform: platform,
|
|
676
|
+
targetRoot: "./"
|
|
677
|
+
},
|
|
678
|
+
dryRun: !1
|
|
679
|
+
}, flowResult = await createFlowExecutor().executeFlow(matchingFlow, flowContext);
|
|
680
|
+
if (!flowResult.success)
|
|
681
|
+
return {
|
|
682
|
+
success: !1,
|
|
683
|
+
error: `Flow execution failed: ${flowResult.error?.message}`
|
|
684
|
+
};
|
|
685
|
+
if (typeof flowResult.target != "string")
|
|
686
|
+
return {
|
|
687
|
+
success: !1,
|
|
688
|
+
error: "Flow did not produce target path"
|
|
689
|
+
};
|
|
690
|
+
let outputFilePath = flowResult.target;
|
|
691
|
+
if (!await exists(outputFilePath))
|
|
692
|
+
return {
|
|
693
|
+
success: !1,
|
|
694
|
+
error: "Flow did not produce output file"
|
|
695
|
+
};
|
|
696
|
+
let convertedContent = await readTextFile(outputFilePath), convertedHash = await calculateFileHash(convertedContent);
|
|
697
|
+
return logger.debug(
|
|
698
|
+
`Successfully forward converted ${sourceRegistryPath} to ${platform} format`,
|
|
699
|
+
{
|
|
700
|
+
originalHash: await calculateFileHash(sourceContent),
|
|
701
|
+
convertedHash
|
|
702
|
+
}
|
|
703
|
+
), {
|
|
704
|
+
success: !0,
|
|
705
|
+
convertedContent,
|
|
706
|
+
convertedHash
|
|
707
|
+
};
|
|
708
|
+
} catch (error) {
|
|
709
|
+
return logger.warn(
|
|
710
|
+
`Forward conversion failed for ${sourceRegistryPath} (platform: ${platform})`,
|
|
711
|
+
{ error }
|
|
712
|
+
), {
|
|
713
|
+
success: !1,
|
|
714
|
+
error: error instanceof Error ? error.message : String(error)
|
|
715
|
+
};
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
function findMatchingExportFlow(flows, sourceRegistryPath, workspacePath, platform, workspaceRoot) {
|
|
719
|
+
for (let flow of flows) {
|
|
720
|
+
let fromPatterns = Array.isArray(flow.from) ? flow.from : [flow.from];
|
|
721
|
+
if (!(fromPatterns.some((p) => typeof p == "object" && "$switch" in p) || fromPatterns.some((p) => typeof p != "string") || !fromPatterns.some(
|
|
722
|
+
(p) => typeof p == "string" && minimatch2(sourceRegistryPath, p, { dot: !0 })
|
|
723
|
+
)) && !(flow.when && !evaluateWhenCondition(flow.when, platform, workspaceRoot)))
|
|
724
|
+
return flow;
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
async function ensureComparableHash(candidate, workspaceRoot) {
|
|
728
|
+
if (candidate.comparableHash !== void 0)
|
|
729
|
+
return candidate.comparableHash;
|
|
730
|
+
let hash = candidate.contentHash;
|
|
731
|
+
if (candidate.mergeStrategy && candidate.mergeKeys && candidate.mergeKeys.length > 0) {
|
|
732
|
+
let extractResult = await extractPackageContribution(candidate);
|
|
733
|
+
extractResult.success && extractResult.extractedHash ? (hash = extractResult.extractedHash, extractResult.extractedContent && (candidate.extractedContent = extractResult.extractedContent)) : hash = await calculateConvertedHash(candidate, workspaceRoot);
|
|
734
|
+
} else
|
|
735
|
+
hash = await calculateConvertedHash(candidate, workspaceRoot);
|
|
736
|
+
return candidate.comparableHash = hash, hash;
|
|
737
|
+
}
|
|
738
|
+
function clearConversionCache() {
|
|
739
|
+
conversionCache.clear();
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
// ../core/src/core/save/save-conflict-analyzer.ts
|
|
743
|
+
async function analyzeGroup(group, force, workspaceRoot) {
|
|
744
|
+
let registryPath = group.registryPath, hasLocal = !!group.local, workspaceCandidates = group.workspace, workspaceCandidateCount = workspaceCandidates.length, isRootFile = registryPath === FILE_PATTERNS.AGENTS_MD || registryPath === FILE_PATTERNS.CLAUDE_MD || registryPath === FILE_PATTERNS.GEMINI_MD || registryPath === FILE_PATTERNS.QWEN_MD || registryPath === FILE_PATTERNS.WARP_MD || workspaceCandidates.some((c) => c.isRootFile), hasPlatformCandidates = workspaceCandidates.some(
|
|
745
|
+
(c) => c.platform && c.platform !== "ai"
|
|
746
|
+
);
|
|
747
|
+
if (workspaceCandidateCount === 0)
|
|
748
|
+
return {
|
|
749
|
+
registryPath,
|
|
750
|
+
type: "no-action-needed",
|
|
751
|
+
workspaceCandidateCount: 0,
|
|
752
|
+
uniqueWorkspaceCandidates: [],
|
|
753
|
+
hasLocalCandidate: hasLocal,
|
|
754
|
+
localMatchesWorkspace: !1,
|
|
755
|
+
isRootFile,
|
|
756
|
+
hasPlatformCandidates: !1,
|
|
757
|
+
recommendedStrategy: "skip"
|
|
758
|
+
};
|
|
759
|
+
let uniqueWorkspace = await deduplicateCandidatesWithMerge(workspaceCandidates, workspaceRoot), localMatchesWorkspace = hasLocal && uniqueWorkspace.length === 1 ? await checkConvertedParity(uniqueWorkspace[0], group.local, workspaceRoot) : !1;
|
|
760
|
+
return hasLocal && uniqueWorkspace.length > 1 && (await Promise.all(
|
|
761
|
+
uniqueWorkspace.map(async (candidate) => ({
|
|
762
|
+
workspacePath: candidate.displayPath,
|
|
763
|
+
platform: candidate.platform || "none",
|
|
764
|
+
matchesLocal: await checkConvertedParity(candidate, group.local, workspaceRoot)
|
|
765
|
+
}))
|
|
766
|
+
)).every((result) => result.matchesLocal) ? {
|
|
767
|
+
registryPath,
|
|
768
|
+
type: "no-change-needed",
|
|
769
|
+
workspaceCandidateCount,
|
|
770
|
+
uniqueWorkspaceCandidates: uniqueWorkspace,
|
|
771
|
+
hasLocalCandidate: hasLocal,
|
|
772
|
+
localMatchesWorkspace: !0,
|
|
773
|
+
isRootFile,
|
|
774
|
+
hasPlatformCandidates,
|
|
775
|
+
recommendedStrategy: "skip"
|
|
776
|
+
} : localMatchesWorkspace ? {
|
|
777
|
+
registryPath,
|
|
778
|
+
type: "no-change-needed",
|
|
779
|
+
workspaceCandidateCount,
|
|
780
|
+
uniqueWorkspaceCandidates: uniqueWorkspace,
|
|
781
|
+
hasLocalCandidate: hasLocal,
|
|
782
|
+
localMatchesWorkspace: !0,
|
|
783
|
+
isRootFile,
|
|
784
|
+
hasPlatformCandidates,
|
|
785
|
+
recommendedStrategy: "skip"
|
|
786
|
+
} : uniqueWorkspace.length === 1 ? {
|
|
787
|
+
registryPath,
|
|
788
|
+
type: "auto-write",
|
|
789
|
+
workspaceCandidateCount,
|
|
790
|
+
uniqueWorkspaceCandidates: uniqueWorkspace,
|
|
791
|
+
hasLocalCandidate: hasLocal,
|
|
792
|
+
localMatchesWorkspace: !1,
|
|
793
|
+
isRootFile,
|
|
794
|
+
hasPlatformCandidates,
|
|
795
|
+
recommendedStrategy: "write-single"
|
|
796
|
+
} : {
|
|
797
|
+
registryPath,
|
|
798
|
+
type: "needs-resolution",
|
|
799
|
+
workspaceCandidateCount,
|
|
800
|
+
uniqueWorkspaceCandidates: uniqueWorkspace,
|
|
801
|
+
hasLocalCandidate: hasLocal,
|
|
802
|
+
localMatchesWorkspace: !1,
|
|
803
|
+
isRootFile,
|
|
804
|
+
hasPlatformCandidates,
|
|
805
|
+
recommendedStrategy: force ? "force-newest" : "interactive"
|
|
806
|
+
};
|
|
807
|
+
}
|
|
808
|
+
async function checkConvertedParity(workspace, local, workspaceRoot) {
|
|
809
|
+
let workspaceHash = await ensureComparableHash(workspace, workspaceRoot);
|
|
810
|
+
if (workspace.mergeStrategy && workspace.mergeKeys && workspace.mergeKeys.length > 0) {
|
|
811
|
+
if (workspace.platform && workspace.platform !== "ai") {
|
|
812
|
+
let forward = await convertSourceToWorkspace(
|
|
813
|
+
local.content,
|
|
814
|
+
workspace.platform,
|
|
815
|
+
local.registryPath,
|
|
816
|
+
workspace.displayPath,
|
|
817
|
+
workspaceRoot
|
|
818
|
+
);
|
|
819
|
+
if (forward.success && forward.convertedContent) {
|
|
820
|
+
let localConvertedExtract = await extractContentByKeys(
|
|
821
|
+
forward.convertedContent,
|
|
822
|
+
workspace.mergeKeys
|
|
823
|
+
);
|
|
824
|
+
if (localConvertedExtract.success && localConvertedExtract.extractedHash)
|
|
825
|
+
return workspaceHash === localConvertedExtract.extractedHash;
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
let localExtract = await extractContentByKeys(local.content, workspace.mergeKeys);
|
|
829
|
+
return localExtract.success && localExtract.extractedHash ? workspaceHash === localExtract.extractedHash : workspaceHash === local.contentHash;
|
|
830
|
+
}
|
|
831
|
+
return workspaceHash === local.contentHash;
|
|
832
|
+
}
|
|
833
|
+
async function deduplicateCandidatesWithMerge(candidates, workspaceRoot) {
|
|
834
|
+
let seen = /* @__PURE__ */ new Set(), unique = [];
|
|
835
|
+
for (let candidate of candidates) {
|
|
836
|
+
let hash = await ensureComparableHash(candidate, workspaceRoot);
|
|
837
|
+
if (logger.debug(
|
|
838
|
+
`Dedup check for ${candidate.displayPath}: hash=${hash}, rawHash=${candidate.contentHash}, seen=${seen.has(hash)}`
|
|
839
|
+
), seen.has(hash)) {
|
|
840
|
+
logger.debug(` Skipping duplicate: ${candidate.displayPath}`);
|
|
841
|
+
continue;
|
|
842
|
+
}
|
|
843
|
+
seen.add(hash), unique.push(candidate);
|
|
844
|
+
}
|
|
845
|
+
return logger.debug(`Deduplication: ${candidates.length} \u2192 ${unique.length} unique candidates`), unique;
|
|
846
|
+
}
|
|
847
|
+
function getNewestCandidate(candidates) {
|
|
848
|
+
if (candidates.length === 0)
|
|
849
|
+
throw new Error("Cannot get newest candidate from empty array");
|
|
850
|
+
if (candidates.length === 1)
|
|
851
|
+
return candidates[0];
|
|
852
|
+
let newest = candidates[0];
|
|
853
|
+
for (let i = 1; i < candidates.length; i++) {
|
|
854
|
+
let current = candidates[i];
|
|
855
|
+
(current.mtime > newest.mtime || current.mtime === newest.mtime && current.displayPath < newest.displayPath) && (newest = current);
|
|
856
|
+
}
|
|
857
|
+
return newest;
|
|
858
|
+
}
|
|
859
|
+
function sortCandidatesByMtime(candidates) {
|
|
860
|
+
return [...candidates].sort((a, b) => b.mtime !== a.mtime ? b.mtime - a.mtime : a.displayPath.localeCompare(b.displayPath));
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
// ../core/src/core/save/save-interactive-resolver.ts
|
|
864
|
+
import { join as join3 } from "path";
|
|
865
|
+
|
|
866
|
+
// ../core/src/core/platform/platform-specific-paths.ts
|
|
867
|
+
function joinSegments(segments) {
|
|
868
|
+
return segments.filter(Boolean).join("/");
|
|
869
|
+
}
|
|
870
|
+
function suffixFileBasename(registryPath, platform) {
|
|
871
|
+
let segments = registryPath.split("/"), fileName = segments.pop();
|
|
872
|
+
if (!fileName)
|
|
873
|
+
return registryPath;
|
|
874
|
+
let lastDotIndex = fileName.lastIndexOf(".");
|
|
875
|
+
if (lastDotIndex <= 0)
|
|
876
|
+
return fileName.endsWith(`.${platform}`) ? segments.push(fileName) : segments.push(`${fileName}.${platform}`), joinSegments(segments);
|
|
877
|
+
let name = fileName.slice(0, lastDotIndex), ext = fileName.slice(lastDotIndex);
|
|
878
|
+
return name.endsWith(`.${platform}`) ? (segments.push(fileName), joinSegments(segments)) : (segments.push(`${name}.${platform}${ext}`), joinSegments(segments));
|
|
879
|
+
}
|
|
880
|
+
function createPlatformSpecificRegistryPath(registryPath, platform) {
|
|
881
|
+
let segments = registryPath.split("/"), fileName = segments[segments.length - 1], isRoot = segments.length === 1;
|
|
882
|
+
if (!fileName)
|
|
883
|
+
return registryPath;
|
|
884
|
+
if (isRoot) {
|
|
885
|
+
let definition = getPlatformDefinition(platform);
|
|
886
|
+
return definition?.rootFile ? definition.rootFile : null;
|
|
887
|
+
}
|
|
888
|
+
return registryPath.endsWith(FILE_PATTERNS.MD_FILES), suffixFileBasename(registryPath, platform);
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
// ../core/src/core/save/save-interactive-resolver.ts
|
|
892
|
+
async function resolveInteractively(input, output, prompt) {
|
|
893
|
+
let { registryPath, workspaceCandidates, group, packageRoot, workspaceRoot } = input, out = output ?? resolveOutput(), prm = prompt ?? resolvePrompt(), sortedCandidates = sortCandidatesByMtime(workspaceCandidates);
|
|
894
|
+
displayConflictHeader(registryPath, sortedCandidates, out);
|
|
895
|
+
let universalSelected = null, platformSpecificCandidates = [], skippedCandidates = [];
|
|
896
|
+
for (let candidate of sortedCandidates) {
|
|
897
|
+
let parityCheck = await isAtParity(candidate, group, packageRoot, workspaceRoot);
|
|
898
|
+
if (parityCheck.atParity) {
|
|
899
|
+
out.info(`
|
|
900
|
+
\u2713 ${candidate.displayPath}`), out.info(` ${parityCheck.reason} - auto-skipping
|
|
901
|
+
`), skippedCandidates.push(candidate);
|
|
902
|
+
continue;
|
|
903
|
+
}
|
|
904
|
+
if (universalSelected) {
|
|
905
|
+
let universalHash = await ensureComparableHash(universalSelected, workspaceRoot);
|
|
906
|
+
if (await ensureComparableHash(candidate, workspaceRoot) === universalHash) {
|
|
907
|
+
out.info(`
|
|
908
|
+
\u2713 ${candidate.displayPath}`), out.info(` Identical to universal - auto-skipping
|
|
909
|
+
`), skippedCandidates.push(candidate);
|
|
910
|
+
continue;
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
switch (await promptCandidateAction(
|
|
914
|
+
candidate,
|
|
915
|
+
registryPath,
|
|
916
|
+
universalSelected !== null,
|
|
917
|
+
prm
|
|
918
|
+
)) {
|
|
919
|
+
case "universal":
|
|
920
|
+
universalSelected = candidate, out.success(`
|
|
921
|
+
\u2713 Selected as universal: ${candidate.displayPath}
|
|
922
|
+
`);
|
|
923
|
+
break;
|
|
924
|
+
case "platform-specific":
|
|
925
|
+
platformSpecificCandidates.push(candidate), out.success(`
|
|
926
|
+
\u2713 Marked as platform-specific: ${candidate.displayPath}
|
|
927
|
+
`);
|
|
928
|
+
break;
|
|
929
|
+
case "skip":
|
|
930
|
+
skippedCandidates.push(candidate), out.info(`
|
|
931
|
+
\u2713 Skipped: ${candidate.displayPath}
|
|
932
|
+
`);
|
|
933
|
+
break;
|
|
934
|
+
}
|
|
935
|
+
}
|
|
936
|
+
return displayResolutionSummary(universalSelected, platformSpecificCandidates, skippedCandidates, out), {
|
|
937
|
+
selectedCandidate: universalSelected,
|
|
938
|
+
platformSpecificCandidates
|
|
939
|
+
};
|
|
940
|
+
}
|
|
941
|
+
async function checkForwardParity(workspaceCandidate, localCandidate, packageRoot, workspaceRoot) {
|
|
942
|
+
try {
|
|
943
|
+
let result = await convertSourceToWorkspace(
|
|
944
|
+
localCandidate.content,
|
|
945
|
+
workspaceCandidate.platform,
|
|
946
|
+
localCandidate.registryPath,
|
|
947
|
+
workspaceCandidate.displayPath,
|
|
948
|
+
workspaceRoot
|
|
949
|
+
);
|
|
950
|
+
if (result.success && result.convertedHash && (logger.debug(
|
|
951
|
+
`Forward conversion check: converted source hash=${result.convertedHash}, workspace hash=${workspaceCandidate.contentHash}`
|
|
952
|
+
), result.convertedHash === workspaceCandidate.contentHash))
|
|
953
|
+
return {
|
|
954
|
+
atParity: !0,
|
|
955
|
+
reason: "Matches source after forward conversion (export flow)"
|
|
956
|
+
};
|
|
957
|
+
} catch (error) {
|
|
958
|
+
logger.debug(`Forward parity check failed: ${error}`);
|
|
959
|
+
}
|
|
960
|
+
return { atParity: !1 };
|
|
961
|
+
}
|
|
962
|
+
async function isAtParity(candidate, group, packageRoot, workspaceRoot) {
|
|
963
|
+
if (group.local) {
|
|
964
|
+
let comparisonHash = await ensureComparableHash(candidate, workspaceRoot);
|
|
965
|
+
if (logger.debug(
|
|
966
|
+
`Parity check for ${candidate.displayPath}: comparisonHash=${comparisonHash}, localHash=${group.local.contentHash}`
|
|
967
|
+
), comparisonHash === group.local.contentHash)
|
|
968
|
+
return {
|
|
969
|
+
atParity: !0,
|
|
970
|
+
reason: "Already matches universal (cached comparable hash)"
|
|
971
|
+
};
|
|
972
|
+
if (candidate.platform && candidate.platform !== "ai" && !(candidate.mergeStrategy && candidate.mergeKeys && candidate.mergeKeys.length > 0)) {
|
|
973
|
+
let forwardCheck = await checkForwardParity(
|
|
974
|
+
candidate,
|
|
975
|
+
group.local,
|
|
976
|
+
packageRoot,
|
|
977
|
+
workspaceRoot
|
|
978
|
+
);
|
|
979
|
+
if (forwardCheck.atParity)
|
|
980
|
+
return forwardCheck;
|
|
981
|
+
}
|
|
982
|
+
}
|
|
983
|
+
if (candidate.platform && candidate.platform !== "ai") {
|
|
984
|
+
let platformPath = createPlatformSpecificRegistryPath(
|
|
985
|
+
group.registryPath,
|
|
986
|
+
candidate.platform
|
|
987
|
+
);
|
|
988
|
+
if (platformPath) {
|
|
989
|
+
let platformFullPath = join3(packageRoot, platformPath);
|
|
990
|
+
if (await exists(platformFullPath))
|
|
991
|
+
try {
|
|
992
|
+
let platformContent = await readTextFile(platformFullPath), platformHash = await calculateFileHash(platformContent);
|
|
993
|
+
if (candidate.contentHash === platformHash)
|
|
994
|
+
return {
|
|
995
|
+
atParity: !0,
|
|
996
|
+
reason: "Already matches platform-specific file"
|
|
997
|
+
};
|
|
998
|
+
} catch (error) {
|
|
999
|
+
logger.debug(`Could not read platform file ${platformFullPath}: ${error}`);
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
1002
|
+
}
|
|
1003
|
+
return { atParity: !1 };
|
|
1004
|
+
}
|
|
1005
|
+
async function promptCandidateAction(candidate, registryPath, universalAlreadySelected, prm) {
|
|
1006
|
+
let candidateLabel = formatCandidateLabel(candidate, !0), choices = universalAlreadySelected ? [
|
|
1007
|
+
{ title: "Mark as platform-specific", value: "platform-specific" },
|
|
1008
|
+
{ title: "Skip", value: "skip" }
|
|
1009
|
+
] : [
|
|
1010
|
+
{ title: "Set as universal", value: "universal" },
|
|
1011
|
+
{ title: "Mark as platform-specific", value: "platform-specific" },
|
|
1012
|
+
{ title: "Skip", value: "skip" }
|
|
1013
|
+
];
|
|
1014
|
+
return await prm.select(
|
|
1015
|
+
` ${candidateLabel}
|
|
1016
|
+
What should we do with this file?`,
|
|
1017
|
+
choices,
|
|
1018
|
+
"Arrow keys to navigate, Enter to select"
|
|
1019
|
+
);
|
|
1020
|
+
}
|
|
1021
|
+
function displayConflictHeader(registryPath, candidates, out) {
|
|
1022
|
+
out.warn(`Multiple workspace versions found for ${registryPath}`), out.info(` Resolving conflicts for ${candidates.length} file(s)...
|
|
1023
|
+
`);
|
|
1024
|
+
}
|
|
1025
|
+
function displayResolutionSummary(universal, platformSpecific, skipped, out) {
|
|
1026
|
+
out.info("\u2500".repeat(60)), out.info("Resolution summary:"), universal ? out.success(` \u2713 Universal: ${universal.displayPath}`) : out.info(" \u2139 No universal content selected"), platformSpecific.length > 0 && (out.success(` \u2713 Platform-specific: ${platformSpecific.length} file(s)`), platformSpecific.forEach((c) => {
|
|
1027
|
+
let platform = c.platform ? `(${c.platform})` : "";
|
|
1028
|
+
out.info(` \u2022 ${c.displayPath} ${platform}`);
|
|
1029
|
+
})), skipped.length > 0 && out.info(` \u2022 Skipped: ${skipped.length} file(s)`), out.info("\u2500".repeat(60) + `
|
|
1030
|
+
`);
|
|
1031
|
+
}
|
|
1032
|
+
function formatCandidateLabel(candidate, includeTimestamp = !1) {
|
|
1033
|
+
let parts = [];
|
|
1034
|
+
if (parts.push(candidate.displayPath), candidate.platform && candidate.platform !== "ai" && parts.push(`(${candidate.platform})`), includeTimestamp) {
|
|
1035
|
+
let timestamp = new Date(candidate.mtime).toLocaleString();
|
|
1036
|
+
parts.push(`[${timestamp}]`);
|
|
1037
|
+
}
|
|
1038
|
+
return parts.join(" ");
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
// ../core/src/core/save/save-resolution-executor.ts
|
|
1042
|
+
async function executeResolution(group, analysis, packageRoot, workspaceRoot, output, prompt) {
|
|
1043
|
+
let strategy = analysis.recommendedStrategy;
|
|
1044
|
+
if (strategy === "skip")
|
|
1045
|
+
return null;
|
|
1046
|
+
let sortedCandidates = sortCandidatesByMtime(analysis.uniqueWorkspaceCandidates);
|
|
1047
|
+
switch (strategy) {
|
|
1048
|
+
case "write-single":
|
|
1049
|
+
return resolveSingle(sortedCandidates[0]);
|
|
1050
|
+
case "write-newest":
|
|
1051
|
+
return resolveIdentical(sortedCandidates);
|
|
1052
|
+
case "force-newest":
|
|
1053
|
+
return resolveForce(sortedCandidates, group.registryPath);
|
|
1054
|
+
case "interactive":
|
|
1055
|
+
return resolveInteractive(
|
|
1056
|
+
group.registryPath,
|
|
1057
|
+
sortedCandidates,
|
|
1058
|
+
analysis.isRootFile,
|
|
1059
|
+
group,
|
|
1060
|
+
packageRoot,
|
|
1061
|
+
workspaceRoot,
|
|
1062
|
+
output,
|
|
1063
|
+
prompt
|
|
1064
|
+
);
|
|
1065
|
+
default:
|
|
1066
|
+
return logger.warn(`Unknown resolution strategy: ${strategy}`), null;
|
|
1067
|
+
}
|
|
1068
|
+
}
|
|
1069
|
+
function resolveSingle(candidate) {
|
|
1070
|
+
return {
|
|
1071
|
+
selection: candidate,
|
|
1072
|
+
platformSpecific: [],
|
|
1073
|
+
strategy: "write-single",
|
|
1074
|
+
wasInteractive: !1
|
|
1075
|
+
};
|
|
1076
|
+
}
|
|
1077
|
+
function resolveIdentical(candidates) {
|
|
1078
|
+
return {
|
|
1079
|
+
selection: getNewestCandidate(candidates),
|
|
1080
|
+
platformSpecific: [],
|
|
1081
|
+
strategy: "write-newest",
|
|
1082
|
+
wasInteractive: !1
|
|
1083
|
+
};
|
|
1084
|
+
}
|
|
1085
|
+
function resolveForce(candidates, registryPath) {
|
|
1086
|
+
let newest = getNewestCandidate(candidates), maxMtime = newest.mtime, tiedCandidates = candidates.filter((c) => c.mtime === maxMtime);
|
|
1087
|
+
if (tiedCandidates.length > 1)
|
|
1088
|
+
logger.info(
|
|
1089
|
+
`Force mode: Multiple files have same modification time (${formatTimestamp(maxMtime)})`
|
|
1090
|
+
), logger.info(` Auto-selecting first alphabetically: ${newest.displayPath}`), logger.info(" Tied files:"), tiedCandidates.forEach((c) => {
|
|
1091
|
+
let marker = c === newest ? "\u2192" : " ";
|
|
1092
|
+
logger.info(` ${marker} ${c.displayPath}`);
|
|
1093
|
+
}), tiedCandidates.filter((c) => c !== newest).forEach((c) => {
|
|
1094
|
+
logger.info(` Skipping: ${c.displayPath} (tied, not alphabetically first)`);
|
|
1095
|
+
}), candidates.filter((c) => c.mtime < maxMtime).forEach((c) => {
|
|
1096
|
+
logger.info(` Skipping: ${c.displayPath} (older)`);
|
|
1097
|
+
});
|
|
1098
|
+
else {
|
|
1099
|
+
logger.info(`Force mode: Auto-selecting newest (${newest.displayPath})`);
|
|
1100
|
+
let skipped = candidates.filter((c) => c !== newest);
|
|
1101
|
+
skipped.length > 0 && skipped.forEach((c) => {
|
|
1102
|
+
logger.info(` Skipping: ${c.displayPath} (older)`);
|
|
1103
|
+
});
|
|
1104
|
+
}
|
|
1105
|
+
return {
|
|
1106
|
+
selection: newest,
|
|
1107
|
+
platformSpecific: [],
|
|
1108
|
+
// Force mode doesn't auto-create platform-specific variants
|
|
1109
|
+
strategy: "force-newest",
|
|
1110
|
+
wasInteractive: !1
|
|
1111
|
+
};
|
|
1112
|
+
}
|
|
1113
|
+
async function resolveInteractive(registryPath, candidates, isRootFile, group, packageRoot, workspaceRoot, output, prompt) {
|
|
1114
|
+
let result = await resolveInteractively({
|
|
1115
|
+
registryPath,
|
|
1116
|
+
workspaceCandidates: candidates,
|
|
1117
|
+
isRootFile,
|
|
1118
|
+
group,
|
|
1119
|
+
packageRoot,
|
|
1120
|
+
workspaceRoot
|
|
1121
|
+
}, output, prompt);
|
|
1122
|
+
return {
|
|
1123
|
+
selection: result.selectedCandidate,
|
|
1124
|
+
platformSpecific: result.platformSpecificCandidates,
|
|
1125
|
+
strategy: "interactive",
|
|
1126
|
+
wasInteractive: !0
|
|
1127
|
+
};
|
|
1128
|
+
}
|
|
1129
|
+
function formatTimestamp(mtime) {
|
|
1130
|
+
return new Date(mtime).toLocaleString();
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1133
|
+
// ../core/src/core/save/save-platform-handler.ts
|
|
1134
|
+
import { join as join4 } from "path";
|
|
1135
|
+
async function pruneExistingPlatformCandidates(packageRoot, groups) {
|
|
1136
|
+
for (let group of groups) {
|
|
1137
|
+
if (!group.local && !group.localRef)
|
|
1138
|
+
continue;
|
|
1139
|
+
let filtered = [];
|
|
1140
|
+
for (let candidate of group.workspace) {
|
|
1141
|
+
let platform = candidate.platform;
|
|
1142
|
+
if (!platform || platform === "ai") {
|
|
1143
|
+
filtered.push(candidate);
|
|
1144
|
+
continue;
|
|
1145
|
+
}
|
|
1146
|
+
let platformPath = createPlatformSpecificRegistryPath(
|
|
1147
|
+
group.registryPath,
|
|
1148
|
+
platform
|
|
1149
|
+
);
|
|
1150
|
+
if (!platformPath) {
|
|
1151
|
+
filtered.push(candidate);
|
|
1152
|
+
continue;
|
|
1153
|
+
}
|
|
1154
|
+
let platformFullPath = join4(packageRoot, platformPath);
|
|
1155
|
+
if (await exists(platformFullPath)) {
|
|
1156
|
+
logger.debug(
|
|
1157
|
+
`Pruning workspace candidate ${candidate.displayPath} for ${group.registryPath} (platform file ${platformPath} exists in source)`
|
|
1158
|
+
);
|
|
1159
|
+
continue;
|
|
1160
|
+
}
|
|
1161
|
+
filtered.push(candidate);
|
|
1162
|
+
}
|
|
1163
|
+
group.workspace = filtered;
|
|
1164
|
+
}
|
|
1165
|
+
}
|
|
1166
|
+
|
|
1167
|
+
// ../core/src/core/save/save-write-coordinator.ts
|
|
1168
|
+
import { dirname, join as join5 } from "path";
|
|
1169
|
+
import { minimatch as minimatch3 } from "minimatch";
|
|
1170
|
+
async function writeResolution(packageRoot, registryPath, resolution, localCandidate, workspaceRoot) {
|
|
1171
|
+
let results = [];
|
|
1172
|
+
if (resolution.selection) {
|
|
1173
|
+
let universalResult = await writeUniversal(
|
|
1174
|
+
packageRoot,
|
|
1175
|
+
registryPath,
|
|
1176
|
+
resolution.selection,
|
|
1177
|
+
localCandidate,
|
|
1178
|
+
workspaceRoot
|
|
1179
|
+
);
|
|
1180
|
+
results.push(universalResult);
|
|
1181
|
+
} else
|
|
1182
|
+
logger.debug(`No universal content selected for ${registryPath} - keeping original untouched`);
|
|
1183
|
+
for (let platformCandidate of resolution.platformSpecific) {
|
|
1184
|
+
let platformResult = await writePlatformSpecific(
|
|
1185
|
+
packageRoot,
|
|
1186
|
+
registryPath,
|
|
1187
|
+
platformCandidate,
|
|
1188
|
+
workspaceRoot
|
|
1189
|
+
);
|
|
1190
|
+
results.push(platformResult);
|
|
1191
|
+
}
|
|
1192
|
+
return results;
|
|
1193
|
+
}
|
|
1194
|
+
async function writeUniversal(packageRoot, registryPath, candidate, localCandidate, workspaceRoot) {
|
|
1195
|
+
let targetPath = join5(packageRoot, registryPath), preparedContent = await prepareContentForWrite(candidate, registryPath, workspaceRoot), writeDecision = shouldWrite(candidate, localCandidate, preparedContent.content), operation = {
|
|
1196
|
+
registryPath,
|
|
1197
|
+
targetPath,
|
|
1198
|
+
content: preparedContent.content,
|
|
1199
|
+
operation: writeDecision.operation,
|
|
1200
|
+
isPlatformSpecific: !1
|
|
1201
|
+
};
|
|
1202
|
+
if (!writeDecision.needed)
|
|
1203
|
+
return logger.debug(`Skipping write for ${registryPath}: content identical to source`), {
|
|
1204
|
+
operation,
|
|
1205
|
+
success: !0
|
|
1206
|
+
};
|
|
1207
|
+
let writeResult = await safeWrite(targetPath, preparedContent.content);
|
|
1208
|
+
if (writeResult.success) {
|
|
1209
|
+
let action = operation.operation === "create" ? "Created" : "Updated";
|
|
1210
|
+
logger.debug(`${action} ${registryPath}${preparedContent.wasExtracted ? " (extracted package contribution)" : ""}`);
|
|
1211
|
+
}
|
|
1212
|
+
return {
|
|
1213
|
+
operation,
|
|
1214
|
+
success: writeResult.success,
|
|
1215
|
+
error: writeResult.error
|
|
1216
|
+
};
|
|
1217
|
+
}
|
|
1218
|
+
async function writePlatformSpecific(packageRoot, registryPath, candidate, workspaceRoot) {
|
|
1219
|
+
let platform = candidate.platform;
|
|
1220
|
+
if (!platform || platform === "ai")
|
|
1221
|
+
return {
|
|
1222
|
+
operation: {
|
|
1223
|
+
registryPath,
|
|
1224
|
+
targetPath: "",
|
|
1225
|
+
content: "",
|
|
1226
|
+
operation: "skip",
|
|
1227
|
+
isPlatformSpecific: !0
|
|
1228
|
+
},
|
|
1229
|
+
success: !1,
|
|
1230
|
+
error: new Error("Candidate has no platform association")
|
|
1231
|
+
};
|
|
1232
|
+
let platformRegistryPath = createPlatformSpecificRegistryPath(registryPath, platform);
|
|
1233
|
+
if (!platformRegistryPath)
|
|
1234
|
+
return {
|
|
1235
|
+
operation: {
|
|
1236
|
+
registryPath,
|
|
1237
|
+
targetPath: "",
|
|
1238
|
+
content: "",
|
|
1239
|
+
operation: "skip",
|
|
1240
|
+
isPlatformSpecific: !0,
|
|
1241
|
+
platform
|
|
1242
|
+
},
|
|
1243
|
+
success: !1,
|
|
1244
|
+
error: new Error(`Could not create platform-specific path for ${platform}`)
|
|
1245
|
+
};
|
|
1246
|
+
let targetPath = join5(packageRoot, platformRegistryPath), preparedContent = await prepareContentForWrite(candidate, platformRegistryPath, workspaceRoot), fileExists = await exists(targetPath), operationType = fileExists ? "update" : "create", operation = {
|
|
1247
|
+
registryPath: platformRegistryPath,
|
|
1248
|
+
targetPath,
|
|
1249
|
+
content: preparedContent.content,
|
|
1250
|
+
operation: operationType,
|
|
1251
|
+
isPlatformSpecific: !0,
|
|
1252
|
+
platform
|
|
1253
|
+
};
|
|
1254
|
+
if (fileExists)
|
|
1255
|
+
try {
|
|
1256
|
+
if (await readTextFile(targetPath) === preparedContent.content)
|
|
1257
|
+
return logger.debug(`Skipping write for ${platformRegistryPath}: content identical`), operation.operation = "skip", {
|
|
1258
|
+
operation,
|
|
1259
|
+
success: !0
|
|
1260
|
+
};
|
|
1261
|
+
} catch (error) {
|
|
1262
|
+
logger.debug(`Could not read existing file ${platformRegistryPath}: ${error}`);
|
|
1263
|
+
}
|
|
1264
|
+
let writeResult = await safeWrite(targetPath, preparedContent.content);
|
|
1265
|
+
if (writeResult.success) {
|
|
1266
|
+
let action = operationType === "create" ? "Created" : "Updated";
|
|
1267
|
+
logger.debug(
|
|
1268
|
+
`${action} platform-specific file: ${platformRegistryPath}${preparedContent.wasExtracted ? " (extracted package contribution)" : ""}`
|
|
1269
|
+
);
|
|
1270
|
+
}
|
|
1271
|
+
return {
|
|
1272
|
+
operation,
|
|
1273
|
+
success: writeResult.success,
|
|
1274
|
+
error: writeResult.error
|
|
1275
|
+
};
|
|
1276
|
+
}
|
|
1277
|
+
async function applyImportTransformation(content, platform, registryPath, workspacePath, workspaceRoot) {
|
|
1278
|
+
try {
|
|
1279
|
+
let platformImportFlows = getPlatformDefinition(platform, workspaceRoot).import || [], allImportFlows = [...getGlobalImportFlows(workspaceRoot) || [], ...platformImportFlows];
|
|
1280
|
+
if (allImportFlows.length === 0)
|
|
1281
|
+
return {
|
|
1282
|
+
success: !1,
|
|
1283
|
+
reason: "No import flows defined for platform"
|
|
1284
|
+
};
|
|
1285
|
+
let matchingFlow = findMatchingFlow(
|
|
1286
|
+
allImportFlows,
|
|
1287
|
+
workspacePath,
|
|
1288
|
+
registryPath,
|
|
1289
|
+
platform,
|
|
1290
|
+
workspaceRoot
|
|
1291
|
+
);
|
|
1292
|
+
if (!matchingFlow)
|
|
1293
|
+
return {
|
|
1294
|
+
success: !1,
|
|
1295
|
+
reason: "No matching import flow found"
|
|
1296
|
+
};
|
|
1297
|
+
if (!matchingFlow.map || matchingFlow.map.length === 0)
|
|
1298
|
+
return {
|
|
1299
|
+
success: !1,
|
|
1300
|
+
reason: "Flow has no map operations"
|
|
1301
|
+
};
|
|
1302
|
+
let tempDir = await allocateTempSubdir(), inputDir = join5(tempDir, "in"), outputDir = join5(tempDir, "out");
|
|
1303
|
+
await ensureDir(inputDir), await ensureDir(outputDir);
|
|
1304
|
+
let inputFileName = registryPath.split("/").pop() || "file.json", inputFilePath = join5(inputDir, inputFileName);
|
|
1305
|
+
await writeTextFile(inputFilePath, content);
|
|
1306
|
+
let flowContext = {
|
|
1307
|
+
workspaceRoot: outputDir,
|
|
1308
|
+
packageRoot: inputDir,
|
|
1309
|
+
platform,
|
|
1310
|
+
packageName: "temp",
|
|
1311
|
+
direction: "install",
|
|
1312
|
+
// Import flows run during install
|
|
1313
|
+
variables: {
|
|
1314
|
+
name: "temp",
|
|
1315
|
+
version: "0.0.0",
|
|
1316
|
+
platform,
|
|
1317
|
+
source: platform,
|
|
1318
|
+
sourcePlatform: platform,
|
|
1319
|
+
targetPlatform: "openpackage"
|
|
1320
|
+
},
|
|
1321
|
+
dryRun: !1
|
|
1322
|
+
}, executor = createFlowExecutor(), concreteFlow = {
|
|
1323
|
+
...matchingFlow,
|
|
1324
|
+
from: inputFileName
|
|
1325
|
+
}, flowResult = await executor.executeFlow(concreteFlow, flowContext);
|
|
1326
|
+
if (!flowResult.success)
|
|
1327
|
+
return {
|
|
1328
|
+
success: !1,
|
|
1329
|
+
reason: `Flow execution failed: ${flowResult.error?.message}`
|
|
1330
|
+
};
|
|
1331
|
+
if (typeof flowResult.target != "string")
|
|
1332
|
+
return {
|
|
1333
|
+
success: !1,
|
|
1334
|
+
reason: "Flow did not produce target path"
|
|
1335
|
+
};
|
|
1336
|
+
let outputFilePath = flowResult.target;
|
|
1337
|
+
if (!await exists(outputFilePath))
|
|
1338
|
+
return {
|
|
1339
|
+
success: !1,
|
|
1340
|
+
reason: "Flow did not produce output file"
|
|
1341
|
+
};
|
|
1342
|
+
let transformedContent = await readTextFile(outputFilePath);
|
|
1343
|
+
return logger.debug(
|
|
1344
|
+
`Successfully transformed extracted content from ${platform} format to universal`,
|
|
1345
|
+
{ registryPath }
|
|
1346
|
+
), {
|
|
1347
|
+
success: !0,
|
|
1348
|
+
transformedContent
|
|
1349
|
+
};
|
|
1350
|
+
} catch (error) {
|
|
1351
|
+
return logger.debug(
|
|
1352
|
+
`Import transformation failed for ${registryPath} (platform: ${platform})`,
|
|
1353
|
+
{ error }
|
|
1354
|
+
), {
|
|
1355
|
+
success: !1,
|
|
1356
|
+
reason: error instanceof Error ? error.message : String(error)
|
|
1357
|
+
};
|
|
1358
|
+
}
|
|
1359
|
+
}
|
|
1360
|
+
function findMatchingFlow(flows, workspacePath, registryPath, platform, workspaceRoot) {
|
|
1361
|
+
let registryPathCandidates = getRegistryPathCandidates(registryPath);
|
|
1362
|
+
for (let flow of flows) {
|
|
1363
|
+
let fromPatterns = Array.isArray(flow.from) ? flow.from : [flow.from], matchesFrom = !1;
|
|
1364
|
+
for (let fromPattern of fromPatterns) {
|
|
1365
|
+
if (typeof fromPattern == "object" && "$switch" in fromPattern) {
|
|
1366
|
+
let defaultValue = fromPattern.$switch?.default;
|
|
1367
|
+
if (typeof defaultValue == "string" && minimatch3(workspacePath, defaultValue, { dot: !0 })) {
|
|
1368
|
+
matchesFrom = !0;
|
|
1369
|
+
break;
|
|
1370
|
+
}
|
|
1371
|
+
continue;
|
|
1372
|
+
}
|
|
1373
|
+
if (typeof fromPattern == "string" && minimatch3(workspacePath, fromPattern, { dot: !0 })) {
|
|
1374
|
+
matchesFrom = !0;
|
|
1375
|
+
break;
|
|
1376
|
+
}
|
|
1377
|
+
}
|
|
1378
|
+
if (!matchesFrom)
|
|
1379
|
+
continue;
|
|
1380
|
+
let toPattern = Array.isArray(flow.to) ? flow.to[0] : flow.to;
|
|
1381
|
+
if (typeof toPattern == "object" && "$switch" in toPattern) {
|
|
1382
|
+
logger.debug("Skipping flow with switch expression in to field");
|
|
1383
|
+
continue;
|
|
1384
|
+
}
|
|
1385
|
+
if (!(typeof toPattern != "string" || !registryPathCandidates.some(
|
|
1386
|
+
(candidatePath) => minimatch3(candidatePath, toPattern, { dot: !0 })
|
|
1387
|
+
))) {
|
|
1388
|
+
if (flow.when && !evaluateWhenCondition2(flow.when, platform)) {
|
|
1389
|
+
logger.debug(`Flow condition not met for ${registryPath}`, { when: flow.when });
|
|
1390
|
+
continue;
|
|
1391
|
+
}
|
|
1392
|
+
return logger.debug(
|
|
1393
|
+
`Matched import flow: from=${workspacePath} to=${registryPath}`,
|
|
1394
|
+
{ platform }
|
|
1395
|
+
), flow;
|
|
1396
|
+
}
|
|
1397
|
+
}
|
|
1398
|
+
logger.debug(
|
|
1399
|
+
"No matching import flow found",
|
|
1400
|
+
{ workspacePath, registryPath, platform, flowCount: flows.length }
|
|
1401
|
+
);
|
|
1402
|
+
}
|
|
1403
|
+
function getRegistryPathCandidates(registryPath) {
|
|
1404
|
+
let candidates = [registryPath];
|
|
1405
|
+
return registryPath.endsWith(".jsonc") ? candidates.push(registryPath.replace(/\.jsonc$/, ".json")) : registryPath.endsWith(".json") && candidates.push(registryPath.replace(/\.json$/, ".jsonc")), candidates;
|
|
1406
|
+
}
|
|
1407
|
+
function evaluateWhenCondition2(when, platform) {
|
|
1408
|
+
if (when.$eq && Array.isArray(when.$eq) && when.$eq.length === 2) {
|
|
1409
|
+
let left = resolveVariable2(when.$eq[0], platform), right = resolveVariable2(when.$eq[1], platform);
|
|
1410
|
+
return left === right;
|
|
1411
|
+
}
|
|
1412
|
+
if (when.$ne && Array.isArray(when.$ne) && when.$ne.length === 2) {
|
|
1413
|
+
let left = resolveVariable2(when.$ne[0], platform), right = resolveVariable2(when.$ne[1], platform);
|
|
1414
|
+
return left !== right;
|
|
1415
|
+
}
|
|
1416
|
+
return when.exists ? !0 : (logger.debug("Unknown condition type in when clause", { when }), !1);
|
|
1417
|
+
}
|
|
1418
|
+
function resolveVariable2(value, platform) {
|
|
1419
|
+
return typeof value == "string" && (value === "$$platform" || value === "$$source") ? platform : value;
|
|
1420
|
+
}
|
|
1421
|
+
async function prepareContentForWrite(candidate, registryPath, workspaceRoot) {
|
|
1422
|
+
if (!!!(candidate.source === "workspace" && candidate.mergeStrategy && candidate.mergeKeys && candidate.mergeKeys.length > 0))
|
|
1423
|
+
return { content: candidate.content, wasExtracted: !1 };
|
|
1424
|
+
logger.debug(
|
|
1425
|
+
`Extracting package contribution from merged file: ${registryPath}`,
|
|
1426
|
+
{
|
|
1427
|
+
strategy: candidate.mergeStrategy,
|
|
1428
|
+
keyCount: candidate.mergeKeys.length,
|
|
1429
|
+
platform: candidate.platform
|
|
1430
|
+
}
|
|
1431
|
+
);
|
|
1432
|
+
let extractResult = await extractPackageContribution(candidate);
|
|
1433
|
+
if (extractResult.success && extractResult.extractedContent) {
|
|
1434
|
+
logger.info(
|
|
1435
|
+
`Successfully extracted package contribution for ${registryPath} (${candidate.mergeKeys.length} key(s))`
|
|
1436
|
+
);
|
|
1437
|
+
let finalContent = extractResult.extractedContent;
|
|
1438
|
+
if (candidate.platform && candidate.platform !== "ai" && workspaceRoot) {
|
|
1439
|
+
logger.info(
|
|
1440
|
+
"Attempting import transformation for merged file",
|
|
1441
|
+
{
|
|
1442
|
+
platform: candidate.platform,
|
|
1443
|
+
registryPath,
|
|
1444
|
+
displayPath: candidate.displayPath,
|
|
1445
|
+
hasWorkspaceRoot: !!workspaceRoot
|
|
1446
|
+
}
|
|
1447
|
+
);
|
|
1448
|
+
let transformResult = await applyImportTransformation(
|
|
1449
|
+
extractResult.extractedContent,
|
|
1450
|
+
candidate.platform,
|
|
1451
|
+
registryPath,
|
|
1452
|
+
candidate.displayPath,
|
|
1453
|
+
workspaceRoot
|
|
1454
|
+
);
|
|
1455
|
+
transformResult.success && transformResult.transformedContent ? (logger.info(
|
|
1456
|
+
`Successfully applied import transformation for platform ${candidate.platform}`,
|
|
1457
|
+
{ registryPath }
|
|
1458
|
+
), finalContent = transformResult.transformedContent) : logger.warn(
|
|
1459
|
+
`Import transformation failed or not applicable: ${transformResult.reason}`,
|
|
1460
|
+
{ registryPath, platform: candidate.platform }
|
|
1461
|
+
);
|
|
1462
|
+
} else
|
|
1463
|
+
logger.debug(
|
|
1464
|
+
"Skipping import transformation",
|
|
1465
|
+
{
|
|
1466
|
+
hasPlatform: !!candidate.platform,
|
|
1467
|
+
platform: candidate.platform,
|
|
1468
|
+
hasWorkspaceRoot: !!workspaceRoot
|
|
1469
|
+
}
|
|
1470
|
+
);
|
|
1471
|
+
return {
|
|
1472
|
+
content: finalContent,
|
|
1473
|
+
wasExtracted: !0
|
|
1474
|
+
};
|
|
1475
|
+
}
|
|
1476
|
+
return logger.warn(
|
|
1477
|
+
`Failed to extract package contribution from merged file: ${registryPath}`,
|
|
1478
|
+
{
|
|
1479
|
+
reason: extractResult.error,
|
|
1480
|
+
fallback: "Using full content"
|
|
1481
|
+
}
|
|
1482
|
+
), logger.warn(
|
|
1483
|
+
`\u26A0\uFE0F Writing full merged content to ${registryPath} - this may include keys from other packages or base workspace content`
|
|
1484
|
+
), { content: candidate.content, wasExtracted: !1 };
|
|
1485
|
+
}
|
|
1486
|
+
function shouldWrite(candidate, localCandidate, preparedContent) {
|
|
1487
|
+
return localCandidate ? preparedContent && preparedContent !== candidate.content ? preparedContent === localCandidate.content ? { needed: !1, operation: "skip" } : { needed: !0, operation: "update" } : candidate.contentHash === localCandidate.contentHash ? { needed: !1, operation: "skip" } : { needed: !0, operation: "update" } : { needed: !0, operation: "create" };
|
|
1488
|
+
}
|
|
1489
|
+
async function safeWrite(targetPath, content) {
|
|
1490
|
+
try {
|
|
1491
|
+
return await ensureDir(dirname(targetPath)), await writeTextFile(targetPath, content), { success: !0 };
|
|
1492
|
+
} catch (error) {
|
|
1493
|
+
return logger.error(`Failed to write file ${targetPath}: ${error}`), {
|
|
1494
|
+
success: !1,
|
|
1495
|
+
error: error instanceof Error ? error : new Error(String(error))
|
|
1496
|
+
};
|
|
1497
|
+
}
|
|
1498
|
+
}
|
|
1499
|
+
|
|
1500
|
+
// ../core/src/core/save/save-result-reporter.ts
|
|
1501
|
+
function buildSaveReport(packageName, analyses, allWriteResults) {
|
|
1502
|
+
let totalGroups = analyses.length, groupsWithAction = analyses.filter(
|
|
1503
|
+
(a) => a.type !== "no-action-needed" && a.type !== "no-change-needed"
|
|
1504
|
+
).length, flatResults = allWriteResults.flat(), successfulWrites = flatResults.filter((r) => r.success), filesSaved = successfulWrites.length, filesCreated = successfulWrites.filter(
|
|
1505
|
+
(r) => r.operation.operation === "create"
|
|
1506
|
+
).length, filesUpdated = successfulWrites.filter(
|
|
1507
|
+
(r) => r.operation.operation === "update"
|
|
1508
|
+
).length, platformSpecificFiles = successfulWrites.filter(
|
|
1509
|
+
(r) => r.operation.isPlatformSpecific
|
|
1510
|
+
).length, interactiveResolutions = analyses.filter(
|
|
1511
|
+
(a) => a.recommendedStrategy === "interactive" && a.type === "needs-resolution"
|
|
1512
|
+
).length, errors = flatResults.filter((r) => !r.success).map((r) => ({
|
|
1513
|
+
path: r.operation.registryPath,
|
|
1514
|
+
error: r.error || new Error("Unknown write error")
|
|
1515
|
+
}));
|
|
1516
|
+
return {
|
|
1517
|
+
packageName,
|
|
1518
|
+
totalGroups,
|
|
1519
|
+
groupsWithAction,
|
|
1520
|
+
filesSaved,
|
|
1521
|
+
filesCreated,
|
|
1522
|
+
filesUpdated,
|
|
1523
|
+
platformSpecificFiles,
|
|
1524
|
+
interactiveResolutions,
|
|
1525
|
+
errors,
|
|
1526
|
+
writeResults: flatResults
|
|
1527
|
+
};
|
|
1528
|
+
}
|
|
1529
|
+
function createCommandResult(report) {
|
|
1530
|
+
return {
|
|
1531
|
+
success: !0,
|
|
1532
|
+
data: {
|
|
1533
|
+
message: formatSaveMessage(report),
|
|
1534
|
+
report
|
|
1535
|
+
}
|
|
1536
|
+
};
|
|
1537
|
+
}
|
|
1538
|
+
function createSuccessResult(packageName, message) {
|
|
1539
|
+
return {
|
|
1540
|
+
success: !0,
|
|
1541
|
+
data: {
|
|
1542
|
+
message,
|
|
1543
|
+
packageName
|
|
1544
|
+
}
|
|
1545
|
+
};
|
|
1546
|
+
}
|
|
1547
|
+
function createErrorResult(error) {
|
|
1548
|
+
return {
|
|
1549
|
+
success: !1,
|
|
1550
|
+
error
|
|
1551
|
+
};
|
|
1552
|
+
}
|
|
1553
|
+
function formatSaveMessage(report) {
|
|
1554
|
+
let lines = [];
|
|
1555
|
+
if (report.filesSaved === 0 && report.errors.length === 0)
|
|
1556
|
+
return `\u2713 Saved ${report.packageName}
|
|
1557
|
+
No changes detected`;
|
|
1558
|
+
lines.push(`\u2713 Saved ${report.packageName}`), report.filesCreated > 0 && lines.push(` ${report.filesCreated} file(s) created`), report.filesUpdated > 0 && lines.push(` ${report.filesUpdated} file(s) updated`), report.platformSpecificFiles > 0 && lines.push(` ${report.platformSpecificFiles} platform-specific file(s)`), report.interactiveResolutions > 0 && lines.push(` ${report.interactiveResolutions} interactive resolution(s)`), report.errors.length > 0 && (lines.push(""), lines.push(`\u26A0\uFE0F ${report.errors.length} error(s) occurred:`), report.errors.forEach((err) => {
|
|
1559
|
+
lines.push(` \u2022 ${err.path}: ${err.error.message}`);
|
|
1560
|
+
}));
|
|
1561
|
+
let successfulWrites = report.writeResults.filter((r) => r.success);
|
|
1562
|
+
if (successfulWrites.length > 0) {
|
|
1563
|
+
lines.push(""), lines.push(" Files saved:");
|
|
1564
|
+
let sorted = [...successfulWrites].sort(
|
|
1565
|
+
(a, b) => a.operation.registryPath.localeCompare(b.operation.registryPath)
|
|
1566
|
+
);
|
|
1567
|
+
for (let result of sorted) {
|
|
1568
|
+
let { registryPath, isPlatformSpecific, platform } = result.operation, label = isPlatformSpecific && platform ? `${registryPath} (${platform})` : `${registryPath} (universal)`;
|
|
1569
|
+
lines.push(` \u251C\u2500\u2500 ${label}`);
|
|
1570
|
+
}
|
|
1571
|
+
}
|
|
1572
|
+
return report.filesSaved > 0 && (lines.push(""), lines.push("\u{1F4A1} Changes saved to package source."), lines.push(" To sync changes to workspace, run:"), lines.push(` opkg install ${report.packageName}`)), lines.join(`
|
|
1573
|
+
`);
|
|
1574
|
+
}
|
|
1575
|
+
|
|
1576
|
+
// ../core/src/core/save/save-to-source-pipeline.ts
|
|
1577
|
+
async function runSaveToSourcePipeline(packageName, options = {}, ctx) {
|
|
1578
|
+
try {
|
|
1579
|
+
await initSharedTempDir(), logger.debug(`Validating save preconditions for ${packageName}`);
|
|
1580
|
+
let validation = await validateSavePreconditions(packageName);
|
|
1581
|
+
if (!validation.valid)
|
|
1582
|
+
return createErrorResult(validation.error);
|
|
1583
|
+
let { cwd, packageRoot, filesMapping } = validation;
|
|
1584
|
+
logger.debug(`Building candidates for ${packageName}`);
|
|
1585
|
+
let candidateResult = await buildCandidates({
|
|
1586
|
+
packageRoot,
|
|
1587
|
+
workspaceRoot: cwd,
|
|
1588
|
+
filesMapping
|
|
1589
|
+
});
|
|
1590
|
+
candidateResult.errors.length > 0 && (logger.warn(`Encountered ${candidateResult.errors.length} error(s) building candidates`), candidateResult.errors.forEach(
|
|
1591
|
+
(err) => logger.warn(` ${err.path}: ${err.reason}`)
|
|
1592
|
+
)), logger.debug("Building candidate groups");
|
|
1593
|
+
let allGroups = buildCandidateGroups(
|
|
1594
|
+
candidateResult.localSourceRefs,
|
|
1595
|
+
candidateResult.workspaceCandidates,
|
|
1596
|
+
cwd
|
|
1597
|
+
), activeGroups = filterGroupsWithWorkspace(allGroups);
|
|
1598
|
+
if (activeGroups.length === 0)
|
|
1599
|
+
return logger.info(`No workspace changes detected for ${packageName}`), createSuccessResult(
|
|
1600
|
+
packageName,
|
|
1601
|
+
`\u2713 Saved ${packageName}
|
|
1602
|
+
No workspace changes detected`
|
|
1603
|
+
);
|
|
1604
|
+
logger.debug("Pruning existing platform-specific files"), await pruneExistingPlatformCandidates(packageRoot, activeGroups);
|
|
1605
|
+
let finalGroups = activeGroups.filter((g) => g.workspace.length > 0);
|
|
1606
|
+
if (finalGroups.length === 0)
|
|
1607
|
+
return logger.info(`No workspace changes detected for ${packageName}`), createSuccessResult(
|
|
1608
|
+
packageName,
|
|
1609
|
+
`\u2713 Saved ${packageName}
|
|
1610
|
+
No workspace changes detected`
|
|
1611
|
+
);
|
|
1612
|
+
for (let group of finalGroups)
|
|
1613
|
+
group.localRef && !group.local && (group.local = await materializeLocalCandidate(group.localRef, packageRoot) ?? void 0);
|
|
1614
|
+
logger.debug(`Processing ${finalGroups.length} group(s) with workspace candidates`);
|
|
1615
|
+
let groupAnalyses = await Promise.all(
|
|
1616
|
+
finalGroups.map(async (group) => ({
|
|
1617
|
+
group,
|
|
1618
|
+
analysis: await analyzeGroup(group, options.force ?? !1, cwd)
|
|
1619
|
+
}))
|
|
1620
|
+
), analyses = groupAnalyses.map((ga) => ga.analysis), allWriteResults = [], autoResolvable = [], interactive = [];
|
|
1621
|
+
for (let ga of groupAnalyses) {
|
|
1622
|
+
if (ga.analysis.type === "no-action-needed" || ga.analysis.type === "no-change-needed") {
|
|
1623
|
+
logger.debug(`Skipping ${ga.group.registryPath}: ${ga.analysis.type}`);
|
|
1624
|
+
continue;
|
|
1625
|
+
}
|
|
1626
|
+
ga.analysis.recommendedStrategy === "interactive" ? interactive.push(ga) : autoResolvable.push(ga);
|
|
1627
|
+
}
|
|
1628
|
+
if (autoResolvable.length > 0) {
|
|
1629
|
+
logger.debug(`Processing ${autoResolvable.length} auto-resolvable group(s) in parallel`);
|
|
1630
|
+
let autoResults = await Promise.all(
|
|
1631
|
+
autoResolvable.map(async ({ group, analysis }) => {
|
|
1632
|
+
let resolution = await executeResolution(group, analysis, packageRoot, cwd, resolveOutput(ctx), resolvePrompt(ctx));
|
|
1633
|
+
return resolution ? writeResolution(packageRoot, group.registryPath, resolution, group.local, cwd) : null;
|
|
1634
|
+
})
|
|
1635
|
+
);
|
|
1636
|
+
for (let result of autoResults)
|
|
1637
|
+
result && allWriteResults.push(result);
|
|
1638
|
+
}
|
|
1639
|
+
for (let { group, analysis } of interactive) {
|
|
1640
|
+
let resolution = await executeResolution(group, analysis, packageRoot, cwd, resolveOutput(ctx), resolvePrompt(ctx));
|
|
1641
|
+
if (!resolution) {
|
|
1642
|
+
logger.debug(`No resolution returned for ${group.registryPath}`);
|
|
1643
|
+
continue;
|
|
1644
|
+
}
|
|
1645
|
+
let writeResults = await writeResolution(
|
|
1646
|
+
packageRoot,
|
|
1647
|
+
group.registryPath,
|
|
1648
|
+
resolution,
|
|
1649
|
+
group.local,
|
|
1650
|
+
cwd
|
|
1651
|
+
);
|
|
1652
|
+
allWriteResults.push(writeResults);
|
|
1653
|
+
}
|
|
1654
|
+
logger.debug("Building save report");
|
|
1655
|
+
let report = buildSaveReport(packageName, analyses, allWriteResults);
|
|
1656
|
+
return createCommandResult(report);
|
|
1657
|
+
} finally {
|
|
1658
|
+
clearConversionCache(), await cleanupSharedTempDir();
|
|
1659
|
+
}
|
|
1660
|
+
}
|
|
1661
|
+
async function validateSavePreconditions(packageName) {
|
|
1662
|
+
let cwd = process.cwd();
|
|
1663
|
+
if (!packageName)
|
|
1664
|
+
return {
|
|
1665
|
+
valid: !1,
|
|
1666
|
+
error: "Package name is required for save."
|
|
1667
|
+
};
|
|
1668
|
+
let index;
|
|
1669
|
+
try {
|
|
1670
|
+
index = (await readWorkspaceIndex(cwd)).index;
|
|
1671
|
+
} catch (error) {
|
|
1672
|
+
return {
|
|
1673
|
+
valid: !1,
|
|
1674
|
+
error: `Failed to read workspace index: ${error}`
|
|
1675
|
+
};
|
|
1676
|
+
}
|
|
1677
|
+
let pkgIndex = index.packages?.[packageName];
|
|
1678
|
+
if (!pkgIndex)
|
|
1679
|
+
return {
|
|
1680
|
+
valid: !1,
|
|
1681
|
+
error: `Package '${packageName}' is not installed in this workspace.
|
|
1682
|
+
Run 'opkg install ${packageName}' to install it first.`
|
|
1683
|
+
};
|
|
1684
|
+
if (!pkgIndex.files || Object.keys(pkgIndex.files).length === 0)
|
|
1685
|
+
return {
|
|
1686
|
+
valid: !1,
|
|
1687
|
+
error: `Package '${packageName}' has no files installed.
|
|
1688
|
+
Nothing to save.`
|
|
1689
|
+
};
|
|
1690
|
+
let source;
|
|
1691
|
+
try {
|
|
1692
|
+
source = await resolvePackageSource(cwd, packageName);
|
|
1693
|
+
} catch (error) {
|
|
1694
|
+
return {
|
|
1695
|
+
valid: !1,
|
|
1696
|
+
error: `Failed to resolve package source: ${error}`
|
|
1697
|
+
};
|
|
1698
|
+
}
|
|
1699
|
+
try {
|
|
1700
|
+
assertMutableSourceOrThrow(source.absolutePath, {
|
|
1701
|
+
packageName: source.packageName,
|
|
1702
|
+
command: "save"
|
|
1703
|
+
});
|
|
1704
|
+
} catch (error) {
|
|
1705
|
+
return {
|
|
1706
|
+
valid: !1,
|
|
1707
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1708
|
+
};
|
|
1709
|
+
}
|
|
1710
|
+
return {
|
|
1711
|
+
valid: !0,
|
|
1712
|
+
cwd,
|
|
1713
|
+
packageRoot: source.absolutePath,
|
|
1714
|
+
filesMapping: pkgIndex.files
|
|
1715
|
+
};
|
|
1716
|
+
}
|
|
1717
|
+
|
|
1718
|
+
// src/commands/save.ts
|
|
1719
|
+
async function setupSaveCommand(args) {
|
|
1720
|
+
let [packageName, options] = args, ctx = await createCliExecutionContext(), out = resolveOutput(ctx), result = await runSaveToSourcePipeline(packageName, options, ctx);
|
|
1721
|
+
if (!result.success)
|
|
1722
|
+
throw new Error(result.error || "Save operation failed");
|
|
1723
|
+
result.data?.message && out.success(result.data.message);
|
|
1724
|
+
}
|
|
1725
|
+
export {
|
|
1726
|
+
setupSaveCommand
|
|
1727
|
+
};
|
|
1728
|
+
//# sourceMappingURL=save-P2U67DTV.js.map
|