@transcend-io/cli 11.0.0 → 12.0.0
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/README.md +229 -84
- package/dist/app-CR3U95JW.mjs +148 -0
- package/dist/app-CR3U95JW.mjs.map +1 -0
- package/dist/{approvePrivacyRequests-C165wLBa.mjs → approvePrivacyRequests-BDG82nlp.mjs} +2 -2
- package/dist/{approvePrivacyRequests-C165wLBa.mjs.map → approvePrivacyRequests-BDG82nlp.mjs.map} +1 -1
- package/dist/bin/bash-complete.mjs +1 -1
- package/dist/bin/cli.mjs +1 -1
- package/dist/bin/deprecated-command.mjs +1 -1
- package/dist/{buildExamples-DOopJFYY.mjs → buildExamples-B6BdW0FD.mjs} +2 -2
- package/dist/{buildExamples-DOopJFYY.mjs.map → buildExamples-B6BdW0FD.mjs.map} +1 -1
- package/dist/{buildXdiSyncEndpoint-DeStnIit.mjs → buildXdiSyncEndpoint-FExEagKc.mjs} +2 -2
- package/dist/{buildXdiSyncEndpoint-DeStnIit.mjs.map → buildXdiSyncEndpoint-FExEagKc.mjs.map} +1 -1
- package/dist/{bulkRestartRequests-_Mu1rwZQ.mjs → bulkRestartRequests-OKwKXc52.mjs} +2 -2
- package/dist/{bulkRestartRequests-_Mu1rwZQ.mjs.map → bulkRestartRequests-OKwKXc52.mjs.map} +1 -1
- package/dist/{bulkRetryEnrichers-C2keTKuQ.mjs → bulkRetryEnrichers-Dl-n8AFF.mjs} +2 -2
- package/dist/{bulkRetryEnrichers-C2keTKuQ.mjs.map → bulkRetryEnrichers-Dl-n8AFF.mjs.map} +1 -1
- package/dist/{cancelPrivacyRequests-DwQr18Fk.mjs → cancelPrivacyRequests-BIgX5SDz.mjs} +2 -2
- package/dist/{cancelPrivacyRequests-DwQr18Fk.mjs.map → cancelPrivacyRequests-BIgX5SDz.mjs.map} +1 -1
- package/dist/{codecs-CTxFjCg4.mjs → codecs-CfcaFoZR.mjs} +1 -1
- package/dist/{codecs-CTxFjCg4.mjs.map → codecs-CfcaFoZR.mjs.map} +1 -1
- package/dist/{collectCsvFilesOrExit-B1VOVGWN.mjs → collectCsvFilesOrExit-BindZ0dF.mjs} +1 -1
- package/dist/{collectCsvFilesOrExit-B1VOVGWN.mjs.map → collectCsvFilesOrExit-BindZ0dF.mjs.map} +1 -1
- package/dist/{collectParquetFilesOrExit-DVlv5ctt.mjs → collectParquetFilesOrExit-CbNQ9CJc.mjs} +1 -1
- package/dist/{collectParquetFilesOrExit-DVlv5ctt.mjs.map → collectParquetFilesOrExit-CbNQ9CJc.mjs.map} +1 -1
- package/dist/command-X7lkPELf.mjs +9 -0
- package/dist/command-X7lkPELf.mjs.map +1 -0
- package/dist/command-output-BJcDRXNr.mjs +2 -0
- package/dist/command-output-BJcDRXNr.mjs.map +1 -0
- package/dist/command-output-BRfF3YFv.mjs +2 -0
- package/dist/command-output-BRfF3YFv.mjs.map +1 -0
- package/dist/commands/admin/chunk-csv/worker.mjs +1 -1
- package/dist/commands/admin/parquet-to-csv/worker.mjs +1 -1
- package/dist/{consentManagersToBusinessEntities-CBJ81aGO.mjs → consentManagersToBusinessEntities-DG0_eby5.mjs} +1 -1
- package/dist/{consentManagersToBusinessEntities-CBJ81aGO.mjs.map → consentManagersToBusinessEntities-DG0_eby5.mjs.map} +1 -1
- package/dist/{constants-Upxn_rQF.mjs → constants-BRsj50Nb.mjs} +2 -2
- package/dist/{constants-Upxn_rQF.mjs.map → constants-BRsj50Nb.mjs.map} +1 -1
- package/dist/{constants-DXzsg2CQ.mjs → constants-DC_bBO09.mjs} +3 -3
- package/dist/{constants-DXzsg2CQ.mjs.map → constants-DC_bBO09.mjs.map} +1 -1
- package/dist/{constants-D_HVDaOy.mjs → constants-qvGk0wlN.mjs} +1 -1
- package/dist/{constants-D_HVDaOy.mjs.map → constants-qvGk0wlN.mjs.map} +1 -1
- package/dist/{context-B33NT3ki.mjs → context-D0A0LxsP.mjs} +1 -1
- package/dist/{context-B33NT3ki.mjs.map → context-D0A0LxsP.mjs.map} +1 -1
- package/dist/{createPoolingCommandUi-CKH-24w5.mjs → createPoolingCommandUi-CyPZ3GqN.mjs} +5 -5
- package/dist/{createPoolingCommandUi-CKH-24w5.mjs.map → createPoolingCommandUi-CyPZ3GqN.mjs.map} +1 -1
- package/dist/{dataFlowsToDataSilos-BF6fzWcm.mjs → dataFlowsToDataSilos-CRqbNjZ0.mjs} +1 -1
- package/dist/{dataFlowsToDataSilos-BF6fzWcm.mjs.map → dataFlowsToDataSilos-CRqbNjZ0.mjs.map} +1 -1
- package/dist/deno-runtime-CB2HBach.mjs +2 -0
- package/dist/deno-runtime-CB2HBach.mjs.map +1 -0
- package/dist/{done-input-validation-BUz-aVC5.mjs → done-input-validation-LiTaCEzF.mjs} +1 -1
- package/dist/{done-input-validation-BUz-aVC5.mjs.map → done-input-validation-LiTaCEzF.mjs.map} +1 -1
- package/dist/{downloadPrivacyRequestFiles-D3bzcegH.mjs → downloadPrivacyRequestFiles-EBySRnvd.mjs} +2 -2
- package/dist/{downloadPrivacyRequestFiles-D3bzcegH.mjs.map → downloadPrivacyRequestFiles-EBySRnvd.mjs.map} +1 -1
- package/dist/{extractClientError-HZ2U7yqV.mjs → extractClientError-DUJxHv07.mjs} +1 -1
- package/dist/{extractClientError-HZ2U7yqV.mjs.map → extractClientError-DUJxHv07.mjs.map} +1 -1
- package/dist/{fetchAllRequests-2TZr-LfT.mjs → fetchAllRequests-BI0tFl_a.mjs} +2 -2
- package/dist/{fetchAllRequests-2TZr-LfT.mjs.map → fetchAllRequests-BI0tFl_a.mjs.map} +1 -1
- package/dist/{formatPolicyBundleVersionSummary-CJmMCLtJ.mjs → formatPolicyBundleVersionSummary-DCHl1Vhy.mjs} +1 -1
- package/dist/{formatPolicyBundleVersionSummary-CJmMCLtJ.mjs.map → formatPolicyBundleVersionSummary-DCHl1Vhy.mjs.map} +1 -1
- package/dist/{fuzzyMatchColumns-CQCzpjWC.mjs → fuzzyMatchColumns-2NmQqCZI.mjs} +2 -2
- package/dist/{fuzzyMatchColumns-CQCzpjWC.mjs.map → fuzzyMatchColumns-2NmQqCZI.mjs.map} +1 -1
- package/dist/{generateCrossAccountApiKeys-DWfJtB3t.mjs → generateCrossAccountApiKeys-pWTrIUE6.mjs} +2 -2
- package/dist/{generateCrossAccountApiKeys-DWfJtB3t.mjs.map → generateCrossAccountApiKeys-pWTrIUE6.mjs.map} +1 -1
- package/dist/impl-0-_oxvck.mjs +2 -0
- package/dist/impl-0-_oxvck.mjs.map +1 -0
- package/dist/{impl-ByFyaZkK.mjs → impl-1SsreZWo.mjs} +2 -2
- package/dist/{impl-ByFyaZkK.mjs.map → impl-1SsreZWo.mjs.map} +1 -1
- package/dist/{impl-D4Ne3Vbg.mjs → impl-7rdGuHOC.mjs} +2 -2
- package/dist/{impl-D4Ne3Vbg.mjs.map → impl-7rdGuHOC.mjs.map} +1 -1
- package/dist/{impl-DRpK-QLC.mjs → impl-ApZGIx6l.mjs} +2 -2
- package/dist/{impl-DRpK-QLC.mjs.map → impl-ApZGIx6l.mjs.map} +1 -1
- package/dist/{impl-DVeGQCrr.mjs → impl-Ay6JeH-7.mjs} +2 -2
- package/dist/{impl-DVeGQCrr.mjs.map → impl-Ay6JeH-7.mjs.map} +1 -1
- package/dist/{impl-C2sSZ8qF.mjs → impl-B2ZXtnpX.mjs} +2 -2
- package/dist/{impl-C2sSZ8qF.mjs.map → impl-B2ZXtnpX.mjs.map} +1 -1
- package/dist/impl-B440iuvL.mjs +3 -0
- package/dist/impl-B440iuvL.mjs.map +1 -0
- package/dist/{impl-BhfxcCgN2.mjs → impl-B7l4bLG5.mjs} +2 -2
- package/dist/impl-B7l4bLG5.mjs.map +1 -0
- package/dist/{impl-BPoEH3Nf2.mjs → impl-B94dMG4x2.mjs} +2 -2
- package/dist/{impl-BPoEH3Nf2.mjs.map → impl-B94dMG4x2.mjs.map} +1 -1
- package/dist/{impl-DXowiNOz.mjs → impl-BH1fCPDh.mjs} +2 -2
- package/dist/{impl-DXowiNOz.mjs.map → impl-BH1fCPDh.mjs.map} +1 -1
- package/dist/{impl-CfvxD3RI.mjs → impl-BLOhoP-b2.mjs} +2 -2
- package/dist/impl-BLOhoP-b2.mjs.map +1 -0
- package/dist/{impl-CHUxKOz_.mjs → impl-BLQDgwxL.mjs} +2 -2
- package/dist/{impl-CHUxKOz_.mjs.map → impl-BLQDgwxL.mjs.map} +1 -1
- package/dist/{impl-Dqqb0lVN.mjs → impl-BWPPVXG3.mjs} +3 -3
- package/dist/{impl-Dqqb0lVN.mjs.map → impl-BWPPVXG3.mjs.map} +1 -1
- package/dist/{impl-Cw0L2xEU.mjs → impl-BdTxFv63.mjs} +2 -2
- package/dist/{impl-Cw0L2xEU.mjs.map → impl-BdTxFv63.mjs.map} +1 -1
- package/dist/impl-BpVe6fMe.mjs +3 -0
- package/dist/{impl--JGnR3wp.mjs.map → impl-BpVe6fMe.mjs.map} +1 -1
- package/dist/impl-BqTMPbwc2.mjs +2 -0
- package/dist/impl-BqTMPbwc2.mjs.map +1 -0
- package/dist/impl-CAoIp9Nl2.mjs +3 -0
- package/dist/impl-CAoIp9Nl2.mjs.map +1 -0
- package/dist/{impl-C_62Z4ED.mjs → impl-CU38kdj8.mjs} +2 -2
- package/dist/{impl-C_62Z4ED.mjs.map → impl-CU38kdj8.mjs.map} +1 -1
- package/dist/{impl-BppXJ1AD2.mjs → impl-CWP6AXSb.mjs} +2 -2
- package/dist/impl-CWP6AXSb.mjs.map +1 -0
- package/dist/impl-CY8C-Fvj2.mjs +2 -0
- package/dist/impl-CY8C-Fvj2.mjs.map +1 -0
- package/dist/{impl-BJyzyrZD.mjs → impl-C_Ifcb00.mjs} +2 -2
- package/dist/{impl-BJyzyrZD.mjs.map → impl-C_Ifcb00.mjs.map} +1 -1
- package/dist/{impl-CPSEzCc9.mjs → impl-Cb42aI60.mjs} +2 -2
- package/dist/{impl-CPSEzCc9.mjs.map → impl-Cb42aI60.mjs.map} +1 -1
- package/dist/impl-Cee7Q1Ne.mjs +2 -0
- package/dist/impl-Cee7Q1Ne.mjs.map +1 -0
- package/dist/impl-CieztjdM.mjs +2 -0
- package/dist/impl-CieztjdM.mjs.map +1 -0
- package/dist/{impl-DqhZR11m.mjs → impl-ClqrQo1M.mjs} +2 -2
- package/dist/{impl-DqhZR11m.mjs.map → impl-ClqrQo1M.mjs.map} +1 -1
- package/dist/impl-CnHfn7rr.mjs +5 -0
- package/dist/{impl-DPtx5iZO.mjs.map → impl-CnHfn7rr.mjs.map} +1 -1
- package/dist/{impl-BpMQYnbR.mjs → impl-CrdqsTUy.mjs} +2 -2
- package/dist/{impl-BpMQYnbR.mjs.map → impl-CrdqsTUy.mjs.map} +1 -1
- package/dist/{impl-DzUTmaID2.mjs → impl-CsaT3aM2.mjs} +2 -2
- package/dist/impl-CsaT3aM2.mjs.map +1 -0
- package/dist/{impl-BEA5cQOT2.mjs → impl-CspH2ZWx.mjs} +2 -2
- package/dist/impl-CspH2ZWx.mjs.map +1 -0
- package/dist/{impl-lsmXIGSo2.mjs → impl-CudSYx-g.mjs} +2 -2
- package/dist/impl-CudSYx-g.mjs.map +1 -0
- package/dist/{impl-DXQHLpMw.mjs → impl-Cxprr2hw.mjs} +2 -2
- package/dist/{impl-DXQHLpMw.mjs.map → impl-Cxprr2hw.mjs.map} +1 -1
- package/dist/{impl-gs5wJ1BK.mjs → impl-D-a5xGld.mjs} +2 -2
- package/dist/{impl-gs5wJ1BK.mjs.map → impl-D-a5xGld.mjs.map} +1 -1
- package/dist/{impl-D94m3EPp.mjs → impl-D1UogVAq.mjs} +2 -2
- package/dist/{impl-D94m3EPp.mjs.map → impl-D1UogVAq.mjs.map} +1 -1
- package/dist/{impl-B5df-igZ.mjs → impl-D1f9mJL_.mjs} +2 -2
- package/dist/{impl-B5df-igZ.mjs.map → impl-D1f9mJL_.mjs.map} +1 -1
- package/dist/{impl-CSk_okwj.mjs → impl-DJcNhpbx.mjs} +2 -2
- package/dist/{impl-CSk_okwj.mjs.map → impl-DJcNhpbx.mjs.map} +1 -1
- package/dist/impl-DQG1pMzD2.mjs +2 -0
- package/dist/impl-DQG1pMzD2.mjs.map +1 -0
- package/dist/impl-DQcgEx7V2.mjs +298 -0
- package/dist/impl-DQcgEx7V2.mjs.map +1 -0
- package/dist/impl-DRKPx_I9.mjs +2 -0
- package/dist/impl-DRKPx_I9.mjs.map +1 -0
- package/dist/{impl-DhxDp4GN.mjs → impl-DTadLabu.mjs} +2 -2
- package/dist/{impl-DhxDp4GN.mjs.map → impl-DTadLabu.mjs.map} +1 -1
- package/dist/{impl-CD2R3L0v.mjs → impl-DXbxO2VJ.mjs} +2 -2
- package/dist/{impl-CD2R3L0v.mjs.map → impl-DXbxO2VJ.mjs.map} +1 -1
- package/dist/{impl-nz1MyjX22.mjs → impl-DcL-8NEa2.mjs} +2 -2
- package/dist/{impl-nz1MyjX22.mjs.map → impl-DcL-8NEa2.mjs.map} +1 -1
- package/dist/{impl-DKepRc812.mjs → impl-DeCRXfAh2.mjs} +2 -2
- package/dist/{impl-DKepRc812.mjs.map → impl-DeCRXfAh2.mjs.map} +1 -1
- package/dist/impl-DezV3QMo.mjs +3 -0
- package/dist/impl-DezV3QMo.mjs.map +1 -0
- package/dist/{impl-COujMWgp.mjs → impl-Dg7XSQdO2.mjs} +2 -2
- package/dist/impl-Dg7XSQdO2.mjs.map +1 -0
- package/dist/{impl-a_Y8bnSn.mjs → impl-DhXzxQDN2.mjs} +5 -5
- package/dist/impl-DhXzxQDN2.mjs.map +1 -0
- package/dist/impl-DiwxYcux.mjs +2 -0
- package/dist/{impl-GytUVAMP.mjs.map → impl-DiwxYcux.mjs.map} +1 -1
- package/dist/{impl-C9OmNI49.mjs → impl-Do2TNA3-.mjs} +2 -2
- package/dist/{impl-C9OmNI49.mjs.map → impl-Do2TNA3-.mjs.map} +1 -1
- package/dist/{impl-DaH1_ZzK.mjs → impl-DqHEIzpj.mjs} +2 -2
- package/dist/{impl-DaH1_ZzK.mjs.map → impl-DqHEIzpj.mjs.map} +1 -1
- package/dist/{impl-CzdUHruN.mjs → impl-Dr_l_SLd.mjs} +2 -2
- package/dist/{impl-CzdUHruN.mjs.map → impl-Dr_l_SLd.mjs.map} +1 -1
- package/dist/{impl-CTwyYUw3.mjs → impl-Ds-gQfdc.mjs} +2 -2
- package/dist/{impl-CTwyYUw3.mjs.map → impl-Ds-gQfdc.mjs.map} +1 -1
- package/dist/{impl-C8jjYvgu.mjs → impl-DtnYcktN.mjs} +2 -2
- package/dist/{impl-C8jjYvgu.mjs.map → impl-DtnYcktN.mjs.map} +1 -1
- package/dist/impl-DtpgD_as.mjs +2 -0
- package/dist/{impl-DQPmSYSE.mjs.map → impl-DtpgD_as.mjs.map} +1 -1
- package/dist/{impl-D2Q4nxUO.mjs → impl-DxQ9yj3H.mjs} +2 -2
- package/dist/{impl-D2Q4nxUO.mjs.map → impl-DxQ9yj3H.mjs.map} +1 -1
- package/dist/impl-FerHBNJx2.mjs +4 -0
- package/dist/{impl-D-AHlqlD2.mjs.map → impl-FerHBNJx2.mjs.map} +1 -1
- package/dist/{impl-CiBT9-GH2.mjs → impl-HUfaARep.mjs} +2 -2
- package/dist/impl-HUfaARep.mjs.map +1 -0
- package/dist/impl-btKJ1-VJ.mjs +3 -0
- package/dist/impl-btKJ1-VJ.mjs.map +1 -0
- package/dist/impl-eeS2lVi5.mjs +2 -0
- package/dist/impl-eeS2lVi5.mjs.map +1 -0
- package/dist/impl-iD6Fknfe.mjs +2 -0
- package/dist/impl-iD6Fknfe.mjs.map +1 -0
- package/dist/impl-ijoNwhGI.mjs +2 -0
- package/dist/impl-ijoNwhGI.mjs.map +1 -0
- package/dist/{impl-zRAjMVgz.mjs → impl-vmLq6HCI.mjs} +2 -2
- package/dist/{impl-zRAjMVgz.mjs.map → impl-vmLq6HCI.mjs.map} +1 -1
- package/dist/{impl-a0BFtbna.mjs → impl-wBcVYHlA.mjs} +2 -2
- package/dist/{impl-a0BFtbna.mjs.map → impl-wBcVYHlA.mjs.map} +1 -1
- package/dist/index.mjs +2 -2
- package/dist/index.mjs.map +1 -1
- package/dist/{initializeCliRuntime-6wI_VIHV.mjs → initializeCliRuntime-BaATMO1X.mjs} +1 -1
- package/dist/{initializeCliRuntime-6wI_VIHV.mjs.map → initializeCliRuntime-BaATMO1X.mjs.map} +1 -1
- package/dist/{inquirer-BeaBetMO.mjs → inquirer-BU5IpgA8.mjs} +2 -2
- package/dist/{inquirer-BeaBetMO.mjs.map → inquirer-BU5IpgA8.mjs.map} +1 -1
- package/dist/{jsonc-D9dDWb1J.mjs → jsonc-CdTubr53.mjs} +1 -1
- package/dist/{jsonc-D9dDWb1J.mjs.map → jsonc-CdTubr53.mjs.map} +1 -1
- package/dist/{listFiles-BdFUbX5x.mjs → listFiles-DDGCBdtx.mjs} +1 -1
- package/dist/{listFiles-BdFUbX5x.mjs.map → listFiles-DDGCBdtx.mjs.map} +1 -1
- package/dist/logger-CPu6zbyQ.mjs +2 -0
- package/dist/logger-CPu6zbyQ.mjs.map +1 -0
- package/dist/{mapConsentCsvRowsToInputs-Dqb6WL4r.mjs → mapConsentCsvRowsToInputs-CkLfxaIf.mjs} +1 -1
- package/dist/{mapConsentCsvRowsToInputs-Dqb6WL4r.mjs.map → mapConsentCsvRowsToInputs-CkLfxaIf.mjs.map} +1 -1
- package/dist/{markRequestDataSiloIdsCompleted-Br5gIS3S.mjs → markRequestDataSiloIdsCompleted-BYNMCkhy.mjs} +2 -2
- package/dist/{markRequestDataSiloIdsCompleted-Br5gIS3S.mjs.map → markRequestDataSiloIdsCompleted-BYNMCkhy.mjs.map} +1 -1
- package/dist/{markSilentPrivacyRequests-Cs_iZNJs.mjs → markSilentPrivacyRequests-gKYivzH1.mjs} +2 -2
- package/dist/{markSilentPrivacyRequests-Cs_iZNJs.mjs.map → markSilentPrivacyRequests-gKYivzH1.mjs.map} +1 -1
- package/dist/{mergeTranscendInputs-DFQcbh8b.mjs → mergeTranscendInputs-D1CvxQu0.mjs} +2 -2
- package/dist/{mergeTranscendInputs-DFQcbh8b.mjs.map → mergeTranscendInputs-D1CvxQu0.mjs.map} +1 -1
- package/dist/missing-manifest-D8y1VL4p.mjs +2 -0
- package/dist/missing-manifest-D8y1VL4p.mjs.map +1 -0
- package/dist/{module-graph-Cb0g74Ft.mjs → module-graph-CAT8_JCG.mjs} +1 -1
- package/dist/{module-graph-Cb0g74Ft.mjs.map → module-graph-CAT8_JCG.mjs.map} +1 -1
- package/dist/{notifyPrivacyRequestsAdditionalTime-kPXTdjgT.mjs → notifyPrivacyRequestsAdditionalTime-DbERhQ1Q.mjs} +2 -2
- package/dist/{notifyPrivacyRequestsAdditionalTime-kPXTdjgT.mjs.map → notifyPrivacyRequestsAdditionalTime-DbERhQ1Q.mjs.map} +1 -1
- package/dist/{parquetToCsvOneFile-ChmPC6dL.mjs → parquetToCsvOneFile-DtbCFm08.mjs} +3 -3
- package/dist/{parquetToCsvOneFile-ChmPC6dL.mjs.map → parquetToCsvOneFile-DtbCFm08.mjs.map} +1 -1
- package/dist/{parseAttributesFromString-CO16oAlx.mjs → parseAttributesFromString-C5hJMRvt.mjs} +2 -2
- package/dist/{parseAttributesFromString-CO16oAlx.mjs.map → parseAttributesFromString-C5hJMRvt.mjs.map} +1 -1
- package/dist/{parseVariablesFromString-BOSfQ4QF.mjs → parseVariablesFromString-6TYW65LF.mjs} +1 -1
- package/dist/{parseVariablesFromString-BOSfQ4QF.mjs.map → parseVariablesFromString-6TYW65LF.mjs.map} +1 -1
- package/dist/paths-J5KScOaQ.mjs +2 -0
- package/dist/paths-J5KScOaQ.mjs.map +1 -0
- package/dist/policy-bundle-manifest-BZnBMZQq.mjs +7 -0
- package/dist/policy-bundle-manifest-BZnBMZQq.mjs.map +1 -0
- package/dist/policy-project-discovery-C29UxWwt.mjs +427 -0
- package/dist/policy-project-discovery-C29UxWwt.mjs.map +1 -0
- package/dist/{policy-runtime-BMKa-Upy.mjs → policy-runtime-6jmkXlBk.mjs} +2 -2
- package/dist/{policy-runtime-BMKa-Upy.mjs.map → policy-runtime-6jmkXlBk.mjs.map} +1 -1
- package/dist/policy-scaffold-config-BUKAyPad.mjs +76 -0
- package/dist/policy-scaffold-config-BUKAyPad.mjs.map +1 -0
- package/dist/{policyEngineCliError-DY8V-qkK.mjs → policyEngineCliError-CZrOHhLv.mjs} +1 -1
- package/dist/{policyEngineCliError-DY8V-qkK.mjs.map → policyEngineCliError-CZrOHhLv.mjs.map} +1 -1
- package/dist/{printResult-CHe3nRN4.mjs → printResult-C2K9ODeE.mjs} +3 -3
- package/dist/printResult-C2K9ODeE.mjs.map +1 -0
- package/dist/project-discovery-BkHpTXDx.mjs +2 -0
- package/dist/project-discovery-BkHpTXDx.mjs.map +1 -0
- package/dist/project-discovery-CWRjlFnC.mjs +2 -0
- package/dist/project-discovery-CWRjlFnC.mjs.map +1 -0
- package/dist/project-plan-apply-Bg_1eMX9.mjs +3 -0
- package/dist/{project-plan-apply-Dvndx455.mjs.map → project-plan-apply-Bg_1eMX9.mjs.map} +1 -1
- package/dist/project-plan-output-BHHlkLxe.mjs +4 -0
- package/dist/project-plan-output-BHHlkLxe.mjs.map +1 -0
- package/dist/prompts-BhXfYD6g.mjs +2 -0
- package/dist/prompts-BhXfYD6g.mjs.map +1 -0
- package/dist/{pullAllDatapoints-uk5ggk6O.mjs → pullAllDatapoints-Bm-Orhse.mjs} +2 -2
- package/dist/{pullAllDatapoints-uk5ggk6O.mjs.map → pullAllDatapoints-Bm-Orhse.mjs.map} +1 -1
- package/dist/{pullChunkedCustomSiloOutstandingIdentifiers-DbFFXAVM.mjs → pullChunkedCustomSiloOutstandingIdentifiers-DTGQOw--.mjs} +2 -2
- package/dist/{pullChunkedCustomSiloOutstandingIdentifiers-DbFFXAVM.mjs.map → pullChunkedCustomSiloOutstandingIdentifiers-DTGQOw--.mjs.map} +1 -1
- package/dist/{pullConsentManagerMetrics-Dv5n1ZGp.mjs → pullConsentManagerMetrics-DKCkfbCB.mjs} +2 -2
- package/dist/{pullConsentManagerMetrics-Dv5n1ZGp.mjs.map → pullConsentManagerMetrics-DKCkfbCB.mjs.map} +1 -1
- package/dist/{pullManualEnrichmentIdentifiersToCsv-D5is0Nrs.mjs → pullManualEnrichmentIdentifiersToCsv-DMC5CQtE.mjs} +2 -2
- package/dist/{pullManualEnrichmentIdentifiersToCsv-D5is0Nrs.mjs.map → pullManualEnrichmentIdentifiersToCsv-DMC5CQtE.mjs.map} +1 -1
- package/dist/{pullTranscendConfiguration-B63M3ild.mjs → pullTranscendConfiguration-CDUDqxpm.mjs} +2 -2
- package/dist/{pullTranscendConfiguration-B63M3ild.mjs.map → pullTranscendConfiguration-CDUDqxpm.mjs.map} +1 -1
- package/dist/{pullUnstructuredSubDataPointRecommendations-YTBzfkRZ.mjs → pullUnstructuredSubDataPointRecommendations-Ci4AOxyH.mjs} +2 -2
- package/dist/{pullUnstructuredSubDataPointRecommendations-YTBzfkRZ.mjs.map → pullUnstructuredSubDataPointRecommendations-Ci4AOxyH.mjs.map} +1 -1
- package/dist/{pushCronIdentifiersFromCsv-Ctq38wAY.mjs → pushCronIdentifiersFromCsv-DhS2z8IG.mjs} +2 -2
- package/dist/{pushCronIdentifiersFromCsv-Ctq38wAY.mjs.map → pushCronIdentifiersFromCsv-DhS2z8IG.mjs.map} +1 -1
- package/dist/{pushManualEnrichmentIdentifiersFromCsv-0WHf6d_m.mjs → pushManualEnrichmentIdentifiersFromCsv-BTJGOmdU.mjs} +2 -2
- package/dist/{pushManualEnrichmentIdentifiersFromCsv-0WHf6d_m.mjs.map → pushManualEnrichmentIdentifiersFromCsv-BTJGOmdU.mjs.map} +1 -1
- package/dist/{readCsv-DLg8pwjd.mjs → readCsv-DNcJ_UfU.mjs} +1 -1
- package/dist/{readCsv-DLg8pwjd.mjs.map → readCsv-DNcJ_UfU.mjs.map} +1 -1
- package/dist/{readTranscendYaml-BNn6-Ofi.mjs → readTranscendYaml-BfqQbPiT.mjs} +3 -3
- package/dist/{readTranscendYaml-BNn6-Ofi.mjs.map → readTranscendYaml-BfqQbPiT.mjs.map} +1 -1
- package/dist/{removeUnverifiedRequestIdentifiers-Do3Pus6m.mjs → removeUnverifiedRequestIdentifiers-BkAcUZZN.mjs} +2 -2
- package/dist/{removeUnverifiedRequestIdentifiers-Do3Pus6m.mjs.map → removeUnverifiedRequestIdentifiers-BkAcUZZN.mjs.map} +1 -1
- package/dist/{renderTable-D68QasPc.mjs → renderTable-CZP2roD1.mjs} +1 -1
- package/dist/{renderTable-D68QasPc.mjs.map → renderTable-CZP2roD1.mjs.map} +1 -1
- package/dist/{request-BiPv6jmP.mjs → request-BpKyN9Xm.mjs} +1 -1
- package/dist/{request-BiPv6jmP.mjs.map → request-BpKyN9Xm.mjs.map} +1 -1
- package/dist/{resolveBundleIdByName-qXZBrUz_.mjs → resolveBundleIdByName-Dm_o5_bg.mjs} +2 -2
- package/dist/{resolveBundleIdByName-qXZBrUz_.mjs.map → resolveBundleIdByName-Dm_o5_bg.mjs.map} +1 -1
- package/dist/{resolvePolicyBundleVersion-BG7XqBBP.mjs → resolvePolicyBundleVersion-DRUwe7fA.mjs} +2 -2
- package/dist/{resolvePolicyBundleVersion-BG7XqBBP.mjs.map → resolvePolicyBundleVersion-DRUwe7fA.mjs.map} +1 -1
- package/dist/{retryRequestDataSilos-CEgUW6Fx.mjs → retryRequestDataSilos-BoPmcB8-.mjs} +2 -2
- package/dist/{retryRequestDataSilos-CEgUW6Fx.mjs.map → retryRequestDataSilos-BoPmcB8-.mjs.map} +1 -1
- package/dist/{run-captured-process-BXwWXuTh.mjs → run-captured-process-BjaSDWXG.mjs} +1 -1
- package/dist/{run-captured-process-BXwWXuTh.mjs.map → run-captured-process-BjaSDWXG.mjs.map} +1 -1
- package/dist/{runOpa-CswzJxIs.mjs → runOpa-ojIqU1tO.mjs} +2 -2
- package/dist/{runOpa-CswzJxIs.mjs.map → runOpa-ojIqU1tO.mjs.map} +1 -1
- package/dist/{scaffold-model-BAbYTR0q.mjs → scaffold-model-CUZER0BC.mjs} +1 -1
- package/dist/scaffold-model-CUZER0BC.mjs.map +1 -0
- package/dist/{scaffold-planning-CNv83t_v.mjs → scaffold-planning-CaetLfv8.mjs} +9 -9
- package/dist/{scaffold-planning-CNv83t_v.mjs.map → scaffold-planning-CaetLfv8.mjs.map} +1 -1
- package/dist/{scaffold-templates-DzDGTZie.mjs → scaffold-templates-CUL82Zf8.mjs} +1 -1
- package/dist/{scaffold-templates-DzDGTZie.mjs.map → scaffold-templates-CUL82Zf8.mjs.map} +1 -1
- package/dist/{skipPreflightJobs-D_1mZ9mr.mjs → skipPreflightJobs-DkBdYw-r.mjs} +2 -2
- package/dist/{skipPreflightJobs-D_1mZ9mr.mjs.map → skipPreflightJobs-DkBdYw-r.mjs.map} +1 -1
- package/dist/{skipRequestDataSilos-CcGl9Z8M.mjs → skipRequestDataSilos-BRFiokix.mjs} +2 -2
- package/dist/{skipRequestDataSilos-CcGl9Z8M.mjs.map → skipRequestDataSilos-BRFiokix.mjs.map} +1 -1
- package/dist/{streamPrivacyRequestsToCsv-1l8RAcnK.mjs → streamPrivacyRequestsToCsv-Bt-H5J-y.mjs} +2 -2
- package/dist/{streamPrivacyRequestsToCsv-1l8RAcnK.mjs.map → streamPrivacyRequestsToCsv-Bt-H5J-y.mjs.map} +1 -1
- package/dist/{syncCodePackages-BHE2Tn1Q.mjs → syncCodePackages-CPkEukDm.mjs} +2 -2
- package/dist/{syncCodePackages-BHE2Tn1Q.mjs.map → syncCodePackages-CPkEukDm.mjs.map} +1 -1
- package/dist/{updateConsentManagerVersionToLatest-yZhBDcv8.mjs → updateConsentManagerVersionToLatest-DWblAwus.mjs} +2 -2
- package/dist/{updateConsentManagerVersionToLatest-yZhBDcv8.mjs.map → updateConsentManagerVersionToLatest-DWblAwus.mjs.map} +1 -1
- package/dist/{uploadConsents-DQqApEaH.mjs → uploadConsents-D7Vg8ZRW.mjs} +2 -2
- package/dist/{uploadConsents-DQqApEaH.mjs.map → uploadConsents-D7Vg8ZRW.mjs.map} +1 -1
- package/dist/{uploadPrivacyRequestsFromCsv-ntuK0eSR.mjs → uploadPrivacyRequestsFromCsv-B_hQwIn6.mjs} +2 -2
- package/dist/{uploadPrivacyRequestsFromCsv-ntuK0eSR.mjs.map → uploadPrivacyRequestsFromCsv-B_hQwIn6.mjs.map} +1 -1
- package/dist/validateTranscendAuth-D-dDINof.mjs +2 -0
- package/dist/{validateTranscendAuth-D6Wr1cxY.mjs.map → validateTranscendAuth-D-dDINof.mjs.map} +1 -1
- package/dist/{writeCsv-D-2siSwE.mjs → writeCsv-CNZrq3zU.mjs} +1 -1
- package/dist/{writeCsv-D-2siSwE.mjs.map → writeCsv-CNZrq3zU.mjs.map} +1 -1
- package/package.json +6 -5
- package/dist/app-DhG-JhT3.mjs +0 -148
- package/dist/app-DhG-JhT3.mjs.map +0 -1
- package/dist/command-Ds6oNo0v.mjs +0 -9
- package/dist/command-Ds6oNo0v.mjs.map +0 -1
- package/dist/deno-runtime-DV52X8-P.mjs +0 -2
- package/dist/deno-runtime-DV52X8-P.mjs.map +0 -1
- package/dist/impl--JGnR3wp.mjs +0 -3
- package/dist/impl-7UqkSO3x2.mjs +0 -2
- package/dist/impl-7UqkSO3x2.mjs.map +0 -1
- package/dist/impl-B4BgGuF_.mjs +0 -3
- package/dist/impl-B4BgGuF_.mjs.map +0 -1
- package/dist/impl-B5oVsX-F2.mjs +0 -303
- package/dist/impl-B5oVsX-F2.mjs.map +0 -1
- package/dist/impl-BEA5cQOT2.mjs.map +0 -1
- package/dist/impl-BhfxcCgN2.mjs.map +0 -1
- package/dist/impl-BppXJ1AD2.mjs.map +0 -1
- package/dist/impl-C6HtWd1t.mjs +0 -2
- package/dist/impl-C6HtWd1t.mjs.map +0 -1
- package/dist/impl-CO4ZqlLV.mjs +0 -3
- package/dist/impl-CO4ZqlLV.mjs.map +0 -1
- package/dist/impl-COujMWgp.mjs.map +0 -1
- package/dist/impl-CUOVJsRg.mjs +0 -3
- package/dist/impl-CUOVJsRg.mjs.map +0 -1
- package/dist/impl-CUiYbk6G.mjs +0 -3
- package/dist/impl-CUiYbk6G.mjs.map +0 -1
- package/dist/impl-CfvxD3RI.mjs.map +0 -1
- package/dist/impl-CiBT9-GH2.mjs.map +0 -1
- package/dist/impl-Cli2XRp02.mjs +0 -2
- package/dist/impl-Cli2XRp02.mjs.map +0 -1
- package/dist/impl-D-AHlqlD2.mjs +0 -4
- package/dist/impl-DPN1En7L.mjs +0 -2
- package/dist/impl-DPN1En7L.mjs.map +0 -1
- package/dist/impl-DPtx5iZO.mjs +0 -5
- package/dist/impl-DQPmSYSE.mjs +0 -2
- package/dist/impl-DXRDWiB1.mjs +0 -2
- package/dist/impl-DXRDWiB1.mjs.map +0 -1
- package/dist/impl-DdIa0zla.mjs +0 -2
- package/dist/impl-DdIa0zla.mjs.map +0 -1
- package/dist/impl-Dp4bZvAV.mjs +0 -2
- package/dist/impl-Dp4bZvAV.mjs.map +0 -1
- package/dist/impl-DryC3OJu.mjs +0 -2
- package/dist/impl-DryC3OJu.mjs.map +0 -1
- package/dist/impl-DzUTmaID2.mjs.map +0 -1
- package/dist/impl-GytUVAMP.mjs +0 -2
- package/dist/impl-Z8oWVENM.mjs +0 -2
- package/dist/impl-Z8oWVENM.mjs.map +0 -1
- package/dist/impl-a_Y8bnSn.mjs.map +0 -1
- package/dist/impl-lsmXIGSo2.mjs.map +0 -1
- package/dist/logger-zplFSeXU.mjs +0 -2
- package/dist/logger-zplFSeXU.mjs.map +0 -1
- package/dist/manifest-BNIpO_zW.mjs +0 -2
- package/dist/manifest-BNIpO_zW.mjs.map +0 -1
- package/dist/missing-manifest-DgcukXfg.mjs +0 -2
- package/dist/missing-manifest-DgcukXfg.mjs.map +0 -1
- package/dist/path-safety-D5Qwl40b.mjs +0 -2
- package/dist/path-safety-D5Qwl40b.mjs.map +0 -1
- package/dist/policy-bundle-manifest-D6dkbjNi.mjs +0 -7
- package/dist/policy-bundle-manifest-D6dkbjNi.mjs.map +0 -1
- package/dist/printResult-CHe3nRN4.mjs.map +0 -1
- package/dist/project-plan-apply-Dvndx455.mjs +0 -3
- package/dist/prompts-ClBXdELk.mjs +0 -4
- package/dist/prompts-ClBXdELk.mjs.map +0 -1
- package/dist/scaffold-model-BAbYTR0q.mjs.map +0 -1
- package/dist/validateTranscendAuth-D6Wr1cxY.mjs +0 -2
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import{c as e,l as t,o as n,u as r}from"./policy-project-discovery-C29UxWwt.mjs";import{n as i,r as a,t as o}from"./jsonc-CdTubr53.mjs";import{posix as s,relative as c,sep as l}from"node:path";const u=`0.42.0`,d=`.github/workflows/transcend-policy.yml`,f=`d23441a48e516b6c34aea4fa41551a30e30af803`,p=`b2b258e089860efaadaaf71bf6e3aecb4a3eeff1`,m=`761188c3b435761fa254beca508a44875619648f`;function h(e,t){return e===`.`?t:s.join(e,t)}function g(n){let r=n.bundleDirectories??[h(n.workspaceDirectory,e)],i=[h(n.workspaceDirectory,`**/*.rego`),h(n.workspaceDirectory,`**/*.json`),h(n.workspaceDirectory,`**/*.yaml`),h(n.workspaceDirectory,`**/*.yml`),h(n.workspaceDirectory,`**/.manifest`),h(n.workspaceDirectory,`.regal/config.yaml`),h(n.workspaceDirectory,`.regal.yaml`),d].map(e=>` - ${JSON.stringify(e)}`).join(`
|
|
2
|
+
`),a;return a=r.length===0?` lint:
|
|
3
|
+
runs-on: ubuntu-latest
|
|
4
|
+
steps:
|
|
5
|
+
- uses: actions/checkout@${f} # v6
|
|
6
|
+
- name: No publishable bundles yet
|
|
7
|
+
run: >-
|
|
8
|
+
echo "Run transcend policy new to add a bundle;
|
|
9
|
+
policy new refreshes this workflow automatically."
|
|
10
|
+
`:r.length===1?` lint:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
steps:
|
|
13
|
+
- uses: actions/checkout@${f} # v6
|
|
14
|
+
- name: Set up OPA
|
|
15
|
+
uses: open-policy-agent/setup-opa@${p} # v2.4.0
|
|
16
|
+
with:
|
|
17
|
+
version: ${JSON.stringify(t)}
|
|
18
|
+
- name: Set up Regal
|
|
19
|
+
uses: open-policy-agent/setup-regal@${m} # v2.0.0
|
|
20
|
+
with:
|
|
21
|
+
version: ${JSON.stringify(u)}
|
|
22
|
+
- name: Install the Transcend CLI
|
|
23
|
+
run: npm install --global @transcend-io/cli@${n.cliVersion}
|
|
24
|
+
- name: Validate Policy Engine project
|
|
25
|
+
env:
|
|
26
|
+
POLICY_DIRECTORY: ${JSON.stringify(r[0])}
|
|
27
|
+
run: >-
|
|
28
|
+
transcend policy lint
|
|
29
|
+
"$POLICY_DIRECTORY"
|
|
30
|
+
--noInteractive
|
|
31
|
+
--json
|
|
32
|
+
`:` lint:
|
|
33
|
+
runs-on: ubuntu-latest
|
|
34
|
+
strategy:
|
|
35
|
+
fail-fast: false
|
|
36
|
+
matrix:
|
|
37
|
+
policy_directory:
|
|
38
|
+
${r.map(e=>` - ${JSON.stringify(e)}`).join(`
|
|
39
|
+
`)}
|
|
40
|
+
steps:
|
|
41
|
+
- uses: actions/checkout@${f} # v6
|
|
42
|
+
- name: Set up OPA
|
|
43
|
+
uses: open-policy-agent/setup-opa@${p} # v2.4.0
|
|
44
|
+
with:
|
|
45
|
+
version: ${JSON.stringify(t)}
|
|
46
|
+
- name: Set up Regal
|
|
47
|
+
uses: open-policy-agent/setup-regal@${m} # v2.0.0
|
|
48
|
+
with:
|
|
49
|
+
version: ${JSON.stringify(u)}
|
|
50
|
+
- name: Install the Transcend CLI
|
|
51
|
+
run: npm install --global @transcend-io/cli@${n.cliVersion}
|
|
52
|
+
- name: Validate Policy Engine project
|
|
53
|
+
env:
|
|
54
|
+
POLICY_DIRECTORY: \${{ matrix.policy_directory }}
|
|
55
|
+
run: transcend policy lint "$POLICY_DIRECTORY" --noInteractive --json
|
|
56
|
+
`,`# Generated by transcend policy init.
|
|
57
|
+
name: Transcend Policy
|
|
58
|
+
|
|
59
|
+
on:
|
|
60
|
+
pull_request:
|
|
61
|
+
paths:
|
|
62
|
+
${i}
|
|
63
|
+
push:
|
|
64
|
+
paths:
|
|
65
|
+
${i}
|
|
66
|
+
|
|
67
|
+
permissions:
|
|
68
|
+
contents: read
|
|
69
|
+
|
|
70
|
+
jobs:
|
|
71
|
+
${a}`}const _=`tsandall.opa`,v=`glebbash.opa-highlight-only`;function y(e,t){return JSON.stringify(e)===JSON.stringify(t)}function b(e,t){try{return{current:a(e??`{}
|
|
72
|
+
`,t)}}catch(t){return{contents:e??`{}
|
|
73
|
+
`,warning:`${t instanceof Error?t.message:String(t)} Existing content was left unchanged.`}}}function x(e,t,n,r){if(t.length===0)return{contents:e??`{}
|
|
74
|
+
`,warnings:r};try{return{contents:o(e,t,n),warnings:r}}catch(t){return{contents:e??`{}
|
|
75
|
+
`,warnings:[...r,`${t instanceof Error?t.message:String(t)} Existing content was left unchanged.`]}}}function S(e,t,n,r){t.forEach(({path:t,value:i,label:a})=>{let o=t.reduce((e,t)=>e&&typeof e==`object`&&!Array.isArray(e)?e[t]:void 0,e);o===void 0?n.push({path:t,value:i}):y(o,i)||r.push(`VS Code setting "${a}" has a repository-specific value and was left unchanged.`)})}function C(e){return e===`.`?"${workspaceFolder}":`\${workspaceFolder}/${e}`}function w(e,t){return c(e,t).split(l).join(`/`)||`.`}function T(e,t,n=r){let i=w(e,t),a=`${n}-bundle`;return C(i===`.`?a:`${i}/${a}`)}function E(e,t){let n=w(e,t);return C(n===`.`?`schemas`:`${n}/schemas`)}function D(e,t){let r=w(e,t);return r===`.`?`**/${n}`:`**/${r}/**/${n}`}function O(e,t,n=r){let i=w(e,t),a=`${n}-bundle`,o=i===`.`?`/${a}`:`/${i}/${a}`,s=i===`.`?`./schemas/${n}/input.json`:`./${i}/schemas/${n}/input.json`;return{fileMatch:[`${o}/input.json`,`${o}/input.example.json`],url:s}}function k(e,t,n,r){let a=b(e,`VS Code policy settings`);if(`warning`in a)return{contents:a.contents,warnings:[a.warning]};let{current:o}=a,s=[],c=[];if(r&&r.length>0){let e=o[`opa.roots`],a=r.map(e=>T(t,n,e));if(e===void 0)s.push({path:[`opa.roots`],value:a});else if(Array.isArray(e)&&e.every(e=>typeof e==`string`)){let t=i(e,a);y(t,e)||s.push({path:[`opa.roots`],value:t})}else c.push(`VS Code setting "opa.roots" is customized and was left unchanged.`)}S(o,[{path:[`opa.schema`],value:E(t,n),label:`opa.schema`},{path:[`opa.checkOnSave`],value:!0,label:`opa.checkOnSave`},{path:[`opa.strictMode`],value:!0,label:`opa.strictMode`},{path:[`opa.bundleMode`],value:!0,label:`opa.bundleMode`},{path:[`opa.formatter`],value:`opa-fmt-rego-v1`,label:`opa.formatter`}],s,c);let l=o[`files.associations`],u=D(t,n);if(l===void 0?s.push({path:[`files.associations`],value:{[u]:`json`}}):typeof l==`object`&&l&&!Array.isArray(l)?S(o,[{path:[`files.associations`,u],value:`json`,label:`files.associations."${u}"`}],s,c):c.push(`VS Code setting "files.associations" is customized and was left unchanged.`),r&&r.length>0){let e=r.map(e=>O(t,n,e)),i=o[`json.schemas`];if(i===void 0)s.push({path:[`json.schemas`],value:e});else if(Array.isArray(i)){let t=[...i];e.forEach(e=>{t.some(t=>typeof t==`object`&&!!t&&!Array.isArray(t)&&y(t,e))||(t.find(t=>typeof t==`object`&&!!t&&!Array.isArray(t)&&typeof t.url==`string`&&t.url===e.url)===void 0?t=[...t,e]:c.push(`VS Code setting "json.schemas" has a repository-specific entry for the ${e.url} schema and was left unchanged.`))}),y(t,i)||s.push({path:[`json.schemas`],value:t})}else c.push(`VS Code setting "json.schemas" is customized and was left unchanged.`)}let d=o[`[rego]`];return d===void 0?s.push({path:[`[rego]`],value:{"editor.defaultFormatter":_,"editor.formatOnSave":!0,"editor.insertSpaces":!1,"editor.tabSize":4}}):typeof d==`object`&&d&&!Array.isArray(d)?S(o,[{path:[`[rego]`,`editor.defaultFormatter`],value:_,label:`[rego].editor.defaultFormatter`},{path:[`[rego]`,`editor.formatOnSave`],value:!0,label:`[rego].editor.formatOnSave`},{path:[`[rego]`,`editor.insertSpaces`],value:!1,label:`[rego].editor.insertSpaces`},{path:[`[rego]`,`editor.tabSize`],value:4,label:`[rego].editor.tabSize`}],s,c):c.push(`VS Code language setting "[rego]" is customized and was left unchanged.`),x(e,s,`VS Code policy settings`,c)}function A(e){let t=b(e,`VS Code policy extension recommendations`);if(`warning`in t)return{contents:t.contents,warnings:[t.warning]};let{current:n}=t,r=[],a=[],o=n.recommendations;if(o===void 0||Array.isArray(o)&&o.every(e=>typeof e==`string`)){let e=o??[],t=i(e,[_]).filter(e=>e!==v);y(e,t)||r.push({path:[`recommendations`],value:t})}else a.push(`VS Code extension recommendations use an unknown shape and were left unchanged.`);let s=n.unwantedRecommendations;if(s===void 0||Array.isArray(s)&&s.every(e=>typeof e==`string`)){let e=s??[],t=i(e,[v]);y(e,t)||r.push({path:[`unwantedRecommendations`],value:t})}else a.push(`VS Code unwanted extension recommendations use an unknown shape and were left unchanged.`);return x(e,r,`VS Code policy extension recommendations`,a)}function j(e,t,n=r){let i=w(e,t),a=`${n}-bundle`,o=i===`.`?a:`${i}/${a}`;return{label:`policy: lint ${n}`,type:`process`,command:`transcend`,args:[`policy`,`lint`,o,`--noInteractive`],group:{kind:`test`,isDefault:!1},problemMatcher:[]}}function M(e){return{label:`policy: lint`,dependsOn:e.map(e=>`policy: lint ${e}`),dependsOrder:`sequence`,group:{kind:`test`,isDefault:!0},problemMatcher:[]}}function N(e,t,n,r){let i=b(e,`VS Code policy tasks`);if(`warning`in i)return{contents:i.contents,warnings:[i.warning]};let{current:a}=i,o=[],s=[],c=a.version;if(c===void 0?o.push({path:[`version`],value:`2.0.0`}):c!==`2.0.0`&&s.push(`VS Code task schema version is customized and was left unchanged.`),!r||r.length===0)return x(e,o,`VS Code policy tasks`,s);let l=r.map(e=>j(t,n,e)),u=M(r),d=[...l,u],f=new Set(d.map(e=>e.label)),p=a.tasks;if(p===void 0)o.push({path:[`tasks`],value:d});else if(Array.isArray(p)){let e=[...p],t=!1;d.forEach(n=>{let r=n.label,i=e.findIndex(e=>typeof e==`object`&&!!e&&!Array.isArray(e)&&e.label===r);if(i===-1){e=[...e,n],t=!0;return}y(e[i],n)||(f.has(r)?(e=e.map((e,t)=>t===i?n:e),t=!0):s.push(`VS Code task "${r}" has repository-specific customization and was left unchanged.`))}),t&&o.push({path:[`tasks`],value:e})}else s.push(`VS Code tasks use an unknown shape and were left unchanged.`);return x(e,o,`VS Code policy tasks`,s)}export{g as a,d as i,k as n,N as r,A as t};
|
|
76
|
+
//# sourceMappingURL=policy-scaffold-config-BUKAyPad.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"policy-scaffold-config-BUKAyPad.mjs","names":[],"sources":["../src/lib/policy/policy-scaffold-artifacts.ts","../src/lib/policy/policy-scaffold-config.ts"],"sourcesContent":["import { posix } from 'node:path';\n\nimport {\n POLICY_STARTER_BUNDLE_DIRECTORY,\n POLICY_STARTER_OPA_VERSION,\n} from './policy-scaffold-templates.js';\n\n/** Regal version pinned by generated policy validation. */\nexport const POLICY_STARTER_REGAL_VERSION = '0.42.0';\n\n/** Repository-relative generated workflow path. */\nexport const POLICY_CI_WORKFLOW_PATH = '.github/workflows/transcend-policy.yml';\n\n/** Immutable actions/checkout v6 release commit. */\nexport const ACTIONS_CHECKOUT_SHA = 'd23441a48e516b6c34aea4fa41551a30e30af803';\n\n/** Immutable open-policy-agent/setup-opa v2.4.0 release commit. */\nexport const SETUP_OPA_SHA = 'b2b258e089860efaadaaf71bf6e3aecb4a3eeff1';\n\n/** Immutable open-policy-agent/setup-regal v2.0.0 release commit. */\nexport const SETUP_REGAL_SHA = '761188c3b435761fa254beca508a44875619648f';\n\n/**\n * Prefix a path below the repository-relative policy workspace.\n *\n * @param workspaceDirectory - POSIX repository-relative workspace\n * @param child - POSIX workspace-relative path or pattern\n * @returns Repository-relative path or pattern\n */\nfunction workspacePath(workspaceDirectory: string, child: string): string {\n return workspaceDirectory === '.' ? child : posix.join(workspaceDirectory, child);\n}\n\n/**\n * Generate credential-free, validation-only Policy Engine CI.\n *\n * Path filters watch the whole policy workspace. Lint runs against each\n * publishable `{root}-bundle/` directory.\n *\n * @param options - Repository paths and exact CLI release\n * @returns Complete GitHub Actions workflow YAML\n */\nexport function generatePolicyGithubActionsWorkflow(options: {\n /** Exact released Transcend CLI version. */\n cliVersion: string;\n /** Policy workspace directory relative to the repository root. */\n workspaceDirectory: string;\n /**\n * Publish directories to lint, relative to the repository root.\n *\n * Pass an empty array for a workspace with no bundles yet (`policy init`).\n * Defaults to the disposable starter bundle under the workspace when omitted.\n */\n bundleDirectories?: readonly string[];\n}): string {\n const bundleDirectories = options.bundleDirectories ?? [\n workspacePath(options.workspaceDirectory, POLICY_STARTER_BUNDLE_DIRECTORY),\n ];\n const watchedPaths = [\n workspacePath(options.workspaceDirectory, '**/*.rego'),\n workspacePath(options.workspaceDirectory, '**/*.json'),\n workspacePath(options.workspaceDirectory, '**/*.yaml'),\n workspacePath(options.workspaceDirectory, '**/*.yml'),\n workspacePath(options.workspaceDirectory, '**/.manifest'),\n workspacePath(options.workspaceDirectory, '.regal/config.yaml'),\n workspacePath(options.workspaceDirectory, '.regal.yaml'),\n POLICY_CI_WORKFLOW_PATH,\n ];\n const pathFilters = watchedPaths.map((path) => ` - ${JSON.stringify(path)}`).join('\\n');\n\n let lintJob: string;\n if (bundleDirectories.length === 0) {\n lintJob = ` lint:\n runs-on: ubuntu-latest\n steps:\n - uses: actions/checkout@${ACTIONS_CHECKOUT_SHA} # v6\n - name: No publishable bundles yet\n run: >-\n echo \"Run transcend policy new to add a bundle;\n policy new refreshes this workflow automatically.\"\n`;\n } else if (bundleDirectories.length === 1) {\n lintJob = ` lint:\n runs-on: ubuntu-latest\n steps:\n - uses: actions/checkout@${ACTIONS_CHECKOUT_SHA} # v6\n - name: Set up OPA\n uses: open-policy-agent/setup-opa@${SETUP_OPA_SHA} # v2.4.0\n with:\n version: ${JSON.stringify(POLICY_STARTER_OPA_VERSION)}\n - name: Set up Regal\n uses: open-policy-agent/setup-regal@${SETUP_REGAL_SHA} # v2.0.0\n with:\n version: ${JSON.stringify(POLICY_STARTER_REGAL_VERSION)}\n - name: Install the Transcend CLI\n run: npm install --global @transcend-io/cli@${options.cliVersion}\n - name: Validate Policy Engine project\n env:\n POLICY_DIRECTORY: ${JSON.stringify(bundleDirectories[0])}\n run: >-\n transcend policy lint\n \"$POLICY_DIRECTORY\"\n --noInteractive\n --json\n`;\n } else {\n lintJob = ` lint:\n runs-on: ubuntu-latest\n strategy:\n fail-fast: false\n matrix:\n policy_directory:\n${bundleDirectories.map((directory) => ` - ${JSON.stringify(directory)}`).join('\\n')}\n steps:\n - uses: actions/checkout@${ACTIONS_CHECKOUT_SHA} # v6\n - name: Set up OPA\n uses: open-policy-agent/setup-opa@${SETUP_OPA_SHA} # v2.4.0\n with:\n version: ${JSON.stringify(POLICY_STARTER_OPA_VERSION)}\n - name: Set up Regal\n uses: open-policy-agent/setup-regal@${SETUP_REGAL_SHA} # v2.0.0\n with:\n version: ${JSON.stringify(POLICY_STARTER_REGAL_VERSION)}\n - name: Install the Transcend CLI\n run: npm install --global @transcend-io/cli@${options.cliVersion}\n - name: Validate Policy Engine project\n env:\n POLICY_DIRECTORY: \\${{ matrix.policy_directory }}\n run: transcend policy lint \"$POLICY_DIRECTORY\" --noInteractive --json\n`;\n }\n\n return `# Generated by transcend policy init.\nname: Transcend Policy\n\non:\n pull_request:\n paths:\n${pathFilters}\n push:\n paths:\n${pathFilters}\n\npermissions:\n contents: read\n\njobs:\n${lintJob}`;\n}\n","import { relative, sep } from 'node:path';\n\nimport {\n mergeJsonc,\n mergeStringArray,\n parseJsoncObject,\n type JsoncUpdate,\n} from '../scaffolding/jsonc.js';\nimport { POLICY_MANIFEST_FILENAME, POLICY_STARTER_ROOT } from './policy-scaffold-templates.js';\n\n/** Recommended VS Code extension for OPA and Regal authoring. */\nexport const POLICY_VSCODE_EXTENSION = 'tsandall.opa';\n\n/** Conflicting syntax-only VS Code extension. */\nexport const CONFLICTING_POLICY_VSCODE_EXTENSION = 'glebbash.opa-highlight-only';\n\n/** Result of safely merging one repository editor artifact. */\nexport interface PolicyEditorMergeResult {\n /** Complete merged contents, or the unchanged original when unsafe. */\n contents: string;\n /** Actionable preservation warnings. */\n warnings: string[];\n}\n\n/** One desired scalar setting that must not replace a customization. */\ninterface DesiredSetting {\n /** JSONC object path. */\n path: string[];\n /** Desired generated value. */\n value: unknown;\n /** Human-readable setting key. */\n label: string;\n}\n\n/**\n * Compare JSON-compatible configuration values.\n *\n * @param left - Existing value\n * @param right - Desired value\n * @returns Whether the values are structurally equal\n */\nfunction configurationValuesEqual(left: unknown, right: unknown): boolean {\n return JSON.stringify(left) === JSON.stringify(right);\n}\n\n/**\n * Parse an editor object while preserving malformed existing content.\n *\n * @param contents - Existing JSONC, or null\n * @param label - Configuration label\n * @returns Parsed object or a preservation result\n */\nfunction parseEditorObject(\n contents: string | null,\n label: string,\n):\n | {\n /** Parsed JSONC object. */\n current: Record<string, unknown>;\n }\n | {\n /** Complete unchanged contents. */\n contents: string;\n /** Actionable warning. */\n warning: string;\n } {\n try {\n return { current: parseJsoncObject(contents ?? '{}\\n', label) };\n } catch (error) {\n return {\n contents: contents ?? '{}\\n',\n warning: `${error instanceof Error ? error.message : String(error)} Existing content was left unchanged.`,\n };\n }\n}\n\n/**\n * Apply safe JSONC updates or preserve the existing file when comments prevent it.\n *\n * @param contents - Existing JSONC\n * @param updates - Proven-safe updates\n * @param label - Configuration label\n * @param warnings - Existing merge warnings\n * @returns Complete contents and warnings\n */\nfunction applyEditorUpdates(\n contents: string | null,\n updates: readonly JsoncUpdate[],\n label: string,\n warnings: string[],\n): PolicyEditorMergeResult {\n if (updates.length === 0) {\n return { contents: contents ?? '{}\\n', warnings };\n }\n try {\n return {\n contents: mergeJsonc(contents, updates, label),\n warnings,\n };\n } catch (error) {\n return {\n contents: contents ?? '{}\\n',\n warnings: [\n ...warnings,\n `${error instanceof Error ? error.message : String(error)} Existing content was left unchanged.`,\n ],\n };\n }\n}\n\n/**\n * Add absent scalar settings without replacing repository customizations.\n *\n * @param current - Parsed editor object\n * @param desired - Desired settings\n * @param updates - Safe updates to append to\n * @param warnings - Conflict warnings to append to\n */\nfunction collectSafeScalarUpdates(\n current: Record<string, unknown>,\n desired: readonly DesiredSetting[],\n updates: JsoncUpdate[],\n warnings: string[],\n): void {\n desired.forEach(({ path, value, label }) => {\n const existing = path.reduce<unknown>((parent, key) => {\n return parent && typeof parent === 'object' && !Array.isArray(parent)\n ? (parent as Record<string, unknown>)[key]\n : undefined;\n }, current);\n if (existing === undefined) {\n updates.push({ path, value });\n } else if (!configurationValuesEqual(existing, value)) {\n warnings.push(\n `VS Code setting \"${label}\" has a repository-specific value and was left unchanged.`,\n );\n }\n });\n}\n\n/**\n * Build a `${workspaceFolder}/…` path for a repository-relative POSIX path.\n *\n * @param repositoryRelativePath - Path relative to the repository root\n * @returns VS Code workspace path\n */\nfunction buildWorkspaceFolderPath(repositoryRelativePath: string): string {\n return repositoryRelativePath === '.'\n ? '${workspaceFolder}'\n : `\\${workspaceFolder}/${repositoryRelativePath}`;\n}\n\n/**\n * Repository-relative POSIX path from the root that owns `.vscode`.\n *\n * @param repositoryRoot - Root that owns `.vscode`\n * @param absolutePath - Absolute path below the repository\n * @returns POSIX repository-relative path\n */\nfunction repositoryRelativePosix(repositoryRoot: string, absolutePath: string): string {\n return relative(repositoryRoot, absolutePath).split(sep).join('/') || '.';\n}\n\n/**\n * Build the workspace-relative OPA publish root for a bundle.\n *\n * @param repositoryRoot - Root that owns `.vscode`\n * @param workspaceDirectory - Selected policy workspace directory\n * @param root - Package root name\n * @returns VS Code workspace path\n */\nfunction buildOpaBundleRoot(\n repositoryRoot: string,\n workspaceDirectory: string,\n root: string = POLICY_STARTER_ROOT,\n): string {\n const workspace = repositoryRelativePosix(repositoryRoot, workspaceDirectory);\n const bundleDir = `${root}-bundle`;\n const bundle = workspace === '.' ? bundleDir : `${workspace}/${bundleDir}`;\n return buildWorkspaceFolderPath(bundle);\n}\n\n/**\n * Build the workspace-relative schemas directory path.\n *\n * @param repositoryRoot - Root that owns `.vscode`\n * @param workspaceDirectory - Selected policy workspace directory\n * @returns VS Code workspace path\n */\nfunction buildOpaSchemaDirectory(repositoryRoot: string, workspaceDirectory: string): string {\n const workspace = repositoryRelativePosix(repositoryRoot, workspaceDirectory);\n const schemas = workspace === '.' ? 'schemas' : `${workspace}/schemas`;\n return buildWorkspaceFolderPath(schemas);\n}\n\n/**\n * Build a `files.associations` glob for every `.manifest` under the workspace.\n *\n * Patterns that contain `/` are matched against the absolute file path, so a\n * leading `**\\/` prefix is required for a portable workspace-relative association.\n *\n * @param repositoryRoot - Root that owns `.vscode`\n * @param workspaceDirectory - Selected policy workspace directory\n * @returns Association pattern key\n */\nfunction buildPolicyManifestAssociationPattern(\n repositoryRoot: string,\n workspaceDirectory: string,\n): string {\n const workspace = repositoryRelativePosix(repositoryRoot, workspaceDirectory);\n return workspace === '.'\n ? `**/${POLICY_MANIFEST_FILENAME}`\n : `**/${workspace}/**/${POLICY_MANIFEST_FILENAME}`;\n}\n\n/**\n * Build `json.schemas` fileMatch globs for a bundle's input documents.\n *\n * @param repositoryRoot - Root that owns `.vscode`\n * @param workspaceDirectory - Selected policy workspace directory\n * @param root - Package root name\n * @returns Absolute-style fileMatch paths and schema URL\n */\nfunction buildBundleInputJsonSchema(\n repositoryRoot: string,\n workspaceDirectory: string,\n root: string = POLICY_STARTER_ROOT,\n): {\n /** VS Code fileMatch globs (leading slash = workspace-relative). */\n fileMatch: string[];\n /** Relative URL to the input schema. */\n url: string;\n} {\n const workspace = repositoryRelativePosix(repositoryRoot, workspaceDirectory);\n const bundleDir = `${root}-bundle`;\n const bundlePrefix = workspace === '.' ? `/${bundleDir}` : `/${workspace}/${bundleDir}`;\n const schemaUrl =\n workspace === '.'\n ? `./schemas/${root}/input.json`\n : `./${workspace}/schemas/${root}/input.json`;\n return {\n fileMatch: [`${bundlePrefix}/input.json`, `${bundlePrefix}/input.example.json`],\n url: schemaUrl,\n };\n}\n\n/**\n * Merge strict workspace-scoped OPA and Rego editor settings.\n *\n * Setting names match the authoritative `open-policy-agent/vscode-opa`\n * extension metadata. Existing conflicting values are preserved and reported.\n *\n * When `bundleRoots` is provided, `opa.roots` and `json.schemas` are populated\n * for those bundles. When omitted (init without bundles), `opa.roots` and\n * `json.schemas` are left empty or unset.\n *\n * @param contents - Existing `.vscode/settings.json` JSONC\n * @param repositoryRoot - Root that owns `.vscode`\n * @param workspaceDirectory - Selected policy workspace directory\n * @param bundleRoots - Bundle roots to add to `opa.roots` and `json.schemas`\n * @returns Merged settings and conflict warnings\n */\nexport function mergePolicyEditorSettings(\n contents: string | null,\n repositoryRoot: string,\n workspaceDirectory: string,\n bundleRoots?: readonly string[],\n): PolicyEditorMergeResult {\n const parsed = parseEditorObject(contents, 'VS Code policy settings');\n if ('warning' in parsed) {\n return { contents: parsed.contents, warnings: [parsed.warning] };\n }\n const { current } = parsed;\n const updates: JsoncUpdate[] = [];\n const warnings: string[] = [];\n\n if (bundleRoots && bundleRoots.length > 0) {\n const roots = current['opa.roots'];\n const desiredRoots = bundleRoots.map((root) =>\n buildOpaBundleRoot(repositoryRoot, workspaceDirectory, root),\n );\n if (roots === undefined) {\n updates.push({ path: ['opa.roots'], value: desiredRoots });\n } else if (Array.isArray(roots) && roots.every((value) => typeof value === 'string')) {\n const merged = mergeStringArray(roots, desiredRoots);\n if (!configurationValuesEqual(merged, roots)) {\n updates.push({ path: ['opa.roots'], value: merged });\n }\n } else {\n warnings.push('VS Code setting \"opa.roots\" is customized and was left unchanged.');\n }\n }\n\n collectSafeScalarUpdates(\n current,\n [\n {\n path: ['opa.schema'],\n value: buildOpaSchemaDirectory(repositoryRoot, workspaceDirectory),\n label: 'opa.schema',\n },\n { path: ['opa.checkOnSave'], value: true, label: 'opa.checkOnSave' },\n { path: ['opa.strictMode'], value: true, label: 'opa.strictMode' },\n { path: ['opa.bundleMode'], value: true, label: 'opa.bundleMode' },\n { path: ['opa.formatter'], value: 'opa-fmt-rego-v1', label: 'opa.formatter' },\n ],\n updates,\n warnings,\n );\n\n const associations = current['files.associations'];\n const manifestAssociation = buildPolicyManifestAssociationPattern(\n repositoryRoot,\n workspaceDirectory,\n );\n if (associations === undefined) {\n updates.push({\n path: ['files.associations'],\n value: { [manifestAssociation]: 'json' },\n });\n } else if (\n associations !== null &&\n typeof associations === 'object' &&\n !Array.isArray(associations)\n ) {\n collectSafeScalarUpdates(\n current,\n [\n {\n path: ['files.associations', manifestAssociation],\n value: 'json',\n label: `files.associations.\"${manifestAssociation}\"`,\n },\n ],\n updates,\n warnings,\n );\n } else {\n warnings.push('VS Code setting \"files.associations\" is customized and was left unchanged.');\n }\n\n if (bundleRoots && bundleRoots.length > 0) {\n const desiredJsonSchemas = bundleRoots.map((root) =>\n buildBundleInputJsonSchema(repositoryRoot, workspaceDirectory, root),\n );\n const jsonSchemas = current['json.schemas'];\n if (jsonSchemas === undefined) {\n updates.push({ path: ['json.schemas'], value: desiredJsonSchemas });\n } else if (Array.isArray(jsonSchemas)) {\n let merged = [...jsonSchemas];\n desiredJsonSchemas.forEach((desired) => {\n const hasMatch = merged.some(\n (entry) =>\n entry !== null &&\n typeof entry === 'object' &&\n !Array.isArray(entry) &&\n configurationValuesEqual(entry, desired),\n );\n if (!hasMatch) {\n const conflicting = merged.find(\n (entry) =>\n entry !== null &&\n typeof entry === 'object' &&\n !Array.isArray(entry) &&\n typeof (entry as Record<string, unknown>).url === 'string' &&\n (entry as Record<string, unknown>).url === desired.url,\n );\n if (conflicting === undefined) {\n merged = [...merged, desired];\n } else {\n warnings.push(\n `VS Code setting \"json.schemas\" has a repository-specific entry for the ${(desired as Record<string, unknown>).url as string} schema and was left unchanged.`,\n );\n }\n }\n });\n if (!configurationValuesEqual(merged, jsonSchemas)) {\n updates.push({ path: ['json.schemas'], value: merged });\n }\n } else {\n warnings.push('VS Code setting \"json.schemas\" is customized and was left unchanged.');\n }\n }\n\n const regoSettings = current['[rego]'];\n if (regoSettings === undefined) {\n updates.push({\n path: ['[rego]'],\n value: {\n 'editor.defaultFormatter': POLICY_VSCODE_EXTENSION,\n 'editor.formatOnSave': true,\n 'editor.insertSpaces': false,\n 'editor.tabSize': 4,\n },\n });\n } else if (\n regoSettings !== null &&\n typeof regoSettings === 'object' &&\n !Array.isArray(regoSettings)\n ) {\n collectSafeScalarUpdates(\n current,\n [\n {\n path: ['[rego]', 'editor.defaultFormatter'],\n value: POLICY_VSCODE_EXTENSION,\n label: '[rego].editor.defaultFormatter',\n },\n {\n path: ['[rego]', 'editor.formatOnSave'],\n value: true,\n label: '[rego].editor.formatOnSave',\n },\n {\n path: ['[rego]', 'editor.insertSpaces'],\n value: false,\n label: '[rego].editor.insertSpaces',\n },\n {\n path: ['[rego]', 'editor.tabSize'],\n value: 4,\n label: '[rego].editor.tabSize',\n },\n ],\n updates,\n warnings,\n );\n } else {\n warnings.push('VS Code language setting \"[rego]\" is customized and was left unchanged.');\n }\n\n return applyEditorUpdates(contents, updates, 'VS Code policy settings', warnings);\n}\n\n/**\n * Merge OPA extension recommendations without disturbing unknown entries.\n *\n * VS Code's `extensions.json` schema supports `unwantedRecommendations`, so\n * the syntax-only extension can be removed from recommendations and discouraged.\n *\n * @param contents - Existing `.vscode/extensions.json` JSONC\n * @returns Merged extension recommendations and warnings\n */\nexport function mergePolicyEditorExtensions(contents: string | null): PolicyEditorMergeResult {\n const parsed = parseEditorObject(contents, 'VS Code policy extension recommendations');\n if ('warning' in parsed) {\n return { contents: parsed.contents, warnings: [parsed.warning] };\n }\n const { current } = parsed;\n const updates: JsoncUpdate[] = [];\n const warnings: string[] = [];\n\n const recommendations = current.recommendations;\n if (\n recommendations === undefined ||\n (Array.isArray(recommendations) && recommendations.every((value) => typeof value === 'string'))\n ) {\n const existing = recommendations ?? [];\n const desired = mergeStringArray(existing, [POLICY_VSCODE_EXTENSION]).filter(\n (value) => value !== CONFLICTING_POLICY_VSCODE_EXTENSION,\n );\n if (!configurationValuesEqual(existing, desired)) {\n updates.push({ path: ['recommendations'], value: desired });\n }\n } else {\n warnings.push(\n 'VS Code extension recommendations use an unknown shape and were left unchanged.',\n );\n }\n\n const unwanted = current.unwantedRecommendations;\n if (\n unwanted === undefined ||\n (Array.isArray(unwanted) && unwanted.every((value) => typeof value === 'string'))\n ) {\n const existing = unwanted ?? [];\n const desired = mergeStringArray(existing, [CONFLICTING_POLICY_VSCODE_EXTENSION]);\n if (!configurationValuesEqual(existing, desired)) {\n updates.push({ path: ['unwantedRecommendations'], value: desired });\n }\n } else {\n warnings.push(\n 'VS Code unwanted extension recommendations use an unknown shape and were left unchanged.',\n );\n }\n\n return applyEditorUpdates(\n contents,\n updates,\n 'VS Code policy extension recommendations',\n warnings,\n );\n}\n\n/**\n * Build a VS Code lint task for one bundle.\n *\n * @param repositoryRoot - Root that owns `.vscode`\n * @param workspaceDirectory - Selected policy workspace directory\n * @param root - Package root name\n * @returns VS Code task object\n */\nexport function buildPolicyLintTask(\n repositoryRoot: string,\n workspaceDirectory: string,\n root: string = POLICY_STARTER_ROOT,\n): Record<string, unknown> {\n const workspace = repositoryRelativePosix(repositoryRoot, workspaceDirectory);\n const bundleDir = `${root}-bundle`;\n const bundle = workspace === '.' ? bundleDir : `${workspace}/${bundleDir}`;\n return {\n label: `policy: lint ${root}`,\n type: 'process',\n command: 'transcend',\n args: ['policy', 'lint', bundle, '--noInteractive'],\n group: {\n kind: 'test',\n isDefault: false,\n },\n problemMatcher: [],\n };\n}\n\n/**\n * Build the aggregate \"policy: lint\" task (runs all bundles).\n *\n * @param roots - All bundle root names\n * @returns VS Code task object\n */\nexport function buildPolicyAggregateLintTask(roots: readonly string[]): Record<string, unknown> {\n return {\n label: 'policy: lint',\n dependsOn: roots.map((root) => `policy: lint ${root}`),\n dependsOrder: 'sequence',\n group: {\n kind: 'test',\n isDefault: true,\n },\n problemMatcher: [],\n };\n}\n\n/**\n * Merge policy lint tasks while preserving custom tasks.\n *\n * When `bundleRoots` is provided, creates per-bundle tasks and an aggregate.\n * Managed `policy: lint*` tasks are updated in place as roots grow. When\n * omitted, skips task creation (init without bundles).\n *\n * @param contents - Existing `.vscode/tasks.json` JSONC\n * @param repositoryRoot - Root that owns `.vscode`\n * @param workspaceDirectory - Selected policy workspace directory\n * @param bundleRoots - Bundle roots to generate tasks for\n * @returns Merged task configuration and warnings\n */\nexport function mergePolicyEditorTasks(\n contents: string | null,\n repositoryRoot: string,\n workspaceDirectory: string,\n bundleRoots?: readonly string[],\n): PolicyEditorMergeResult {\n const parsed = parseEditorObject(contents, 'VS Code policy tasks');\n if ('warning' in parsed) {\n return { contents: parsed.contents, warnings: [parsed.warning] };\n }\n const { current } = parsed;\n const updates: JsoncUpdate[] = [];\n const warnings: string[] = [];\n const version = current.version;\n if (version === undefined) {\n updates.push({ path: ['version'], value: '2.0.0' });\n } else if (version !== '2.0.0') {\n warnings.push('VS Code task schema version is customized and was left unchanged.');\n }\n\n if (!bundleRoots || bundleRoots.length === 0) {\n return applyEditorUpdates(contents, updates, 'VS Code policy tasks', warnings);\n }\n\n const desiredPerBundleTasks = bundleRoots.map((root) =>\n buildPolicyLintTask(repositoryRoot, workspaceDirectory, root),\n );\n const desiredAggregateTask = buildPolicyAggregateLintTask(bundleRoots);\n const desiredTasks = [...desiredPerBundleTasks, desiredAggregateTask];\n const managedLabels = new Set(desiredTasks.map((task) => task.label as string));\n const tasks = current.tasks;\n if (tasks === undefined) {\n updates.push({ path: ['tasks'], value: desiredTasks });\n } else if (Array.isArray(tasks)) {\n let merged = [...tasks];\n let changed = false;\n desiredTasks.forEach((desired) => {\n const label = desired.label as string;\n const matchingIndex = merged.findIndex(\n (task) =>\n task !== null &&\n typeof task === 'object' &&\n !Array.isArray(task) &&\n (task as Record<string, unknown>).label === label,\n );\n if (matchingIndex === -1) {\n merged = [...merged, desired];\n changed = true;\n return;\n }\n if (!configurationValuesEqual(merged[matchingIndex], desired)) {\n if (managedLabels.has(label)) {\n merged = merged.map((task, index) => (index === matchingIndex ? desired : task));\n changed = true;\n } else {\n warnings.push(\n `VS Code task \"${label}\" has repository-specific customization and was left unchanged.`,\n );\n }\n }\n });\n if (changed) {\n updates.push({ path: ['tasks'], value: merged });\n }\n } else {\n warnings.push('VS Code tasks use an unknown shape and were left unchanged.');\n }\n\n return applyEditorUpdates(contents, updates, 'VS Code policy tasks', warnings);\n}\n"],"mappings":"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"}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
let e=!1;function t(t){e=t}function n(){return e}var r=class extends Error{constructor(e,t){super(e,{cause:t?.cause}),this.name=`PolicyEngineCliError`}};function i(e){if(!n())return e.message;let t=[e.message,``,`Debug details:`],r=e.cause;return r instanceof Error?t.push(r.stack??r.message):r===void 0?t.push(`No underlying cause was recorded.`):t.push(String(r)),t.join(`
|
|
2
2
|
`)}function a(e){if(e instanceof r)return i(e)}function o(e){return a(e)||(e instanceof Error?n()?e.stack??String(e):e.message:String(e))}export{o as n,t as r,r as t};
|
|
3
|
-
//# sourceMappingURL=policyEngineCliError-
|
|
3
|
+
//# sourceMappingURL=policyEngineCliError-CZrOHhLv.mjs.map
|
package/dist/{policyEngineCliError-DY8V-qkK.mjs.map → policyEngineCliError-CZrOHhLv.mjs.map}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"policyEngineCliError-
|
|
1
|
+
{"version":3,"file":"policyEngineCliError-CZrOHhLv.mjs","names":[],"sources":["../src/commands/policy/helpers/policyEngineCliError.ts"],"sourcesContent":["/** Whether policy commands should include technical error details in output. */\nlet policyEngineCliDebugEnabled = false;\n\n/**\n * Enables or disables technical Policy Engine CLI error output for the current command run.\n *\n * @param enabled - When true, API causes and stack traces are included in failures\n */\nexport function setPolicyEngineCliDebug(enabled: boolean): void {\n policyEngineCliDebugEnabled = enabled;\n}\n\n/**\n * Returns true when `--debug` was passed to a policy command.\n *\n * @returns Whether debug output is enabled\n */\nexport function isPolicyEngineCliDebugEnabled(): boolean {\n return policyEngineCliDebugEnabled;\n}\n\n/**\n * User-facing Policy Engine CLI error without a stack trace by default.\n */\nexport class PolicyEngineCliError extends Error {\n /**\n * Creates a policy CLI error suitable for end-user output.\n *\n * @param message - User-readable error text\n * @param options - Optional underlying failure\n */\n constructor(\n message: string,\n options?: {\n /** Original failure, typically a got HTTPError */\n cause?: unknown;\n },\n ) {\n super(message, { cause: options?.cause });\n this.name = 'PolicyEngineCliError';\n }\n}\n\n/**\n * Formats a Policy Engine CLI error for terminal output.\n *\n * @param error - Thrown policy CLI error\n * @returns Message only by default; technical details when debug mode is enabled\n */\nexport function formatPolicyEngineCliErrorForTerminal(error: PolicyEngineCliError): string {\n if (!isPolicyEngineCliDebugEnabled()) {\n return error.message;\n }\n\n const parts = [error.message, '', 'Debug details:'];\n const cause = error.cause;\n\n if (cause instanceof Error) {\n parts.push(cause.stack ?? cause.message);\n } else if (cause !== undefined) {\n parts.push(String(cause));\n } else {\n parts.push('No underlying cause was recorded.');\n }\n\n return parts.join('\\n');\n}\n\n/**\n * Formats thrown values for Stricli, suppressing stacks for policy CLI errors unless debug is enabled.\n *\n * @param exc - Thrown value from a command\n * @returns Terminal-safe error text\n */\nexport function formatPolicyEngineCliException(exc: unknown): string | undefined {\n if (exc instanceof PolicyEngineCliError) {\n return formatPolicyEngineCliErrorForTerminal(exc);\n }\n\n return undefined;\n}\n\n/**\n * Formats any thrown command value for terminal output.\n *\n * `PolicyEngineCliError` values are formatted by `formatPolicyEngineCliException`.\n * Other errors (e.g. bundle-not-found, missing OPA binary, local OPA failures)\n * are plain `Error`s -- return only their message by default so operators don't\n * see a minified stack trace, and surface the stack only when a policy command\n * opted in via `--debug`.\n *\n * @param exc - Thrown value from a command\n * @returns Terminal-safe error text\n */\nexport function formatCliException(exc: unknown): string {\n const policyMessage = formatPolicyEngineCliException(exc);\n if (policyMessage) {\n return policyMessage;\n }\n\n if (exc instanceof Error) {\n return isPolicyEngineCliDebugEnabled() ? (exc.stack ?? String(exc)) : exc.message;\n }\n\n return String(exc);\n}\n"],"mappings":"AACA,IAAI,EAA8B,GAOlC,SAAgB,EAAwB,EAAwB,CAC9D,EAA8B,EAQhC,SAAgB,GAAyC,CACvD,OAAO,EAMT,IAAa,EAAb,cAA0C,KAAM,CAO9C,YACE,EACA,EAIA,CACA,MAAM,EAAS,CAAE,MAAO,GAAS,MAAO,CAAC,CACzC,KAAK,KAAO,yBAUhB,SAAgB,EAAsC,EAAqC,CACzF,GAAI,CAAC,GAA+B,CAClC,OAAO,EAAM,QAGf,IAAM,EAAQ,CAAC,EAAM,QAAS,GAAI,iBAAiB,CAC7C,EAAQ,EAAM,MAUpB,OARI,aAAiB,MACnB,EAAM,KAAK,EAAM,OAAS,EAAM,QAAQ,CAC/B,IAAU,IAAA,GAGnB,EAAM,KAAK,oCAAoC,CAF/C,EAAM,KAAK,OAAO,EAAM,CAAC,CAKpB,EAAM,KAAK;EAAK,CASzB,SAAgB,EAA+B,EAAkC,CAC/E,GAAI,aAAe,EACjB,OAAO,EAAsC,EAAI,CAkBrD,SAAgB,EAAmB,EAAsB,CAUvD,OATsB,EAA+B,EACjD,GAIA,aAAe,MACV,GAA+B,CAAI,EAAI,OAAS,OAAO,EAAI,CAAI,EAAI,QAGrE,OAAO,EAAI"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{t as e}from"./policyEngineCliError-
|
|
1
|
+
import{t as e}from"./policyEngineCliError-CZrOHhLv.mjs";import t from"got";const n=5120,r=51200;function i(e,n){let r=e.replace(/\/$/,``);if(/(^|\/)v1$/i.test(r))throw Error(`--transcend-url must not include a trailing "/v1" (the CLI appends it automatically). Got "${e}"; use "${r.replace(/\/v1$/i,``)}" instead.`);return t.extend({prefixUrl:r,headers:{Authorization:`Bearer ${n}`,accept:`application/json`}})}function a(e){if(typeof e==`string`)try{return JSON.parse(e).message}catch{return e.length>0?e:void 0}if(e&&typeof e==`object`&&`message`in e){let t=e.message;if(typeof t==`string`&&t.length>0)return t}}function o(e,t){if(!e)return;let n=e[t]??e[t.toLowerCase()];return Array.isArray(n)?n[0]:n}function s(e,t){if(e)return e;let n=o(t,`retry-after`),r=o(t,`x-ratelimit-reset`),i=`Rate limit exceeded (429 Too Many Requests).
|
|
2
2
|
|
|
3
3
|
Fix:
|
|
4
4
|
- Wait and retry the command`;return n?i+=`\n- Retry after ${n} second(s)`:r&&(i+=`\n- Retry after ${r}`),i}function c(e,t,n){switch(e){case 401:return`Authentication failed (401 Unauthorized).
|
|
@@ -18,7 +18,7 @@ Fix:
|
|
|
18
18
|
- Run \`transcend policy bundles\` to see available bundles
|
|
19
19
|
- Run \`transcend policy versions --bundle-name=<name>\` to see available versions`;case 400:return t??`The request was invalid. Check your command flags and try again.`;case 409:return t??`The request conflicted with the current policy bundle state.`;case 413:return t??`Policy bundle upload is too large (413 Payload Too Large).
|
|
20
20
|
|
|
21
|
-
Uploaded bundles must be at most 5 KiB compressed and 50 KiB decompressed gzip tarballs containing only manifest
|
|
21
|
+
Uploaded bundles must be at most 5 KiB compressed and 50 KiB decompressed gzip tarballs containing only .manifest and .rego files.
|
|
22
22
|
|
|
23
23
|
Fix:
|
|
24
24
|
- Remove unnecessary files from the bundle directory before publishing
|
|
@@ -30,4 +30,4 @@ Fix:
|
|
|
30
30
|
- Check your network connection
|
|
31
31
|
- Confirm --transcend-url points at the correct environment
|
|
32
32
|
- Retry the command in a few moments`;if(e&&typeof e==`object`&&`response`in e){let t=e.response,n=t?.statusCode,r=a(t?.body);if(n)return c(n,r,t?.headers);if(r)return r}return e instanceof Error?e.message:String(e)}function d(t){throw new e(u(t),{cause:t})}async function f(e){try{return await e}catch(e){d(e)}}function p(e,t){if(t.json){e.write(`${JSON.stringify(t.data,null,2)}\n`);return}t.renderTable&&e.write(`${t.renderTable()}\n`)}export{n as a,i,f as n,r as o,d as r,p as t};
|
|
33
|
-
//# sourceMappingURL=printResult-
|
|
33
|
+
//# sourceMappingURL=printResult-C2K9ODeE.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"printResult-C2K9ODeE.mjs","names":[],"sources":["../src/commands/policy/constants.ts","../src/commands/policy/helpers/buildPolicyEngineClient.ts","../src/commands/policy/helpers/formatPolicyEngineRequestError.ts","../src/commands/policy/helpers/printResult.ts"],"sourcesContent":["/** Maximum compressed policy bundle upload size in bytes (5 KiB). */\nexport const MAX_BUNDLE_COMPRESSED_BYTES = 5120;\n\n/** Maximum decompressed policy bundle size enforced by the server (50 KiB). */\nexport const MAX_BUNDLE_DECOMPRESSED_BYTES = 51200;\n\n/** Placeholder for nullable API fields in table output. */\nexport const EMPTY_CELL = '-';\n","import got, { type Got } from 'got';\n\n/**\n * Creates a got client for Policy Engine REST endpoints on the monolith.\n *\n * The CLI appends `v1/...` paths to this base URL, so callers must NOT include\n * a trailing `/v1` (that would produce `/v1/v1/...` and an opaque `400`). We\n * detect and reject that mistake with a clear hint.\n *\n * @param transcendUrl - Transcend API base URL (without `/v1`)\n * @param auth - Transcend API key\n * @returns Configured got instance\n */\nexport function buildPolicyEngineClient(transcendUrl: string, auth: string): Got {\n const normalized = transcendUrl.replace(/\\/$/, '');\n if (/(^|\\/)v1$/i.test(normalized)) {\n throw new Error(\n `--transcend-url must not include a trailing \"/v1\" (the CLI appends it automatically). ` +\n `Got \"${transcendUrl}\"; use \"${normalized.replace(/\\/v1$/i, '')}\" instead.`,\n );\n }\n return got.extend({\n prefixUrl: normalized,\n headers: {\n Authorization: `Bearer ${auth}`,\n accept: 'application/json',\n },\n });\n}\n","import { PolicyEngineCliError } from './policyEngineCliError.js';\n\n/** Parsed JSON error body from a Policy Engine API response. */\ninterface PolicyEngineErrorBody {\n /** Human-readable error message */\n message?: string;\n}\n\n/** HTTP response metadata on a got HTTPError. */\ninterface PolicyEngineHttpResponse {\n /** HTTP status code */\n statusCode?: number;\n /** Response body (JSON or raw text) */\n body?: unknown;\n /** Response headers */\n headers?: Record<string, string | string[] | undefined>;\n}\n\n/** Shape of a got HTTPError with response metadata. */\ninterface PolicyEngineHttpError {\n /** HTTP response metadata */\n response?: PolicyEngineHttpResponse;\n}\n\nconst AUTH_ERROR_MESSAGE = `Authentication failed (401 Unauthorized).\n\nYour Transcend API key is missing, invalid, or does not have permission for this command.\n\nFix:\n- Set a valid API key: export TRANSCEND_API_KEY=<your-key>\n or pass --auth=<your-key>\n- Ensure the key has the required Policy Engine scopes (e.g. View/Manage/Activate Policy)\n- Confirm you are pointing at the correct environment (--transcend-url)`;\n\nconst NOT_FOUND_MESSAGE = `Resource not found (404 Not Found).\n\nThe requested policy bundle or version does not exist.\n\nFix:\n- Check the --bundle-name value\n- Run \\`transcend policy bundles\\` to see available bundles\n- Run \\`transcend policy versions --bundle-name=<name>\\` to see available versions`;\n\nconst PAYLOAD_TOO_LARGE_MESSAGE = `Policy bundle upload is too large (413 Payload Too Large).\n\nUploaded bundles must be at most 5 KiB compressed and 50 KiB decompressed gzip tarballs containing only .manifest and .rego files.\n\nFix:\n- Remove unnecessary files from the bundle directory before publishing\n- Ensure the compiled .tar.gz stays within the upload limits`;\n\nconst NETWORK_ERROR_MESSAGE = `Connection to Transcend failed.\n\nThe Policy Engine API could not be reached.\n\nFix:\n- Check your network connection\n- Confirm --transcend-url points at the correct environment\n- Retry the command in a few moments`;\n\n/**\n * Extracts a human-readable message from a Policy Engine API error body.\n *\n * @param body - Raw or parsed response body\n * @returns API message when present\n */\nfunction extractApiMessage(body: unknown): string | undefined {\n if (typeof body === 'string') {\n try {\n const parsed = JSON.parse(body) as PolicyEngineErrorBody;\n return parsed.message;\n } catch {\n return body.length > 0 ? body : undefined;\n }\n }\n\n if (body && typeof body === 'object' && 'message' in body) {\n const message = (body as PolicyEngineErrorBody).message;\n if (typeof message === 'string' && message.length > 0) {\n return message;\n }\n }\n\n return undefined;\n}\n\n/**\n * Reads a single response header value.\n *\n * @param headers - Response headers\n * @param name - Header name\n * @returns Header value when present\n */\nfunction readResponseHeader(\n headers: Record<string, string | string[] | undefined> | undefined,\n name: string,\n): string | undefined {\n if (!headers) {\n return undefined;\n }\n\n const value = headers[name] ?? headers[name.toLowerCase()];\n if (Array.isArray(value)) {\n return value[0];\n }\n\n return value;\n}\n\n/**\n * Builds a rate-limit error message using response headers when available.\n *\n * @param apiMessage - Message from the response body, when present\n * @param headers - Response headers\n * @returns User-readable rate-limit text\n */\nfunction formatRateLimitMessage(\n apiMessage: string | undefined,\n headers: Record<string, string | string[] | undefined> | undefined,\n): string {\n if (apiMessage) {\n return apiMessage;\n }\n\n const retryAfter = readResponseHeader(headers, 'retry-after');\n const resetAt = readResponseHeader(headers, 'x-ratelimit-reset');\n\n let message = `Rate limit exceeded (429 Too Many Requests).\n\nFix:\n- Wait and retry the command`;\n\n if (retryAfter) {\n message += `\\n- Retry after ${retryAfter} second(s)`;\n } else if (resetAt) {\n message += `\\n- Retry after ${resetAt}`;\n }\n\n return message;\n}\n\n/**\n * Maps common HTTP status codes to actionable CLI messages.\n *\n * @param statusCode - HTTP status code\n * @param apiMessage - Message from the API response body, when present\n * @param headers - Response headers\n * @returns User-readable error text\n */\nfunction formatHttpStatusError(\n statusCode: number,\n apiMessage?: string,\n headers?: Record<string, string | string[] | undefined>,\n): string {\n switch (statusCode) {\n case 401:\n return AUTH_ERROR_MESSAGE;\n case 403:\n return (\n apiMessage ??\n 'Access was denied (403 Forbidden). If this persists, contact your Transcend admin.'\n );\n case 404:\n return NOT_FOUND_MESSAGE;\n case 400:\n return apiMessage ?? 'The request was invalid. Check your command flags and try again.';\n case 409:\n return apiMessage ?? 'The request conflicted with the current policy bundle state.';\n case 413:\n return apiMessage ?? PAYLOAD_TOO_LARGE_MESSAGE;\n case 429:\n return formatRateLimitMessage(apiMessage, headers);\n default:\n if (statusCode >= 500) {\n return `Transcend server error (${statusCode}). Try again in a few moments. If the problem persists, contact Transcend support.`;\n }\n return apiMessage ?? `Request failed with status code ${statusCode}.`;\n }\n}\n\n/**\n * Returns true when the error looks like a network or timeout failure.\n *\n * @param error - Thrown error\n * @returns Whether the error is likely a connectivity issue\n */\nfunction isNetworkError(error: unknown): boolean {\n if (!(error instanceof Error)) {\n return false;\n }\n\n const code = (error as NodeJS.ErrnoException).code;\n if (\n code === 'ECONNREFUSED' ||\n code === 'ENOTFOUND' ||\n code === 'ETIMEDOUT' ||\n code === 'ECONNRESET'\n ) {\n return true;\n }\n\n const message = error.message.toLowerCase();\n return (\n message.includes('network') ||\n message.includes('timeout') ||\n message.includes('econnrefused') ||\n message.includes('enotfound') ||\n message.includes('etimedout')\n );\n}\n\n/**\n * Extracts a useful error message from a failed Policy Engine HTTP request.\n *\n * @param error - The thrown error, typically a got `HTTPError`\n * @returns A message suitable for CLI output\n */\nexport function formatPolicyEngineRequestError(error: unknown): string {\n if (isNetworkError(error)) {\n return NETWORK_ERROR_MESSAGE;\n }\n\n if (error && typeof error === 'object' && 'response' in error) {\n const response = (error as PolicyEngineHttpError).response;\n const statusCode = response?.statusCode;\n const apiMessage = extractApiMessage(response?.body);\n\n if (statusCode) {\n return formatHttpStatusError(statusCode, apiMessage, response?.headers);\n }\n\n if (apiMessage) {\n return apiMessage;\n }\n }\n\n return error instanceof Error ? error.message : String(error);\n}\n\n/**\n * Rethrows a Policy Engine request failure with a user-readable message.\n *\n * @param error - The thrown error, typically a got `HTTPError`\n */\nexport function throwPolicyEngineRequestError(error: unknown): never {\n throw new PolicyEngineCliError(formatPolicyEngineRequestError(error), { cause: error });\n}\n\n/**\n * Awaits a Policy Engine HTTP request and maps failures to user-readable errors.\n *\n * @param request - Promise returned by a got client call (e.g. `.json()`)\n * @returns Parsed response body\n */\nexport async function policyEngineRequest<T>(request: Promise<T>): Promise<T> {\n try {\n return await request;\n } catch (error) {\n throwPolicyEngineRequestError(error);\n }\n}\n","/** Options for printing CLI command output. */\nexport interface PrintResultOptions {\n /** When true, print raw JSON */\n json: boolean;\n /** JSON-serializable payload */\n data: unknown;\n /** Table renderer used when json is false */\n renderTable?: () => string;\n}\n\n/**\n * Prints JSON or a human-readable table to stdout.\n *\n * @param stdout - Process stdout stream\n * @param options - Output options\n */\nexport function printResult(stdout: NodeJS.WriteStream, options: PrintResultOptions): void {\n if (options.json) {\n stdout.write(`${JSON.stringify(options.data, null, 2)}\\n`);\n return;\n }\n\n if (options.renderTable) {\n stdout.write(`${options.renderTable()}\\n`);\n }\n}\n"],"mappings":"2EACA,MAAa,EAA8B,KAG9B,EAAgC,MCS7C,SAAgB,EAAwB,EAAsB,EAAmB,CAC/E,IAAM,EAAa,EAAa,QAAQ,MAAO,GAAG,CAClD,GAAI,aAAa,KAAK,EAAW,CAC/B,MAAU,MACR,8FACU,EAAa,UAAU,EAAW,QAAQ,SAAU,GAAG,CAAC,YACnE,CAEH,OAAO,EAAI,OAAO,CAChB,UAAW,EACX,QAAS,CACP,cAAe,UAAU,IACzB,OAAQ,mBACT,CACF,CAAC,CCuCJ,SAAS,EAAkB,EAAmC,CAC5D,GAAI,OAAO,GAAS,SAClB,GAAI,CAEF,OADe,KAAK,MAAM,EACb,CAAC,aACR,CACN,OAAO,EAAK,OAAS,EAAI,EAAO,IAAA,GAIpC,GAAI,GAAQ,OAAO,GAAS,UAAY,YAAa,EAAM,CACzD,IAAM,EAAW,EAA+B,QAChD,GAAI,OAAO,GAAY,UAAY,EAAQ,OAAS,EAClD,OAAO,GAcb,SAAS,EACP,EACA,EACoB,CACpB,GAAI,CAAC,EACH,OAGF,IAAM,EAAQ,EAAQ,IAAS,EAAQ,EAAK,aAAa,EAKzD,OAJI,MAAM,QAAQ,EAAM,CACf,EAAM,GAGR,EAUT,SAAS,EACP,EACA,EACQ,CACR,GAAI,EACF,OAAO,EAGT,IAAM,EAAa,EAAmB,EAAS,cAAc,CACvD,EAAU,EAAmB,EAAS,oBAAoB,CAE5D,EAAU;;;8BAWd,OANI,EACF,GAAW,mBAAmB,EAAW,YAChC,IACT,GAAW,mBAAmB,KAGzB,EAWT,SAAS,EACP,EACA,EACA,EACQ,CACR,OAAQ,EAAR,CACE,IAAK,KACH,MAAO;;;;;;;;yEACT,IAAK,KACH,OACE,GACA,qFAEJ,IAAK,KACH,MAAO;;;;;;;oFACT,IAAK,KACH,OAAO,GAAc,mEACvB,IAAK,KACH,OAAO,GAAc,+DACvB,IAAK,KACH,OAAO,GAAc;;;;;;8DACvB,IAAK,KACH,OAAO,EAAuB,EAAY,EAAQ,CACpD,QAIE,OAHI,GAAc,IACT,2BAA2B,EAAW,oFAExC,GAAc,mCAAmC,EAAW,IAUzE,SAAS,EAAe,EAAyB,CAC/C,GAAI,EAAE,aAAiB,OACrB,MAAO,GAGT,IAAM,EAAQ,EAAgC,KAC9C,GACE,IAAS,gBACT,IAAS,aACT,IAAS,aACT,IAAS,aAET,MAAO,GAGT,IAAM,EAAU,EAAM,QAAQ,aAAa,CAC3C,OACE,EAAQ,SAAS,UAAU,EAC3B,EAAQ,SAAS,UAAU,EAC3B,EAAQ,SAAS,eAAe,EAChC,EAAQ,SAAS,YAAY,EAC7B,EAAQ,SAAS,YAAY,CAUjC,SAAgB,EAA+B,EAAwB,CACrE,GAAI,EAAe,EAAM,CACvB,MAAO;;;;;;;sCAGT,GAAI,GAAS,OAAO,GAAU,UAAY,aAAc,EAAO,CAC7D,IAAM,EAAY,EAAgC,SAC5C,EAAa,GAAU,WACvB,EAAa,EAAkB,GAAU,KAAK,CAEpD,GAAI,EACF,OAAO,EAAsB,EAAY,EAAY,GAAU,QAAQ,CAGzE,GAAI,EACF,OAAO,EAIX,OAAO,aAAiB,MAAQ,EAAM,QAAU,OAAO,EAAM,CAQ/D,SAAgB,EAA8B,EAAuB,CACnE,MAAM,IAAI,EAAqB,EAA+B,EAAM,CAAE,CAAE,MAAO,EAAO,CAAC,CASzF,eAAsB,EAAuB,EAAiC,CAC5E,GAAI,CACF,OAAO,MAAM,QACN,EAAO,CACd,EAA8B,EAAM,EClPxC,SAAgB,EAAY,EAA4B,EAAmC,CACzF,GAAI,EAAQ,KAAM,CAChB,EAAO,MAAM,GAAG,KAAK,UAAU,EAAQ,KAAM,KAAM,EAAE,CAAC,IAAI,CAC1D,OAGE,EAAQ,aACV,EAAO,MAAM,GAAG,EAAQ,aAAa,CAAC,IAAI"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{i as e}from"./paths-J5KScOaQ.mjs";import{a as t,i as n,n as r,r as i}from"./project-discovery-CWRjlFnC.mjs";import{a}from"./readTranscendYaml-BfqQbPiT.mjs";import{CustomFunctionPayloadType as o,CustomFunctionType as s}from"@transcend-io/privacy-types";import{dirname as c,extname as l,isAbsolute as u,join as d,relative as f,resolve as p,sep as m}from"node:path";import h from"js-yaml";import{existsSync as g,readFileSync as _,writeFileSync as v}from"node:fs";import{decodeCodec as y}from"@transcend-io/type-utils";import*as b from"io-ts";import{isMap as x,isScalar as S,isSeq as C,parseDocument as w}from"yaml";import{Buffer as T}from"node:buffer";import{isIP as E}from"node:net";const D=64*1024,O=new Set([`DENO_CERT`,`DENO_DIR`,`DENO_TLS_CA_STORE`,`HTTP_PROXY`,`HTTPS_PROXY`,`NODE_EXTRA_CA_CERTS`,`NO_COLOR`,`NO_PROXY`,`NPM_CONFIG_REGISTRY`]);function k(e){let t=/^\[([^\]]+)\](?::(\d+))?$/u.exec(e);if(t){let e=t[2];return E(t[1])===6&&(!e||Number(e)>0&&Number(e)<=65535)}if(E(e)>0)return!0;let n=/^([^:]+):(\d+)$/u.exec(e),r=n?.[1]??e,i=n?.[2];return(!i||Number(i)>0&&Number(i)<=65535)&&r.length<=253&&r.split(`.`).every(e=>e.length>0&&e.length<=63&&/^[A-Za-z\d](?:[A-Za-z\d-]*[A-Za-z\d])?$/u.test(e))}function A(e){let t=e.env??{},n=Object.keys(t),r=n.filter(e=>!/^[A-Za-z][A-Za-z\d_]*$/u.test(e)),i=n.filter(e=>O.has(e)||e.startsWith(`DENO_`));if(r.length>0||i.length>0)throw Error([r.length>0?`Invalid environment variable names: ${r.join(`, `)}`:void 0,i.length>0?`Reserved environment variable names: ${i.join(`, `)}`:void 0].filter(Boolean).join(`. `));let a=n.filter(e=>T.byteLength(t[e])>D);if(a.length>0)throw Error(`Environment values exceed ${D} bytes: ${a.join(`, `)}`);let o=e.allowedHosts??[];if(o.length>1&&o.includes(`*`))throw Error(`allowed-hosts cannot combine "*" with specific hosts.`);let s=o.filter(e=>e!==`*`&&!k(e));if(s.length>0)throw Error(`Invalid allowed-hosts: ${s.join(`, `)}`)}const j=b.intersection([b.type({name:b.string,code:b.string}),b.partial({id:b.string,description:b.string,type:b.union([b.literal(s.Dsr),b.literal(s.General)]),"data-silo-id":b.string,"sombra-id":b.string,"sombra-auth-env":b.string,"allowed-hosts":b.array(b.string),"timeout-ms":b.number,"allow-third-party-imports":b.boolean,env:b.record(b.string,b.string),"test-payload":b.string,"test-payload-type":b.union([b.literal(o.DataPoint),b.literal(o.RequestEnricher)]),"test-payloads":b.array(b.intersection([b.type({payload:b.string}),b.partial({"payload-type":b.union([b.literal(o.DataPoint),b.literal(o.RequestEnricher)])})]))})]),M=b.type({functions:b.array(j)});function N(e){let t=p(`/`,`__custom-function-manifest__`),n=f(t,p(t,e));return n!==`..`&&!n.startsWith(`..${m}`)&&!u(n)}function P(e,t={}){let n=e.functions.map(({id:e})=>e).filter((e,t,n)=>e!==void 0&&n.indexOf(e)!==t);if(n.length>0)throw Error(`Duplicate custom function ids in manifest: ${[...new Set(n)].join(`, `)}`);let r=e.functions.map(({name:e})=>e),i=e.functions.filter(({id:e,name:t})=>e===void 0&&r.filter(e=>e===t).length>1).map(({name:e})=>e);if(i.length>0)throw Error(`Duplicate custom function names in manifest without ids: ${[...new Set(i)].join(`, `)}. Add an \`id\` field to each duplicated entry to disambiguate.`);e.functions.forEach(e=>{if(e[`test-payload`]!==void 0&&e[`test-payloads`]!==void 0)throw Error(`Custom function "${e.name}" sets both test-payload and test-payloads — use test-payloads alone to define multiple payloads.`);if(e[`test-payload-type`]!==void 0&&e[`test-payload`]===void 0)throw Error(`Custom function "${e.name}" sets test-payload-type without test-payload — set payload-type per item in test-payloads instead.`);if(e.type!==s.Dsr&&(e[`test-payload-type`]!==void 0||e[`test-payloads`]?.some(({"payload-type":e})=>e!==void 0)))throw Error(`Custom function "${e.name}" sets a DSR payload type but is not type DSR.`);[{field:`code`,path:e.code},...e[`test-payload`]===void 0?[]:[{field:`test-payload`,path:e[`test-payload`]}],...(e[`test-payloads`]??[]).map(({payload:e},t)=>({field:`test-payloads[${t}].payload`,path:e}))].forEach(({field:n,path:r})=>{if(!t.skipPathValidation&&!N(r))throw Error(`Custom function "${e.name}" has a ${n} path outside the manifest directory: ${r}`)})})}function F(e,t={}){let n=y(M,h.load(e));return P(n,t),n}function I(e,t,n){let r=c(p(e)),i=(e,t)=>{let i=p(r,t);if(n?.(i),!g(i))throw Error(`Test payload file for custom function "${e}" does not exist: ${i}`);let a=_(i,`utf-8`);try{return JSON.parse(a)}catch(t){throw Error(`Test payload file for custom function "${e}" is not valid JSON (${i}): ${t.message}`)}};return t.functions.map(e=>{A({...e.env?{env:e.env}:{},...e[`allowed-hosts`]?{allowedHosts:e[`allowed-hosts`]}:{}});let t=p(r,e.code);if(n?.(t),!g(t))throw Error(`Code file for custom function "${e.name}" does not exist: ${t}`);let a;return e[`test-payload`]===void 0?e[`test-payloads`]!==void 0&&e[`test-payloads`].length>0&&(a=e[`test-payloads`].map(t=>({payload:i(e.name,t.payload),...t[`payload-type`]===void 0?{}:{payloadType:t[`payload-type`]}}))):a=[{payload:i(e.name,e[`test-payload`]),...e[`test-payload-type`]===void 0?{}:{payloadType:e[`test-payload-type`]}}],{name:e.name,code:_(t,`utf-8`),...e.id===void 0?{}:{id:e.id},...e.description===void 0?{}:{description:e.description},...e.type===void 0?{}:{type:e.type},...e[`data-silo-id`]===void 0?{}:{dataSiloId:e[`data-silo-id`]},...e[`sombra-id`]===void 0?{}:{sombraId:e[`sombra-id`]},...e[`sombra-auth-env`]===void 0?{}:{sombraAuthEnv:e[`sombra-auth-env`]},...e[`allowed-hosts`]===void 0?{}:{allowedHosts:e[`allowed-hosts`]},...e[`timeout-ms`]===void 0?{}:{timeoutMs:e[`timeout-ms`]},...e[`allow-third-party-imports`]===void 0?{}:{allowThirdPartyImports:e[`allow-third-party-imports`]},...e.env===void 0?{}:{env:e.env},...a===void 0?{}:{testPayloads:a}}})}function L(e,t={},n){return I(e,F(a(_(e,`utf-8`),t,`Also check that there are no extra variables defined in your manifest: ${e}`)),n)}function R(e,t,n={},r){let i=F(a(h.dump({functions:[t]}),n,`Also check that there are no extra variables defined for "${t.name}" in: ${e}`)),o=i.functions[0],s=p(c(p(e)),o.code);return{config:I(e,i,r)[0],sourcePath:s}}function z(e,t){let n=w(e);if(n.errors.length>0)throw n.errors[0];let r=n.get(`functions`);if(!C(r))throw Error("Expected a `functions` list in custom function manifest");r.items.length===0&&(r.flow=!1),r.add(n.createNode(t));let i=n.toString();return F(i),i}function B(e,t){let n=w(_(e,`utf-8`)),r=n.get(`functions`);if(!C(r))throw Error(`Expected a \`functions\` list in manifest: ${e}`);let i=0;return r.items.forEach((e,r)=>{let a=t[r];if(!a||!x(e))return;let o=!1;if(a.id&&!e.has(`id`)&&(e.items.unshift(n.createPair(`id`,a.id)),o=!0),a.dataSiloId&&!e.has(`data-silo-id`)){let t=e.items.findIndex(e=>S(e.key)&&e.key.value===`id`);e.items.splice(t+1,0,n.createPair(`data-silo-id`,a.dataSiloId)),o=!0}o&&(i+=1)}),i>0&&v(e,n.toString()),i}function V(e,r){let i=n(e,r),a=t(e,i)??i,o=[],s=t=>{e.fs.readdirSync(t,{withFileTypes:!0}).forEach(n=>{if(n.name===`.git`||n.name===`.worktrees`||n.name===`node_modules`)return;let r=d(t,n.name);if(n.isDirectory()&&!n.isSymbolicLink())s(r);else if(n.isFile()){if(n.name===`transcend-functions.yml`){o.push(r);return}if([`.yml`,`.yaml`].includes(l(n.name)))try{if(e.fs.statSync(r).size>256e3)return;F(e.fs.readFileSync(r,`utf8`),{skipPathValidation:!0}),o.push(r)}catch{}}})};return s(a),o.sort((e,t)=>e.localeCompare(t))}function H(t,n){let a=t.process.cwd(),{manifestDirectory:o,manifestPath:s,targetDirectory:c}=e(a,n),l=i(t,{invocationDirectory:a,targetDirectory:c,standaloneProjectRoot:c===p(a,`transcend/custom-functions`)?a:c}),u=d(o,`deno.jsonc`),f=d(o,`deno.json`),m=t.fs.existsSync(u)?u:f;return{invocationDirectory:a,targetDirectory:c,...l,manifestDirectory:o,manifestPath:s,denoConfigPath:m,relativePaths:r(t,o)}}export{F as a,B as c,N as i,A as l,H as n,R as o,z as r,L as s,V as t};
|
|
2
|
+
//# sourceMappingURL=project-discovery-BkHpTXDx.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"project-discovery-BkHpTXDx.mjs","names":[],"sources":["../src/lib/custom-functions/execution-context.ts","../src/lib/custom-functions/manifest.ts","../src/lib/custom-functions/project-discovery.ts"],"sourcesContent":["import { Buffer } from 'node:buffer';\nimport { isIP } from 'node:net';\n\n/** Maximum environment value size supported by the local process. */\nexport const CUSTOM_FUNCTION_MAX_ENV_VALUE_BYTES = 64 * 1024;\n\nconst RESERVED_ENV_NAMES = new Set([\n 'DENO_CERT',\n 'DENO_DIR',\n 'DENO_TLS_CA_STORE',\n 'HTTP_PROXY',\n 'HTTPS_PROXY',\n 'NODE_EXTRA_CA_CERTS',\n 'NO_COLOR',\n 'NO_PROXY',\n 'NPM_CONFIG_REGISTRY',\n]);\n\n/**\n * Check whether a value matches Sombra's hostname/IP contract.\n *\n * @param host - Manifest allowed-host value\n * @returns Whether the host is valid\n */\nfunction isValidAllowedHost(host: string): boolean {\n const bracketedIp = /^\\[([^\\]]+)\\](?::(\\d+))?$/u.exec(host);\n if (bracketedIp) {\n const port = bracketedIp[2];\n return isIP(bracketedIp[1]!) === 6 && (!port || (Number(port) > 0 && Number(port) <= 65_535));\n }\n if (isIP(host) > 0) {\n return true;\n }\n const hostWithPort = /^([^:]+):(\\d+)$/u.exec(host);\n const hostname = hostWithPort?.[1] ?? host;\n const port = hostWithPort?.[2];\n return (\n (!port || (Number(port) > 0 && Number(port) <= 65_535)) &&\n hostname.length <= 253 &&\n hostname\n .split('.')\n .every(\n (label) =>\n label.length > 0 &&\n label.length <= 63 &&\n /^[A-Za-z\\d](?:[A-Za-z\\d-]*[A-Za-z\\d])?$/u.test(label),\n )\n );\n}\n\n/**\n * Validate execution settings shared by check, push, and local simulation.\n *\n * @param config - Environment and network settings\n */\nexport function validateCustomFunctionExecutionContext(config: {\n /** User-defined environment variables. */\n env?: Record<string, string>;\n /** Native network destinations. */\n allowedHosts?: string[];\n}): void {\n const environment = config.env ?? {};\n const envNames = Object.keys(environment);\n const malformedEnvNames = envNames.filter((name) => !/^[A-Za-z][A-Za-z\\d_]*$/u.test(name));\n const reservedEnvNames = envNames.filter(\n (name) => RESERVED_ENV_NAMES.has(name) || name.startsWith('DENO_'),\n );\n if (malformedEnvNames.length > 0 || reservedEnvNames.length > 0) {\n throw new Error(\n [\n malformedEnvNames.length > 0\n ? `Invalid environment variable names: ${malformedEnvNames.join(', ')}`\n : undefined,\n reservedEnvNames.length > 0\n ? `Reserved environment variable names: ${reservedEnvNames.join(', ')}`\n : undefined,\n ]\n .filter(Boolean)\n .join('. '),\n );\n }\n const oversizedEnvNames = envNames.filter(\n (name) => Buffer.byteLength(environment[name]!) > CUSTOM_FUNCTION_MAX_ENV_VALUE_BYTES,\n );\n if (oversizedEnvNames.length > 0) {\n throw new Error(\n `Environment values exceed ${CUSTOM_FUNCTION_MAX_ENV_VALUE_BYTES} bytes: ${oversizedEnvNames.join(', ')}`,\n );\n }\n const allowedHosts = config.allowedHosts ?? [];\n if (allowedHosts.length > 1 && allowedHosts.includes('*')) {\n throw new Error('allowed-hosts cannot combine \"*\" with specific hosts.');\n }\n const invalidHosts = allowedHosts.filter((host) => host !== '*' && !isValidAllowedHost(host));\n if (invalidHosts.length > 0) {\n throw new Error(`Invalid allowed-hosts: ${invalidHosts.join(', ')}`);\n }\n}\n","import { existsSync, readFileSync, writeFileSync } from 'node:fs';\nimport { dirname, isAbsolute, relative, resolve, sep } from 'node:path';\n\nimport { CustomFunctionPayloadType, CustomFunctionType } from '@transcend-io/privacy-types';\nimport type { CustomFunctionConfigInput } from '@transcend-io/sdk';\nimport { decodeCodec, type ObjByString } from '@transcend-io/type-utils';\nimport * as t from 'io-ts';\nimport yaml from 'js-yaml';\nimport { isMap, isScalar, isSeq, parseDocument } from 'yaml';\n\nimport { replaceVariablesInYaml } from '../readTranscendYaml.js';\nimport { validateCustomFunctionExecutionContext } from './execution-context.js';\n\nexport const CustomFunctionManifestEntry = t.intersection([\n t.type({\n /** Display name of the custom function — used as the sync key when no `id` is set */\n name: t.string,\n /** Path to the TypeScript source file, relative to the manifest */\n code: t.string,\n }),\n t.partial({\n /**\n * Custom function ID. Optional, but required to disambiguate when multiple\n * custom functions share a name. Written back by `push --updateManifest`.\n */\n id: t.string,\n /** Description shown in the Transcend dashboard */\n description: t.string,\n /** Custom function type. Defaults to GENERAL */\n type: t.union([t.literal(CustomFunctionType.Dsr), t.literal(CustomFunctionType.General)]),\n /** Data silo ID to attach to (required for DSR functions) */\n 'data-silo-id': t.string,\n /** The Sombra gateway the function belongs to */\n 'sombra-id': t.string,\n /**\n * Name of the environment variable holding the internal key of the\n * function's Sombra gateway. The key itself never lives in the manifest —\n * it is read from the CLI's process environment at push time. Overrides\n * `--sombraAuth` for this entry.\n */\n 'sombra-auth-env': t.string,\n /** Hosts the function may make network requests to */\n 'allowed-hosts': t.array(t.string),\n /** Execution timeout in milliseconds */\n 'timeout-ms': t.number,\n /** Whether the function may import third party modules */\n 'allow-third-party-imports': t.boolean,\n /** Environment variables to expose to the function */\n env: t.record(t.string, t.string),\n /**\n * Path to a JSON file (relative to the manifest) holding the test payload\n * to run the function with before pushing. When set, the function is\n * test-run after signing and only pushed/promoted when the test passes.\n * Shorthand for a single-item `test-payloads` list.\n */\n 'test-payload': t.string,\n /**\n * Which export to invoke when test-running a DSR function:\n * `DATA_POINT` invokes the default export, `REQUEST_ENRICHER` invokes the\n * `enricher` export. Defaults to DATA_POINT. Ignored for GENERAL functions.\n * Only valid alongside `test-payload`.\n */\n 'test-payload-type': t.union([\n t.literal(CustomFunctionPayloadType.DataPoint),\n t.literal(CustomFunctionPayloadType.RequestEnricher),\n ]),\n /**\n * Test payloads to run the function with before pushing. Every payload\n * runs and all must pass for the function to be pushed/promoted. DSR\n * functions should list one payload per export they implement\n * (`DATA_POINT` for the default export, `REQUEST_ENRICHER` for the\n * `enricher` export). Mutually exclusive with `test-payload`.\n */\n 'test-payloads': t.array(\n t.intersection([\n t.type({\n /** Path to the JSON payload file, relative to the manifest */\n payload: t.string,\n }),\n t.partial({\n /** Which export the payload invokes (DSR only). Defaults to DATA_POINT */\n 'payload-type': t.union([\n t.literal(CustomFunctionPayloadType.DataPoint),\n t.literal(CustomFunctionPayloadType.RequestEnricher),\n ]),\n }),\n ]),\n ),\n }),\n]);\n\n/** Override type */\nexport type CustomFunctionManifestEntry = t.TypeOf<typeof CustomFunctionManifestEntry>;\n\nexport const CustomFunctionsManifest = t.type({\n /** The custom functions to sync */\n functions: t.array(CustomFunctionManifestEntry),\n});\n\n/** Override type */\nexport type CustomFunctionsManifest = t.TypeOf<typeof CustomFunctionsManifest>;\n\n/**\n * Determine whether a relative manifest path stays inside the manifest's\n * directory after normalization.\n *\n * @param filePath - Path from a custom function manifest entry\n * @returns Whether the normalized path is contained by the manifest directory\n */\nexport function isCustomFunctionManifestPathContained(filePath: string): boolean {\n const manifestDirectory = resolve('/', '__custom-function-manifest__');\n const relativePath = relative(manifestDirectory, resolve(manifestDirectory, filePath));\n return relativePath !== '..' && !relativePath.startsWith(`..${sep}`) && !isAbsolute(relativePath);\n}\n\n/**\n * Validate semantic constraints shared by manifest readers and editors.\n *\n * @param manifest - Shape-validated custom function manifest\n * @param options - Validation behavior\n */\nexport function validateCustomFunctionsManifest(\n manifest: CustomFunctionsManifest,\n options: {\n /** Skip path validation while identifying candidate manifest files. */\n skipPathValidation?: boolean;\n } = {},\n): void {\n // IDs must be unique — two entries cannot target the same function\n const duplicateIds = manifest.functions\n .map(({ id }) => id)\n .filter((id, index, ids) => id !== undefined && ids.indexOf(id) !== index);\n if (duplicateIds.length > 0) {\n throw new Error(\n `Duplicate custom function ids in manifest: ${[...new Set(duplicateIds)].join(', ')}`,\n );\n }\n\n // Names may repeat only when every entry sharing the name carries an `id`\n // to disambiguate; an id-less entry syncs by name and would be ambiguous.\n const allNames = manifest.functions.map(({ name }) => name);\n const ambiguousNames = manifest.functions\n .filter(({ id, name }) => id === undefined && allNames.filter((n) => n === name).length > 1)\n .map(({ name }) => name);\n if (ambiguousNames.length > 0) {\n throw new Error(\n `Duplicate custom function names in manifest without ids: ${[...new Set(ambiguousNames)].join(', ')}. ` +\n 'Add an `id` field to each duplicated entry to disambiguate.',\n );\n }\n\n manifest.functions.forEach((entry) => {\n if (entry['test-payload'] !== undefined && entry['test-payloads'] !== undefined) {\n throw new Error(\n `Custom function \"${entry.name}\" sets both test-payload and test-payloads — ` +\n 'use test-payloads alone to define multiple payloads.',\n );\n }\n if (entry['test-payload-type'] !== undefined && entry['test-payload'] === undefined) {\n throw new Error(\n `Custom function \"${entry.name}\" sets test-payload-type without test-payload — ` +\n 'set payload-type per item in test-payloads instead.',\n );\n }\n if (\n entry.type !== CustomFunctionType.Dsr &&\n (entry['test-payload-type'] !== undefined ||\n entry['test-payloads']?.some(\n ({ ['payload-type']: payloadType }) => payloadType !== undefined,\n ))\n ) {\n throw new Error(\n `Custom function \"${entry.name}\" sets a DSR payload type but is not type DSR.`,\n );\n }\n\n const referencedPaths = [\n { field: 'code', path: entry.code },\n ...(entry['test-payload'] === undefined\n ? []\n : [{ field: 'test-payload', path: entry['test-payload'] }]),\n ...(entry['test-payloads'] ?? []).map(({ payload }, index) => ({\n field: `test-payloads[${index}].payload`,\n path: payload,\n })),\n ];\n referencedPaths.forEach(({ field, path }) => {\n if (!options.skipPathValidation && !isCustomFunctionManifestPathContained(path)) {\n throw new Error(\n `Custom function \"${entry.name}\" has a ${field} path outside the manifest directory: ${path}`,\n );\n }\n });\n });\n}\n\n/**\n * Parse and validate a custom functions manifest without resolving variables\n * or reading any referenced files.\n *\n * Unresolved `<<parameters.*>>` values remain plain strings in the returned\n * manifest.\n *\n * @param contents - Custom function manifest YAML\n * @param options - Validation behavior\n * @returns The parsed, shape-validated, and semantically valid manifest\n */\nexport function parseCustomFunctionsManifest(\n contents: string,\n options: {\n /** Skip path validation while identifying candidate manifest files. */\n skipPathValidation?: boolean;\n } = {},\n): CustomFunctionsManifest {\n const manifest = decodeCodec(CustomFunctionsManifest, yaml.load(contents));\n validateCustomFunctionsManifest(manifest, options);\n return manifest;\n}\n\n/**\n * A custom function config from the manifest, plus CLI-level settings that\n * are not part of the SDK sync input.\n */\nexport type CustomFunctionManifestConfig = CustomFunctionConfigInput & {\n /** Env variable name holding the internal key of the function's Sombra gateway */\n sombraAuthEnv?: string;\n /** Parsed JSON test payloads to run the function with before pushing */\n testPayloads?: {\n /** The parsed JSON payload */\n payload: object;\n /** Which export the payload invokes when test-running a DSR function */\n payloadType?: Exclude<CustomFunctionPayloadType, typeof CustomFunctionPayloadType.Maestro>;\n }[];\n};\n\n/**\n * Hydrate parsed manifest entries from their referenced files.\n *\n * @param filePath - Path to the manifest YAML file\n * @param manifest - Parsed manifest\n * @param assertPath - Optional safety check applied before referenced files are read\n * @returns Custom Function configs with code and payloads loaded\n */\nfunction hydrateCustomFunctionsManifest(\n filePath: string,\n manifest: CustomFunctionsManifest,\n assertPath?: (path: string) => void,\n): CustomFunctionManifestConfig[] {\n const manifestDir = dirname(resolve(filePath));\n\n /**\n * Load and parse a JSON test payload file.\n *\n * @param entryName - The manifest entry name, for error messages\n * @param payloadFile - The payload file path, relative to the manifest\n * @returns The parsed payload object\n */\n const loadTestPayload = (entryName: string, payloadFile: string): object => {\n const testPayloadPath = resolve(manifestDir, payloadFile);\n assertPath?.(testPayloadPath);\n if (!existsSync(testPayloadPath)) {\n throw new Error(\n `Test payload file for custom function \"${entryName}\" does not exist: ${testPayloadPath}`,\n );\n }\n const rawPayload = readFileSync(testPayloadPath, 'utf-8');\n try {\n return JSON.parse(rawPayload);\n } catch (err) {\n throw new Error(\n `Test payload file for custom function \"${entryName}\" is not valid JSON ` +\n `(${testPayloadPath}): ${(err as Error).message}`,\n );\n }\n };\n\n return manifest.functions.map((entry) => {\n validateCustomFunctionExecutionContext({\n ...(entry.env ? { env: entry.env } : {}),\n ...(entry['allowed-hosts'] ? { allowedHosts: entry['allowed-hosts'] } : {}),\n });\n const codePath = resolve(manifestDir, entry.code);\n assertPath?.(codePath);\n if (!existsSync(codePath)) {\n throw new Error(`Code file for custom function \"${entry.name}\" does not exist: ${codePath}`);\n }\n\n // `test-payload` is shorthand for a single-item `test-payloads` list\n let testPayloads: CustomFunctionManifestConfig['testPayloads'];\n if (entry['test-payload'] !== undefined) {\n testPayloads = [\n {\n payload: loadTestPayload(entry.name, entry['test-payload']),\n ...(entry['test-payload-type'] !== undefined\n ? { payloadType: entry['test-payload-type'] }\n : {}),\n },\n ];\n } else if (entry['test-payloads'] !== undefined && entry['test-payloads'].length > 0) {\n testPayloads = entry['test-payloads'].map((item) => ({\n payload: loadTestPayload(entry.name, item.payload),\n ...(item['payload-type'] !== undefined ? { payloadType: item['payload-type'] } : {}),\n }));\n }\n\n return {\n name: entry.name,\n code: readFileSync(codePath, 'utf-8'),\n ...(entry.id !== undefined ? { id: entry.id } : {}),\n ...(entry.description !== undefined ? { description: entry.description } : {}),\n ...(entry.type !== undefined ? { type: entry.type } : {}),\n ...(entry['data-silo-id'] !== undefined ? { dataSiloId: entry['data-silo-id'] } : {}),\n ...(entry['sombra-id'] !== undefined ? { sombraId: entry['sombra-id'] } : {}),\n ...(entry['sombra-auth-env'] !== undefined\n ? { sombraAuthEnv: entry['sombra-auth-env'] }\n : {}),\n ...(entry['allowed-hosts'] !== undefined ? { allowedHosts: entry['allowed-hosts'] } : {}),\n ...(entry['timeout-ms'] !== undefined ? { timeoutMs: entry['timeout-ms'] } : {}),\n ...(entry['allow-third-party-imports'] !== undefined\n ? { allowThirdPartyImports: entry['allow-third-party-imports'] }\n : {}),\n ...(entry.env !== undefined ? { env: entry.env } : {}),\n ...(testPayloads !== undefined ? { testPayloads } : {}),\n };\n });\n}\n\n/**\n * Read a custom functions manifest from disk, apply variable substitution,\n * validate it, and hydrate its referenced code and payload files.\n *\n * Variable substitution deliberately happens before parsing and hydration to\n * preserve push behavior.\n *\n * @param filePath - Path to the manifest YAML file\n * @param variables - Variables to fill into `<<parameters.x>>` placeholders\n * @param assertPath - Optional safety check applied before referenced files are read\n * @returns The custom function configs, with code loaded from disk\n */\nexport function readCustomFunctionsManifest(\n filePath: string,\n variables: ObjByString = {},\n assertPath?: (path: string) => void,\n): CustomFunctionManifestConfig[] {\n const fileContents = readFileSync(filePath, 'utf-8');\n const replacedVariables = replaceVariablesInYaml(\n fileContents,\n variables,\n `Also check that there are no extra variables defined in your manifest: ${filePath}`,\n );\n const manifest = parseCustomFunctionsManifest(replacedVariables);\n return hydrateCustomFunctionsManifest(filePath, manifest, assertPath);\n}\n\n/**\n * Resolve and hydrate one selected manifest entry.\n *\n * This lets local execution ignore unrelated entries whose files or\n * variables are currently incomplete.\n *\n * @param filePath - Path to the containing manifest\n * @param entry - Selected raw manifest entry\n * @param variables - Variables to fill into `<<parameters.x>>` placeholders\n * @param assertPath - Optional safety check applied before referenced files are read\n * @returns Hydrated selected configuration and its source path\n */\nexport function readCustomFunctionManifestEntry(\n filePath: string,\n entry: CustomFunctionManifestEntry,\n variables: ObjByString = {},\n assertPath?: (path: string) => void,\n): { config: CustomFunctionManifestConfig; sourcePath: string } {\n const entryContents = yaml.dump({ functions: [entry] });\n const replacedVariables = replaceVariablesInYaml(\n entryContents,\n variables,\n `Also check that there are no extra variables defined for \"${entry.name}\" in: ${filePath}`,\n );\n const manifest = parseCustomFunctionsManifest(replacedVariables);\n const resolvedEntry = manifest.functions[0]!;\n const manifestDirectory = dirname(resolve(filePath));\n const sourcePath = resolve(manifestDirectory, resolvedEntry.code);\n return {\n config: hydrateCustomFunctionsManifest(filePath, manifest, assertPath)[0]!,\n sourcePath,\n };\n}\n\n/**\n * Append an entry to a manifest YAML document while preserving the existing\n * document's comments, ordering, scalar styles, and unresolved placeholders.\n *\n * The complete result is parsed and validated with the same codec and\n * semantic rules used by {@link readCustomFunctionsManifest}.\n *\n * @param contents - Existing custom function manifest YAML\n * @param entry - Manifest entry to append\n * @returns Updated manifest YAML\n */\nexport function insertCustomFunctionManifestEntry(\n contents: string,\n entry: CustomFunctionManifestEntry,\n): string {\n const document = parseDocument(contents);\n if (document.errors.length > 0) {\n throw document.errors[0];\n }\n\n const functions = document.get('functions');\n if (!isSeq(functions)) {\n throw new Error('Expected a `functions` list in custom function manifest');\n }\n\n if (functions.items.length === 0) {\n functions.flow = false;\n }\n functions.add(document.createNode(entry));\n const updatedContents = document.toString();\n parseCustomFunctionsManifest(updatedContents);\n return updatedContents;\n}\n\n/**\n * The IDs assigned to a manifest entry during a push, to write back into the\n * manifest file.\n */\nexport interface CustomFunctionManifestIds {\n /** Custom function ID */\n id?: string;\n /** Data silo (DSR integration) ID, for DSR functions */\n dataSiloId?: string;\n}\n\n/**\n * Write custom function and data silo IDs back into a manifest file, so\n * future pushes match by ID instead of by (potentially non-unique) name and\n * DSR entries keep pointing at their integration.\n *\n * The raw file text is edited via a comment- and formatting-preserving YAML\n * document, so comments and un-substituted `<<parameters.x>>` placeholders\n * survive untouched. Only entries that are missing the respective key are\n * modified.\n *\n * @param filePath - Path to the manifest YAML file\n * @param idsByIndex - IDs for each manifest entry, by array index (undefined\n * entries are left unchanged)\n * @returns The number of entries that were updated\n */\nexport function writeCustomFunctionIdsToManifest(\n filePath: string,\n idsByIndex: (CustomFunctionManifestIds | undefined)[],\n): number {\n const document = parseDocument(readFileSync(filePath, 'utf-8'));\n const functions = document.get('functions');\n if (!isSeq(functions)) {\n throw new Error(`Expected a \\`functions\\` list in manifest: ${filePath}`);\n }\n\n let updated = 0;\n functions.items.forEach((item, index) => {\n const ids = idsByIndex[index];\n if (!ids || !isMap(item)) {\n return;\n }\n let changed = false;\n if (ids.id && !item.has('id')) {\n // Place `id` first so it reads as the entry's key\n item.items.unshift(document.createPair('id', ids.id));\n changed = true;\n }\n if (ids.dataSiloId && !item.has('data-silo-id')) {\n // Place `data-silo-id` right after `id` (or first when there is no id)\n const idIndex = item.items.findIndex((pair) => isScalar(pair.key) && pair.key.value === 'id');\n item.items.splice(idIndex + 1, 0, document.createPair('data-silo-id', ids.dataSiloId));\n changed = true;\n }\n if (changed) {\n updated += 1;\n }\n });\n\n if (updated > 0) {\n writeFileSync(filePath, document.toString());\n }\n return updated;\n}\n","import { extname, join, resolve } from 'node:path';\n\nimport type { LocalContext } from '../../context.js';\nimport {\n collectProjectRelativePaths,\n discoverProjectRepository,\n findExistingAncestor,\n findRepositoryRoot,\n} from '../scaffolding/project-discovery.js';\nimport { parseCustomFunctionsManifest } from './manifest.js';\nimport { DEFAULT_CUSTOM_FUNCTION_DIRECTORY, resolveCustomFunctionProjectPaths } from './paths.js';\nimport type { CustomFunctionProjectState } from './scaffold-model.js';\n\n/**\n * Find Custom Function manifests below the surrounding repository.\n *\n * @param context - CLI context\n * @param startDirectory - Existing directory from which to locate the repository\n * @returns Absolute manifest paths in stable order\n */\nexport function discoverCustomFunctionManifests(\n context: LocalContext,\n startDirectory: string,\n): string[] {\n const existingAncestor = findExistingAncestor(context, startDirectory);\n const root = findRepositoryRoot(context, existingAncestor) ?? existingAncestor;\n const manifests: string[] = [];\n const visit = (directory: string): void => {\n context.fs.readdirSync(directory, { withFileTypes: true }).forEach((entry) => {\n if (entry.name === '.git' || entry.name === '.worktrees' || entry.name === 'node_modules') {\n return;\n }\n const absolute = join(directory, entry.name);\n if (entry.isDirectory() && !entry.isSymbolicLink()) {\n visit(absolute);\n } else if (entry.isFile()) {\n if (entry.name === 'transcend-functions.yml') {\n manifests.push(absolute);\n return;\n }\n if (['.yml', '.yaml'].includes(extname(entry.name))) {\n try {\n if (context.fs.statSync(absolute).size > 256_000) {\n return;\n }\n parseCustomFunctionsManifest(context.fs.readFileSync(absolute, 'utf8'), {\n skipPathValidation: true,\n });\n manifests.push(absolute);\n } catch {\n // Most repository YAML files are not Custom Function manifests.\n }\n }\n }\n });\n };\n visit(root);\n return manifests.sort((left, right) => left.localeCompare(right));\n}\n\n/**\n * Collect all repository state required by the pure planners.\n *\n * @param context - CLI context\n * @param options - User-selected target paths\n * @returns Project state\n */\nexport function discoverCustomFunctionProject(\n context: LocalContext,\n options: {\n /** Optional positional directory. */\n directory?: string;\n /** Optional explicit manifest path. */\n manifest?: string;\n },\n): CustomFunctionProjectState {\n const cwd = context.process.cwd();\n const { manifestDirectory, manifestPath, targetDirectory } = resolveCustomFunctionProjectPaths(\n cwd,\n options,\n );\n const repository = discoverProjectRepository(context, {\n invocationDirectory: cwd,\n targetDirectory,\n standaloneProjectRoot:\n targetDirectory === resolve(cwd, DEFAULT_CUSTOM_FUNCTION_DIRECTORY) ? cwd : targetDirectory,\n });\n const denoJsonc = join(manifestDirectory, 'deno.jsonc');\n const denoJson = join(manifestDirectory, 'deno.json');\n const denoConfigPath = context.fs.existsSync(denoJsonc) ? denoJsonc : denoJson;\n\n return {\n invocationDirectory: cwd,\n targetDirectory,\n ...repository,\n manifestDirectory,\n manifestPath,\n denoConfigPath,\n relativePaths: collectProjectRelativePaths(context, manifestDirectory),\n };\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{basename as e,dirname as t,isAbsolute as n,join as r,relative as i,resolve as a,sep as o}from"node:path";import{createHash as s}from"node:crypto";function c(e,t){let n=e[t];if(!n)throw Error(`Missing preflight snapshot for ${t}`);return n}function l(e,t){let n=c(e,t);if(n.kind===`directory`||n.kind===`link`)throw Error(`Expected a regular file or absent path: ${t}`);return n.kind===`file`?{path:t,contents:n.contents,mode:n.mode}:{path:t,contents:null}}function u(e){let{snapshot:t,after:n,description:r,createOnly:i}=e;if(i&&t.contents!==null)throw Error(`Refusing to overwrite existing file: ${t.path}`);if(t.contents!==n)return{kind:`file`,path:t.path,before:t.contents,after:n,description:r,...t.mode===void 0?{}:{mode:t.mode},...i===void 0?{}:{createOnly:i}}}const d=`.agents/skills`,f=[`.aider-desk/skills`,d,`.autohand/skills`,`.augment/skills`,`.bob/skills`,`.claude/skills`,`.codeartsdoer/skills`,`.codebuddy/skills`,`.codemaker/skills`,`.codestudio/skills`,`.commandcode/skills`,`.continue/skills`,`.cortex/skills`,`.crush/skills`,`.devin/skills`,`.factory/skills`,`agent/skills`,`.forge/skills`,`.goose/skills`,`.grok/skills`,`.hermes/skills`,`.inferencesh/skills`,`.jazz/skills`,`.junie/skills`,`.iflow/skills`,`.kilocode/skills`,`.kimchi/skills`,`.kiro/skills`,`.kode/skills`,`.lingma/skills`,`.mcpjam/skills`,`.minimax/skills`,`.vibe/skills`,`.moxby/skills`,`.mux/skills`,`.openhands/skills`,`.ona/skills`,`.pi/skills`,`.posit/assistant/skills`,`.qoder/skills`,`.qwen/skills`,`.reasonix/skills`,`.rovodev/skills`,`.roo/skills`,`.tabnine/agent/skills`,`.terramind/skills`,`.tinycloud/skills`,`.trae/skills`,`.windsurf/skills`,`.zcode/skills`,`.zencoder/skills`,`.neovate/skills`,`.pochi/skills`,`.adal/skills`],p=[d,`.cursor/skills`,`.codex/skills`,`.github/skills`,`.gemini/skills`,`.opencode/skills`,`.cline/skills`,`.warp/skills`,`.zed/skills`,`.replit/skills`];function m(e){return e.length===1?{canonical:e[0].path,aliases:[]}:{canonical:d,aliases:e.length>1?e.filter(({path:e,supportsAgentsSkills:t})=>e!==`.agents/skills`&&!t).map(({path:e})=>e):[]}}function h(e,t){let n=e.trimEnd();return`${n}\n\n<!-- managed-by: ${t}; content-sha256: ${s(`sha256`).update(n).digest(`hex`)} -->\n`}function g(e,t){let n=t.replace(/[.*+?^${}()|[\]\\]/gu,`\\$&`),r=e.match(RegExp(`<!-- managed-by: ${n}; content-sha256: ([a-f0-9]{64}) -->\\s*$`,`u`));if(!r||r.index===void 0)return!1;let i=e.slice(0,r.index).trimEnd();return s(`sha256`).update(i).digest(`hex`)===r[1]}function _(e,t,n){let i=m(t),a=new Set,o=r(e,i.canonical,n.name);return n.files.forEach(({path:e})=>a.add(r(o,e))),i.aliases.forEach(t=>{let i=r(e,t,n.name);a.add(i),n.files.forEach(({path:e})=>a.add(r(i,e)))}),t.forEach(({path:t})=>{n.files.forEach(({path:i})=>{a.add(r(e,t,n.name,i))})}),[...a].sort((e,t)=>e.localeCompare(t))}function v(e){let{rootDirectory:n,existingDirectories:a,snapshots:o,skill:s,preserveModified:d=!1}=e,f={changes:[],unchanged:[],warnings:[]},p=m(a),_=r(n,p.canonical,s.name),v=s.files.map(e=>({path:e.path,contents:h(e.contents,s.owner)})),y=(e,t)=>{v.forEach(n=>{let i=r(e,n.path),a=l(o,i);if(a.contents!==null&&a.contents!==n.contents&&!g(a.contents,s.owner)){if(d){f.unchanged.push(i),f.warnings.push(`Customized managed skill file was left unchanged: ${i}. Merge the updated guidance manually.`);return}throw Error(`Refusing to replace user-managed skill: ${i}. Apply the skill update manually.`)}let c=u({snapshot:a,after:n.contents,description:`${t}: ${n.path}`});c?f.changes.push(c):f.unchanged.push(i)})};return y(_,`Install ${s.displayName} skill file`),p.aliases.forEach(e=>{let a=r(n,e,s.name),l=c(o,a),u=i(t(a),_);if(l.kind===`link`&&l.target===u){f.unchanged.push(a);return}if(l.kind===`directory`){y(a,`Update ${s.displayName} skill copy in ${e}`);return}if(l.kind!==`absent`){if(d){f.unchanged.push(a),f.warnings.push(`Customized skill target was left unchanged: ${a}. Expose the canonical skill manually if needed.`);return}throw Error(`Refusing to replace unexpected skill target: ${a}`)}let p={kind:`link`,path:a,target:u,fallbackFiles:v,description:`Expose the canonical skill in ${e}`};f.changes.push(p)}),a.map(({path:e})=>e).filter(e=>e!==p.canonical&&!p.aliases.includes(e)).forEach(e=>{let t=r(n,e,s.name),i=r(t,`SKILL.md`),a=l(o,i);a.contents!==null&&g(a.contents,s.owner)?y(t,`Update existing managed skill in ${e}`):a.contents?.includes(`managed-by: ${s.owner}`)&&(f.unchanged.push(i),f.warnings.push(`Existing customized managed skill was left unchanged: ${t}`))}),f}function y(n,r){let i=a(r),o=[];for(;;)try{return n.fs.lstatSync(i),a(n.fs.realpathSync(i),...o)}catch(n){let r=n.code;if(r!==`ENOENT`&&r!==`ENOTDIR`)throw n;let a=t(i);if(a===i)throw n;o.unshift(e(i)),i=a}}function b(e,t,r){let a=i(y(e,t),y(e,r));return a!==`..`&&!a.startsWith(`..${o}`)&&!n(a)}function x(e,t,n){if(!b(e,t,n))throw Error(`Refusing to access path outside project root through a symlink: ${n}`)}function S(e,t,n){return Object.fromEntries(n.map(n=>{x(e,t,n);try{let t=e.fs.lstatSync(n);if(t.isSymbolicLink())return[n,{kind:`link`,path:n,target:e.fs.readlinkSync(n)}];if(t.isDirectory())return[n,{kind:`directory`,path:n}];if(!t.isFile())throw Error(`Unsupported filesystem entry: ${n}`);return[n,{kind:`file`,path:n,contents:e.fs.readFileSync(n,`utf8`),mode:t.mode}]}catch(e){if(e.code===`ENOENT`)return[n,{kind:`absent`,path:n}];throw e}}))}function C(e,n){let i=n;for(;;){if(e.fs.existsSync(r(i,`.git`)))return i;let n=t(i);if(n===i)return;i=n}}function w(e,n){let r=n;for(;!e.fs.existsSync(r);){let e=t(r);if(e===r)return r;r=e}return e.fs.statSync(r).isDirectory()?r:t(r)}function T(e,t){let r=i(e,t);return r!==`..`&&!r.startsWith(`..${o}`)&&!n(r)}function E(e,t,n={}){if(!e.fs.existsSync(t))return[];let a=[],s=c=>{e.fs.readdirSync(c,{withFileTypes:!0}).forEach(e=>{if(e.name===`.git`||e.name===`node_modules`)return;let l=r(c,e.name),u=e.isDirectory()&&!e.isSymbolicLink();(n.includeDirectories!==!1||!u)&&a.push(i(t,l).split(o).join(`/`)),u&&s(l)})};return s(t),a.sort((e,t)=>e.localeCompare(t))}function D(e,t,n){return n===0?!1:e.fs.readdirSync(t,{withFileTypes:!0}).some(i=>{if(!i.isDirectory()||i.isSymbolicLink())return!1;let a=r(t,i.name);return e.fs.existsSync(r(a,`SKILL.md`))||D(e,a,n-1)})}function O(e,t){if(!e.fs.existsSync(t)||!e.fs.statSync(t).isDirectory())return[];let n=new Set(p),i=new Set([...f,...p]);return e.fs.readdirSync(t,{withFileTypes:!0}).forEach(n=>{if(!n.isDirectory()||n.isSymbolicLink())return;let a=r(t,n.name,`skills`);e.fs.existsSync(a)&&e.fs.statSync(a).isDirectory()&&D(e,a,3)&&i.add(`${n.name}/skills`)}),[...i].filter(n=>{let i=r(t,n);return e.fs.existsSync(i)&&e.fs.statSync(i).isDirectory()}).sort((e,t)=>e.localeCompare(t)).map(e=>({path:e,supportsAgentsSkills:n.has(e)}))}function k(e,t){if(!t)return!1;if(e.fs.existsSync(r(t,`.github`)))return!0;let n=r(t,`.git`),i=n;if(e.fs.existsSync(n)&&e.fs.statSync(n).isFile()){let r=e.fs.readFileSync(n,`utf8`).match(/^gitdir:\s*(.+)$/mu)?.[1];r&&(i=a(t,r))}let o=r(i,`commondir`);e.fs.existsSync(o)&&e.fs.statSync(o).isFile()&&(i=a(i,e.fs.readFileSync(o,`utf8`).trim()));let s=r(i,`config`);return e.fs.existsSync(s)&&e.fs.readFileSync(s,`utf8`).includes(`github.com`)}function A(e,t){let n=C(e,w(e,t.invocationDirectory)),r=n&&T(n,t.targetDirectory)?n:C(e,w(e,t.targetDirectory)),i=t.standaloneProjectRoot!==t.targetDirectory&&T(t.standaloneProjectRoot,t.targetDirectory),a=r,o=i?n??t.standaloneProjectRoot:a??t.standaloneProjectRoot;return x(e,o,t.targetDirectory),{projectRoot:o,...a?{repositoryRoot:a}:{},existingSkillDirectories:O(e,o),usesGithub:k(e,a)}}export{C as a,_ as c,c as d,u as f,w as i,v as l,E as n,x as o,A as r,b as s,S as t,l as u};
|
|
2
|
+
//# sourceMappingURL=project-discovery-CWRjlFnC.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"project-discovery-CWRjlFnC.mjs","names":[],"sources":["../src/lib/scaffolding/project-plan.ts","../src/lib/scaffolding/agent-skill.ts","../src/lib/scaffolding/path-safety.ts","../src/lib/scaffolding/project-discovery.ts"],"sourcesContent":["/** A file observed while collecting project state. */\nexport interface ProjectFileSnapshot {\n /** Absolute path. */\n path: string;\n /** Existing UTF-8 contents, or null when absent. */\n contents: string | null;\n /** Existing file mode, when present. */\n mode?: number;\n}\n\n/** State of one potentially mutated path. */\nexport type PlanningPathSnapshot =\n | {\n /** Snapshot kind. */\n kind: 'absent';\n /** Absolute path. */\n path: string;\n }\n | {\n /** Snapshot kind. */\n kind: 'file';\n /** Absolute path. */\n path: string;\n /** UTF-8 contents. */\n contents: string;\n /** File mode. */\n mode: number;\n }\n | {\n /** Snapshot kind. */\n kind: 'link';\n /** Absolute path. */\n path: string;\n /** Link target. */\n target: string;\n }\n | {\n /** Snapshot kind. */\n kind: 'directory';\n /** Absolute path. */\n path: string;\n };\n\n/** A planned regular-file create or update. */\nexport interface PlannedFileChange {\n /** Discriminator. */\n kind: 'file';\n /** Absolute destination path. */\n path: string;\n /** Existing contents expected immediately before apply. */\n before: string | null;\n /** Complete desired contents. */\n after: string;\n /** Human-readable reason. */\n description: string;\n /** Existing file mode to preserve. */\n mode?: number;\n /** Whether an existing destination must always be rejected. */\n createOnly?: boolean;\n}\n\n/** A planned project-local symbolic link with a copy fallback. */\nexport interface PlannedLinkChange {\n /** Discriminator. */\n kind: 'link';\n /** Absolute link path. */\n path: string;\n /** Relative link target. */\n target: string;\n /** Managed files to copy when links are unavailable. */\n fallbackFiles: {\n /** Destination relative to the link path. */\n path: string;\n /** Complete desired contents. */\n contents: string;\n }[];\n /** Human-readable reason. */\n description: string;\n}\n\n/** One staged project mutation. */\nexport type PlannedChange = PlannedFileChange | PlannedLinkChange;\n\n/** Directory contents that must still match their planning snapshot. */\nexport interface PlannedDirectoryPrecondition {\n /** Directory inspected during planning. */\n path: string;\n /** Sorted relative entries expected immediately before apply. */\n relativePaths: string[];\n}\n\n/** Minimal plan consumed by the transactional filesystem applicator. */\nexport interface ProjectPlan {\n /** Approved physical root for every planned mutation. */\n rootDirectory: string;\n /** Directory-wide state that must still match the preview. */\n directoryPreconditions?: PlannedDirectoryPrecondition[];\n /** Ordered staged mutations. */\n changes: PlannedChange[];\n}\n\n/**\n * Read a required candidate snapshot.\n *\n * @param snapshots - Snapshots keyed by absolute path\n * @param path - Absolute path\n * @returns Snapshot\n */\nexport function getPlanningPathSnapshot(\n snapshots: Readonly<Record<string, PlanningPathSnapshot>>,\n path: string,\n): PlanningPathSnapshot {\n const snapshot = snapshots[path];\n if (!snapshot) {\n throw new Error(`Missing preflight snapshot for ${path}`);\n }\n return snapshot;\n}\n\n/**\n * Adapt a planning snapshot to a regular-file plan input.\n *\n * @param snapshots - Snapshots keyed by absolute path\n * @param path - Absolute file path\n * @returns File snapshot\n */\nexport function getPlanningFileSnapshot(\n snapshots: Readonly<Record<string, PlanningPathSnapshot>>,\n path: string,\n): ProjectFileSnapshot {\n const snapshot = getPlanningPathSnapshot(snapshots, path);\n if (snapshot.kind === 'directory' || snapshot.kind === 'link') {\n throw new Error(`Expected a regular file or absent path: ${path}`);\n }\n return snapshot.kind === 'file'\n ? { path, contents: snapshot.contents, mode: snapshot.mode }\n : { path, contents: null };\n}\n\n/**\n * Stage a file only when its desired content differs.\n *\n * @param input - File change input\n * @returns A file change, or undefined for a no-op\n */\nexport function planFileChange(input: {\n /** File snapshot. */\n snapshot: ProjectFileSnapshot;\n /** Desired contents. */\n after: string;\n /** Human-readable reason. */\n description: string;\n /** Refuse every existing file. */\n createOnly?: boolean;\n}): PlannedFileChange | undefined {\n const { snapshot, after, description, createOnly } = input;\n if (createOnly && snapshot.contents !== null) {\n throw new Error(`Refusing to overwrite existing file: ${snapshot.path}`);\n }\n if (snapshot.contents === after) {\n return undefined;\n }\n return {\n kind: 'file',\n path: snapshot.path,\n before: snapshot.contents,\n after,\n description,\n ...(snapshot.mode === undefined ? {} : { mode: snapshot.mode }),\n ...(createOnly === undefined ? {} : { createOnly }),\n };\n}\n","import { createHash } from 'node:crypto';\nimport { dirname, join, relative } from 'node:path';\n\nimport {\n getPlanningFileSnapshot,\n getPlanningPathSnapshot,\n planFileChange,\n type PlannedChange,\n type PlannedLinkChange,\n type PlanningPathSnapshot,\n} from './project-plan.js';\n\n/** Universal project-level Agent Skills directory. */\nexport const UNIVERSAL_AGENT_SKILLS_DIRECTORY = '.agents/skills';\n\n/**\n * Unambiguous agent-owned project directories from vercel-labs/skills at\n * 1682051d48c34f5eb135e6475c1a965dce05e820. Generic skill catalog paths such\n * as `skills` and `data/skills` are excluded because they do not prove that a\n * project has configured an agent.\n */\nexport const PROJECT_SKILL_DIRECTORIES = [\n '.aider-desk/skills',\n UNIVERSAL_AGENT_SKILLS_DIRECTORY,\n '.autohand/skills',\n '.augment/skills',\n '.bob/skills',\n '.claude/skills',\n '.codeartsdoer/skills',\n '.codebuddy/skills',\n '.codemaker/skills',\n '.codestudio/skills',\n '.commandcode/skills',\n '.continue/skills',\n '.cortex/skills',\n '.crush/skills',\n '.devin/skills',\n '.factory/skills',\n 'agent/skills',\n '.forge/skills',\n '.goose/skills',\n '.grok/skills',\n '.hermes/skills',\n '.inferencesh/skills',\n '.jazz/skills',\n '.junie/skills',\n '.iflow/skills',\n '.kilocode/skills',\n '.kimchi/skills',\n '.kiro/skills',\n '.kode/skills',\n '.lingma/skills',\n '.mcpjam/skills',\n '.minimax/skills',\n '.vibe/skills',\n '.moxby/skills',\n '.mux/skills',\n '.openhands/skills',\n '.ona/skills',\n '.pi/skills',\n '.posit/assistant/skills',\n '.qoder/skills',\n '.qwen/skills',\n '.reasonix/skills',\n '.rovodev/skills',\n '.roo/skills',\n '.tabnine/agent/skills',\n '.terramind/skills',\n '.tinycloud/skills',\n '.trae/skills',\n '.windsurf/skills',\n '.zcode/skills',\n '.zencoder/skills',\n '.neovate/skills',\n '.pochi/skills',\n '.adal/skills',\n] as const;\n\n/**\n * Existing legacy/native project directories for agents that the pinned\n * registry installs through `.agents/skills`.\n */\nexport const AGENTS_SKILLS_COMPATIBLE_PROJECT_DIRECTORIES = [\n UNIVERSAL_AGENT_SKILLS_DIRECTORY,\n '.cursor/skills',\n '.codex/skills',\n '.github/skills',\n '.gemini/skills',\n '.opencode/skills',\n '.cline/skills',\n '.warp/skills',\n '.zed/skills',\n '.replit/skills',\n] as const;\n\n/** One existing project-level skill container. */\nexport interface ExistingProjectSkillDirectory {\n /** Repository-relative directory. */\n path: string;\n /** Whether its agent can read the universal Agent Skills directory. */\n supportsAgentsSkills: boolean;\n}\n\n/** One source file in a managed Agent Skill. */\nexport interface AgentSkillFile {\n /** Path relative to the skill directory. */\n path: string;\n /** Complete Markdown contents. */\n contents: string;\n}\n\n/** Portable definition of one CLI-managed Agent Skill. */\nexport interface ManagedAgentSkillDefinition {\n /** Namespaced skill directory name. */\n name: string;\n /** Human-readable domain name used in plan descriptions. */\n displayName: string;\n /** Stable tool identifier written into integrity markers. */\n owner: string;\n /** Complete portable file set. */\n files: readonly AgentSkillFile[];\n}\n\n/** Pure planned mutations for one managed Agent Skill. */\nexport interface ManagedAgentSkillPlan {\n /** Skill file and link changes. */\n changes: PlannedChange[];\n /** Skill paths already in their desired state. */\n unchanged: string[];\n /** Customized paths preserved for manual review. */\n warnings: string[];\n}\n\n/**\n * Choose one canonical skill directory and aliases for incompatible agents.\n *\n * @param existingDirectories - Existing project-level skill containers\n * @returns Canonical and alias directories\n */\nexport function resolveAgentSkillDirectories(\n existingDirectories: readonly ExistingProjectSkillDirectory[],\n): {\n /** Directory that owns the canonical skill. */\n canonical: string;\n /** Existing directories that require links to the canonical skill. */\n aliases: string[];\n} {\n if (existingDirectories.length === 1) {\n return { canonical: existingDirectories[0]!.path, aliases: [] };\n }\n return {\n canonical: UNIVERSAL_AGENT_SKILLS_DIRECTORY,\n aliases:\n existingDirectories.length > 1\n ? existingDirectories\n .filter(({ path, supportsAgentsSkills }) => {\n return path !== UNIVERSAL_AGENT_SKILLS_DIRECTORY && !supportsAgentsSkills;\n })\n .map(({ path }) => path)\n : [],\n };\n}\n\n/**\n * Add a digest marker to CLI-managed skill contents.\n *\n * @param contents - Skill Markdown\n * @param owner - Stable tool identifier\n * @returns Managed skill contents\n */\nexport function buildManagedAgentSkill(contents: string, owner: string): string {\n const body = contents.trimEnd();\n const digest = createHash('sha256').update(body).digest('hex');\n return `${body}\\n\\n<!-- managed-by: ${owner}; content-sha256: ${digest} -->\\n`;\n}\n\n/**\n * Check whether managed skill contents still match their recorded digest.\n *\n * @param contents - Existing skill Markdown\n * @param owner - Stable tool identifier\n * @returns Whether automatic replacement is safe\n */\nexport function isUnmodifiedManagedAgentSkill(contents: string, owner: string): boolean {\n const escapedOwner = owner.replace(/[.*+?^${}()|[\\]\\\\]/gu, '\\\\$&');\n const marker = contents.match(\n new RegExp(`<!-- managed-by: ${escapedOwner}; content-sha256: ([a-f0-9]{64}) -->\\\\s*$`, 'u'),\n );\n if (!marker || marker.index === undefined) {\n return false;\n }\n const body = contents.slice(0, marker.index).trimEnd();\n return createHash('sha256').update(body).digest('hex') === marker[1];\n}\n\n/**\n * Enumerate paths that may be touched while installing a managed Agent Skill.\n *\n * @param rootDirectory - Root that owns project-level skill directories\n * @param existingDirectories - Existing project-level skill containers\n * @param skill - Managed skill definition\n * @returns Absolute candidate paths\n */\nexport function getManagedAgentSkillCandidatePaths(\n rootDirectory: string,\n existingDirectories: readonly ExistingProjectSkillDirectory[],\n skill: ManagedAgentSkillDefinition,\n): string[] {\n const directories = resolveAgentSkillDirectories(existingDirectories);\n const paths = new Set<string>();\n const canonicalDirectory = join(rootDirectory, directories.canonical, skill.name);\n skill.files.forEach(({ path }) => paths.add(join(canonicalDirectory, path)));\n directories.aliases.forEach((directory) => {\n const aliasDirectory = join(rootDirectory, directory, skill.name);\n paths.add(aliasDirectory);\n skill.files.forEach(({ path }) => paths.add(join(aliasDirectory, path)));\n });\n existingDirectories.forEach(({ path: directory }) => {\n skill.files.forEach(({ path }) => {\n paths.add(join(rootDirectory, directory, skill.name, path));\n });\n });\n return [...paths].sort((left, right) => left.localeCompare(right));\n}\n\n/**\n * Plan one direct managed skill installation and links into other existing directories.\n *\n * @param input - Root, discovered directories, snapshots, and skill definition\n * @returns Pure skill plan\n */\nexport function planManagedAgentSkill(input: {\n /** Root that owns project-level skill directories. */\n rootDirectory: string;\n /** Existing project-level skill containers. */\n existingDirectories: readonly ExistingProjectSkillDirectory[];\n /** Potential mutation paths keyed by absolute path. */\n snapshots: Readonly<Record<string, PlanningPathSnapshot>>;\n /** Managed skill definition. */\n skill: ManagedAgentSkillDefinition;\n /** Preserve customized files and unexpected alias targets with warnings. */\n preserveModified?: boolean;\n}): ManagedAgentSkillPlan {\n const { rootDirectory, existingDirectories, snapshots, skill, preserveModified = false } = input;\n const plan: ManagedAgentSkillPlan = { changes: [], unchanged: [], warnings: [] };\n const directories = resolveAgentSkillDirectories(existingDirectories);\n const canonicalDirectory = join(rootDirectory, directories.canonical, skill.name);\n const managedFiles = skill.files.map((file) => ({\n path: file.path,\n contents: buildManagedAgentSkill(file.contents, skill.owner),\n }));\n const planManagedFiles = (directory: string, description: string): void => {\n managedFiles.forEach((file) => {\n const path = join(directory, file.path);\n const snapshot = getPlanningFileSnapshot(snapshots, path);\n if (\n snapshot.contents !== null &&\n snapshot.contents !== file.contents &&\n !isUnmodifiedManagedAgentSkill(snapshot.contents, skill.owner)\n ) {\n if (preserveModified) {\n plan.unchanged.push(path);\n plan.warnings.push(\n `Customized managed skill file was left unchanged: ${path}. Merge the updated guidance manually.`,\n );\n return;\n }\n throw new Error(\n `Refusing to replace user-managed skill: ${path}. Apply the skill update manually.`,\n );\n }\n const change = planFileChange({\n snapshot,\n after: file.contents,\n description: `${description}: ${file.path}`,\n });\n if (change) {\n plan.changes.push(change);\n } else {\n plan.unchanged.push(path);\n }\n });\n };\n planManagedFiles(canonicalDirectory, `Install ${skill.displayName} skill file`);\n\n directories.aliases.forEach((directory) => {\n const targetDirectory = join(rootDirectory, directory, skill.name);\n const snapshot = getPlanningPathSnapshot(snapshots, targetDirectory);\n const relativeTarget = relative(dirname(targetDirectory), canonicalDirectory);\n if (snapshot.kind === 'link' && snapshot.target === relativeTarget) {\n plan.unchanged.push(targetDirectory);\n return;\n }\n if (snapshot.kind === 'directory') {\n planManagedFiles(targetDirectory, `Update ${skill.displayName} skill copy in ${directory}`);\n return;\n }\n if (snapshot.kind !== 'absent') {\n if (preserveModified) {\n plan.unchanged.push(targetDirectory);\n plan.warnings.push(\n `Customized skill target was left unchanged: ${targetDirectory}. Expose the canonical skill manually if needed.`,\n );\n return;\n }\n throw new Error(`Refusing to replace unexpected skill target: ${targetDirectory}`);\n }\n const change: PlannedLinkChange = {\n kind: 'link',\n path: targetDirectory,\n target: relativeTarget,\n fallbackFiles: managedFiles,\n description: `Expose the canonical skill in ${directory}`,\n };\n plan.changes.push(change);\n });\n\n existingDirectories\n .map(({ path }) => path)\n .filter(\n (directory) =>\n directory !== directories.canonical && !directories.aliases.includes(directory),\n )\n .forEach((directory) => {\n const existingDirectory = join(rootDirectory, directory, skill.name);\n const skillPath = join(existingDirectory, 'SKILL.md');\n const snapshot = getPlanningFileSnapshot(snapshots, skillPath);\n if (\n snapshot.contents !== null &&\n isUnmodifiedManagedAgentSkill(snapshot.contents, skill.owner)\n ) {\n planManagedFiles(existingDirectory, `Update existing managed skill in ${directory}`);\n } else if (snapshot.contents?.includes(`managed-by: ${skill.owner}`)) {\n plan.unchanged.push(skillPath);\n plan.warnings.push(\n `Existing customized managed skill was left unchanged: ${existingDirectory}`,\n );\n }\n });\n\n return plan;\n}\n","import { basename, dirname, isAbsolute, relative, resolve, sep } from 'node:path';\n\nimport type { LocalContext } from '../../context.js';\n\n/**\n * Resolve a path through its nearest existing ancestor.\n *\n * This retains nonexistent suffixes while resolving every existing symlink in\n * the prefix, which is required before safely creating a new file.\n *\n * @param context - CLI context\n * @param path - Absolute or relative path\n * @returns Physical absolute path\n */\nfunction resolvePhysicalPath(context: LocalContext, path: string): string {\n let current = resolve(path);\n const suffix: string[] = [];\n while (true) {\n try {\n context.fs.lstatSync(current);\n return resolve(context.fs.realpathSync(current), ...suffix);\n } catch (error) {\n const code = (error as NodeJS.ErrnoException).code;\n if (code !== 'ENOENT' && code !== 'ENOTDIR') {\n throw error;\n }\n const parent = dirname(current);\n if (parent === current) {\n throw error;\n }\n suffix.unshift(basename(current));\n current = parent;\n }\n }\n}\n\n/**\n * Determine whether a path physically remains under an approved root.\n *\n * @param context - CLI context\n * @param root - Approved directory\n * @param path - Candidate destination or referenced file\n * @returns Whether existing symlinks keep the path under the root\n */\nexport function isPathPhysicallyContained(\n context: LocalContext,\n root: string,\n path: string,\n): boolean {\n const physicalRoot = resolvePhysicalPath(context, root);\n const physicalPath = resolvePhysicalPath(context, path);\n const child = relative(physicalRoot, physicalPath);\n return child !== '..' && !child.startsWith(`..${sep}`) && !isAbsolute(child);\n}\n\n/**\n * Reject a path whose existing symlink ancestors escape an approved root.\n *\n * @param context - CLI context\n * @param root - Approved directory\n * @param path - Candidate path\n */\nexport function assertPathPhysicallyContained(\n context: LocalContext,\n root: string,\n path: string,\n): void {\n if (!isPathPhysicallyContained(context, root, path)) {\n throw new Error(`Refusing to access path outside project root through a symlink: ${path}`);\n }\n}\n","import { dirname, isAbsolute, join, relative, resolve, sep } from 'node:path';\n\nimport type { LocalContext } from '../../context.js';\nimport {\n AGENTS_SKILLS_COMPATIBLE_PROJECT_DIRECTORIES,\n PROJECT_SKILL_DIRECTORIES,\n type ExistingProjectSkillDirectory,\n} from './agent-skill.js';\nimport { assertPathPhysicallyContained } from './path-safety.js';\nimport type { PlanningPathSnapshot } from './project-plan.js';\n\n/** Repository-level state shared by project scaffolds. */\nexport interface ProjectRepositoryState {\n /** Root that owns repository-level setup. */\n projectRoot: string;\n /** Nearest repository root, when present. */\n repositoryRoot?: string;\n /** Existing project-level skill directories. */\n existingSkillDirectories: ExistingProjectSkillDirectory[];\n /** Whether the repository appears to use GitHub. */\n usesGithub: boolean;\n}\n\n/**\n * Collect candidate paths once, before preview.\n *\n * @param context - CLI context\n * @param rootDirectory - Approved planning root\n * @param paths - Absolute paths\n * @returns In-memory path snapshots\n */\nexport function collectPlanningSnapshots(\n context: LocalContext,\n rootDirectory: string,\n paths: readonly string[],\n): Readonly<Record<string, PlanningPathSnapshot>> {\n return Object.fromEntries(\n paths.map((path): [string, PlanningPathSnapshot] => {\n assertPathPhysicallyContained(context, rootDirectory, path);\n try {\n const stat = context.fs.lstatSync(path);\n if (stat.isSymbolicLink()) {\n return [path, { kind: 'link', path, target: context.fs.readlinkSync(path) }];\n }\n if (stat.isDirectory()) {\n return [path, { kind: 'directory', path }];\n }\n if (!stat.isFile()) {\n throw new Error(`Unsupported filesystem entry: ${path}`);\n }\n return [\n path,\n {\n kind: 'file',\n path,\n contents: context.fs.readFileSync(path, 'utf8'),\n mode: stat.mode,\n },\n ];\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') {\n return [path, { kind: 'absent', path }];\n }\n throw error;\n }\n }),\n );\n}\n\n/**\n * Walk upward for a repository marker.\n *\n * @param context - CLI context\n * @param startDirectory - Starting directory\n * @returns Nearest repository root\n */\nexport function findRepositoryRoot(\n context: LocalContext,\n startDirectory: string,\n): string | undefined {\n let current = startDirectory;\n while (true) {\n if (context.fs.existsSync(join(current, '.git'))) {\n return current;\n }\n const parent = dirname(current);\n if (parent === current) {\n return undefined;\n }\n current = parent;\n }\n}\n\n/**\n * Find the nearest existing ancestor of a target that may not exist yet.\n *\n * @param context - CLI context\n * @param target - Absolute target\n * @returns Existing directory\n */\nexport function findExistingAncestor(context: LocalContext, target: string): string {\n let current = target;\n while (!context.fs.existsSync(current)) {\n const parent = dirname(current);\n if (parent === current) {\n return current;\n }\n current = parent;\n }\n const stat = context.fs.statSync(current);\n return stat.isDirectory() ? current : dirname(current);\n}\n\n/**\n * Determine whether a path is lexically at or below a root.\n *\n * @param root - Candidate parent directory\n * @param path - Candidate descendant\n * @returns Whether the path is contained without resolving links\n */\nfunction isPathContained(root: string, path: string): boolean {\n const child = relative(root, path);\n return child !== '..' && !child.startsWith(`..${sep}`) && !isAbsolute(child);\n}\n\n/**\n * Collect relative paths below a project directory for collision checks.\n *\n * @param context - CLI context\n * @param root - Directory to inspect\n * @param options - Directory-entry inclusion\n * @returns Relative file, link, and directory paths\n */\nexport function collectProjectRelativePaths(\n context: LocalContext,\n root: string,\n options: {\n /** Include directory entries in addition to files and links. */\n includeDirectories?: boolean;\n } = {},\n): string[] {\n if (!context.fs.existsSync(root)) {\n return [];\n }\n const paths: string[] = [];\n const visit = (directory: string): void => {\n context.fs.readdirSync(directory, { withFileTypes: true }).forEach((entry) => {\n if (entry.name === '.git' || entry.name === 'node_modules') {\n return;\n }\n const absolute = join(directory, entry.name);\n const isDirectory = entry.isDirectory() && !entry.isSymbolicLink();\n if (options.includeDirectories !== false || !isDirectory) {\n paths.push(relative(root, absolute).split(sep).join('/'));\n }\n if (isDirectory) {\n visit(absolute);\n }\n });\n };\n visit(root);\n return paths.sort((left, right) => left.localeCompare(right));\n}\n\n/**\n * Determine whether a skill container has a SKILL.md within three levels.\n *\n * @param context - CLI context\n * @param directory - Candidate skill container\n * @param depth - Remaining nested directory levels\n * @returns Whether a skill exists below the container\n */\nfunction containsSkill(context: LocalContext, directory: string, depth: number): boolean {\n if (depth === 0) {\n return false;\n }\n return context.fs.readdirSync(directory, { withFileTypes: true }).some((entry) => {\n if (!entry.isDirectory() || entry.isSymbolicLink()) {\n return false;\n }\n const child = join(directory, entry.name);\n return (\n context.fs.existsSync(join(child, 'SKILL.md')) || containsSkill(context, child, depth - 1)\n );\n });\n}\n\n/**\n * Find existing project skill containers without consulting home directories.\n *\n * @param context - CLI context\n * @param projectRoot - Repository root or standalone project root\n * @returns Existing containers and `.agents/skills` compatibility\n */\nexport function detectExistingProjectSkillDirectories(\n context: LocalContext,\n projectRoot: string,\n): ExistingProjectSkillDirectory[] {\n if (!context.fs.existsSync(projectRoot) || !context.fs.statSync(projectRoot).isDirectory()) {\n return [];\n }\n const compatible = new Set<string>(AGENTS_SKILLS_COMPATIBLE_PROJECT_DIRECTORIES);\n const candidates = new Set<string>([\n ...PROJECT_SKILL_DIRECTORIES,\n ...AGENTS_SKILLS_COMPATIBLE_PROJECT_DIRECTORIES,\n ]);\n context.fs.readdirSync(projectRoot, { withFileTypes: true }).forEach((entry) => {\n if (!entry.isDirectory() || entry.isSymbolicLink()) {\n return;\n }\n const skillsDirectory = join(projectRoot, entry.name, 'skills');\n if (\n context.fs.existsSync(skillsDirectory) &&\n context.fs.statSync(skillsDirectory).isDirectory() &&\n containsSkill(context, skillsDirectory, 3)\n ) {\n candidates.add(`${entry.name}/skills`);\n }\n });\n return [...candidates]\n .filter((candidate) => {\n const path = join(projectRoot, candidate);\n return context.fs.existsSync(path) && context.fs.statSync(path).isDirectory();\n })\n .sort((left, right) => left.localeCompare(right))\n .map((path) => ({\n path,\n supportsAgentsSkills: compatible.has(path),\n }));\n}\n\n/**\n * Detect whether a git repository points at GitHub.\n *\n * @param context - CLI context\n * @param repositoryRoot - Repository root\n * @returns Whether GitHub setup is applicable\n */\nexport function repositoryUsesGithub(\n context: LocalContext,\n repositoryRoot: string | undefined,\n): boolean {\n if (!repositoryRoot) {\n return false;\n }\n if (context.fs.existsSync(join(repositoryRoot, '.github'))) {\n return true;\n }\n const gitMarker = join(repositoryRoot, '.git');\n let gitDirectory = gitMarker;\n if (context.fs.existsSync(gitMarker) && context.fs.statSync(gitMarker).isFile()) {\n const marker = context.fs.readFileSync(gitMarker, 'utf8').match(/^gitdir:\\s*(.+)$/mu)?.[1];\n if (marker) {\n gitDirectory = resolve(repositoryRoot, marker);\n }\n }\n const commonDirectoryPath = join(gitDirectory, 'commondir');\n if (\n context.fs.existsSync(commonDirectoryPath) &&\n context.fs.statSync(commonDirectoryPath).isFile()\n ) {\n gitDirectory = resolve(\n gitDirectory,\n context.fs.readFileSync(commonDirectoryPath, 'utf8').trim(),\n );\n }\n const configPath = join(gitDirectory, 'config');\n return (\n context.fs.existsSync(configPath) &&\n context.fs.readFileSync(configPath, 'utf8').includes('github.com')\n );\n}\n\n/**\n * Discover repository-owned state for a project scaffold.\n *\n * @param context - CLI context\n * @param options - Target and standalone root\n * @returns Repository-level project state\n */\nexport function discoverProjectRepository(\n context: LocalContext,\n options: {\n /** Directory from which the CLI was invoked. */\n invocationDirectory: string;\n /** Absolute project target used to locate a surrounding repository. */\n targetDirectory: string;\n /** Root to use when the target is not inside a repository. */\n standaloneProjectRoot: string;\n },\n): ProjectRepositoryState {\n const invocationAncestor = findExistingAncestor(context, options.invocationDirectory);\n const invocationRepositoryRoot = findRepositoryRoot(context, invocationAncestor);\n const targetRepositoryRoot =\n invocationRepositoryRoot && isPathContained(invocationRepositoryRoot, options.targetDirectory)\n ? invocationRepositoryRoot\n : findRepositoryRoot(context, findExistingAncestor(context, options.targetDirectory));\n const shouldAnchorToStandaloneRoot =\n options.standaloneProjectRoot !== options.targetDirectory &&\n isPathContained(options.standaloneProjectRoot, options.targetDirectory);\n const repositoryRoot = targetRepositoryRoot;\n const projectRoot = shouldAnchorToStandaloneRoot\n ? (invocationRepositoryRoot ?? options.standaloneProjectRoot)\n : (repositoryRoot ?? options.standaloneProjectRoot);\n assertPathPhysicallyContained(context, projectRoot, options.targetDirectory);\n return {\n projectRoot,\n ...(repositoryRoot ? { repositoryRoot } : {}),\n existingSkillDirectories: detectExistingProjectSkillDirectories(context, projectRoot),\n usesGithub: repositoryUsesGithub(context, repositoryRoot),\n };\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import{n as e,o as t}from"./project-discovery-CWRjlFnC.mjs";import{basename as n,dirname as r,join as i,relative as a,resolve as o,sep as s}from"node:path";function c(e,t){try{return e.fs.lstatSync(t),!0}catch(e){if(e.code===`ENOENT`)return!1;throw e}}function l(e,t){if(!c(e,t))return{kind:`absent`,path:t};let n=e.fs.lstatSync(t);if(n.isSymbolicLink())return{kind:`link`,path:t,target:e.fs.readlinkSync(t)};if(!n.isFile())throw Error(`Cannot transactionally replace non-file path: ${t}`);return{kind:`file`,path:t,contents:e.fs.readFileSync(t,`utf8`),mode:n.mode}}function u(e,t,a,o){e.fs.mkdirSync(r(t),{recursive:!0});let s=i(r(t),`.${n(t)}.${e.process.pid}.${Date.now()}.tmp`);try{e.fs.writeFileSync(s,a,o===void 0?void 0:{mode:o&511}),e.fs.renameSync(s,t)}finally{c(e,s)&&e.fs.rmSync(s,{force:!0})}}function d(e,t,a,o){e.fs.mkdirSync(r(t),{recursive:!0});let s=i(r(t),`.${n(t)}.${e.process.pid}.${Date.now()}.tmp`);try{e.fs.writeFileSync(s,a,o===void 0?void 0:{mode:o&511}),e.fs.linkSync(s,t)}finally{c(e,s)&&e.fs.rmSync(s,{force:!0})}}function f(e,t){if(t.kind===`absent`){c(e,t.path)&&e.fs.rmSync(t.path,{recursive:!0,force:!0});return}if(e.fs.mkdirSync(r(t.path),{recursive:!0}),t.kind===`link`){c(e,t.path)&&e.fs.rmSync(t.path,{recursive:!0,force:!0}),e.fs.symlinkSync(t.target,t.path,`dir`);return}c(e,t.path)&&!e.fs.lstatSync(t.path).isFile()&&e.fs.rmSync(t.path,{recursive:!0,force:!0}),u(e,t.path,t.contents,t.mode)}function p(e,t){let n=c(e,t.path);if(t.before===null){if(n)throw Error(`File appeared after preview: ${t.path}`);return}if(!n)throw Error(`File changed after preview: ${t.path}`);let r=e.fs.lstatSync(t.path);if(!r.isFile()||t.mode!==void 0&&r.mode!==t.mode||e.fs.readFileSync(t.path,`utf8`)!==t.before)throw Error(`File changed after preview: ${t.path}`);if(t.createOnly)throw Error(`Refusing to overwrite existing file: ${t.path}`)}function m(e,t){if(t.kind===`file`){p(e,t);return}if(c(e,t.path))throw Error(`Skill target appeared after preview: ${t.path}`)}function h(t,n){let r=e(t,n.path);if(r.length!==n.relativePaths.length||r.some((e,t)=>e!==n.relativePaths[t]))throw Error(`Directory changed after preview: ${n.path}`)}function g(e,t){let n=o(t.rootDirectory),i=new Set;for(let l of t.changes){let t=r(o(l.path));for(;;){let o=a(n,t);if(o===`..`||o.startsWith(`..${s}`)||(c(e,t)||i.add(t),t===n))break;t=r(t)}}return[...i].sort((e,t)=>t.split(s).length-e.split(s).length)}function _(e,t){let n=[];for(let r of t)if(c(e,r))try{e.fs.rmdirSync(r)}catch(e){e.code!==`ENOENT`&&n.push(`${r}: ${e.message}`)}return n}function v(e,n,a){try{e.fs.symlinkSync(n.target,n.path,`dir`)}catch(o){let s=o.code;if(![`EPERM`,`EACCES`,`ENOTSUP`,`UNKNOWN`].includes(s??``))throw o;n.fallbackFiles.forEach(o=>{let s=i(n.path,o.path);t(e,a,s),e.fs.mkdirSync(r(s),{recursive:!0}),e.fs.writeFileSync(s,o.contents)})}}async function y(e,n){for(let r of n.changes)t(e,n.rootDirectory,r.path),m(e,r);for(let r of n.directoryPreconditions??[])t(e,n.rootDirectory,r.path),h(e,r);let r=g(e,n),i=[];try{for(let r of n.changes)if(t(e,n.rootDirectory,r.path),m(e,r),r.kind===`file`){let t=l(e,r.path);r.before===null?d(e,r.path,r.after,r.mode):u(e,r.path,r.after,r.mode),i.push(t)}else i.push({kind:`absent`,path:r.path}),v(e,r,n.rootDirectory)}catch(t){let n=[];for(let t of i.reverse())try{f(e,t)}catch(e){n.push(`${t.path}: ${e.message}`)}throw n.push(..._(e,r)),n.length>0?Error(`${t.message}\nRollback also failed:\n${n.join(`
|
|
2
|
+
`)}`):t}}export{y as t};
|
|
3
|
+
//# sourceMappingURL=project-plan-apply-Bg_1eMX9.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"project-plan-apply-Dvndx455.mjs","names":[],"sources":["../src/lib/scaffolding/project-plan-apply.ts"],"sourcesContent":["import { basename, dirname, join, relative, resolve, sep } from 'node:path';\n\nimport type { LocalContext } from '../../context.js';\nimport { assertPathPhysicallyContained } from './path-safety.js';\nimport { collectProjectRelativePaths } from './project-discovery.js';\nimport type {\n PlannedDirectoryPrecondition,\n PlannedChange,\n PlannedFileChange,\n PlannedLinkChange,\n ProjectPlan,\n} from './project-plan.js';\n\n/** Original state retained for rollback. */\ntype RollbackSnapshot =\n | {\n /** Snapshot kind. */\n kind: 'absent';\n /** Absolute path. */\n path: string;\n }\n | {\n /** Snapshot kind. */\n kind: 'file';\n /** Absolute path. */\n path: string;\n /** Original UTF-8 contents. */\n contents: string;\n /** Original mode. */\n mode: number;\n }\n | {\n /** Snapshot kind. */\n kind: 'link';\n /** Absolute path. */\n path: string;\n /** Original link target. */\n target: string;\n };\n\n/**\n * Check a path without following its final symbolic link.\n *\n * @param context - CLI context\n * @param path - Candidate path\n * @returns Whether any filesystem entry exists\n */\nfunction pathExists(context: LocalContext, path: string): boolean {\n try {\n context.fs.lstatSync(path);\n return true;\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') {\n return false;\n }\n throw error;\n }\n}\n\n/**\n * Read one path for rollback.\n *\n * @param context - CLI context\n * @param path - Absolute path\n * @returns Original state\n */\nfunction snapshotPath(context: LocalContext, path: string): RollbackSnapshot {\n if (!pathExists(context, path)) {\n return { kind: 'absent', path };\n }\n const stat = context.fs.lstatSync(path);\n if (stat.isSymbolicLink()) {\n return { kind: 'link', path, target: context.fs.readlinkSync(path) };\n }\n if (!stat.isFile()) {\n throw new Error(`Cannot transactionally replace non-file path: ${path}`);\n }\n return {\n kind: 'file',\n path,\n contents: context.fs.readFileSync(path, 'utf8'),\n mode: stat.mode,\n };\n}\n\n/**\n * Write a regular file through a temporary sibling and rename.\n *\n * @param context - CLI context\n * @param path - Destination\n * @param contents - Desired contents\n * @param mode - Optional mode to preserve\n */\nfunction writeAtomic(context: LocalContext, path: string, contents: string, mode?: number): void {\n context.fs.mkdirSync(dirname(path), { recursive: true });\n const temporaryPath = join(\n dirname(path),\n `.${basename(path)}.${context.process.pid}.${Date.now()}.tmp`,\n );\n try {\n context.fs.writeFileSync(\n temporaryPath,\n contents,\n mode === undefined ? undefined : { mode: mode & 0o777 },\n );\n context.fs.renameSync(temporaryPath, path);\n } finally {\n if (pathExists(context, temporaryPath)) {\n context.fs.rmSync(temporaryPath, { force: true });\n }\n }\n}\n\n/**\n * Create a file atomically without replacing a destination that appeared.\n *\n * @param context - CLI context\n * @param path - Destination path\n * @param contents - UTF-8 contents\n * @param mode - Optional file mode\n */\nfunction writeAtomicCreateOnly(\n context: LocalContext,\n path: string,\n contents: string,\n mode?: number,\n): void {\n context.fs.mkdirSync(dirname(path), { recursive: true });\n const temporaryPath = join(\n dirname(path),\n `.${basename(path)}.${context.process.pid}.${Date.now()}.tmp`,\n );\n try {\n context.fs.writeFileSync(\n temporaryPath,\n contents,\n mode === undefined ? undefined : { mode: mode & 0o777 },\n );\n context.fs.linkSync(temporaryPath, path);\n } finally {\n if (pathExists(context, temporaryPath)) {\n context.fs.rmSync(temporaryPath, { force: true });\n }\n }\n}\n\n/**\n * Restore one path.\n *\n * @param context - CLI context\n * @param snapshot - Original state\n */\nfunction restoreSnapshot(context: LocalContext, snapshot: RollbackSnapshot): void {\n if (snapshot.kind === 'absent') {\n if (pathExists(context, snapshot.path)) {\n context.fs.rmSync(snapshot.path, { recursive: true, force: true });\n }\n return;\n }\n context.fs.mkdirSync(dirname(snapshot.path), { recursive: true });\n if (snapshot.kind === 'link') {\n if (pathExists(context, snapshot.path)) {\n context.fs.rmSync(snapshot.path, { recursive: true, force: true });\n }\n context.fs.symlinkSync(snapshot.target, snapshot.path, 'dir');\n return;\n }\n if (pathExists(context, snapshot.path) && !context.fs.lstatSync(snapshot.path).isFile()) {\n context.fs.rmSync(snapshot.path, { recursive: true, force: true });\n }\n writeAtomic(context, snapshot.path, snapshot.contents, snapshot.mode);\n}\n\n/**\n * Verify a planned regular-file precondition.\n *\n * @param context - CLI context\n * @param change - Planned file\n */\nfunction preflightFile(context: LocalContext, change: PlannedFileChange): void {\n const exists = pathExists(context, change.path);\n if (change.before === null) {\n if (exists) {\n throw new Error(`File appeared after preview: ${change.path}`);\n }\n return;\n }\n if (!exists) {\n throw new Error(`File changed after preview: ${change.path}`);\n }\n const stat = context.fs.lstatSync(change.path);\n if (\n !stat.isFile() ||\n (change.mode !== undefined && stat.mode !== change.mode) ||\n context.fs.readFileSync(change.path, 'utf8') !== change.before\n ) {\n throw new Error(`File changed after preview: ${change.path}`);\n }\n if (change.createOnly) {\n throw new Error(`Refusing to overwrite existing file: ${change.path}`);\n }\n}\n\n/**\n * Verify one planned mutation still matches preview assumptions.\n *\n * @param context - CLI context\n * @param change - Planned mutation\n */\nfunction preflightChange(context: LocalContext, change: PlannedChange): void {\n if (change.kind === 'file') {\n preflightFile(context, change);\n return;\n }\n if (pathExists(context, change.path)) {\n throw new Error(`Skill target appeared after preview: ${change.path}`);\n }\n}\n\n/**\n * Verify directory-wide contents observed during planning.\n *\n * @param context - CLI context\n * @param precondition - Expected directory snapshot\n */\nfunction preflightDirectory(\n context: LocalContext,\n precondition: PlannedDirectoryPrecondition,\n): void {\n const currentPaths = collectProjectRelativePaths(context, precondition.path);\n if (\n currentPaths.length !== precondition.relativePaths.length ||\n currentPaths.some((path, index) => path !== precondition.relativePaths[index])\n ) {\n throw new Error(`Directory changed after preview: ${precondition.path}`);\n }\n}\n\n/**\n * Record missing parent directories that this plan may create.\n *\n * @param context - CLI context\n * @param plan - Approved project plan\n * @returns Missing directories ordered deepest first\n */\nfunction collectCreatedDirectoryCandidates(context: LocalContext, plan: ProjectPlan): string[] {\n const rootDirectory = resolve(plan.rootDirectory);\n const directories = new Set<string>();\n for (const change of plan.changes) {\n let current = dirname(resolve(change.path));\n while (true) {\n const child = relative(rootDirectory, current);\n if (child === '..' || child.startsWith(`..${sep}`)) {\n break;\n }\n if (!pathExists(context, current)) {\n directories.add(current);\n }\n if (current === rootDirectory) {\n break;\n }\n current = dirname(current);\n }\n }\n return [...directories].sort((left, right) => right.split(sep).length - left.split(sep).length);\n}\n\n/**\n * Remove empty parent directories created while applying a failed plan.\n *\n * @param context - CLI context\n * @param directories - Previously absent directories\n * @returns Rollback failures\n */\nfunction removeCreatedDirectories(context: LocalContext, directories: readonly string[]): string[] {\n const errors: string[] = [];\n for (const directory of directories) {\n if (!pathExists(context, directory)) {\n continue;\n }\n try {\n context.fs.rmdirSync(directory);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') {\n errors.push(`${directory}: ${(error as Error).message}`);\n }\n }\n }\n return errors;\n}\n\n/**\n * Install a project-local skill link, copying on platforms where links fail.\n *\n * @param context - CLI context\n * @param change - Planned link\n * @param rootDirectory - Approved mutation root\n */\nfunction applyLink(context: LocalContext, change: PlannedLinkChange, rootDirectory: string): void {\n try {\n context.fs.symlinkSync(change.target, change.path, 'dir');\n } catch (error) {\n const code = (error as NodeJS.ErrnoException).code;\n if (!['EPERM', 'EACCES', 'ENOTSUP', 'UNKNOWN'].includes(code ?? '')) {\n throw error;\n }\n change.fallbackFiles.forEach((file) => {\n const path = join(change.path, file.path);\n assertPathPhysicallyContained(context, rootDirectory, path);\n context.fs.mkdirSync(dirname(path), { recursive: true });\n context.fs.writeFileSync(path, file.contents);\n });\n }\n}\n\n/**\n * Apply an approved project plan and restore source-controlled files on error.\n *\n * @param context - CLI context\n * @param plan - Approved plan\n */\nexport async function applyProjectPlan(context: LocalContext, plan: ProjectPlan): Promise<void> {\n for (const change of plan.changes) {\n assertPathPhysicallyContained(context, plan.rootDirectory, change.path);\n preflightChange(context, change);\n }\n for (const precondition of plan.directoryPreconditions ?? []) {\n assertPathPhysicallyContained(context, plan.rootDirectory, precondition.path);\n preflightDirectory(context, precondition);\n }\n\n const createdDirectoryCandidates = collectCreatedDirectoryCandidates(context, plan);\n const applied: RollbackSnapshot[] = [];\n try {\n for (const change of plan.changes) {\n assertPathPhysicallyContained(context, plan.rootDirectory, change.path);\n preflightChange(context, change);\n if (change.kind === 'file') {\n const snapshot = snapshotPath(context, change.path);\n if (change.before === null) {\n writeAtomicCreateOnly(context, change.path, change.after, change.mode);\n } else {\n writeAtomic(context, change.path, change.after, change.mode);\n }\n applied.push(snapshot);\n } else {\n applied.push({ kind: 'absent', path: change.path });\n applyLink(context, change, plan.rootDirectory);\n }\n }\n } catch (error) {\n const rollbackErrors: string[] = [];\n for (const snapshot of applied.reverse()) {\n try {\n restoreSnapshot(context, snapshot);\n } catch (rollbackError) {\n rollbackErrors.push(`${snapshot.path}: ${(rollbackError as Error).message}`);\n }\n }\n rollbackErrors.push(...removeCreatedDirectories(context, createdDirectoryCandidates));\n if (rollbackErrors.length > 0) {\n throw new Error(\n `${(error as Error).message}\\nRollback also failed:\\n${rollbackErrors.join('\\n')}`,\n );\n }\n throw error;\n }\n}\n"],"mappings":"qLA+CA,SAAS,EAAW,EAAuB,EAAuB,CAChE,GAAI,CAEF,OADA,EAAQ,GAAG,UAAU,EAAK,CACnB,SACA,EAAO,CACd,GAAK,EAAgC,OAAS,SAC5C,MAAO,GAET,MAAM,GAWV,SAAS,EAAa,EAAuB,EAAgC,CAC3E,GAAI,CAAC,EAAW,EAAS,EAAK,CAC5B,MAAO,CAAE,KAAM,SAAU,OAAM,CAEjC,IAAM,EAAO,EAAQ,GAAG,UAAU,EAAK,CACvC,GAAI,EAAK,gBAAgB,CACvB,MAAO,CAAE,KAAM,OAAQ,OAAM,OAAQ,EAAQ,GAAG,aAAa,EAAK,CAAE,CAEtE,GAAI,CAAC,EAAK,QAAQ,CAChB,MAAU,MAAM,iDAAiD,IAAO,CAE1E,MAAO,CACL,KAAM,OACN,OACA,SAAU,EAAQ,GAAG,aAAa,EAAM,OAAO,CAC/C,KAAM,EAAK,KACZ,CAWH,SAAS,EAAY,EAAuB,EAAc,EAAkB,EAAqB,CAC/F,EAAQ,GAAG,UAAU,EAAQ,EAAK,CAAE,CAAE,UAAW,GAAM,CAAC,CACxD,IAAM,EAAgB,EACpB,EAAQ,EAAK,CACb,IAAI,EAAS,EAAK,CAAC,GAAG,EAAQ,QAAQ,IAAI,GAAG,KAAK,KAAK,CAAC,MACzD,CACD,GAAI,CACF,EAAQ,GAAG,cACT,EACA,EACA,IAAS,IAAA,GAAY,IAAA,GAAY,CAAE,KAAM,EAAO,IAAO,CACxD,CACD,EAAQ,GAAG,WAAW,EAAe,EAAK,QAClC,CACJ,EAAW,EAAS,EAAc,EACpC,EAAQ,GAAG,OAAO,EAAe,CAAE,MAAO,GAAM,CAAC,EAavD,SAAS,EACP,EACA,EACA,EACA,EACM,CACN,EAAQ,GAAG,UAAU,EAAQ,EAAK,CAAE,CAAE,UAAW,GAAM,CAAC,CACxD,IAAM,EAAgB,EACpB,EAAQ,EAAK,CACb,IAAI,EAAS,EAAK,CAAC,GAAG,EAAQ,QAAQ,IAAI,GAAG,KAAK,KAAK,CAAC,MACzD,CACD,GAAI,CACF,EAAQ,GAAG,cACT,EACA,EACA,IAAS,IAAA,GAAY,IAAA,GAAY,CAAE,KAAM,EAAO,IAAO,CACxD,CACD,EAAQ,GAAG,SAAS,EAAe,EAAK,QAChC,CACJ,EAAW,EAAS,EAAc,EACpC,EAAQ,GAAG,OAAO,EAAe,CAAE,MAAO,GAAM,CAAC,EAWvD,SAAS,EAAgB,EAAuB,EAAkC,CAChF,GAAI,EAAS,OAAS,SAAU,CAC1B,EAAW,EAAS,EAAS,KAAK,EACpC,EAAQ,GAAG,OAAO,EAAS,KAAM,CAAE,UAAW,GAAM,MAAO,GAAM,CAAC,CAEpE,OAGF,GADA,EAAQ,GAAG,UAAU,EAAQ,EAAS,KAAK,CAAE,CAAE,UAAW,GAAM,CAAC,CAC7D,EAAS,OAAS,OAAQ,CACxB,EAAW,EAAS,EAAS,KAAK,EACpC,EAAQ,GAAG,OAAO,EAAS,KAAM,CAAE,UAAW,GAAM,MAAO,GAAM,CAAC,CAEpE,EAAQ,GAAG,YAAY,EAAS,OAAQ,EAAS,KAAM,MAAM,CAC7D,OAEE,EAAW,EAAS,EAAS,KAAK,EAAI,CAAC,EAAQ,GAAG,UAAU,EAAS,KAAK,CAAC,QAAQ,EACrF,EAAQ,GAAG,OAAO,EAAS,KAAM,CAAE,UAAW,GAAM,MAAO,GAAM,CAAC,CAEpE,EAAY,EAAS,EAAS,KAAM,EAAS,SAAU,EAAS,KAAK,CASvE,SAAS,EAAc,EAAuB,EAAiC,CAC7E,IAAM,EAAS,EAAW,EAAS,EAAO,KAAK,CAC/C,GAAI,EAAO,SAAW,KAAM,CAC1B,GAAI,EACF,MAAU,MAAM,gCAAgC,EAAO,OAAO,CAEhE,OAEF,GAAI,CAAC,EACH,MAAU,MAAM,+BAA+B,EAAO,OAAO,CAE/D,IAAM,EAAO,EAAQ,GAAG,UAAU,EAAO,KAAK,CAC9C,GACE,CAAC,EAAK,QAAQ,EACb,EAAO,OAAS,IAAA,IAAa,EAAK,OAAS,EAAO,MACnD,EAAQ,GAAG,aAAa,EAAO,KAAM,OAAO,GAAK,EAAO,OAExD,MAAU,MAAM,+BAA+B,EAAO,OAAO,CAE/D,GAAI,EAAO,WACT,MAAU,MAAM,wCAAwC,EAAO,OAAO,CAU1E,SAAS,EAAgB,EAAuB,EAA6B,CAC3E,GAAI,EAAO,OAAS,OAAQ,CAC1B,EAAc,EAAS,EAAO,CAC9B,OAEF,GAAI,EAAW,EAAS,EAAO,KAAK,CAClC,MAAU,MAAM,wCAAwC,EAAO,OAAO,CAU1E,SAAS,EACP,EACA,EACM,CACN,IAAM,EAAe,EAA4B,EAAS,EAAa,KAAK,CAC5E,GACE,EAAa,SAAW,EAAa,cAAc,QACnD,EAAa,MAAM,EAAM,IAAU,IAAS,EAAa,cAAc,GAAO,CAE9E,MAAU,MAAM,oCAAoC,EAAa,OAAO,CAW5E,SAAS,EAAkC,EAAuB,EAA6B,CAC7F,IAAM,EAAgB,EAAQ,EAAK,cAAc,CAC3C,EAAc,IAAI,IACxB,IAAK,IAAM,KAAU,EAAK,QAAS,CACjC,IAAI,EAAU,EAAQ,EAAQ,EAAO,KAAK,CAAC,CAC3C,OAAa,CACX,IAAM,EAAQ,EAAS,EAAe,EAAQ,CAO9C,GANI,IAAU,MAAQ,EAAM,WAAW,KAAK,IAAM,GAG7C,EAAW,EAAS,EAAQ,EAC/B,EAAY,IAAI,EAAQ,CAEtB,IAAY,GACd,MAEF,EAAU,EAAQ,EAAQ,EAG9B,MAAO,CAAC,GAAG,EAAY,CAAC,MAAM,EAAM,IAAU,EAAM,MAAM,EAAI,CAAC,OAAS,EAAK,MAAM,EAAI,CAAC,OAAO,CAUjG,SAAS,EAAyB,EAAuB,EAA0C,CACjG,IAAM,EAAmB,EAAE,CAC3B,IAAK,IAAM,KAAa,EACjB,KAAW,EAAS,EAAU,CAGnC,GAAI,CACF,EAAQ,GAAG,UAAU,EAAU,OACxB,EAAO,CACT,EAAgC,OAAS,UAC5C,EAAO,KAAK,GAAG,EAAU,IAAK,EAAgB,UAAU,CAI9D,OAAO,EAUT,SAAS,EAAU,EAAuB,EAA2B,EAA6B,CAChG,GAAI,CACF,EAAQ,GAAG,YAAY,EAAO,OAAQ,EAAO,KAAM,MAAM,OAClD,EAAO,CACd,IAAM,EAAQ,EAAgC,KAC9C,GAAI,CAAC,CAAC,QAAS,SAAU,UAAW,UAAU,CAAC,SAAS,GAAQ,GAAG,CACjE,MAAM,EAER,EAAO,cAAc,QAAS,GAAS,CACrC,IAAM,EAAO,EAAK,EAAO,KAAM,EAAK,KAAK,CACzC,EAA8B,EAAS,EAAe,EAAK,CAC3D,EAAQ,GAAG,UAAU,EAAQ,EAAK,CAAE,CAAE,UAAW,GAAM,CAAC,CACxD,EAAQ,GAAG,cAAc,EAAM,EAAK,SAAS,EAC7C,EAUN,eAAsB,EAAiB,EAAuB,EAAkC,CAC9F,IAAK,IAAM,KAAU,EAAK,QACxB,EAA8B,EAAS,EAAK,cAAe,EAAO,KAAK,CACvE,EAAgB,EAAS,EAAO,CAElC,IAAK,IAAM,KAAgB,EAAK,wBAA0B,EAAE,CAC1D,EAA8B,EAAS,EAAK,cAAe,EAAa,KAAK,CAC7E,EAAmB,EAAS,EAAa,CAG3C,IAAM,EAA6B,EAAkC,EAAS,EAAK,CAC7E,EAA8B,EAAE,CACtC,GAAI,CACF,IAAK,IAAM,KAAU,EAAK,QAGxB,GAFA,EAA8B,EAAS,EAAK,cAAe,EAAO,KAAK,CACvE,EAAgB,EAAS,EAAO,CAC5B,EAAO,OAAS,OAAQ,CAC1B,IAAM,EAAW,EAAa,EAAS,EAAO,KAAK,CAC/C,EAAO,SAAW,KACpB,EAAsB,EAAS,EAAO,KAAM,EAAO,MAAO,EAAO,KAAK,CAEtE,EAAY,EAAS,EAAO,KAAM,EAAO,MAAO,EAAO,KAAK,CAE9D,EAAQ,KAAK,EAAS,MAEtB,EAAQ,KAAK,CAAE,KAAM,SAAU,KAAM,EAAO,KAAM,CAAC,CACnD,EAAU,EAAS,EAAQ,EAAK,cAAc,OAG3C,EAAO,CACd,IAAM,EAA2B,EAAE,CACnC,IAAK,IAAM,KAAY,EAAQ,SAAS,CACtC,GAAI,CACF,EAAgB,EAAS,EAAS,OAC3B,EAAe,CACtB,EAAe,KAAK,GAAG,EAAS,KAAK,IAAK,EAAwB,UAAU,CAShF,MANA,EAAe,KAAK,GAAG,EAAyB,EAAS,EAA2B,CAAC,CACjF,EAAe,OAAS,EAChB,MACR,GAAI,EAAgB,QAAQ,2BAA2B,EAAe,KAAK;EAAK,GACjF,CAEG"}
|
|
1
|
+
{"version":3,"file":"project-plan-apply-Bg_1eMX9.mjs","names":[],"sources":["../src/lib/scaffolding/project-plan-apply.ts"],"sourcesContent":["import { basename, dirname, join, relative, resolve, sep } from 'node:path';\n\nimport type { LocalContext } from '../../context.js';\nimport { assertPathPhysicallyContained } from './path-safety.js';\nimport { collectProjectRelativePaths } from './project-discovery.js';\nimport type {\n PlannedDirectoryPrecondition,\n PlannedChange,\n PlannedFileChange,\n PlannedLinkChange,\n ProjectPlan,\n} from './project-plan.js';\n\n/** Original state retained for rollback. */\ntype RollbackSnapshot =\n | {\n /** Snapshot kind. */\n kind: 'absent';\n /** Absolute path. */\n path: string;\n }\n | {\n /** Snapshot kind. */\n kind: 'file';\n /** Absolute path. */\n path: string;\n /** Original UTF-8 contents. */\n contents: string;\n /** Original mode. */\n mode: number;\n }\n | {\n /** Snapshot kind. */\n kind: 'link';\n /** Absolute path. */\n path: string;\n /** Original link target. */\n target: string;\n };\n\n/**\n * Check a path without following its final symbolic link.\n *\n * @param context - CLI context\n * @param path - Candidate path\n * @returns Whether any filesystem entry exists\n */\nfunction pathExists(context: LocalContext, path: string): boolean {\n try {\n context.fs.lstatSync(path);\n return true;\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') {\n return false;\n }\n throw error;\n }\n}\n\n/**\n * Read one path for rollback.\n *\n * @param context - CLI context\n * @param path - Absolute path\n * @returns Original state\n */\nfunction snapshotPath(context: LocalContext, path: string): RollbackSnapshot {\n if (!pathExists(context, path)) {\n return { kind: 'absent', path };\n }\n const stat = context.fs.lstatSync(path);\n if (stat.isSymbolicLink()) {\n return { kind: 'link', path, target: context.fs.readlinkSync(path) };\n }\n if (!stat.isFile()) {\n throw new Error(`Cannot transactionally replace non-file path: ${path}`);\n }\n return {\n kind: 'file',\n path,\n contents: context.fs.readFileSync(path, 'utf8'),\n mode: stat.mode,\n };\n}\n\n/**\n * Write a regular file through a temporary sibling and rename.\n *\n * @param context - CLI context\n * @param path - Destination\n * @param contents - Desired contents\n * @param mode - Optional mode to preserve\n */\nfunction writeAtomic(context: LocalContext, path: string, contents: string, mode?: number): void {\n context.fs.mkdirSync(dirname(path), { recursive: true });\n const temporaryPath = join(\n dirname(path),\n `.${basename(path)}.${context.process.pid}.${Date.now()}.tmp`,\n );\n try {\n context.fs.writeFileSync(\n temporaryPath,\n contents,\n mode === undefined ? undefined : { mode: mode & 0o777 },\n );\n context.fs.renameSync(temporaryPath, path);\n } finally {\n if (pathExists(context, temporaryPath)) {\n context.fs.rmSync(temporaryPath, { force: true });\n }\n }\n}\n\n/**\n * Create a file atomically without replacing a destination that appeared.\n *\n * @param context - CLI context\n * @param path - Destination path\n * @param contents - UTF-8 contents\n * @param mode - Optional file mode\n */\nfunction writeAtomicCreateOnly(\n context: LocalContext,\n path: string,\n contents: string,\n mode?: number,\n): void {\n context.fs.mkdirSync(dirname(path), { recursive: true });\n const temporaryPath = join(\n dirname(path),\n `.${basename(path)}.${context.process.pid}.${Date.now()}.tmp`,\n );\n try {\n context.fs.writeFileSync(\n temporaryPath,\n contents,\n mode === undefined ? undefined : { mode: mode & 0o777 },\n );\n context.fs.linkSync(temporaryPath, path);\n } finally {\n if (pathExists(context, temporaryPath)) {\n context.fs.rmSync(temporaryPath, { force: true });\n }\n }\n}\n\n/**\n * Restore one path.\n *\n * @param context - CLI context\n * @param snapshot - Original state\n */\nfunction restoreSnapshot(context: LocalContext, snapshot: RollbackSnapshot): void {\n if (snapshot.kind === 'absent') {\n if (pathExists(context, snapshot.path)) {\n context.fs.rmSync(snapshot.path, { recursive: true, force: true });\n }\n return;\n }\n context.fs.mkdirSync(dirname(snapshot.path), { recursive: true });\n if (snapshot.kind === 'link') {\n if (pathExists(context, snapshot.path)) {\n context.fs.rmSync(snapshot.path, { recursive: true, force: true });\n }\n context.fs.symlinkSync(snapshot.target, snapshot.path, 'dir');\n return;\n }\n if (pathExists(context, snapshot.path) && !context.fs.lstatSync(snapshot.path).isFile()) {\n context.fs.rmSync(snapshot.path, { recursive: true, force: true });\n }\n writeAtomic(context, snapshot.path, snapshot.contents, snapshot.mode);\n}\n\n/**\n * Verify a planned regular-file precondition.\n *\n * @param context - CLI context\n * @param change - Planned file\n */\nfunction preflightFile(context: LocalContext, change: PlannedFileChange): void {\n const exists = pathExists(context, change.path);\n if (change.before === null) {\n if (exists) {\n throw new Error(`File appeared after preview: ${change.path}`);\n }\n return;\n }\n if (!exists) {\n throw new Error(`File changed after preview: ${change.path}`);\n }\n const stat = context.fs.lstatSync(change.path);\n if (\n !stat.isFile() ||\n (change.mode !== undefined && stat.mode !== change.mode) ||\n context.fs.readFileSync(change.path, 'utf8') !== change.before\n ) {\n throw new Error(`File changed after preview: ${change.path}`);\n }\n if (change.createOnly) {\n throw new Error(`Refusing to overwrite existing file: ${change.path}`);\n }\n}\n\n/**\n * Verify one planned mutation still matches preview assumptions.\n *\n * @param context - CLI context\n * @param change - Planned mutation\n */\nfunction preflightChange(context: LocalContext, change: PlannedChange): void {\n if (change.kind === 'file') {\n preflightFile(context, change);\n return;\n }\n if (pathExists(context, change.path)) {\n throw new Error(`Skill target appeared after preview: ${change.path}`);\n }\n}\n\n/**\n * Verify directory-wide contents observed during planning.\n *\n * @param context - CLI context\n * @param precondition - Expected directory snapshot\n */\nfunction preflightDirectory(\n context: LocalContext,\n precondition: PlannedDirectoryPrecondition,\n): void {\n const currentPaths = collectProjectRelativePaths(context, precondition.path);\n if (\n currentPaths.length !== precondition.relativePaths.length ||\n currentPaths.some((path, index) => path !== precondition.relativePaths[index])\n ) {\n throw new Error(`Directory changed after preview: ${precondition.path}`);\n }\n}\n\n/**\n * Record missing parent directories that this plan may create.\n *\n * @param context - CLI context\n * @param plan - Approved project plan\n * @returns Missing directories ordered deepest first\n */\nfunction collectCreatedDirectoryCandidates(context: LocalContext, plan: ProjectPlan): string[] {\n const rootDirectory = resolve(plan.rootDirectory);\n const directories = new Set<string>();\n for (const change of plan.changes) {\n let current = dirname(resolve(change.path));\n while (true) {\n const child = relative(rootDirectory, current);\n if (child === '..' || child.startsWith(`..${sep}`)) {\n break;\n }\n if (!pathExists(context, current)) {\n directories.add(current);\n }\n if (current === rootDirectory) {\n break;\n }\n current = dirname(current);\n }\n }\n return [...directories].sort((left, right) => right.split(sep).length - left.split(sep).length);\n}\n\n/**\n * Remove empty parent directories created while applying a failed plan.\n *\n * @param context - CLI context\n * @param directories - Previously absent directories\n * @returns Rollback failures\n */\nfunction removeCreatedDirectories(context: LocalContext, directories: readonly string[]): string[] {\n const errors: string[] = [];\n for (const directory of directories) {\n if (!pathExists(context, directory)) {\n continue;\n }\n try {\n context.fs.rmdirSync(directory);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') {\n errors.push(`${directory}: ${(error as Error).message}`);\n }\n }\n }\n return errors;\n}\n\n/**\n * Install a project-local skill link, copying on platforms where links fail.\n *\n * @param context - CLI context\n * @param change - Planned link\n * @param rootDirectory - Approved mutation root\n */\nfunction applyLink(context: LocalContext, change: PlannedLinkChange, rootDirectory: string): void {\n try {\n context.fs.symlinkSync(change.target, change.path, 'dir');\n } catch (error) {\n const code = (error as NodeJS.ErrnoException).code;\n if (!['EPERM', 'EACCES', 'ENOTSUP', 'UNKNOWN'].includes(code ?? '')) {\n throw error;\n }\n change.fallbackFiles.forEach((file) => {\n const path = join(change.path, file.path);\n assertPathPhysicallyContained(context, rootDirectory, path);\n context.fs.mkdirSync(dirname(path), { recursive: true });\n context.fs.writeFileSync(path, file.contents);\n });\n }\n}\n\n/**\n * Apply an approved project plan and restore source-controlled files on error.\n *\n * @param context - CLI context\n * @param plan - Approved plan\n */\nexport async function applyProjectPlan(context: LocalContext, plan: ProjectPlan): Promise<void> {\n for (const change of plan.changes) {\n assertPathPhysicallyContained(context, plan.rootDirectory, change.path);\n preflightChange(context, change);\n }\n for (const precondition of plan.directoryPreconditions ?? []) {\n assertPathPhysicallyContained(context, plan.rootDirectory, precondition.path);\n preflightDirectory(context, precondition);\n }\n\n const createdDirectoryCandidates = collectCreatedDirectoryCandidates(context, plan);\n const applied: RollbackSnapshot[] = [];\n try {\n for (const change of plan.changes) {\n assertPathPhysicallyContained(context, plan.rootDirectory, change.path);\n preflightChange(context, change);\n if (change.kind === 'file') {\n const snapshot = snapshotPath(context, change.path);\n if (change.before === null) {\n writeAtomicCreateOnly(context, change.path, change.after, change.mode);\n } else {\n writeAtomic(context, change.path, change.after, change.mode);\n }\n applied.push(snapshot);\n } else {\n applied.push({ kind: 'absent', path: change.path });\n applyLink(context, change, plan.rootDirectory);\n }\n }\n } catch (error) {\n const rollbackErrors: string[] = [];\n for (const snapshot of applied.reverse()) {\n try {\n restoreSnapshot(context, snapshot);\n } catch (rollbackError) {\n rollbackErrors.push(`${snapshot.path}: ${(rollbackError as Error).message}`);\n }\n }\n rollbackErrors.push(...removeCreatedDirectories(context, createdDirectoryCandidates));\n if (rollbackErrors.length > 0) {\n throw new Error(\n `${(error as Error).message}\\nRollback also failed:\\n${rollbackErrors.join('\\n')}`,\n );\n }\n throw error;\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import{isAbsolute as e,relative as t,sep as n}from"node:path";import r from"colors";import{createTwoFilesPatch as i}from"diff";function a(r,i){let a=t(r,i).split(n).join(`/`);return a.length===0?`.`:a===`..`||a.startsWith(`../`)||e(a)?i.split(n).join(`/`):a}function o(e){return`'${e.replace(/'/gu,`'\\''`)}'`}function s(e,t){return t.kind===`link`?{kind:`link`,target:`${a(e,t.path)} -> ${t.target}`,description:t.description}:{kind:t.before===null?`create`:`merge`,target:a(e,t.path),description:t.description}}function c(e,t){let n=Math.max(...t.details.map(({label:e})=>e.length+1),0),o=[r.bold(t.title),...t.details.map(({label:e,path:i})=>{let o=`${e}:`.padEnd(n);return` ${r.dim(o)} ${r.cyan(a(t.cwd,i))}`}),``];return e.changes.length===0?o.push(r.dim(`No changes needed.`)):(o.push(r.bold(`Changes`)),e.changes.forEach(e=>{let n=s(t.cwd,e),c={create:r.green(n.kind.padEnd(7)),merge:r.cyan(n.kind.padEnd(7)),link:r.magenta(n.kind.padEnd(7))}[n.kind];if(o.push(` ${c} ${n.target}`),e.kind===`file`&&e.before!==null){let n=a(t.cwd,e.path),r=i(n,n,e.before,e.after,``,``,{context:3});o.push(...r.trimEnd().split(`
|
|
2
|
+
`).map(e=>` ${e}`))}})),e.unchanged.length>0&&(o.push(``,r.bold(`Left unchanged`)),e.unchanged.forEach(e=>o.push(` ${r.dim(a(t.cwd,e))}`))),e.warnings.length>0&&(o.push(``,r.bold(r.yellow(`Warnings`))),e.warnings.forEach(e=>o.push(` ${r.yellow(e)}`))),`${o.join(`
|
|
3
|
+
`)}\n`}export{s as i,o as n,c as r,a as t};
|
|
4
|
+
//# sourceMappingURL=project-plan-output-BHHlkLxe.mjs.map
|